diff --git a/.devcontainer/post_create_command.sh b/.devcontainer/post_create_command.sh
index e80f9d30aadc02..d879876d8adadc 100755
--- a/.devcontainer/post_create_command.sh
+++ b/.devcontainer/post_create_command.sh
@@ -1,11 +1,12 @@
#!/bin/bash
-cd web && npm install
+npm add -g pnpm@9.12.2
+cd web && pnpm install
pipx install poetry
echo 'alias start-api="cd /workspaces/dify/api && poetry run python -m flask run --host 0.0.0.0 --port=5001 --debug"' >> ~/.bashrc
echo 'alias start-worker="cd /workspaces/dify/api && poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion"' >> ~/.bashrc
-echo 'alias start-web="cd /workspaces/dify/web && npm run dev"' >> ~/.bashrc
+echo 'alias start-web="cd /workspaces/dify/web && pnpm dev"' >> ~/.bashrc
echo 'alias start-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify up -d"' >> ~/.bashrc
echo 'alias stop-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify down"' >> ~/.bashrc
diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml
index 98075c97cd19fd..b9547b645260d0 100644
--- a/.github/workflows/api-tests.yml
+++ b/.github/workflows/api-tests.yml
@@ -26,6 +26,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Setup Poetry and Python ${{ matrix.python-version }}
uses: ./.github/actions/setup-poetry
@@ -47,15 +50,9 @@ jobs:
- name: Run Unit tests
run: poetry run -P api bash dev/pytest/pytest_unit_tests.sh
- - name: Run ModelRuntime
- run: poetry run -P api bash dev/pytest/pytest_model_runtime.sh
-
- name: Run dify config tests
run: poetry run -P api python dev/pytest/pytest_config_tests.py
- - name: Run Tool
- run: poetry run -P api bash dev/pytest/pytest_tools.sh
-
- name: Run mypy
run: |
poetry run -C api python -m mypy --install-types --non-interactive .
diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml
index 8e5279fb67659b..49ab983778253d 100644
--- a/.github/workflows/build-push.yml
+++ b/.github/workflows/build-push.yml
@@ -79,10 +79,12 @@ jobs:
cache-to: type=gha,mode=max,scope=${{ matrix.service_name }}
- name: Export digest
+ env:
+ DIGEST: ${{ steps.build.outputs.digest }}
run: |
mkdir -p /tmp/digests
- digest="${{ steps.build.outputs.digest }}"
- touch "/tmp/digests/${digest#sha256:}"
+ sanitized_digest=${DIGEST#sha256:}
+ touch "/tmp/digests/${sanitized_digest}"
- name: Upload digest
uses: actions/upload-artifact@v4
@@ -132,10 +134,15 @@ jobs:
- name: Create manifest list and push
working-directory: /tmp/digests
+ env:
+ IMAGE_NAME: ${{ env[matrix.image_name_env] }}
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
- $(printf '${{ env[matrix.image_name_env] }}@sha256:%s ' *)
+ $(printf "$IMAGE_NAME@sha256:%s " *)
- name: Inspect image
+ env:
+ IMAGE_NAME: ${{ env[matrix.image_name_env] }}
+ IMAGE_VERSION: ${{ steps.meta.outputs.version }}
run: |
- docker buildx imagetools inspect ${{ env[matrix.image_name_env] }}:${{ steps.meta.outputs.version }}
+ docker buildx imagetools inspect "$IMAGE_NAME:$IMAGE_VERSION"
diff --git a/.github/workflows/db-migration-test.yml b/.github/workflows/db-migration-test.yml
index 3d881c4c3dd7ca..69bff839a6f1a7 100644
--- a/.github/workflows/db-migration-test.yml
+++ b/.github/workflows/db-migration-test.yml
@@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
+ - plugins/beta
paths:
- api/migrations/**
- .github/workflows/db-migration-test.yml
@@ -19,6 +20,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Setup Poetry and Python
uses: ./.github/actions/setup-poetry
diff --git a/.github/workflows/expose_service_ports.sh b/.github/workflows/expose_service_ports.sh
index d3146cd90dc02b..16f24439e6e826 100755
--- a/.github/workflows/expose_service_ports.sh
+++ b/.github/workflows/expose_service_ports.sh
@@ -9,6 +9,6 @@ yq eval '.services["pgvecto-rs"].ports += ["5431:5432"]' -i docker/docker-compos
yq eval '.services["elasticsearch"].ports += ["9200:9200"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["8091-8096:8091-8096"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["11210:11210"]' -i docker/docker-compose.yaml
-yq eval '.services.tidb.ports += ["4000:4000"]' -i docker/docker-compose.yaml
+yq eval '.services.tidb.ports += ["4000:4000"]' -i docker/tidb/docker-compose.yaml
echo "Ports exposed for sandbox, weaviate, tidb, qdrant, chroma, milvus, pgvector, pgvecto-rs, elasticsearch, couchbase"
diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml
index c80037195a4c97..d73a782c935f80 100644
--- a/.github/workflows/style.yml
+++ b/.github/workflows/style.yml
@@ -17,6 +17,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Check changed files
id: changed-files
@@ -59,6 +62,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Check changed files
id: changed-files
@@ -66,21 +72,27 @@ jobs:
with:
files: web/**
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10
+ run_install: false
+
- name: Setup NodeJS
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
with:
node-version: 20
- cache: yarn
+ cache: pnpm
cache-dependency-path: ./web/package.json
- name: Web dependencies
if: steps.changed-files.outputs.any_changed == 'true'
- run: yarn install --frozen-lockfile
+ run: pnpm install --frozen-lockfile
- name: Web style check
if: steps.changed-files.outputs.any_changed == 'true'
- run: yarn run lint
+ run: pnpm run lint
docker-compose-template:
name: Docker Compose Template
@@ -89,6 +101,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Check changed files
id: changed-files
@@ -117,6 +132,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Check changed files
id: changed-files
diff --git a/.github/workflows/tool-test-sdks.yaml b/.github/workflows/tool-test-sdks.yaml
index fb4bcb9d66fa08..93edb2737a7d3a 100644
--- a/.github/workflows/tool-test-sdks.yaml
+++ b/.github/workflows/tool-test-sdks.yaml
@@ -26,16 +26,19 @@ jobs:
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: ''
- cache-dependency-path: 'yarn.lock'
+ cache-dependency-path: 'pnpm-lock.yaml'
- name: Install Dependencies
- run: yarn install
+ run: pnpm install --frozen-lockfile
- name: Test
- run: yarn test
+ run: pnpm test
diff --git a/.github/workflows/translate-i18n-base-on-english.yml b/.github/workflows/translate-i18n-base-on-english.yml
index 3f51b3b2c79946..80b78a13119bcf 100644
--- a/.github/workflows/translate-i18n-base-on-english.yml
+++ b/.github/workflows/translate-i18n-base-on-english.yml
@@ -16,6 +16,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # last 2 commits
+ persist-credentials: false
- name: Check for file changes in i18n/en-US
id: check_files
@@ -38,11 +39,11 @@ jobs:
- name: Install dependencies
if: env.FILES_CHANGED == 'true'
- run: yarn install --frozen-lockfile
+ run: pnpm install --frozen-lockfile
- name: Run npm script
if: env.FILES_CHANGED == 'true'
- run: npm run auto-gen-i18n
+ run: pnpm run auto-gen-i18n
- name: Create Pull Request
if: env.FILES_CHANGED == 'true'
diff --git a/.github/workflows/vdb-tests.yml b/.github/workflows/vdb-tests.yml
index fab0b8c426a94e..5e3f7a557aa6db 100644
--- a/.github/workflows/vdb-tests.yml
+++ b/.github/workflows/vdb-tests.yml
@@ -28,6 +28,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Setup Poetry and Python ${{ matrix.python-version }}
uses: ./.github/actions/setup-poetry
@@ -51,7 +54,15 @@ jobs:
- name: Expose Service Ports
run: sh .github/workflows/expose_service_ports.sh
- - name: Set up Vector Stores (TiDB, Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
+ - name: Set up Vector Store (TiDB)
+ uses: hoverkraft-tech/compose-action@v2.0.2
+ with:
+ compose-file: docker/tidb/docker-compose.yaml
+ services: |
+ tidb
+ tiflash
+
+ - name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
uses: hoverkraft-tech/compose-action@v2.0.2
with:
compose-file: |
@@ -67,7 +78,9 @@ jobs:
pgvector
chroma
elasticsearch
- tidb
+
+ - name: Check TiDB Ready
+ run: poetry run -P api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
- name: Test Vector Stores
run: poetry run -P api bash dev/pytest/pytest_vdb.sh
diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml
index 5aee64b8e6da02..e32db548a4b164 100644
--- a/.github/workflows/web-tests.yml
+++ b/.github/workflows/web-tests.yml
@@ -22,25 +22,34 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ persist-credentials: false
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@v45
with:
files: web/**
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- if: steps.changed-files.outputs.any_changed == 'true'
- with:
- node-version: 20
- cache: yarn
- cache-dependency-path: ./web/package.json
-
- - name: Install dependencies
- if: steps.changed-files.outputs.any_changed == 'true'
- run: yarn install --frozen-lockfile
-
- - name: Run tests
- if: steps.changed-files.outputs.any_changed == 'true'
- run: yarn test
+ # to run pnpm, should install package canvas, but it always install failed on amd64 under ubuntu-latest
+ # - name: Install pnpm
+ # uses: pnpm/action-setup@v4
+ # with:
+ # version: 10
+ # run_install: false
+
+ # - name: Setup Node.js
+ # uses: actions/setup-node@v4
+ # if: steps.changed-files.outputs.any_changed == 'true'
+ # with:
+ # node-version: 20
+ # cache: pnpm
+ # cache-dependency-path: ./web/package.json
+
+ # - name: Install dependencies
+ # if: steps.changed-files.outputs.any_changed == 'true'
+ # run: pnpm install --frozen-lockfile
+
+ # - name: Run tests
+ # if: steps.changed-files.outputs.any_changed == 'true'
+ # run: pnpm test
diff --git a/.gitignore b/.gitignore
index 1423bfee56e922..296aeee873406a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -163,6 +163,7 @@ docker/volumes/db/data/*
docker/volumes/redis/data/*
docker/volumes/weaviate/*
docker/volumes/qdrant/*
+docker/tidb/volumes/*
docker/volumes/etcd/*
docker/volumes/minio/*
docker/volumes/milvus/*
@@ -175,6 +176,7 @@ docker/volumes/pgvector/data/*
docker/volumes/pgvecto_rs/data/*
docker/volumes/couchbase/*
docker/volumes/oceanbase/*
+docker/volumes/plugin_daemon/*
!docker/volumes/oceanbase/init.d
docker/nginx/conf.d/default.conf
@@ -193,3 +195,9 @@ api/.vscode
.idea/
.vscode
+
+# pnpm
+/.pnpm-store
+
+# plugin migrate
+plugins.jsonl
diff --git a/README.md b/README.md
index 3920ff107ca439..2378cffe9be88b 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,72 @@ Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-host
**7. Backend-as-a-Service**:
All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic.
+## Feature Comparison
+
+
+ Feature
+ Dify.AI
+ LangChain
+ Flowise
+ OpenAI Assistants API
+
+
+ Programming Approach
+ API + App-oriented
+ Python Code
+ App-oriented
+ API-oriented
+
+
+ Supported LLMs
+ Rich Variety
+ Rich Variety
+ Rich Variety
+ OpenAI-only
+
+
+ RAG Engine
+ ✅
+ ✅
+ ✅
+ ✅
+
+
+ Agent
+ ✅
+ ✅
+ ❌
+ ✅
+
+
+ Workflow
+ ✅
+ ❌
+ ✅
+ ❌
+
+
+ Observability
+ ✅
+ ✅
+ ❌
+ ❌
+
+
+ Enterprise Feature (SSO/Access control)
+ ✅
+ ❌
+ ❌
+ ❌
+
+
+ Local Deployment
+ ✅
+ ✅
+ ✅
+ ❌
+
+
## Using Dify
diff --git a/README_FR.md b/README_FR.md
index 3890c2a996f8dc..afbf18b069b209 100644
--- a/README_FR.md
+++ b/README_FR.md
@@ -55,7 +55,7 @@
Dify est une plateforme de développement d'applications LLM open source. Son interface intuitive combine un flux de travail d'IA, un pipeline RAG, des capacités d'agent, une gestion de modèles, des fonctionnalités d'observabilité, et plus encore, vous permettant de passer rapidement du prototype à la production. Voici une liste des fonctionnalités principales:
-**1. Flux de travail**:
+**1. Flux de travail** :
Construisez et testez des flux de travail d'IA puissants sur un canevas visuel, en utilisant toutes les fonctionnalités suivantes et plus encore.
@@ -63,27 +63,25 @@ Dify est une plateforme de développement d'applications LLM open source. Son in
-**2. Prise en charge complète des modèles**:
+**2. Prise en charge complète des modèles** :
Intégration transparente avec des centaines de LLM propriétaires / open source provenant de dizaines de fournisseurs d'inférence et de solutions auto-hébergées, couvrant GPT, Mistral, Llama3, et tous les modèles compatibles avec l'API OpenAI. Une liste complète des fournisseurs de modèles pris en charge se trouve [ici](https://docs.dify.ai/getting-started/readme/model-providers).

-**3. IDE de prompt**:
+**3. IDE de prompt** :
Interface intuitive pour créer des prompts, comparer les performances des modèles et ajouter des fonctionnalités supplémentaires telles que la synthèse vocale à une application basée sur des chats.
-**4. Pipeline RAG**:
+**4. Pipeline RAG** :
Des capacités RAG étendues qui couvrent tout, de l'ingestion de documents à la récupération, avec un support prêt à l'emploi pour l'extraction de texte à partir de PDF, PPT et autres formats de document courants.
-**5. Capac
-
-ités d'agent**:
+**5. Capacités d'agent** :
Vous pouvez définir des agents basés sur l'appel de fonction LLM ou ReAct, et ajouter des outils pré-construits ou personnalisés pour l'agent. Dify fournit plus de 50 outils intégrés pour les agents d'IA, tels que la recherche Google, DALL·E, Stable Diffusion et WolframAlpha.
-**6. LLMOps**:
+**6. LLMOps** :
Surveillez et analysez les journaux d'application et les performances au fil du temps. Vous pouvez continuellement améliorer les prompts, les ensembles de données et les modèles en fonction des données de production et des annotations.
-**7. Backend-as-a-Service**:
+**7. Backend-as-a-Service** :
Toutes les offres de Dify sont accompagnées d'API correspondantes, vous permettant d'intégrer facilement Dify dans votre propre logique métier.
diff --git a/README_JA.md b/README_JA.md
index c4266c66f8f24b..e7c99fba2c4b25 100644
--- a/README_JA.md
+++ b/README_JA.md
@@ -164,7 +164,7 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ
- **企業/組織向けのDify**
企業中心の機能を提供しています。[メールを送信](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)して企業のニーズについて相談してください。
- > AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングどして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。
+ > AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t23mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングとして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。
## 最新の情報を入手
diff --git a/README_KL.md b/README_KL.md
index 852382284ba2b8..c8cd22810e83f6 100644
--- a/README_KL.md
+++ b/README_KL.md
@@ -87,9 +87,7 @@ Dify is an open-source LLM app development platform. Its intuitive interface com
## Feature Comparison
-
+
Feature
Dify.AI
LangChain
diff --git a/README_SI.md b/README_SI.md
index 8e4f0fa6a67513..29e2ad4fb50328 100644
--- a/README_SI.md
+++ b/README_SI.md
@@ -106,6 +106,73 @@ Prosimo, glejte naša pogosta vprašanja [FAQ](https://docs.dify.ai/getting-star
**7. Backend-as-a-Service**:
AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez težav integrirate v svojo poslovno logiko.
+## Primerjava Funkcij
+
+
+
+ Funkcija
+ Dify.AI
+ LangChain
+ Flowise
+ OpenAI Assistants API
+
+
+ Programski pristop
+ API + usmerjeno v aplikacije
+ Python koda
+ Usmerjeno v aplikacije
+ Usmerjeno v API
+
+
+ Podprti LLM-ji
+ Bogata izbira
+ Bogata izbira
+ Bogata izbira
+ Samo OpenAI
+
+
+ RAG pogon
+ ✅
+ ✅
+ ✅
+ ✅
+
+
+ Agent
+ ✅
+ ✅
+ ❌
+ ✅
+
+
+ Potek dela
+ ✅
+ ❌
+ ✅
+ ❌
+
+
+ Spremljanje
+ ✅
+ ✅
+ ❌
+ ❌
+
+
+ Funkcija za podjetja (SSO/nadzor dostopa)
+ ✅
+ ❌
+ ❌
+ ❌
+
+
+ Lokalna namestitev
+ ✅
+ ✅
+ ✅
+ ❌
+
+
## Uporaba Dify
@@ -187,4 +254,4 @@ Zaradi zaščite vaše zasebnosti se izogibajte objavljanju varnostnih vprašanj
## Licenca
-To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
\ No newline at end of file
+To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
diff --git a/api/.dockerignore b/api/.dockerignore
index 91a5254ea7e151..447edcda08c093 100644
--- a/api/.dockerignore
+++ b/api/.dockerignore
@@ -1,7 +1,10 @@
.env
*.env.*
+storage/generate_files/*
storage/privkeys/*
+storage/tools/*
+storage/upload_files/*
# Logs
logs
@@ -9,6 +12,8 @@ logs
# jetbrains
.idea
+.mypy_cache
+.ruff_cache
# venv
.venv
\ No newline at end of file
diff --git a/api/.env.example b/api/.env.example
index 95da531a1d68d5..39eb7ad766eaf3 100644
--- a/api/.env.example
+++ b/api/.env.example
@@ -409,7 +409,6 @@ MAX_VARIABLE_SIZE=204800
APP_MAX_EXECUTION_TIME=1200
APP_MAX_ACTIVE_REQUESTS=0
-
# Celery beat configuration
CELERY_BEAT_SCHEDULER_TIME=1
@@ -422,6 +421,22 @@ POSITION_PROVIDER_PINS=
POSITION_PROVIDER_INCLUDES=
POSITION_PROVIDER_EXCLUDES=
+# Plugin configuration
+PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
+PLUGIN_DAEMON_URL=http://127.0.0.1:5002
+PLUGIN_REMOTE_INSTALL_PORT=5003
+PLUGIN_REMOTE_INSTALL_HOST=localhost
+PLUGIN_MAX_PACKAGE_SIZE=15728640
+INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
+INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
+
+# Marketplace configuration
+MARKETPLACE_ENABLED=true
+MARKETPLACE_API_URL=https://marketplace.dify.ai
+
+# Endpoint configuration
+ENDPOINT_URL_TEMPLATE=http://localhost:5002/e/{hook_id}
+
# Reset password token expiry minutes
RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5
diff --git a/api/Dockerfile b/api/Dockerfile
index 8207c66f83795b..0d4679b76accef 100644
--- a/api/Dockerfile
+++ b/api/Dockerfile
@@ -58,6 +58,8 @@ RUN \
expat libldap-2.5-0 perl libsqlite3-0 zlib1g \
# install a chinese font to support the use of tools like matplotlib
fonts-noto-cjk \
+ # install a package to improve the accuracy of guessing mime type and file extension
+ media-types \
# install libmagic to support the use of python-magic guess MIMETYPE
libmagic1 \
&& apt-get autoremove -y \
@@ -71,6 +73,10 @@ ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
# Download nltk data
RUN python -c "import nltk; nltk.download('punkt'); nltk.download('averaged_perceptron_tagger')"
+ENV TIKTOKEN_CACHE_DIR=/app/api/.tiktoken_cache
+
+RUN python -c "import tiktoken; tiktoken.encoding_for_model('gpt2')"
+
# Copy source code
COPY . /app/api/
diff --git a/api/README.md b/api/README.md
index 6e9f2e8fbb2d57..c3abc25be16095 100644
--- a/api/README.md
+++ b/api/README.md
@@ -37,7 +37,13 @@
4. Create environment.
- Dify API service uses [Poetry](https://python-poetry.org/docs/) to manage dependencies. You can execute `poetry shell` to activate the environment.
+ Dify API service uses [Poetry](https://python-poetry.org/docs/) to manage dependencies. First, you need to add the poetry shell plugin, if you don't have it already, in order to run in a virtual environment. [Note: Poetry shell is no longer a native command so you need to install the poetry plugin beforehand]
+
+ ```bash
+ poetry self add poetry-plugin-shell
+ ```
+
+ Then, You can execute `poetry shell` to activate the environment.
5. Install dependencies
diff --git a/api/app_factory.py b/api/app_factory.py
index c0714116a3e692..52ae05583a09b0 100644
--- a/api/app_factory.py
+++ b/api/app_factory.py
@@ -2,6 +2,7 @@
import time
from configs import dify_config
+from contexts.wrapper import RecyclableContextVar
from dify_app import DifyApp
@@ -16,6 +17,12 @@ def create_flask_app_with_configs() -> DifyApp:
dify_app = DifyApp(__name__)
dify_app.config.from_mapping(dify_config.model_dump())
+ # add before request hook
+ @dify_app.before_request
+ def before_request():
+ # add an unique identifier to each request
+ RecyclableContextVar.increment_thread_recycles()
+
return dify_app
diff --git a/api/commands.py b/api/commands.py
index 334e7daab57997..27c0dadd162c81 100644
--- a/api/commands.py
+++ b/api/commands.py
@@ -25,6 +25,8 @@
from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation
from models.provider import Provider, ProviderModel
from services.account_service import RegisterService, TenantService
+from services.plugin.data_migration import PluginDataMigration
+from services.plugin.plugin_migration import PluginMigration
@click.command("reset-password", help="Reset the account password.")
@@ -524,7 +526,7 @@ def add_qdrant_doc_id_index(field: str):
)
)
- except Exception as e:
+ except Exception:
click.echo(click.style("Failed to create Qdrant client.", fg="red"))
click.echo(click.style(f"Index creation complete. Created {create_count} collection indexes.", fg="green"))
@@ -593,7 +595,7 @@ def upgrade_db():
click.echo(click.style("Database migration successful!", fg="green"))
- except Exception as e:
+ except Exception:
logging.exception("Failed to execute database migration")
finally:
lock.release()
@@ -639,7 +641,7 @@ def fix_app_site_missing():
account = accounts[0]
print("Fixing missing site for app {}".format(app.id))
app_was_created.send(app, account=account)
- except Exception as e:
+ except Exception:
failed_app_ids.append(app_id)
click.echo(click.style("Failed to fix missing site for app {}".format(app_id), fg="red"))
logging.exception(f"Failed to fix app related site missing issue, app_id: {app_id}")
@@ -649,3 +651,69 @@ def fix_app_site_missing():
break
click.echo(click.style("Fix for missing app-related sites completed successfully!", fg="green"))
+
+
+@click.command("migrate-data-for-plugin", help="Migrate data for plugin.")
+def migrate_data_for_plugin():
+ """
+ Migrate data for plugin.
+ """
+ click.echo(click.style("Starting migrate data for plugin.", fg="white"))
+
+ PluginDataMigration.migrate()
+
+ click.echo(click.style("Migrate data for plugin completed.", fg="green"))
+
+
+@click.command("extract-plugins", help="Extract plugins.")
+@click.option("--output_file", prompt=True, help="The file to store the extracted plugins.", default="plugins.jsonl")
+@click.option("--workers", prompt=True, help="The number of workers to extract plugins.", default=10)
+def extract_plugins(output_file: str, workers: int):
+ """
+ Extract plugins.
+ """
+ click.echo(click.style("Starting extract plugins.", fg="white"))
+
+ PluginMigration.extract_plugins(output_file, workers)
+
+ click.echo(click.style("Extract plugins completed.", fg="green"))
+
+
+@click.command("extract-unique-identifiers", help="Extract unique identifiers.")
+@click.option(
+ "--output_file",
+ prompt=True,
+ help="The file to store the extracted unique identifiers.",
+ default="unique_identifiers.json",
+)
+@click.option(
+ "--input_file", prompt=True, help="The file to store the extracted unique identifiers.", default="plugins.jsonl"
+)
+def extract_unique_plugins(output_file: str, input_file: str):
+ """
+ Extract unique plugins.
+ """
+ click.echo(click.style("Starting extract unique plugins.", fg="white"))
+
+ PluginMigration.extract_unique_plugins_to_file(input_file, output_file)
+
+ click.echo(click.style("Extract unique plugins completed.", fg="green"))
+
+
+@click.command("install-plugins", help="Install plugins.")
+@click.option(
+ "--input_file", prompt=True, help="The file to store the extracted unique identifiers.", default="plugins.jsonl"
+)
+@click.option(
+ "--output_file", prompt=True, help="The file to store the installed plugins.", default="installed_plugins.jsonl"
+)
+@click.option("--workers", prompt=True, help="The number of workers to install plugins.", default=100)
+def install_plugins(input_file: str, output_file: str, workers: int):
+ """
+ Install plugins.
+ """
+ click.echo(click.style("Starting install plugins.", fg="white"))
+
+ PluginMigration.install_plugins(input_file, output_file, workers)
+
+ click.echo(click.style("Install plugins completed.", fg="green"))
diff --git a/api/configs/feature/__init__.py b/api/configs/feature/__init__.py
index ba3542baf34c65..2af9729073c58f 100644
--- a/api/configs/feature/__init__.py
+++ b/api/configs/feature/__init__.py
@@ -134,6 +134,60 @@ class CodeExecutionSandboxConfig(BaseSettings):
)
+class PluginConfig(BaseSettings):
+ """
+ Plugin configs
+ """
+
+ PLUGIN_DAEMON_URL: HttpUrl = Field(
+ description="Plugin API URL",
+ default="http://localhost:5002",
+ )
+
+ PLUGIN_DAEMON_KEY: str = Field(
+ description="Plugin API key",
+ default="plugin-api-key",
+ )
+
+ INNER_API_KEY_FOR_PLUGIN: str = Field(description="Inner api key for plugin", default="inner-api-key")
+
+ PLUGIN_REMOTE_INSTALL_HOST: str = Field(
+ description="Plugin Remote Install Host",
+ default="localhost",
+ )
+
+ PLUGIN_REMOTE_INSTALL_PORT: PositiveInt = Field(
+ description="Plugin Remote Install Port",
+ default=5003,
+ )
+
+ PLUGIN_MAX_PACKAGE_SIZE: PositiveInt = Field(
+ description="Maximum allowed size for plugin packages in bytes",
+ default=15728640,
+ )
+
+ PLUGIN_MAX_BUNDLE_SIZE: PositiveInt = Field(
+ description="Maximum allowed size for plugin bundles in bytes",
+ default=15728640 * 12,
+ )
+
+
+class MarketplaceConfig(BaseSettings):
+ """
+ Configuration for marketplace
+ """
+
+ MARKETPLACE_ENABLED: bool = Field(
+ description="Enable or disable marketplace",
+ default=True,
+ )
+
+ MARKETPLACE_API_URL: HttpUrl = Field(
+ description="Marketplace API URL",
+ default="https://marketplace.dify.ai",
+ )
+
+
class EndpointConfig(BaseSettings):
"""
Configuration for various application endpoints and URLs
@@ -160,6 +214,10 @@ class EndpointConfig(BaseSettings):
default="",
)
+ ENDPOINT_URL_TEMPLATE: str = Field(
+ description="Template url for endpoint plugin", default="http://localhost:5002/e/{hook_id}"
+ )
+
class FileAccessConfig(BaseSettings):
"""
@@ -315,8 +373,8 @@ def WEB_API_CORS_ALLOW_ORIGINS(self) -> list[str]:
)
RESPECT_XFORWARD_HEADERS_ENABLED: bool = Field(
- description="Enable or disable the X-Forwarded-For Proxy Fix middleware from Werkzeug"
- " to respect X-* headers to redirect clients",
+ description="Enable handling of X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Port headers"
+ " when the app is behind a single trusted reverse proxy.",
default=False,
)
@@ -793,6 +851,8 @@ class FeatureConfig(
AuthConfig, # Changed from OAuthConfig to AuthConfig
BillingConfig,
CodeExecutionSandboxConfig,
+ PluginConfig,
+ MarketplaceConfig,
DataSetConfig,
EndpointConfig,
FileAccessConfig,
diff --git a/api/configs/middleware/__init__.py b/api/configs/middleware/__init__.py
index f6a44eaa471e62..2aa06c5aac4987 100644
--- a/api/configs/middleware/__init__.py
+++ b/api/configs/middleware/__init__.py
@@ -1,3 +1,4 @@
+import os
from typing import Any, Literal, Optional
from urllib.parse import quote_plus
@@ -166,6 +167,11 @@ def SQLALCHEMY_DATABASE_URI(self) -> str:
default=False,
)
+ RETRIEVAL_SERVICE_EXECUTORS: NonNegativeInt = Field(
+ description="Number of processes for the retrieval service, default to CPU cores.",
+ default=os.cpu_count(),
+ )
+
@computed_field
def SQLALCHEMY_ENGINE_OPTIONS(self) -> dict[str, Any]:
return {
diff --git a/api/configs/packaging/__init__.py b/api/configs/packaging/__init__.py
index fd3d6afb3a6feb..8473f7bad5f414 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.15.3",
+ default="1.0.0",
)
COMMIT_SHA: str = Field(
diff --git a/api/constants/__init__.py b/api/constants/__init__.py
index 4500ef4306fc2a..b5dfd9cb1836f5 100644
--- a/api/constants/__init__.py
+++ b/api/constants/__init__.py
@@ -15,7 +15,7 @@
if dify_config.ETL_TYPE == "Unstructured":
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "mdx", "pdf", "html", "htm", "xlsx", "xls"]
- DOCUMENT_EXTENSIONS.extend(("docx", "csv", "eml", "msg", "pptx", "xml", "epub"))
+ DOCUMENT_EXTENSIONS.extend(("doc", "docx", "csv", "eml", "msg", "pptx", "xml", "epub"))
if dify_config.UNSTRUCTURED_API_URL:
DOCUMENT_EXTENSIONS.append("ppt")
DOCUMENT_EXTENSIONS.extend([ext.upper() for ext in DOCUMENT_EXTENSIONS])
diff --git a/api/contexts/__init__.py b/api/contexts/__init__.py
index 85380b73304043..91438d086a6b21 100644
--- a/api/contexts/__init__.py
+++ b/api/contexts/__init__.py
@@ -1,9 +1,30 @@
from contextvars import ContextVar
+from threading import Lock
from typing import TYPE_CHECKING
+from contexts.wrapper import RecyclableContextVar
+
if TYPE_CHECKING:
+ from core.plugin.entities.plugin_daemon import PluginModelProviderEntity
+ from core.tools.plugin_tool.provider import PluginToolProviderController
from core.workflow.entities.variable_pool import VariablePool
+
tenant_id: ContextVar[str] = ContextVar("tenant_id")
workflow_variable_pool: ContextVar["VariablePool"] = ContextVar("workflow_variable_pool")
+
+"""
+To avoid race-conditions caused by gunicorn thread recycling, using RecyclableContextVar to replace with
+"""
+plugin_tool_providers: RecyclableContextVar[dict[str, "PluginToolProviderController"]] = RecyclableContextVar(
+ ContextVar("plugin_tool_providers")
+)
+plugin_tool_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar(ContextVar("plugin_tool_providers_lock"))
+
+plugin_model_providers: RecyclableContextVar[list["PluginModelProviderEntity"] | None] = RecyclableContextVar(
+ ContextVar("plugin_model_providers")
+)
+plugin_model_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar(
+ ContextVar("plugin_model_providers_lock")
+)
diff --git a/api/contexts/wrapper.py b/api/contexts/wrapper.py
new file mode 100644
index 00000000000000..8cd53487ef260a
--- /dev/null
+++ b/api/contexts/wrapper.py
@@ -0,0 +1,65 @@
+from contextvars import ContextVar
+from typing import Generic, TypeVar
+
+T = TypeVar("T")
+
+
+class HiddenValue:
+ pass
+
+
+_default = HiddenValue()
+
+
+class RecyclableContextVar(Generic[T]):
+ """
+ RecyclableContextVar is a wrapper around ContextVar
+ It's safe to use in gunicorn with thread recycling, but features like `reset` are not available for now
+
+ NOTE: you need to call `increment_thread_recycles` before requests
+ """
+
+ _thread_recycles: ContextVar[int] = ContextVar("thread_recycles")
+
+ @classmethod
+ def increment_thread_recycles(cls):
+ try:
+ recycles = cls._thread_recycles.get()
+ cls._thread_recycles.set(recycles + 1)
+ except LookupError:
+ cls._thread_recycles.set(0)
+
+ def __init__(self, context_var: ContextVar[T]):
+ self._context_var = context_var
+ self._updates = ContextVar[int](context_var.name + "_updates", default=0)
+
+ def get(self, default: T | HiddenValue = _default) -> T:
+ thread_recycles = self._thread_recycles.get(0)
+ self_updates = self._updates.get()
+ if thread_recycles > self_updates:
+ self._updates.set(thread_recycles)
+
+ # check if thread is recycled and should be updated
+ if thread_recycles < self_updates:
+ return self._context_var.get()
+ else:
+ # thread_recycles >= self_updates, means current context is invalid
+ if isinstance(default, HiddenValue) or default is _default:
+ raise LookupError
+ else:
+ return default
+
+ def set(self, value: T):
+ # it leads to a situation that self.updates is less than cls.thread_recycles if `set` was never called before
+ # increase it manually
+ thread_recycles = self._thread_recycles.get(0)
+ self_updates = self._updates.get()
+ if thread_recycles > self_updates:
+ self._updates.set(thread_recycles)
+
+ if self._updates.get() == self._thread_recycles.get(0):
+ # after increment,
+ self._updates.set(self._updates.get() + 1)
+
+ # set the context
+ self._context_var.set(value)
diff --git a/api/controllers/console/__init__.py b/api/controllers/console/__init__.py
index cb6b0d097b1fc9..8b5378c132f55b 100644
--- a/api/controllers/console/__init__.py
+++ b/api/controllers/console/__init__.py
@@ -2,7 +2,7 @@
from libs.external_api import ExternalApi
-from .app.app_import import AppImportApi, AppImportConfirmApi
+from .app.app_import import AppImportApi, AppImportCheckDependenciesApi, AppImportConfirmApi
from .explore.audio import ChatAudioApi, ChatTextApi
from .explore.completion import ChatApi, ChatStopApi, CompletionApi, CompletionStopApi
from .explore.conversation import (
@@ -40,6 +40,7 @@
# Import App
api.add_resource(AppImportApi, "/apps/imports")
api.add_resource(AppImportConfirmApi, "/apps/imports//confirm")
+api.add_resource(AppImportCheckDependenciesApi, "/apps/imports//check-dependencies")
# Import other controllers
from . import admin, apikey, extension, feature, ping, setup, version
@@ -166,4 +167,15 @@
from .tag import tags
# Import workspace controllers
-from .workspace import account, load_balancing_config, members, model_providers, models, tool_providers, workspace
+from .workspace import (
+ account,
+ agent_providers,
+ endpoint,
+ load_balancing_config,
+ members,
+ model_providers,
+ models,
+ plugin,
+ tool_providers,
+ workspace,
+)
diff --git a/api/controllers/console/admin.py b/api/controllers/console/admin.py
index 1286188f7f4d28..6e3273f5d4b610 100644
--- a/api/controllers/console/admin.py
+++ b/api/controllers/console/admin.py
@@ -2,6 +2,8 @@
from flask import request
from flask_restful import Resource, reqparse # type: ignore
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound, Unauthorized
from configs import dify_config
@@ -54,7 +56,8 @@ def post(self):
parser.add_argument("position", type=int, required=True, nullable=False, location="json")
args = parser.parse_args()
- app = App.query.filter(App.id == args["app_id"]).first()
+ with Session(db.engine) as session:
+ app = session.execute(select(App).filter(App.id == args["app_id"])).scalar_one_or_none()
if not app:
raise NotFound(f"App '{args['app_id']}' is not found")
@@ -70,7 +73,10 @@ def post(self):
privacy_policy = site.privacy_policy or args["privacy_policy"] or ""
custom_disclaimer = site.custom_disclaimer or args["custom_disclaimer"] or ""
- recommended_app = RecommendedApp.query.filter(RecommendedApp.app_id == args["app_id"]).first()
+ with Session(db.engine) as session:
+ recommended_app = session.execute(
+ select(RecommendedApp).filter(RecommendedApp.app_id == args["app_id"])
+ ).scalar_one_or_none()
if not recommended_app:
recommended_app = RecommendedApp(
@@ -110,17 +116,27 @@ class InsertExploreAppApi(Resource):
@only_edition_cloud
@admin_required
def delete(self, app_id):
- recommended_app = RecommendedApp.query.filter(RecommendedApp.app_id == str(app_id)).first()
+ with Session(db.engine) as session:
+ recommended_app = session.execute(
+ select(RecommendedApp).filter(RecommendedApp.app_id == str(app_id))
+ ).scalar_one_or_none()
+
if not recommended_app:
return {"result": "success"}, 204
- app = App.query.filter(App.id == recommended_app.app_id).first()
+ with Session(db.engine) as session:
+ app = session.execute(select(App).filter(App.id == recommended_app.app_id)).scalar_one_or_none()
+
if app:
app.is_public = False
- installed_apps = InstalledApp.query.filter(
- InstalledApp.app_id == recommended_app.app_id, InstalledApp.tenant_id != InstalledApp.app_owner_tenant_id
- ).all()
+ with Session(db.engine) as session:
+ installed_apps = session.execute(
+ select(InstalledApp).filter(
+ InstalledApp.app_id == recommended_app.app_id,
+ InstalledApp.tenant_id != InstalledApp.app_owner_tenant_id,
+ )
+ ).all()
for installed_app in installed_apps:
db.session.delete(installed_app)
diff --git a/api/controllers/console/apikey.py b/api/controllers/console/apikey.py
index ca8ddc32094ac5..eb42507c633b88 100644
--- a/api/controllers/console/apikey.py
+++ b/api/controllers/console/apikey.py
@@ -3,6 +3,8 @@
import flask_restful # type: ignore
from flask_login import current_user # type: ignore
from flask_restful import Resource, fields, marshal_with
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden
from extensions.ext_database import db
@@ -26,7 +28,16 @@
def _get_resource(resource_id, tenant_id, resource_model):
- resource = resource_model.query.filter_by(id=resource_id, tenant_id=tenant_id).first()
+ if resource_model == App:
+ with Session(db.engine) as session:
+ resource = session.execute(
+ select(resource_model).filter_by(id=resource_id, tenant_id=tenant_id)
+ ).scalar_one_or_none()
+ else:
+ with Session(db.engine) as session:
+ resource = session.execute(
+ select(resource_model).filter_by(id=resource_id, tenant_id=tenant_id)
+ ).scalar_one_or_none()
if resource is None:
flask_restful.abort(404, message=f"{resource_model.__name__} not found.")
diff --git a/api/controllers/console/app/app_import.py b/api/controllers/console/app/app_import.py
index 7e2888d71c79c8..47acb47a2c239e 100644
--- a/api/controllers/console/app/app_import.py
+++ b/api/controllers/console/app/app_import.py
@@ -5,14 +5,16 @@
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden
+from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import (
account_initialization_required,
setup_required,
)
from extensions.ext_database import db
-from fields.app_fields import app_import_fields
+from fields.app_fields import app_import_check_dependencies_fields, app_import_fields
from libs.login import login_required
from models import Account
+from models.model import App
from services.app_dsl_service import AppDslService, ImportStatus
@@ -88,3 +90,20 @@ def post(self, import_id):
if result.status == ImportStatus.FAILED.value:
return result.model_dump(mode="json"), 400
return result.model_dump(mode="json"), 200
+
+
+class AppImportCheckDependenciesApi(Resource):
+ @setup_required
+ @login_required
+ @get_app_model
+ @account_initialization_required
+ @marshal_with(app_import_check_dependencies_fields)
+ def get(self, app_model: App):
+ if not current_user.is_editor:
+ raise Forbidden()
+
+ with Session(db.engine) as session:
+ import_service = AppDslService(session)
+ result = import_service.check_dependencies(app_model=app_model)
+
+ return result.model_dump(mode="json"), 200
diff --git a/api/controllers/console/app/site.py b/api/controllers/console/app/site.py
index db29b95c4140ff..c5a9c4d76e38ae 100644
--- a/api/controllers/console/app/site.py
+++ b/api/controllers/console/app/site.py
@@ -2,6 +2,7 @@
from flask_login import current_user # type: ignore
from flask_restful import Resource, marshal_with, reqparse # type: ignore
+from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden, NotFound
from constants.languages import supported_language
@@ -50,33 +51,37 @@ def post(self, app_model):
if not current_user.is_editor:
raise Forbidden()
- site = Site.query.filter(Site.app_id == app_model.id).one_or_404()
-
- for attr_name in [
- "title",
- "icon_type",
- "icon",
- "icon_background",
- "description",
- "default_language",
- "chat_color_theme",
- "chat_color_theme_inverted",
- "customize_domain",
- "copyright",
- "privacy_policy",
- "custom_disclaimer",
- "customize_token_strategy",
- "prompt_public",
- "show_workflow_steps",
- "use_icon_as_answer_icon",
- ]:
- value = args.get(attr_name)
- if value is not None:
- setattr(site, attr_name, value)
-
- site.updated_by = current_user.id
- site.updated_at = datetime.now(UTC).replace(tzinfo=None)
- db.session.commit()
+ with Session(db.engine) as session:
+ site = session.query(Site).filter(Site.app_id == app_model.id).first()
+
+ if not site:
+ raise NotFound
+
+ for attr_name in [
+ "title",
+ "icon_type",
+ "icon",
+ "icon_background",
+ "description",
+ "default_language",
+ "chat_color_theme",
+ "chat_color_theme_inverted",
+ "customize_domain",
+ "copyright",
+ "privacy_policy",
+ "custom_disclaimer",
+ "customize_token_strategy",
+ "prompt_public",
+ "show_workflow_steps",
+ "use_icon_as_answer_icon",
+ ]:
+ value = args.get(attr_name)
+ if value is not None:
+ setattr(site, attr_name, value)
+
+ site.updated_by = current_user.id
+ site.updated_at = datetime.now(UTC).replace(tzinfo=None)
+ session.commit()
return site
diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py
index 6942ac6fbe62dd..0cc5f31ddddfde 100644
--- a/api/controllers/console/app/workflow.py
+++ b/api/controllers/console/app/workflow.py
@@ -20,6 +20,7 @@
from libs.helper import TimestampField, uuid_value
from libs.login import current_user, login_required
from models import App
+from models.account import Account
from models.model import AppMode
from services.app_generate_service import AppGenerateService
from services.errors.app import WorkflowHashNotEqualError
@@ -96,6 +97,9 @@ def post(self, app_model: App):
else:
abort(415)
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
workflow_service = WorkflowService()
try:
@@ -139,6 +143,9 @@ def post(self, app_model: App):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
parser.add_argument("query", type=str, required=True, location="json", default="")
@@ -160,7 +167,7 @@ def post(self, app_model: App):
raise ConversationCompletedError()
except ValueError as e:
raise e
- except Exception as e:
+ except Exception:
logging.exception("internal server error.")
raise InternalServerError()
@@ -178,6 +185,9 @@ def post(self, app_model: App, node_id: str):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
args = parser.parse_args()
@@ -194,7 +204,7 @@ def post(self, app_model: App, node_id: str):
raise ConversationCompletedError()
except ValueError as e:
raise e
- except Exception as e:
+ except Exception:
logging.exception("internal server error.")
raise InternalServerError()
@@ -212,6 +222,9 @@ def post(self, app_model: App, node_id: str):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
args = parser.parse_args()
@@ -228,7 +241,7 @@ def post(self, app_model: App, node_id: str):
raise ConversationCompletedError()
except ValueError as e:
raise e
- except Exception as e:
+ except Exception:
logging.exception("internal server error.")
raise InternalServerError()
@@ -246,6 +259,9 @@ def post(self, app_model: App):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, nullable=False, location="json")
parser.add_argument("files", type=list, required=False, location="json")
@@ -294,13 +310,20 @@ def post(self, app_model: App, node_id: str):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, nullable=False, location="json")
args = parser.parse_args()
+ inputs = args.get("inputs")
+ if inputs == None:
+ raise ValueError("missing inputs")
+
workflow_service = WorkflowService()
workflow_node_execution = workflow_service.run_draft_workflow_node(
- app_model=app_model, node_id=node_id, user_inputs=args.get("inputs"), account=current_user
+ app_model=app_model, node_id=node_id, user_inputs=inputs, account=current_user
)
return workflow_node_execution
@@ -339,6 +362,9 @@ def post(self, app_model: App):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
workflow_service = WorkflowService()
workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user)
@@ -376,12 +402,17 @@ def get(self, app_model: App, block_type: str):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
parser = reqparse.RequestParser()
parser.add_argument("q", type=str, location="args")
args = parser.parse_args()
+ q = args.get("q")
+
filters = None
- if args.get("q"):
+ if q:
try:
filters = json.loads(args.get("q", ""))
except json.JSONDecodeError:
@@ -407,6 +438,9 @@ def post(self, app_model: App):
if not current_user.is_editor:
raise Forbidden()
+ if not isinstance(current_user, Account):
+ raise Forbidden()
+
if request.data:
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=False, nullable=True, location="json")
diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py
index 241ecdbd5341e8..773ee657278f46 100644
--- a/api/controllers/console/auth/forgot_password.py
+++ b/api/controllers/console/auth/forgot_password.py
@@ -3,6 +3,8 @@
from flask import request
from flask_restful import Resource, reqparse # type: ignore
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from constants.languages import languages
from controllers.console import api
@@ -43,7 +45,8 @@ def post(self):
else:
language = "en-US"
- account = Account.query.filter_by(email=args["email"]).first()
+ with Session(db.engine) as session:
+ account = session.execute(select(Account).filter_by(email=args["email"])).scalar_one_or_none()
token = None
if account is None:
if FeatureService.get_system_features().is_allow_register:
@@ -116,7 +119,8 @@ def post(self):
password_hashed = hash_password(new_password, salt)
base64_password_hashed = base64.b64encode(password_hashed).decode()
- account = Account.query.filter_by(email=reset_data.get("email")).first()
+ with Session(db.engine) as session:
+ account = session.execute(select(Account).filter_by(email=reset_data.get("email"))).scalar_one_or_none()
if account:
account.password = base64_password_hashed
account.password_salt = base64_salt
@@ -137,7 +141,7 @@ def post(self):
)
except WorkSpaceNotAllowedCreateError:
pass
- except AccountRegisterError as are:
+ except AccountRegisterError:
raise AccountInFreezeError()
return {"result": "success"}
diff --git a/api/controllers/console/auth/oauth.py b/api/controllers/console/auth/oauth.py
index 2a08362c6d62a9..33bafbf4631290 100644
--- a/api/controllers/console/auth/oauth.py
+++ b/api/controllers/console/auth/oauth.py
@@ -5,6 +5,8 @@
import requests
from flask import current_app, redirect, request
from flask_restful import Resource # type: ignore
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from werkzeug.exceptions import Unauthorized
from configs import dify_config
@@ -135,7 +137,8 @@ def _get_account_by_openid_or_email(provider: str, user_info: OAuthUserInfo) ->
account: Optional[Account] = Account.get_by_openid(provider, user_info.id)
if not account:
- account = Account.query.filter_by(email=user_info.email).first()
+ with Session(db.engine) as session:
+ account = session.execute(select(Account).filter_by(email=user_info.email)).scalar_one_or_none()
return account
diff --git a/api/controllers/console/datasets/data_source.py b/api/controllers/console/datasets/data_source.py
index 3a4a6d75e1d3a8..5e180b5cf1e225 100644
--- a/api/controllers/console/datasets/data_source.py
+++ b/api/controllers/console/datasets/data_source.py
@@ -4,6 +4,8 @@
from flask import request
from flask_login import current_user # type: ignore
from flask_restful import Resource, marshal_with, reqparse # type: ignore
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound
from controllers.console import api
@@ -76,7 +78,10 @@ def get(self):
def patch(self, binding_id, action):
binding_id = str(binding_id)
action = str(action)
- data_source_binding = DataSourceOauthBinding.query.filter_by(id=binding_id).first()
+ with Session(db.engine) as session:
+ data_source_binding = session.execute(
+ select(DataSourceOauthBinding).filter_by(id=binding_id)
+ ).scalar_one_or_none()
if data_source_binding is None:
raise NotFound("Data source binding not found.")
# enable binding
@@ -108,47 +113,53 @@ class DataSourceNotionListApi(Resource):
def get(self):
dataset_id = request.args.get("dataset_id", default=None, type=str)
exist_page_ids = []
- # import notion in the exist dataset
- if dataset_id:
- dataset = DatasetService.get_dataset(dataset_id)
- if not dataset:
- raise NotFound("Dataset not found.")
- if dataset.data_source_type != "notion_import":
- raise ValueError("Dataset is not notion type.")
- documents = Document.query.filter_by(
- dataset_id=dataset_id,
- tenant_id=current_user.current_tenant_id,
- data_source_type="notion_import",
- enabled=True,
+ with Session(db.engine) as session:
+ # import notion in the exist dataset
+ if dataset_id:
+ dataset = DatasetService.get_dataset(dataset_id)
+ if not dataset:
+ raise NotFound("Dataset not found.")
+ if dataset.data_source_type != "notion_import":
+ raise ValueError("Dataset is not notion type.")
+
+ documents = session.execute(
+ select(Document).filter_by(
+ dataset_id=dataset_id,
+ tenant_id=current_user.current_tenant_id,
+ data_source_type="notion_import",
+ enabled=True,
+ )
+ ).all()
+ if documents:
+ for document in documents:
+ data_source_info = json.loads(document.data_source_info)
+ exist_page_ids.append(data_source_info["notion_page_id"])
+ # get all authorized pages
+ data_source_bindings = session.scalars(
+ select(DataSourceOauthBinding).filter_by(
+ tenant_id=current_user.current_tenant_id, provider="notion", disabled=False
+ )
).all()
- if documents:
- for document in documents:
- data_source_info = json.loads(document.data_source_info)
- exist_page_ids.append(data_source_info["notion_page_id"])
- # get all authorized pages
- data_source_bindings = DataSourceOauthBinding.query.filter_by(
- tenant_id=current_user.current_tenant_id, provider="notion", disabled=False
- ).all()
- if not data_source_bindings:
- return {"notion_info": []}, 200
- pre_import_info_list = []
- for data_source_binding in data_source_bindings:
- source_info = data_source_binding.source_info
- pages = source_info["pages"]
- # Filter out already bound pages
- for page in pages:
- if page["page_id"] in exist_page_ids:
- page["is_bound"] = True
- else:
- page["is_bound"] = False
- pre_import_info = {
- "workspace_name": source_info["workspace_name"],
- "workspace_icon": source_info["workspace_icon"],
- "workspace_id": source_info["workspace_id"],
- "pages": pages,
- }
- pre_import_info_list.append(pre_import_info)
- return {"notion_info": pre_import_info_list}, 200
+ if not data_source_bindings:
+ return {"notion_info": []}, 200
+ pre_import_info_list = []
+ for data_source_binding in data_source_bindings:
+ source_info = data_source_binding.source_info
+ pages = source_info["pages"]
+ # Filter out already bound pages
+ for page in pages:
+ if page["page_id"] in exist_page_ids:
+ page["is_bound"] = True
+ else:
+ page["is_bound"] = False
+ pre_import_info = {
+ "workspace_name": source_info["workspace_name"],
+ "workspace_icon": source_info["workspace_icon"],
+ "workspace_id": source_info["workspace_id"],
+ "pages": pages,
+ }
+ pre_import_info_list.append(pre_import_info)
+ return {"notion_info": pre_import_info_list}, 200
class DataSourceNotionApi(Resource):
@@ -158,14 +169,17 @@ class DataSourceNotionApi(Resource):
def get(self, workspace_id, page_id, page_type):
workspace_id = str(workspace_id)
page_id = str(page_id)
- data_source_binding = DataSourceOauthBinding.query.filter(
- db.and_(
- DataSourceOauthBinding.tenant_id == current_user.current_tenant_id,
- DataSourceOauthBinding.provider == "notion",
- DataSourceOauthBinding.disabled == False,
- DataSourceOauthBinding.source_info["workspace_id"] == f'"{workspace_id}"',
- )
- ).first()
+ with Session(db.engine) as session:
+ data_source_binding = session.execute(
+ select(DataSourceOauthBinding).filter(
+ db.and_(
+ DataSourceOauthBinding.tenant_id == current_user.current_tenant_id,
+ DataSourceOauthBinding.provider == "notion",
+ DataSourceOauthBinding.disabled == False,
+ DataSourceOauthBinding.source_info["workspace_id"] == f'"{workspace_id}"',
+ )
+ )
+ ).scalar_one_or_none()
if not data_source_binding:
raise NotFound("Data source binding not found.")
diff --git a/api/controllers/console/datasets/datasets.py b/api/controllers/console/datasets/datasets.py
index fee651480a6e9d..e934903910efec 100644
--- a/api/controllers/console/datasets/datasets.py
+++ b/api/controllers/console/datasets/datasets.py
@@ -14,6 +14,7 @@
from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
from core.indexing_runner import IndexingRunner
from core.model_runtime.entities.model_entities import ModelType
+from core.plugin.entities.plugin import ModelProviderID
from core.provider_manager import ProviderManager
from core.rag.datasource.vdb.vector_type import VectorType
from core.rag.extractor.entity.extract_setting import ExtractSetting
@@ -72,7 +73,9 @@ def get(self):
data = marshal(datasets, dataset_detail_fields)
for item in data:
+ # convert embedding_model_provider to plugin standard format
if item["indexing_technique"] == "high_quality":
+ item["embedding_model_provider"] = str(ModelProviderID(item["embedding_model_provider"]))
item_model = f"{item['embedding_model']}:{item['embedding_model_provider']}"
if item_model in model_names:
item["embedding_available"] = True
diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py
index c95214e9fbf11e..7ba9f5e121c9e3 100644
--- a/api/controllers/console/datasets/datasets_document.py
+++ b/api/controllers/console/datasets/datasets_document.py
@@ -7,7 +7,6 @@
from flask_login import current_user # type: ignore
from flask_restful import Resource, fields, marshal, marshal_with, reqparse # type: ignore
from sqlalchemy import asc, desc
-from transformers.hf_argparser import string_to_bool # type: ignore
from werkzeug.exceptions import Forbidden, NotFound
import services
@@ -40,6 +39,7 @@
from core.model_manager import ModelManager
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.errors.invoke import InvokeAuthorizationError
+from core.plugin.manager.exc import PluginDaemonClientSideError
from core.rag.extractor.entity.extract_setting import ExtractSetting
from extensions.ext_database import db
from extensions.ext_redis import redis_client
@@ -150,8 +150,20 @@ def get(self, dataset_id):
sort = request.args.get("sort", default="-created_at", type=str)
# "yes", "true", "t", "y", "1" convert to True, while others convert to False.
try:
- fetch = string_to_bool(request.args.get("fetch", default="false"))
- except (ArgumentTypeError, ValueError, Exception) as e:
+ fetch_val = request.args.get("fetch", default="false")
+ if isinstance(fetch_val, bool):
+ fetch = fetch_val
+ else:
+ if fetch_val.lower() in ("yes", "true", "t", "y", "1"):
+ fetch = True
+ elif fetch_val.lower() in ("no", "false", "f", "n", "0"):
+ fetch = False
+ else:
+ raise ArgumentTypeError(
+ f"Truthy value expected: got {fetch_val} but expected one of yes/no, true/false, t/f, y/n, 1/0 "
+ f"(case insensitive)."
+ )
+ except (ArgumentTypeError, ValueError, Exception):
fetch = False
dataset = DatasetService.get_dataset(dataset_id)
if not dataset:
@@ -429,6 +441,8 @@ def get(self, dataset_id, document_id):
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
+ except PluginDaemonClientSideError as ex:
+ raise ProviderNotInitializeError(ex.description)
except Exception as e:
raise IndexingEstimateError(str(e))
@@ -529,6 +543,8 @@ def get(self, dataset_id, batch):
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
+ except PluginDaemonClientSideError as ex:
+ raise ProviderNotInitializeError(ex.description)
except Exception as e:
raise IndexingEstimateError(str(e))
diff --git a/api/controllers/console/init_validate.py b/api/controllers/console/init_validate.py
index d9ae5cf29fc626..cfed5fe7a43dd5 100644
--- a/api/controllers/console/init_validate.py
+++ b/api/controllers/console/init_validate.py
@@ -2,8 +2,11 @@
from flask import session
from flask_restful import Resource, reqparse # type: ignore
+from sqlalchemy import select
+from sqlalchemy.orm import Session
from configs import dify_config
+from extensions.ext_database import db
from libs.helper import StrLen
from models.model import DifySetup
from services.account_service import TenantService
@@ -42,7 +45,11 @@ def post(self):
def get_init_validate_status():
if dify_config.EDITION == "SELF_HOSTED":
if os.environ.get("INIT_PASSWORD"):
- return session.get("is_init_validated") or DifySetup.query.first()
+ if session.get("is_init_validated"):
+ return True
+
+ with Session(db.engine) as db_session:
+ return db_session.execute(select(DifySetup)).scalar_one_or_none()
return True
diff --git a/api/controllers/console/setup.py b/api/controllers/console/setup.py
index aba6f0aad9ee54..3b47f8f12febe4 100644
--- a/api/controllers/console/setup.py
+++ b/api/controllers/console/setup.py
@@ -4,7 +4,7 @@
from configs import dify_config
from libs.helper import StrLen, email, extract_remote_ip
from libs.password import valid_password
-from models.model import DifySetup
+from models.model import DifySetup, db
from services.account_service import RegisterService, TenantService
from . import api
@@ -52,8 +52,9 @@ def post(self):
def get_setup_status():
if dify_config.EDITION == "SELF_HOSTED":
- return DifySetup.query.first()
- return True
+ return db.session.query(DifySetup).first()
+ else:
+ return True
api.add_resource(SetupApi, "/setup")
diff --git a/api/controllers/console/workspace/__init__.py b/api/controllers/console/workspace/__init__.py
index e69de29bb2d1d6..7af2b44a4ae413 100644
--- a/api/controllers/console/workspace/__init__.py
+++ b/api/controllers/console/workspace/__init__.py
@@ -0,0 +1,56 @@
+from functools import wraps
+
+from flask_login import current_user # type: ignore
+from sqlalchemy.orm import Session
+from werkzeug.exceptions import Forbidden
+
+from extensions.ext_database import db
+from models.account import TenantPluginPermission
+
+
+def plugin_permission_required(
+ install_required: bool = False,
+ debug_required: bool = False,
+):
+ def interceptor(view):
+ @wraps(view)
+ def decorated(*args, **kwargs):
+ user = current_user
+ tenant_id = user.current_tenant_id
+
+ with Session(db.engine) as session:
+ permission = (
+ session.query(TenantPluginPermission)
+ .filter(
+ TenantPluginPermission.tenant_id == tenant_id,
+ )
+ .first()
+ )
+
+ if not permission:
+ # no permission set, allow access for everyone
+ return view(*args, **kwargs)
+
+ if install_required:
+ if permission.install_permission == TenantPluginPermission.InstallPermission.NOBODY:
+ raise Forbidden()
+ if permission.install_permission == TenantPluginPermission.InstallPermission.ADMINS:
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+ if permission.install_permission == TenantPluginPermission.InstallPermission.EVERYONE:
+ pass
+
+ if debug_required:
+ if permission.debug_permission == TenantPluginPermission.DebugPermission.NOBODY:
+ raise Forbidden()
+ if permission.debug_permission == TenantPluginPermission.DebugPermission.ADMINS:
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+ if permission.debug_permission == TenantPluginPermission.DebugPermission.EVERYONE:
+ pass
+
+ return view(*args, **kwargs)
+
+ return decorated
+
+ return interceptor
diff --git a/api/controllers/console/workspace/agent_providers.py b/api/controllers/console/workspace/agent_providers.py
new file mode 100644
index 00000000000000..a41d6c501c61f5
--- /dev/null
+++ b/api/controllers/console/workspace/agent_providers.py
@@ -0,0 +1,36 @@
+from flask_login import current_user # type: ignore
+from flask_restful import Resource # type: ignore
+
+from controllers.console import api
+from controllers.console.wraps import account_initialization_required, setup_required
+from core.model_runtime.utils.encoders import jsonable_encoder
+from libs.login import login_required
+from services.agent_service import AgentService
+
+
+class AgentProviderListApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self):
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
+
+ return jsonable_encoder(AgentService.list_agent_providers(user_id, tenant_id))
+
+
+class AgentProviderApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self, provider_name: str):
+ user = current_user
+ user_id = user.id
+ tenant_id = user.current_tenant_id
+ return jsonable_encoder(AgentService.get_agent_provider(user_id, tenant_id, provider_name))
+
+
+api.add_resource(AgentProviderListApi, "/workspaces/current/agent-providers")
+api.add_resource(AgentProviderApi, "/workspaces/current/agent-provider/")
diff --git a/api/controllers/console/workspace/endpoint.py b/api/controllers/console/workspace/endpoint.py
new file mode 100644
index 00000000000000..a5bd2a4bcf30f3
--- /dev/null
+++ b/api/controllers/console/workspace/endpoint.py
@@ -0,0 +1,205 @@
+from flask_login import current_user # type: ignore
+from flask_restful import Resource, reqparse # type: ignore
+from werkzeug.exceptions import Forbidden
+
+from controllers.console import api
+from controllers.console.wraps import account_initialization_required, setup_required
+from core.model_runtime.utils.encoders import jsonable_encoder
+from libs.login import login_required
+from services.plugin.endpoint_service import EndpointService
+
+
+class EndpointCreateApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("plugin_unique_identifier", type=str, required=True)
+ parser.add_argument("settings", type=dict, required=True)
+ parser.add_argument("name", type=str, required=True)
+ args = parser.parse_args()
+
+ plugin_unique_identifier = args["plugin_unique_identifier"]
+ settings = args["settings"]
+ name = args["name"]
+
+ return {
+ "success": EndpointService.create_endpoint(
+ tenant_id=user.current_tenant_id,
+ user_id=user.id,
+ plugin_unique_identifier=plugin_unique_identifier,
+ name=name,
+ settings=settings,
+ )
+ }
+
+
+class EndpointListApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("page", type=int, required=True, location="args")
+ parser.add_argument("page_size", type=int, required=True, location="args")
+ args = parser.parse_args()
+
+ page = args["page"]
+ page_size = args["page_size"]
+
+ return jsonable_encoder(
+ {
+ "endpoints": EndpointService.list_endpoints(
+ tenant_id=user.current_tenant_id,
+ user_id=user.id,
+ page=page,
+ page_size=page_size,
+ )
+ }
+ )
+
+
+class EndpointListForSinglePluginApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("page", type=int, required=True, location="args")
+ parser.add_argument("page_size", type=int, required=True, location="args")
+ parser.add_argument("plugin_id", type=str, required=True, location="args")
+ args = parser.parse_args()
+
+ page = args["page"]
+ page_size = args["page_size"]
+ plugin_id = args["plugin_id"]
+
+ return jsonable_encoder(
+ {
+ "endpoints": EndpointService.list_endpoints_for_single_plugin(
+ tenant_id=user.current_tenant_id,
+ user_id=user.id,
+ plugin_id=plugin_id,
+ page=page,
+ page_size=page_size,
+ )
+ }
+ )
+
+
+class EndpointDeleteApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("endpoint_id", type=str, required=True)
+ args = parser.parse_args()
+
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ endpoint_id = args["endpoint_id"]
+
+ return {
+ "success": EndpointService.delete_endpoint(
+ tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
+ )
+ }
+
+
+class EndpointUpdateApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("endpoint_id", type=str, required=True)
+ parser.add_argument("settings", type=dict, required=True)
+ parser.add_argument("name", type=str, required=True)
+ args = parser.parse_args()
+
+ endpoint_id = args["endpoint_id"]
+ settings = args["settings"]
+ name = args["name"]
+
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ return {
+ "success": EndpointService.update_endpoint(
+ tenant_id=user.current_tenant_id,
+ user_id=user.id,
+ endpoint_id=endpoint_id,
+ name=name,
+ settings=settings,
+ )
+ }
+
+
+class EndpointEnableApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("endpoint_id", type=str, required=True)
+ args = parser.parse_args()
+
+ endpoint_id = args["endpoint_id"]
+
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ return {
+ "success": EndpointService.enable_endpoint(
+ tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
+ )
+ }
+
+
+class EndpointDisableApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("endpoint_id", type=str, required=True)
+ args = parser.parse_args()
+
+ endpoint_id = args["endpoint_id"]
+
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ return {
+ "success": EndpointService.disable_endpoint(
+ tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
+ )
+ }
+
+
+api.add_resource(EndpointCreateApi, "/workspaces/current/endpoints/create")
+api.add_resource(EndpointListApi, "/workspaces/current/endpoints/list")
+api.add_resource(EndpointListForSinglePluginApi, "/workspaces/current/endpoints/list/plugin")
+api.add_resource(EndpointDeleteApi, "/workspaces/current/endpoints/delete")
+api.add_resource(EndpointUpdateApi, "/workspaces/current/endpoints/update")
+api.add_resource(EndpointEnableApi, "/workspaces/current/endpoints/enable")
+api.add_resource(EndpointDisableApi, "/workspaces/current/endpoints/disable")
diff --git a/api/controllers/console/workspace/load_balancing_config.py b/api/controllers/console/workspace/load_balancing_config.py
index 7009343d9923da..6e1d87cb12d48a 100644
--- a/api/controllers/console/workspace/load_balancing_config.py
+++ b/api/controllers/console/workspace/load_balancing_config.py
@@ -112,10 +112,10 @@ def post(self, provider: str, config_id: str):
# Load Balancing Config
api.add_resource(
LoadBalancingCredentialsValidateApi,
- "/workspaces/current/model-providers//models/load-balancing-configs/credentials-validate",
+ "/workspaces/current/model-providers//models/load-balancing-configs/credentials-validate",
)
api.add_resource(
LoadBalancingConfigCredentialsValidateApi,
- "/workspaces/current/model-providers//models/load-balancing-configs//credentials-validate",
+ "/workspaces/current/model-providers//models/load-balancing-configs//credentials-validate",
)
diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py
index 2d11295b0fdf61..d7d1cc8d00756d 100644
--- a/api/controllers/console/workspace/model_providers.py
+++ b/api/controllers/console/workspace/model_providers.py
@@ -79,7 +79,7 @@ def post(self, provider: str):
response = {"result": "success" if result else "error"}
if not result:
- response["error"] = error
+ response["error"] = error or "Unknown error"
return response
@@ -125,9 +125,10 @@ class ModelProviderIconApi(Resource):
Get model provider icon
"""
- def get(self, provider: str, icon_type: str, lang: str):
+ def get(self, tenant_id: str, provider: str, icon_type: str, lang: str):
model_provider_service = ModelProviderService()
icon, mimetype = model_provider_service.get_model_provider_icon(
+ tenant_id=tenant_id,
provider=provider,
icon_type=icon_type,
lang=lang,
@@ -183,53 +184,17 @@ def get(self, provider: str):
return data
-class ModelProviderFreeQuotaSubmitApi(Resource):
- @setup_required
- @login_required
- @account_initialization_required
- def post(self, provider: str):
- model_provider_service = ModelProviderService()
- result = model_provider_service.free_quota_submit(tenant_id=current_user.current_tenant_id, provider=provider)
-
- return result
-
-
-class ModelProviderFreeQuotaQualificationVerifyApi(Resource):
- @setup_required
- @login_required
- @account_initialization_required
- def get(self, provider: str):
- parser = reqparse.RequestParser()
- parser.add_argument("token", type=str, required=False, nullable=True, location="args")
- args = parser.parse_args()
-
- model_provider_service = ModelProviderService()
- result = model_provider_service.free_quota_qualification_verify(
- tenant_id=current_user.current_tenant_id, provider=provider, token=args["token"]
- )
-
- return result
-
-
api.add_resource(ModelProviderListApi, "/workspaces/current/model-providers")
-api.add_resource(ModelProviderCredentialApi, "/workspaces/current/model-providers//credentials")
-api.add_resource(ModelProviderValidateApi, "/workspaces/current/model-providers//credentials/validate")
-api.add_resource(ModelProviderApi, "/workspaces/current/model-providers/")
-api.add_resource(
- ModelProviderIconApi, "/workspaces/current/model-providers///"
-)
+api.add_resource(ModelProviderCredentialApi, "/workspaces/current/model-providers//credentials")
+api.add_resource(ModelProviderValidateApi, "/workspaces/current/model-providers//credentials/validate")
+api.add_resource(ModelProviderApi, "/workspaces/current/model-providers/")
api.add_resource(
- PreferredProviderTypeUpdateApi, "/workspaces/current/model-providers//preferred-provider-type"
-)
-api.add_resource(
- ModelProviderPaymentCheckoutUrlApi, "/workspaces/current/model-providers//checkout-url"
-)
-api.add_resource(
- ModelProviderFreeQuotaSubmitApi, "/workspaces/current/model-providers//free-quota-submit"
+ PreferredProviderTypeUpdateApi, "/workspaces/current/model-providers//preferred-provider-type"
)
+api.add_resource(ModelProviderPaymentCheckoutUrlApi, "/workspaces/current/model-providers//checkout-url")
api.add_resource(
- ModelProviderFreeQuotaQualificationVerifyApi,
- "/workspaces/current/model-providers//free-quota-qualification-verify",
+ ModelProviderIconApi,
+ "/workspaces//model-providers///",
)
diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py
index 618262e502ab33..8b72a1ea3d076d 100644
--- a/api/controllers/console/workspace/models.py
+++ b/api/controllers/console/workspace/models.py
@@ -325,7 +325,7 @@ def post(self, provider: str):
response = {"result": "success" if result else "error"}
if not result:
- response["error"] = error
+ response["error"] = error or ""
return response
@@ -362,26 +362,26 @@ def get(self, model_type):
return jsonable_encoder({"data": models})
-api.add_resource(ModelProviderModelApi, "/workspaces/current/model-providers//models")
+api.add_resource(ModelProviderModelApi, "/workspaces/current/model-providers//models")
api.add_resource(
ModelProviderModelEnableApi,
- "/workspaces/current/model-providers//models/enable",
+ "/workspaces/current/model-providers//models/enable",
endpoint="model-provider-model-enable",
)
api.add_resource(
ModelProviderModelDisableApi,
- "/workspaces/current/model-providers//models/disable",
+ "/workspaces/current/model-providers//models/disable",
endpoint="model-provider-model-disable",
)
api.add_resource(
- ModelProviderModelCredentialApi, "/workspaces/current/model-providers//models/credentials"
+ ModelProviderModelCredentialApi, "/workspaces/current/model-providers//models/credentials"
)
api.add_resource(
- ModelProviderModelValidateApi, "/workspaces/current/model-providers//models/credentials/validate"
+ ModelProviderModelValidateApi, "/workspaces/current/model-providers//models/credentials/validate"
)
api.add_resource(
- ModelProviderModelParameterRuleApi, "/workspaces/current/model-providers//models/parameter-rules"
+ ModelProviderModelParameterRuleApi, "/workspaces/current/model-providers//models/parameter-rules"
)
api.add_resource(ModelProviderAvailableModelApi, "/workspaces/current/models/model-types/")
api.add_resource(DefaultModelApi, "/workspaces/current/default-model")
diff --git a/api/controllers/console/workspace/plugin.py b/api/controllers/console/workspace/plugin.py
new file mode 100644
index 00000000000000..f4c32ede2b1b4e
--- /dev/null
+++ b/api/controllers/console/workspace/plugin.py
@@ -0,0 +1,475 @@
+import io
+
+from flask import request, send_file
+from flask_login import current_user # type: ignore
+from flask_restful import Resource, reqparse # type: ignore
+from werkzeug.exceptions import Forbidden
+
+from configs import dify_config
+from controllers.console import api
+from controllers.console.workspace import plugin_permission_required
+from controllers.console.wraps import account_initialization_required, setup_required
+from core.model_runtime.utils.encoders import jsonable_encoder
+from core.plugin.manager.exc import PluginDaemonClientSideError
+from libs.login import login_required
+from models.account import TenantPluginPermission
+from services.plugin.plugin_permission_service import PluginPermissionService
+from services.plugin.plugin_service import PluginService
+
+
+class PluginDebuggingKeyApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def get(self):
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return {
+ "key": PluginService.get_debugging_key(tenant_id),
+ "host": dify_config.PLUGIN_REMOTE_INSTALL_HOST,
+ "port": dify_config.PLUGIN_REMOTE_INSTALL_PORT,
+ }
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginListApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self):
+ tenant_id = current_user.current_tenant_id
+ try:
+ plugins = PluginService.list(tenant_id)
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder({"plugins": plugins})
+
+
+class PluginListInstallationsFromIdsApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("plugin_ids", type=list, required=True, location="json")
+ args = parser.parse_args()
+
+ try:
+ plugins = PluginService.list_installations_from_ids(tenant_id, args["plugin_ids"])
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder({"plugins": plugins})
+
+
+class PluginIconApi(Resource):
+ @setup_required
+ def get(self):
+ req = reqparse.RequestParser()
+ req.add_argument("tenant_id", type=str, required=True, location="args")
+ req.add_argument("filename", type=str, required=True, location="args")
+ args = req.parse_args()
+
+ try:
+ icon_bytes, mimetype = PluginService.get_asset(args["tenant_id"], args["filename"])
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ icon_cache_max_age = dify_config.TOOL_ICON_CACHE_MAX_AGE
+ return send_file(io.BytesIO(icon_bytes), mimetype=mimetype, max_age=icon_cache_max_age)
+
+
+class PluginUploadFromPkgApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ file = request.files["pkg"]
+
+ # check file size
+ if file.content_length > dify_config.PLUGIN_MAX_PACKAGE_SIZE:
+ raise ValueError("File size exceeds the maximum allowed size")
+
+ content = file.read()
+ try:
+ response = PluginService.upload_pkg(tenant_id, content)
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginUploadFromGithubApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("repo", type=str, required=True, location="json")
+ parser.add_argument("version", type=str, required=True, location="json")
+ parser.add_argument("package", type=str, required=True, location="json")
+ args = parser.parse_args()
+
+ try:
+ response = PluginService.upload_pkg_from_github(tenant_id, args["repo"], args["version"], args["package"])
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginUploadFromBundleApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ file = request.files["bundle"]
+
+ # check file size
+ if file.content_length > dify_config.PLUGIN_MAX_BUNDLE_SIZE:
+ raise ValueError("File size exceeds the maximum allowed size")
+
+ content = file.read()
+ try:
+ response = PluginService.upload_bundle(tenant_id, content)
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginInstallFromPkgApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
+ args = parser.parse_args()
+
+ # check if all plugin_unique_identifiers are valid string
+ for plugin_unique_identifier in args["plugin_unique_identifiers"]:
+ if not isinstance(plugin_unique_identifier, str):
+ raise ValueError("Invalid plugin unique identifier")
+
+ try:
+ response = PluginService.install_from_local_pkg(tenant_id, args["plugin_unique_identifiers"])
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginInstallFromGithubApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("repo", type=str, required=True, location="json")
+ parser.add_argument("version", type=str, required=True, location="json")
+ parser.add_argument("package", type=str, required=True, location="json")
+ parser.add_argument("plugin_unique_identifier", type=str, required=True, location="json")
+ args = parser.parse_args()
+
+ try:
+ response = PluginService.install_from_github(
+ tenant_id,
+ args["plugin_unique_identifier"],
+ args["repo"],
+ args["version"],
+ args["package"],
+ )
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginInstallFromMarketplaceApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(install_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
+ args = parser.parse_args()
+
+ # check if all plugin_unique_identifiers are valid string
+ for plugin_unique_identifier in args["plugin_unique_identifiers"]:
+ if not isinstance(plugin_unique_identifier, str):
+ raise ValueError("Invalid plugin unique identifier")
+
+ try:
+ response = PluginService.install_from_marketplace_pkg(tenant_id, args["plugin_unique_identifiers"])
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+ return jsonable_encoder(response)
+
+
+class PluginFetchManifestApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def get(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
+ args = parser.parse_args()
+
+ try:
+ return jsonable_encoder(
+ {
+ "manifest": PluginService.fetch_plugin_manifest(
+ tenant_id, args["plugin_unique_identifier"]
+ ).model_dump()
+ }
+ )
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginFetchInstallTasksApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def get(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("page", type=int, required=True, location="args")
+ parser.add_argument("page_size", type=int, required=True, location="args")
+ args = parser.parse_args()
+
+ try:
+ return jsonable_encoder(
+ {"tasks": PluginService.fetch_install_tasks(tenant_id, args["page"], args["page_size"])}
+ )
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginFetchInstallTaskApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def get(self, task_id: str):
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return jsonable_encoder({"task": PluginService.fetch_install_task(tenant_id, task_id)})
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginDeleteInstallTaskApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self, task_id: str):
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return {"success": PluginService.delete_install_task(tenant_id, task_id)}
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginDeleteAllInstallTaskItemsApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return {"success": PluginService.delete_all_install_task_items(tenant_id)}
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginDeleteInstallTaskItemApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self, task_id: str, identifier: str):
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return {"success": PluginService.delete_install_task_item(tenant_id, task_id, identifier)}
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginUpgradeFromMarketplaceApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
+ parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
+ args = parser.parse_args()
+
+ try:
+ return jsonable_encoder(
+ PluginService.upgrade_plugin_with_marketplace(
+ tenant_id, args["original_plugin_unique_identifier"], args["new_plugin_unique_identifier"]
+ )
+ )
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginUpgradeFromGithubApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self):
+ tenant_id = current_user.current_tenant_id
+
+ parser = reqparse.RequestParser()
+ parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
+ parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
+ parser.add_argument("repo", type=str, required=True, location="json")
+ parser.add_argument("version", type=str, required=True, location="json")
+ parser.add_argument("package", type=str, required=True, location="json")
+ args = parser.parse_args()
+
+ try:
+ return jsonable_encoder(
+ PluginService.upgrade_plugin_with_github(
+ tenant_id,
+ args["original_plugin_unique_identifier"],
+ args["new_plugin_unique_identifier"],
+ args["repo"],
+ args["version"],
+ args["package"],
+ )
+ )
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginUninstallApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @plugin_permission_required(debug_required=True)
+ def post(self):
+ req = reqparse.RequestParser()
+ req.add_argument("plugin_installation_id", type=str, required=True, location="json")
+ args = req.parse_args()
+
+ tenant_id = current_user.current_tenant_id
+
+ try:
+ return {"success": PluginService.uninstall(tenant_id, args["plugin_installation_id"])}
+ except PluginDaemonClientSideError as e:
+ raise ValueError(e)
+
+
+class PluginChangePermissionApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def post(self):
+ user = current_user
+ if not user.is_admin_or_owner:
+ raise Forbidden()
+
+ req = reqparse.RequestParser()
+ req.add_argument("install_permission", type=str, required=True, location="json")
+ req.add_argument("debug_permission", type=str, required=True, location="json")
+ args = req.parse_args()
+
+ install_permission = TenantPluginPermission.InstallPermission(args["install_permission"])
+ debug_permission = TenantPluginPermission.DebugPermission(args["debug_permission"])
+
+ tenant_id = user.current_tenant_id
+
+ return {"success": PluginPermissionService.change_permission(tenant_id, install_permission, debug_permission)}
+
+
+class PluginFetchPermissionApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self):
+ tenant_id = current_user.current_tenant_id
+
+ permission = PluginPermissionService.get_permission(tenant_id)
+ if not permission:
+ return jsonable_encoder(
+ {
+ "install_permission": TenantPluginPermission.InstallPermission.EVERYONE,
+ "debug_permission": TenantPluginPermission.DebugPermission.EVERYONE,
+ }
+ )
+
+ return jsonable_encoder(
+ {
+ "install_permission": permission.install_permission,
+ "debug_permission": permission.debug_permission,
+ }
+ )
+
+
+api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
+api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
+api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
+api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
+api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
+api.add_resource(PluginUploadFromGithubApi, "/workspaces/current/plugin/upload/github")
+api.add_resource(PluginUploadFromBundleApi, "/workspaces/current/plugin/upload/bundle")
+api.add_resource(PluginInstallFromPkgApi, "/workspaces/current/plugin/install/pkg")
+api.add_resource(PluginInstallFromGithubApi, "/workspaces/current/plugin/install/github")
+api.add_resource(PluginUpgradeFromMarketplaceApi, "/workspaces/current/plugin/upgrade/marketplace")
+api.add_resource(PluginUpgradeFromGithubApi, "/workspaces/current/plugin/upgrade/github")
+api.add_resource(PluginInstallFromMarketplaceApi, "/workspaces/current/plugin/install/marketplace")
+api.add_resource(PluginFetchManifestApi, "/workspaces/current/plugin/fetch-manifest")
+api.add_resource(PluginFetchInstallTasksApi, "/workspaces/current/plugin/tasks")
+api.add_resource(PluginFetchInstallTaskApi, "/workspaces/current/plugin/tasks/")
+api.add_resource(PluginDeleteInstallTaskApi, "/workspaces/current/plugin/tasks//delete")
+api.add_resource(PluginDeleteAllInstallTaskItemsApi, "/workspaces/current/plugin/tasks/delete_all")
+api.add_resource(PluginDeleteInstallTaskItemApi, "/workspaces/current/plugin/tasks//delete/")
+api.add_resource(PluginUninstallApi, "/workspaces/current/plugin/uninstall")
+
+api.add_resource(PluginChangePermissionApi, "/workspaces/current/plugin/permission/change")
+api.add_resource(PluginFetchPermissionApi, "/workspaces/current/plugin/permission/fetch")
diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py
index 964f3862291a2e..39ab454922352f 100644
--- a/api/controllers/console/workspace/tool_providers.py
+++ b/api/controllers/console/workspace/tool_providers.py
@@ -25,8 +25,10 @@ class ToolProviderListApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
req = reqparse.RequestParser()
req.add_argument(
@@ -47,28 +49,43 @@ class ToolBuiltinProviderListToolsApi(Resource):
@login_required
@account_initialization_required
def get(self, provider):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ tenant_id = user.current_tenant_id
return jsonable_encoder(
BuiltinToolManageService.list_builtin_tool_provider_tools(
- user_id,
tenant_id,
provider,
)
)
+class ToolBuiltinProviderInfoApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ def get(self, provider):
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
+
+ return jsonable_encoder(BuiltinToolManageService.get_builtin_tool_provider_info(user_id, tenant_id, provider))
+
+
class ToolBuiltinProviderDeleteApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self, provider):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
return BuiltinToolManageService.delete_builtin_tool_provider(
user_id,
@@ -82,11 +99,13 @@ class ToolBuiltinProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self, provider):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@@ -131,11 +150,13 @@ class ToolApiProviderAddApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@@ -168,6 +189,11 @@ class ToolApiProviderGetRemoteSchemaApi(Resource):
@login_required
@account_initialization_required
def get(self):
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
+
parser = reqparse.RequestParser()
parser.add_argument("url", type=str, required=True, nullable=False, location="args")
@@ -175,8 +201,8 @@ def get(self):
args = parser.parse_args()
return ApiToolManageService.get_api_tool_provider_remote_schema(
- current_user.id,
- current_user.current_tenant_id,
+ user_id,
+ tenant_id,
args["url"],
)
@@ -186,8 +212,10 @@ class ToolApiProviderListToolsApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
@@ -209,11 +237,13 @@ class ToolApiProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@@ -248,11 +278,13 @@ class ToolApiProviderDeleteApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
@@ -272,8 +304,10 @@ class ToolApiProviderGetApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
@@ -293,7 +327,11 @@ class ToolBuiltinProviderCredentialsSchemaApi(Resource):
@login_required
@account_initialization_required
def get(self, provider):
- return BuiltinToolManageService.list_builtin_provider_credentials_schema(provider)
+ user = current_user
+
+ tenant_id = user.current_tenant_id
+
+ return BuiltinToolManageService.list_builtin_provider_credentials_schema(provider, tenant_id)
class ToolApiProviderSchemaApi(Resource):
@@ -344,11 +382,13 @@ class ToolWorkflowProviderCreateApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_app_id", type=uuid_value, required=True, nullable=False, location="json")
@@ -381,11 +421,13 @@ class ToolWorkflowProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="json")
@@ -421,11 +463,13 @@ class ToolWorkflowProviderDeleteApi(Resource):
@login_required
@account_initialization_required
def post(self):
- if not current_user.is_admin_or_owner:
+ user = current_user
+
+ if not user.is_admin_or_owner:
raise Forbidden()
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user_id = user.id
+ tenant_id = user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="json")
@@ -444,8 +488,10 @@ class ToolWorkflowProviderGetApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("workflow_tool_id", type=uuid_value, required=False, nullable=True, location="args")
@@ -476,8 +522,10 @@ class ToolWorkflowProviderListToolApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="args")
@@ -498,8 +546,10 @@ class ToolBuiltinListApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
return jsonable_encoder(
[
@@ -517,8 +567,10 @@ class ToolApiListApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
return jsonable_encoder(
[
@@ -536,8 +588,10 @@ class ToolWorkflowListApi(Resource):
@login_required
@account_initialization_required
def get(self):
- user_id = current_user.id
- tenant_id = current_user.current_tenant_id
+ user = current_user
+
+ user_id = user.id
+ tenant_id = user.current_tenant_id
return jsonable_encoder(
[
@@ -563,16 +617,18 @@ def get(self):
api.add_resource(ToolProviderListApi, "/workspaces/current/tool-providers")
# builtin tool provider
-api.add_resource(ToolBuiltinProviderListToolsApi, "/workspaces/current/tool-provider/builtin//tools")
-api.add_resource(ToolBuiltinProviderDeleteApi, "/workspaces/current/tool-provider/builtin//delete")
-api.add_resource(ToolBuiltinProviderUpdateApi, "/workspaces/current/tool-provider/builtin//update")
+api.add_resource(ToolBuiltinProviderListToolsApi, "/workspaces/current/tool-provider/builtin//tools")
+api.add_resource(ToolBuiltinProviderInfoApi, "/workspaces/current/tool-provider/builtin//info")
+api.add_resource(ToolBuiltinProviderDeleteApi, "/workspaces/current/tool-provider/builtin//delete")
+api.add_resource(ToolBuiltinProviderUpdateApi, "/workspaces/current/tool-provider/builtin//update")
api.add_resource(
- ToolBuiltinProviderGetCredentialsApi, "/workspaces/current/tool-provider/builtin//credentials"
+ ToolBuiltinProviderGetCredentialsApi, "/workspaces/current/tool-provider/builtin//credentials"
)
api.add_resource(
- ToolBuiltinProviderCredentialsSchemaApi, "/workspaces/current/tool-provider/builtin//credentials_schema"
+ ToolBuiltinProviderCredentialsSchemaApi,
+ "/workspaces/current/tool-provider/builtin//credentials_schema",
)
-api.add_resource(ToolBuiltinProviderIconApi, "/workspaces/current/tool-provider/builtin//icon")
+api.add_resource(ToolBuiltinProviderIconApi, "/workspaces/current/tool-provider/builtin//icon")
# api tool provider
api.add_resource(ToolApiProviderAddApi, "/workspaces/current/tool-provider/api/add")
diff --git a/api/controllers/console/wraps.py b/api/controllers/console/wraps.py
index 111db7ccf2da04..a6f64700f2a09e 100644
--- a/api/controllers/console/wraps.py
+++ b/api/controllers/console/wraps.py
@@ -7,6 +7,7 @@
from configs import dify_config
from controllers.console.workspace.error import AccountNotInitializedError
+from extensions.ext_database import db
from models.model import DifySetup
from services.feature_service import FeatureService, LicenseStatus
from services.operation_service import OperationService
@@ -134,9 +135,13 @@ def setup_required(view):
@wraps(view)
def decorated(*args, **kwargs):
# check setup
- if dify_config.EDITION == "SELF_HOSTED" and os.environ.get("INIT_PASSWORD") and not DifySetup.query.first():
+ if (
+ dify_config.EDITION == "SELF_HOSTED"
+ and os.environ.get("INIT_PASSWORD")
+ and not db.session.query(DifySetup).first()
+ ):
raise NotInitValidateError()
- elif dify_config.EDITION == "SELF_HOSTED" and not DifySetup.query.first():
+ elif dify_config.EDITION == "SELF_HOSTED" and not db.session.query(DifySetup).first():
raise NotSetupError()
return view(*args, **kwargs)
diff --git a/api/controllers/files/__init__.py b/api/controllers/files/__init__.py
index 97d5c3f88fb522..d4c32457082575 100644
--- a/api/controllers/files/__init__.py
+++ b/api/controllers/files/__init__.py
@@ -6,4 +6,4 @@
api = ExternalApi(bp)
-from . import image_preview, tool_files
+from . import image_preview, tool_files, upload
diff --git a/api/controllers/files/image_preview.py b/api/controllers/files/image_preview.py
index 2357288a50ae36..6f39908b6e5c64 100644
--- a/api/controllers/files/image_preview.py
+++ b/api/controllers/files/image_preview.py
@@ -1,3 +1,5 @@
+from urllib.parse import quote
+
from flask import Response, request
from flask_restful import Resource, reqparse # type: ignore
from werkzeug.exceptions import NotFound
@@ -71,7 +73,8 @@ def get(self, file_id):
if upload_file.size > 0:
response.headers["Content-Length"] = str(upload_file.size)
if args["as_attachment"]:
- response.headers["Content-Disposition"] = f"attachment; filename={upload_file.name}"
+ encoded_filename = quote(upload_file.name)
+ response.headers["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}"
return response
diff --git a/api/controllers/files/upload.py b/api/controllers/files/upload.py
new file mode 100644
index 00000000000000..ca5ea54435ecfd
--- /dev/null
+++ b/api/controllers/files/upload.py
@@ -0,0 +1,69 @@
+from flask import request
+from flask_restful import Resource, marshal_with # type: ignore
+from werkzeug.exceptions import Forbidden
+
+import services
+from controllers.console.wraps import setup_required
+from controllers.files import api
+from controllers.files.error import UnsupportedFileTypeError
+from controllers.inner_api.plugin.wraps import get_user
+from controllers.service_api.app.error import FileTooLargeError
+from core.file.helpers import verify_plugin_file_signature
+from fields.file_fields import file_fields
+from services.file_service import FileService
+
+
+class PluginUploadFileApi(Resource):
+ @setup_required
+ @marshal_with(file_fields)
+ def post(self):
+ # get file from request
+ file = request.files["file"]
+
+ timestamp = request.args.get("timestamp")
+ nonce = request.args.get("nonce")
+ sign = request.args.get("sign")
+ tenant_id = request.args.get("tenant_id")
+ if not tenant_id:
+ raise Forbidden("Invalid request.")
+
+ user_id = request.args.get("user_id")
+ user = get_user(tenant_id, user_id)
+
+ filename = file.filename
+ mimetype = file.mimetype
+
+ if not filename or not mimetype:
+ raise Forbidden("Invalid request.")
+
+ if not timestamp or not nonce or not sign:
+ raise Forbidden("Invalid request.")
+
+ if not verify_plugin_file_signature(
+ filename=filename,
+ mimetype=mimetype,
+ tenant_id=tenant_id,
+ user_id=user_id,
+ timestamp=timestamp,
+ nonce=nonce,
+ sign=sign,
+ ):
+ raise Forbidden("Invalid request.")
+
+ try:
+ upload_file = FileService.upload_file(
+ filename=filename,
+ content=file.read(),
+ mimetype=mimetype,
+ user=user,
+ source=None,
+ )
+ except services.errors.file.FileTooLargeError as file_too_large_error:
+ raise FileTooLargeError(file_too_large_error.description)
+ except services.errors.file.UnsupportedFileTypeError:
+ raise UnsupportedFileTypeError()
+
+ return upload_file, 201
+
+
+api.add_resource(PluginUploadFileApi, "/files/upload/for-plugin")
diff --git a/api/controllers/inner_api/__init__.py b/api/controllers/inner_api/__init__.py
index 9f124736a966ea..f147a3453fc072 100644
--- a/api/controllers/inner_api/__init__.py
+++ b/api/controllers/inner_api/__init__.py
@@ -5,4 +5,5 @@
bp = Blueprint("inner_api", __name__, url_prefix="/inner/api")
api = ExternalApi(bp)
+from .plugin import plugin
from .workspace import workspace
diff --git a/api/core/model_runtime/model_providers/anthropic/__init__.py b/api/controllers/inner_api/plugin/__init__.py
similarity index 100%
rename from api/core/model_runtime/model_providers/anthropic/__init__.py
rename to api/controllers/inner_api/plugin/__init__.py
diff --git a/api/controllers/inner_api/plugin/plugin.py b/api/controllers/inner_api/plugin/plugin.py
new file mode 100644
index 00000000000000..fe892922e98494
--- /dev/null
+++ b/api/controllers/inner_api/plugin/plugin.py
@@ -0,0 +1,293 @@
+from flask_restful import Resource # type: ignore
+
+from controllers.console.wraps import setup_required
+from controllers.inner_api import api
+from controllers.inner_api.plugin.wraps import get_user_tenant, plugin_data
+from controllers.inner_api.wraps import plugin_inner_api_only
+from core.file.helpers import get_signed_file_url_for_plugin
+from core.model_runtime.utils.encoders import jsonable_encoder
+from core.plugin.backwards_invocation.app import PluginAppBackwardsInvocation
+from core.plugin.backwards_invocation.base import BaseBackwardsInvocationResponse
+from core.plugin.backwards_invocation.encrypt import PluginEncrypter
+from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocation
+from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation
+from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation
+from core.plugin.entities.request import (
+ RequestInvokeApp,
+ RequestInvokeEncrypt,
+ RequestInvokeLLM,
+ RequestInvokeModeration,
+ RequestInvokeParameterExtractorNode,
+ RequestInvokeQuestionClassifierNode,
+ RequestInvokeRerank,
+ RequestInvokeSpeech2Text,
+ RequestInvokeSummary,
+ RequestInvokeTextEmbedding,
+ RequestInvokeTool,
+ RequestInvokeTTS,
+ RequestRequestUploadFile,
+)
+from core.tools.entities.tool_entities import ToolProviderType
+from libs.helper import compact_generate_response
+from models.account import Account, Tenant
+from models.model import EndUser
+
+
+class PluginInvokeLLMApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeLLM)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeLLM):
+ def generator():
+ response = PluginModelBackwardsInvocation.invoke_llm(user_model.id, tenant_model, payload)
+ return PluginModelBackwardsInvocation.convert_to_event_stream(response)
+
+ return compact_generate_response(generator())
+
+
+class PluginInvokeTextEmbeddingApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeTextEmbedding)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTextEmbedding):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginModelBackwardsInvocation.invoke_text_embedding(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeRerankApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeRerank)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeRerank):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginModelBackwardsInvocation.invoke_rerank(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeTTSApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeTTS)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTTS):
+ def generator():
+ response = PluginModelBackwardsInvocation.invoke_tts(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ return PluginModelBackwardsInvocation.convert_to_event_stream(response)
+
+ return compact_generate_response(generator())
+
+
+class PluginInvokeSpeech2TextApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeSpeech2Text)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeSpeech2Text):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginModelBackwardsInvocation.invoke_speech2text(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeModerationApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeModeration)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeModeration):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginModelBackwardsInvocation.invoke_moderation(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeToolApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeTool)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTool):
+ def generator():
+ return PluginToolBackwardsInvocation.convert_to_event_stream(
+ PluginToolBackwardsInvocation.invoke_tool(
+ tenant_id=tenant_model.id,
+ user_id=user_model.id,
+ tool_type=ToolProviderType.value_of(payload.tool_type),
+ provider=payload.provider,
+ tool_name=payload.tool,
+ tool_parameters=payload.tool_parameters,
+ ),
+ )
+
+ return compact_generate_response(generator())
+
+
+class PluginInvokeParameterExtractorNodeApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeParameterExtractorNode)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeParameterExtractorNode):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginNodeBackwardsInvocation.invoke_parameter_extractor(
+ tenant_id=tenant_model.id,
+ user_id=user_model.id,
+ parameters=payload.parameters,
+ model_config=payload.model,
+ instruction=payload.instruction,
+ query=payload.query,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeQuestionClassifierNodeApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeQuestionClassifierNode)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeQuestionClassifierNode):
+ try:
+ return jsonable_encoder(
+ BaseBackwardsInvocationResponse(
+ data=PluginNodeBackwardsInvocation.invoke_question_classifier(
+ tenant_id=tenant_model.id,
+ user_id=user_model.id,
+ query=payload.query,
+ model_config=payload.model,
+ classes=payload.classes,
+ instruction=payload.instruction,
+ )
+ )
+ )
+ except Exception as e:
+ return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
+
+
+class PluginInvokeAppApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeApp)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeApp):
+ response = PluginAppBackwardsInvocation.invoke_app(
+ app_id=payload.app_id,
+ user_id=user_model.id,
+ tenant_id=tenant_model.id,
+ conversation_id=payload.conversation_id,
+ query=payload.query,
+ stream=payload.response_mode == "streaming",
+ inputs=payload.inputs,
+ files=payload.files,
+ )
+
+ return compact_generate_response(PluginAppBackwardsInvocation.convert_to_event_stream(response))
+
+
+class PluginInvokeEncryptApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeEncrypt)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeEncrypt):
+ """
+ encrypt or decrypt data
+ """
+ try:
+ return BaseBackwardsInvocationResponse(
+ data=PluginEncrypter.invoke_encrypt(tenant_model, payload)
+ ).model_dump()
+ except Exception as e:
+ return BaseBackwardsInvocationResponse(error=str(e)).model_dump()
+
+
+class PluginInvokeSummaryApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestInvokeSummary)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeSummary):
+ try:
+ return BaseBackwardsInvocationResponse(
+ data={
+ "summary": PluginModelBackwardsInvocation.invoke_summary(
+ user_id=user_model.id,
+ tenant=tenant_model,
+ payload=payload,
+ )
+ }
+ ).model_dump()
+ except Exception as e:
+ return BaseBackwardsInvocationResponse(error=str(e)).model_dump()
+
+
+class PluginUploadFileRequestApi(Resource):
+ @setup_required
+ @plugin_inner_api_only
+ @get_user_tenant
+ @plugin_data(payload_type=RequestRequestUploadFile)
+ def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestRequestUploadFile):
+ # generate signed url
+ url = get_signed_file_url_for_plugin(payload.filename, payload.mimetype, tenant_model.id, user_model.id)
+ return BaseBackwardsInvocationResponse(data={"url": url}).model_dump()
+
+
+api.add_resource(PluginInvokeLLMApi, "/invoke/llm")
+api.add_resource(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding")
+api.add_resource(PluginInvokeRerankApi, "/invoke/rerank")
+api.add_resource(PluginInvokeTTSApi, "/invoke/tts")
+api.add_resource(PluginInvokeSpeech2TextApi, "/invoke/speech2text")
+api.add_resource(PluginInvokeModerationApi, "/invoke/moderation")
+api.add_resource(PluginInvokeToolApi, "/invoke/tool")
+api.add_resource(PluginInvokeParameterExtractorNodeApi, "/invoke/parameter-extractor")
+api.add_resource(PluginInvokeQuestionClassifierNodeApi, "/invoke/question-classifier")
+api.add_resource(PluginInvokeAppApi, "/invoke/app")
+api.add_resource(PluginInvokeEncryptApi, "/invoke/encrypt")
+api.add_resource(PluginInvokeSummaryApi, "/invoke/summary")
+api.add_resource(PluginUploadFileRequestApi, "/upload/file/request")
diff --git a/api/controllers/inner_api/plugin/wraps.py b/api/controllers/inner_api/plugin/wraps.py
new file mode 100644
index 00000000000000..c31f9d22ed6da5
--- /dev/null
+++ b/api/controllers/inner_api/plugin/wraps.py
@@ -0,0 +1,116 @@
+from collections.abc import Callable
+from functools import wraps
+from typing import Optional
+
+from flask import request
+from flask_restful import reqparse # type: ignore
+from pydantic import BaseModel
+from sqlalchemy.orm import Session
+
+from extensions.ext_database import db
+from models.account import Account, Tenant
+from models.model import EndUser
+from services.account_service import AccountService
+
+
+def get_user(tenant_id: str, user_id: str | None) -> Account | EndUser:
+ try:
+ with Session(db.engine) as session:
+ if not user_id:
+ user_id = "DEFAULT-USER"
+
+ if user_id == "DEFAULT-USER":
+ user_model = session.query(EndUser).filter(EndUser.session_id == "DEFAULT-USER").first()
+ if not user_model:
+ user_model = EndUser(
+ tenant_id=tenant_id,
+ type="service_api",
+ is_anonymous=True if user_id == "DEFAULT-USER" else False,
+ session_id=user_id,
+ )
+ session.add(user_model)
+ session.commit()
+ else:
+ user_model = AccountService.load_user(user_id)
+ if not user_model:
+ user_model = session.query(EndUser).filter(EndUser.id == user_id).first()
+ if not user_model:
+ raise ValueError("user not found")
+ except Exception:
+ raise ValueError("user not found")
+
+ return user_model
+
+
+def get_user_tenant(view: Optional[Callable] = None):
+ def decorator(view_func):
+ @wraps(view_func)
+ def decorated_view(*args, **kwargs):
+ # fetch json body
+ parser = reqparse.RequestParser()
+ parser.add_argument("tenant_id", type=str, required=True, location="json")
+ parser.add_argument("user_id", type=str, required=True, location="json")
+
+ kwargs = parser.parse_args()
+
+ user_id = kwargs.get("user_id")
+ tenant_id = kwargs.get("tenant_id")
+
+ if not tenant_id:
+ raise ValueError("tenant_id is required")
+
+ if not user_id:
+ user_id = "DEFAULT-USER"
+
+ del kwargs["tenant_id"]
+ del kwargs["user_id"]
+
+ try:
+ tenant_model = (
+ db.session.query(Tenant)
+ .filter(
+ Tenant.id == tenant_id,
+ )
+ .first()
+ )
+ except Exception:
+ raise ValueError("tenant not found")
+
+ if not tenant_model:
+ raise ValueError("tenant not found")
+
+ kwargs["tenant_model"] = tenant_model
+ kwargs["user_model"] = get_user(tenant_id, user_id)
+
+ return view_func(*args, **kwargs)
+
+ return decorated_view
+
+ if view is None:
+ return decorator
+ else:
+ return decorator(view)
+
+
+def plugin_data(view: Optional[Callable] = None, *, payload_type: type[BaseModel]):
+ def decorator(view_func):
+ def decorated_view(*args, **kwargs):
+ try:
+ data = request.get_json()
+ except Exception:
+ raise ValueError("invalid json")
+
+ try:
+ payload = payload_type(**data)
+ except Exception as e:
+ raise ValueError(f"invalid payload: {str(e)}")
+
+ kwargs["payload"] = payload
+ return view_func(*args, **kwargs)
+
+ return decorated_view
+
+ if view is None:
+ return decorator
+ else:
+ return decorator(view)
diff --git a/api/controllers/inner_api/workspace/workspace.py b/api/controllers/inner_api/workspace/workspace.py
index 8b2a7c0c4ea1f7..436854698e6568 100644
--- a/api/controllers/inner_api/workspace/workspace.py
+++ b/api/controllers/inner_api/workspace/workspace.py
@@ -4,7 +4,7 @@
from controllers.console.wraps import setup_required
from controllers.inner_api import api
-from controllers.inner_api.wraps import inner_api_only
+from controllers.inner_api.wraps import enterprise_inner_api_only
from events.tenant_event import tenant_was_created
from models.account import Account
from services.account_service import TenantService
@@ -12,7 +12,7 @@
class EnterpriseWorkspace(Resource):
@setup_required
- @inner_api_only
+ @enterprise_inner_api_only
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, location="json")
@@ -33,7 +33,7 @@ def post(self):
class EnterpriseWorkspaceNoOwnerEmail(Resource):
@setup_required
- @inner_api_only
+ @enterprise_inner_api_only
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, location="json")
@@ -50,8 +50,8 @@ def post(self):
"plan": tenant.plan,
"status": tenant.status,
"custom_config": json.loads(tenant.custom_config) if tenant.custom_config else {},
- "created_at": tenant.created_at.isoformat() if tenant.created_at else None,
- "updated_at": tenant.updated_at.isoformat() if tenant.updated_at else None,
+ "created_at": tenant.created_at.isoformat() + "Z" if tenant.created_at else None,
+ "updated_at": tenant.updated_at.isoformat() + "Z" if tenant.updated_at else None,
}
return {
diff --git a/api/controllers/inner_api/wraps.py b/api/controllers/inner_api/wraps.py
index d4587235f6aef8..86d3ad3dc5ec96 100644
--- a/api/controllers/inner_api/wraps.py
+++ b/api/controllers/inner_api/wraps.py
@@ -10,7 +10,7 @@
from models.model import EndUser
-def inner_api_only(view):
+def enterprise_inner_api_only(view):
@wraps(view)
def decorated(*args, **kwargs):
if not dify_config.INNER_API:
@@ -18,7 +18,7 @@ def decorated(*args, **kwargs):
# get header 'X-Inner-Api-Key'
inner_api_key = request.headers.get("X-Inner-Api-Key")
- if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY:
+ if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY_FOR_PLUGIN:
abort(401)
return view(*args, **kwargs)
@@ -26,7 +26,7 @@ def decorated(*args, **kwargs):
return decorated
-def inner_api_user_auth(view):
+def enterprise_inner_api_user_auth(view):
@wraps(view)
def decorated(*args, **kwargs):
if not dify_config.INNER_API:
@@ -60,3 +60,19 @@ def decorated(*args, **kwargs):
return view(*args, **kwargs)
return decorated
+
+
+def plugin_inner_api_only(view):
+ @wraps(view)
+ def decorated(*args, **kwargs):
+ if not dify_config.PLUGIN_DAEMON_KEY:
+ abort(404)
+
+ # get header 'X-Inner-Api-Key'
+ inner_api_key = request.headers.get("X-Inner-Api-Key")
+ if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY_FOR_PLUGIN:
+ abort(404)
+
+ return view(*args, **kwargs)
+
+ return decorated
diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py
index 773ea0e0c69385..d5a5afd6a6a942 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.wraps import FetchUserArg, WhereisUserArg, validate_app_token
from core.app.entities.app_invoke_entities import InvokeFrom
from fields.conversation_fields import message_file_fields
+from fields.message_fields import feedback_fields, retriever_resource_fields
from fields.raws import FilesContainedField
from libs.helper import TimestampField, uuid_value
from models.model import App, AppMode, EndUser
@@ -18,26 +19,6 @@
class MessageListApi(Resource):
- feedback_fields = {"rating": fields.String}
- retriever_resource_fields = {
- "id": fields.String,
- "message_id": fields.String,
- "position": fields.Integer,
- "dataset_id": fields.String,
- "dataset_name": fields.String,
- "document_id": fields.String,
- "document_name": fields.String,
- "data_source_type": fields.String,
- "segment_id": fields.String,
- "score": fields.Float,
- "hit_count": fields.Integer,
- "word_count": fields.Integer,
- "segment_position": fields.Integer,
- "index_node_hash": fields.String,
- "content": fields.String,
- "created_at": TimestampField,
- }
-
agent_thought_fields = {
"id": fields.String,
"chain_id": fields.String,
@@ -89,7 +70,7 @@ def get(self, app_model: App, end_user: EndUser):
try:
return MessageService.pagination_by_first_id(
- app_model, end_user, args["conversation_id"], args["first_id"], args["limit"]
+ app_model, end_user, args["conversation_id"], args["first_id"], args["limit"], "desc"
)
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
diff --git a/api/controllers/service_api/dataset/document.py b/api/controllers/service_api/dataset/document.py
index 3053e75a0c17ce..d4e67b659614f9 100644
--- a/api/controllers/service_api/dataset/document.py
+++ b/api/controllers/service_api/dataset/document.py
@@ -336,6 +336,10 @@ def post(self, tenant_id, dataset_id, document_id):
if not dataset:
raise ValueError("Dataset is not exist.")
+
+ # indexing_technique is already set in dataset since this is an update
+ args["indexing_technique"] = dataset.indexing_technique
+
if "file" in request.files:
# save file info
file = request.files["file"]
diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py
index e6e546690c6557..494b357d46624b 100644
--- a/api/controllers/web/message.py
+++ b/api/controllers/web/message.py
@@ -21,7 +21,7 @@
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.errors.invoke import InvokeError
from fields.conversation_fields import message_file_fields
-from fields.message_fields import agent_thought_fields
+from fields.message_fields import agent_thought_fields, feedback_fields, retriever_resource_fields
from fields.raws import FilesContainedField
from libs import helper
from libs.helper import TimestampField, uuid_value
@@ -34,27 +34,6 @@
class MessageListApi(WebApiResource):
- feedback_fields = {"rating": fields.String}
-
- retriever_resource_fields = {
- "id": fields.String,
- "message_id": fields.String,
- "position": fields.Integer,
- "dataset_id": fields.String,
- "dataset_name": fields.String,
- "document_id": fields.String,
- "document_name": fields.String,
- "data_source_type": fields.String,
- "segment_id": fields.String,
- "score": fields.Float,
- "hit_count": fields.Integer,
- "word_count": fields.Integer,
- "segment_position": fields.Integer,
- "index_node_hash": fields.String,
- "content": fields.String,
- "created_at": TimestampField,
- }
-
message_fields = {
"id": fields.String,
"conversation_id": fields.String,
diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py
index ae086ba8ed66ec..13c4e4c3d18c05 100644
--- a/api/core/agent/base_agent_runner.py
+++ b/api/core/agent/base_agent_runner.py
@@ -1,7 +1,6 @@
import json
import logging
import uuid
-from datetime import UTC, datetime
from typing import Optional, Union, cast
from core.agent.entities import AgentEntity, AgentToolEntity
@@ -32,19 +31,16 @@
from core.model_runtime.entities.message_entities import ImagePromptMessageContent
from core.model_runtime.entities.model_entities import ModelFeature
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
-from core.model_runtime.utils.encoders import jsonable_encoder
from core.prompt.utils.extract_thread_messages import extract_thread_messages
+from core.tools.__base.tool import Tool
from core.tools.entities.tool_entities import (
ToolParameter,
- ToolRuntimeVariablePool,
)
-from core.tools.tool.dataset_retriever_tool import DatasetRetrieverTool
-from core.tools.tool.tool import Tool
from core.tools.tool_manager import ToolManager
+from core.tools.utils.dataset_retriever_tool import DatasetRetrieverTool
from extensions.ext_database import db
from factories import file_factory
from models.model import Conversation, Message, MessageAgentThought, MessageFile
-from models.tools import ToolConversationVariables
logger = logging.getLogger(__name__)
@@ -62,11 +58,9 @@ def __init__(
queue_manager: AppQueueManager,
message: Message,
user_id: str,
+ model_instance: ModelInstance,
memory: Optional[TokenBufferMemory] = None,
prompt_messages: Optional[list[PromptMessage]] = None,
- variables_pool: Optional[ToolRuntimeVariablePool] = None,
- db_variables: Optional[ToolConversationVariables] = None,
- model_instance: ModelInstance,
) -> None:
self.tenant_id = tenant_id
self.application_generate_entity = application_generate_entity
@@ -79,8 +73,6 @@ def __init__(
self.user_id = user_id
self.memory = memory
self.history_prompt_messages = self.organize_agent_history(prompt_messages=prompt_messages or [])
- self.variables_pool = variables_pool
- self.db_variables_pool = db_variables
self.model_instance = model_instance
# init callback
@@ -141,11 +133,10 @@ def _convert_tool_to_prompt_message_tool(self, tool: AgentToolEntity) -> tuple[P
agent_tool=tool,
invoke_from=self.application_generate_entity.invoke_from,
)
- tool_entity.load_variables(self.variables_pool)
-
+ assert tool_entity.entity.description
message_tool = PromptMessageTool(
name=tool.tool_name,
- description=tool_entity.description.llm if tool_entity.description else "",
+ description=tool_entity.entity.description.llm,
parameters={
"type": "object",
"properties": {},
@@ -153,7 +144,7 @@ def _convert_tool_to_prompt_message_tool(self, tool: AgentToolEntity) -> tuple[P
},
)
- parameters = tool_entity.get_all_runtime_parameters()
+ parameters = tool_entity.get_merged_runtime_parameters()
for parameter in parameters:
if parameter.form != ToolParameter.ToolParameterForm.LLM:
continue
@@ -186,9 +177,11 @@ def _convert_dataset_retriever_tool_to_prompt_message_tool(self, tool: DatasetRe
"""
convert dataset retriever tool to prompt message tool
"""
+ assert tool.entity.description
+
prompt_tool = PromptMessageTool(
- name=tool.identity.name if tool.identity else "unknown",
- description=tool.description.llm if tool.description else "",
+ name=tool.entity.identity.name,
+ description=tool.entity.description.llm,
parameters={
"type": "object",
"properties": {},
@@ -234,8 +227,7 @@ def _init_prompt_tools(self) -> tuple[dict[str, Tool], list[PromptMessageTool]]:
# save prompt tool
prompt_messages_tools.append(prompt_tool)
# save tool entity
- if dataset_tool.identity is not None:
- tool_instances[dataset_tool.identity.name] = dataset_tool
+ tool_instances[dataset_tool.entity.identity.name] = dataset_tool
return tool_instances, prompt_messages_tools
@@ -320,24 +312,24 @@ def create_agent_thought(
def save_agent_thought(
self,
agent_thought: MessageAgentThought,
- tool_name: str,
- tool_input: Union[str, dict],
- thought: str,
+ tool_name: str | None,
+ tool_input: Union[str, dict, None],
+ thought: str | None,
observation: Union[str, dict, None],
tool_invoke_meta: Union[str, dict, None],
- answer: str,
+ answer: str | None,
messages_ids: list[str],
llm_usage: LLMUsage | None = None,
):
"""
Save agent thought
"""
- queried_thought = (
+ updated_agent_thought = (
db.session.query(MessageAgentThought).filter(MessageAgentThought.id == agent_thought.id).first()
)
- if not queried_thought:
- raise ValueError(f"Agent thought {agent_thought.id} not found")
- agent_thought = queried_thought
+ if not updated_agent_thought:
+ raise ValueError("agent thought not found")
+ agent_thought = updated_agent_thought
if thought:
agent_thought.thought = thought
@@ -349,39 +341,39 @@ def save_agent_thought(
if isinstance(tool_input, dict):
try:
tool_input = json.dumps(tool_input, ensure_ascii=False)
- except Exception as e:
+ except Exception:
tool_input = json.dumps(tool_input)
- agent_thought.tool_input = tool_input
+ updated_agent_thought.tool_input = tool_input
if observation:
if isinstance(observation, dict):
try:
observation = json.dumps(observation, ensure_ascii=False)
- except Exception as e:
+ except Exception:
observation = json.dumps(observation)
- agent_thought.observation = observation
+ updated_agent_thought.observation = observation
if answer:
agent_thought.answer = answer
if messages_ids is not None and len(messages_ids) > 0:
- agent_thought.message_files = json.dumps(messages_ids)
+ updated_agent_thought.message_files = json.dumps(messages_ids)
if llm_usage:
- agent_thought.message_token = llm_usage.prompt_tokens
- agent_thought.message_price_unit = llm_usage.prompt_price_unit
- agent_thought.message_unit_price = llm_usage.prompt_unit_price
- agent_thought.answer_token = llm_usage.completion_tokens
- agent_thought.answer_price_unit = llm_usage.completion_price_unit
- agent_thought.answer_unit_price = llm_usage.completion_unit_price
- agent_thought.tokens = llm_usage.total_tokens
- agent_thought.total_price = llm_usage.total_price
+ updated_agent_thought.message_token = llm_usage.prompt_tokens
+ updated_agent_thought.message_price_unit = llm_usage.prompt_price_unit
+ updated_agent_thought.message_unit_price = llm_usage.prompt_unit_price
+ updated_agent_thought.answer_token = llm_usage.completion_tokens
+ updated_agent_thought.answer_price_unit = llm_usage.completion_price_unit
+ updated_agent_thought.answer_unit_price = llm_usage.completion_unit_price
+ updated_agent_thought.tokens = llm_usage.total_tokens
+ updated_agent_thought.total_price = llm_usage.total_price
# check if tool labels is not empty
- labels = agent_thought.tool_labels or {}
- tools = agent_thought.tool.split(";") if agent_thought.tool else []
+ labels = updated_agent_thought.tool_labels or {}
+ tools = updated_agent_thought.tool.split(";") if updated_agent_thought.tool else []
for tool in tools:
if not tool:
continue
@@ -392,39 +384,17 @@ def save_agent_thought(
else:
labels[tool] = {"en_US": tool, "zh_Hans": tool}
- agent_thought.tool_labels_str = json.dumps(labels)
+ updated_agent_thought.tool_labels_str = json.dumps(labels)
if tool_invoke_meta is not None:
if isinstance(tool_invoke_meta, dict):
try:
tool_invoke_meta = json.dumps(tool_invoke_meta, ensure_ascii=False)
- except Exception as e:
+ except Exception:
tool_invoke_meta = json.dumps(tool_invoke_meta)
- agent_thought.tool_meta_str = tool_invoke_meta
-
- db.session.commit()
- db.session.close()
-
- def update_db_variables(self, tool_variables: ToolRuntimeVariablePool, db_variables: ToolConversationVariables):
- """
- convert tool variables to db variables
- """
- queried_variables = (
- db.session.query(ToolConversationVariables)
- .filter(
- ToolConversationVariables.conversation_id == self.message.conversation_id,
- )
- .first()
- )
-
- if not queried_variables:
- return
+ updated_agent_thought.tool_meta_str = tool_invoke_meta
- db_variables = queried_variables
-
- db_variables.updated_at = datetime.now(UTC).replace(tzinfo=None)
- db_variables.variables_str = json.dumps(jsonable_encoder(tool_variables.pool))
db.session.commit()
db.session.close()
@@ -464,11 +434,11 @@ def organize_agent_history(self, prompt_messages: list[PromptMessage]) -> list[P
tool_call_response: list[ToolPromptMessage] = []
try:
tool_inputs = json.loads(agent_thought.tool_input)
- except Exception as e:
+ except Exception:
tool_inputs = {tool: {} for tool in tools}
try:
tool_responses = json.loads(agent_thought.observation)
- except Exception as e:
+ except Exception:
tool_responses = dict.fromkeys(tools, agent_thought.observation)
for tool in tools:
@@ -515,7 +485,11 @@ def organize_agent_user_prompt(self, message: Message) -> UserPromptMessage:
files = db.session.query(MessageFile).filter(MessageFile.message_id == message.id).all()
if not files:
return UserPromptMessage(content=message.query)
- file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict())
+ if message.app_model_config:
+ file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict())
+ else:
+ file_extra_config = None
+
if not file_extra_config:
return UserPromptMessage(content=message.query)
diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py
index bbe1865daf058c..ae70ff2bf183ae 100644
--- a/api/core/agent/cot_agent_runner.py
+++ b/api/core/agent/cot_agent_runner.py
@@ -1,6 +1,6 @@
import json
from abc import ABC, abstractmethod
-from collections.abc import Generator, Mapping
+from collections.abc import Generator, Mapping, Sequence
from typing import Any, Optional
from core.agent.base_agent_runner import BaseAgentRunner
@@ -18,8 +18,8 @@
)
from core.ops.ops_trace_manager import TraceQueueManager
from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform
+from core.tools.__base.tool import Tool
from core.tools.entities.tool_entities import ToolInvokeMeta
-from core.tools.tool.tool import Tool
from core.tools.tool_engine import ToolEngine
from models.model import Message
@@ -27,11 +27,11 @@
class CotAgentRunner(BaseAgentRunner, ABC):
_is_first_iteration = True
_ignore_observation_providers = ["wenxin"]
- _historic_prompt_messages: list[PromptMessage] | None = None
- _agent_scratchpad: list[AgentScratchpadUnit] | None = None
- _instruction: str = "" # FIXME this must be str for now
- _query: str | None = None
- _prompt_messages_tools: list[PromptMessageTool] = []
+ _historic_prompt_messages: list[PromptMessage]
+ _agent_scratchpad: list[AgentScratchpadUnit]
+ _instruction: str
+ _query: str
+ _prompt_messages_tools: Sequence[PromptMessageTool]
def run(
self,
@@ -42,6 +42,7 @@ def run(
"""
Run Cot agent application
"""
+
app_generate_entity = self.application_generate_entity
self._repack_app_generate_entity(app_generate_entity)
self._init_react_state(query)
@@ -54,17 +55,19 @@ def run(
app_generate_entity.model_conf.stop.append("Observation")
app_config = self.app_config
+ assert app_config.agent
# init instruction
inputs = inputs or {}
- instruction = app_config.prompt_template.simple_prompt_template
- self._instruction = self._fill_in_inputs_from_external_data_tools(instruction=instruction or "", inputs=inputs)
+ instruction = app_config.prompt_template.simple_prompt_template or ""
+ self._instruction = self._fill_in_inputs_from_external_data_tools(instruction, inputs)
iteration_step = 1
max_iteration_steps = min(app_config.agent.max_iteration if app_config.agent else 5, 5) + 1
# convert tools into ModelRuntime Tool format
- tool_instances, self._prompt_messages_tools = self._init_prompt_tools()
+ tool_instances, prompt_messages_tools = self._init_prompt_tools()
+ self._prompt_messages_tools = prompt_messages_tools
function_call_state = True
llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
@@ -116,14 +119,7 @@ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: L
callbacks=[],
)
- if not isinstance(chunks, Generator):
- raise ValueError("Expected streaming response from LLM")
-
- # check llm result
- if not chunks:
- raise ValueError("failed to invoke llm")
-
- usage_dict: dict[str, Optional[LLMUsage]] = {"usage": None}
+ usage_dict: dict[str, Optional[LLMUsage]] = {}
react_chunks = CotAgentOutputParser.handle_react_stream_output(chunks, usage_dict)
scratchpad = AgentScratchpadUnit(
agent_response="",
@@ -143,25 +139,25 @@ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: L
if isinstance(chunk, AgentScratchpadUnit.Action):
action = chunk
# detect action
- if scratchpad.agent_response is not None:
- scratchpad.agent_response += json.dumps(chunk.model_dump())
+ assert scratchpad.agent_response is not None
+ scratchpad.agent_response += json.dumps(chunk.model_dump())
scratchpad.action_str = json.dumps(chunk.model_dump())
scratchpad.action = action
else:
- if scratchpad.agent_response is not None:
- scratchpad.agent_response += chunk
- if scratchpad.thought is not None:
- scratchpad.thought += chunk
+ assert scratchpad.agent_response is not None
+ scratchpad.agent_response += chunk
+ assert scratchpad.thought is not None
+ scratchpad.thought += chunk
yield LLMResultChunk(
model=self.model_config.model,
prompt_messages=prompt_messages,
system_fingerprint="",
delta=LLMResultChunkDelta(index=0, message=AssistantPromptMessage(content=chunk), usage=None),
)
- if scratchpad.thought is not None:
- scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you"
- if self._agent_scratchpad is not None:
- self._agent_scratchpad.append(scratchpad)
+
+ assert scratchpad.thought is not None
+ scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you"
+ self._agent_scratchpad.append(scratchpad)
# get llm usage
if "usage" in usage_dict:
@@ -256,8 +252,6 @@ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: L
answer=final_answer,
messages_ids=[],
)
- if self.variables_pool is not None and self.db_variables_pool is not None:
- self.update_db_variables(self.variables_pool, self.db_variables_pool)
# publish end event
self.queue_manager.publish(
QueueMessageEndEvent(
@@ -275,7 +269,7 @@ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: L
def _handle_invoke_action(
self,
action: AgentScratchpadUnit.Action,
- tool_instances: dict[str, Tool],
+ tool_instances: Mapping[str, Tool],
message_file_ids: list[str],
trace_manager: Optional[TraceQueueManager] = None,
) -> tuple[str, ToolInvokeMeta]:
@@ -315,11 +309,7 @@ def _handle_invoke_action(
)
# publish files
- for message_file_id, save_as in message_files:
- if save_as is not None and self.variables_pool:
- # FIXME the save_as type is confusing, it should be a string or not
- self.variables_pool.set_file(tool_name=tool_call_name, value=message_file_id, name=str(save_as))
-
+ for message_file_id in message_files:
# publish message file
self.queue_manager.publish(
QueueMessageFileEvent(message_file_id=message_file_id), PublishFrom.APPLICATION_MANAGER
@@ -342,7 +332,7 @@ def _fill_in_inputs_from_external_data_tools(self, instruction: str, inputs: Map
for key, value in inputs.items():
try:
instruction = instruction.replace(f"{{{{{key}}}}}", str(value))
- except Exception as e:
+ except Exception:
continue
return instruction
@@ -379,7 +369,7 @@ def _format_assistant_message(self, agent_scratchpad: list[AgentScratchpadUnit])
return message
def _organize_historic_prompt_messages(
- self, current_session_messages: Optional[list[PromptMessage]] = None
+ self, current_session_messages: list[PromptMessage] | None = None
) -> list[PromptMessage]:
"""
organize historic prompt messages
@@ -391,8 +381,7 @@ def _organize_historic_prompt_messages(
for message in self.history_prompt_messages:
if isinstance(message, AssistantPromptMessage):
if not current_scratchpad:
- if not isinstance(message.content, str | None):
- raise NotImplementedError("expected str type")
+ assert isinstance(message.content, str)
current_scratchpad = AgentScratchpadUnit(
agent_response=message.content,
thought=message.content or "I am thinking about how to help you",
@@ -411,9 +400,8 @@ def _organize_historic_prompt_messages(
except:
pass
elif isinstance(message, ToolPromptMessage):
- if not current_scratchpad:
- continue
- if isinstance(message.content, str):
+ if current_scratchpad:
+ assert isinstance(message.content, str)
current_scratchpad.observation = message.content
else:
raise NotImplementedError("expected str type")
diff --git a/api/core/agent/cot_chat_agent_runner.py b/api/core/agent/cot_chat_agent_runner.py
index 6a96c349b2611c..7d407a49768cbd 100644
--- a/api/core/agent/cot_chat_agent_runner.py
+++ b/api/core/agent/cot_chat_agent_runner.py
@@ -19,8 +19,8 @@ def _organize_system_prompt(self) -> SystemPromptMessage:
"""
Organize system prompt
"""
- if not self.app_config.agent:
- raise ValueError("Agent configuration is not set")
+ assert self.app_config.agent
+ assert self.app_config.agent.prompt
prompt_entity = self.app_config.agent.prompt
if not prompt_entity:
@@ -83,8 +83,10 @@ def _organize_prompt_messages(self) -> list[PromptMessage]:
assistant_message.content = "" # FIXME: type check tell mypy that assistant_message.content is str
for unit in agent_scratchpad:
if unit.is_final():
+ assert isinstance(assistant_message.content, str)
assistant_message.content += f"Final Answer: {unit.agent_response}"
else:
+ assert isinstance(assistant_message.content, str)
assistant_message.content += f"Thought: {unit.thought}\n\n"
if unit.action_str:
assistant_message.content += f"Action: {unit.action_str}\n\n"
diff --git a/api/core/agent/entities.py b/api/core/agent/entities.py
index 2ae87dca3f8cbd..e68b4f235610c6 100644
--- a/api/core/agent/entities.py
+++ b/api/core/agent/entities.py
@@ -1,7 +1,9 @@
-from enum import Enum
-from typing import Any, Literal, Optional, Union
+from enum import StrEnum
+from typing import Any, Optional, Union
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
+
+from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType
class AgentToolEntity(BaseModel):
@@ -9,10 +11,11 @@ class AgentToolEntity(BaseModel):
Agent Tool Entity.
"""
- provider_type: Literal["builtin", "api", "workflow"]
+ provider_type: ToolProviderType
provider_id: str
tool_name: str
- tool_parameters: dict[str, Any] = {}
+ tool_parameters: dict[str, Any] = Field(default_factory=dict)
+ plugin_unique_identifier: str | None = None
class AgentPromptEntity(BaseModel):
@@ -66,7 +69,7 @@ class AgentEntity(BaseModel):
Agent Entity.
"""
- class Strategy(Enum):
+ class Strategy(StrEnum):
"""
Agent Strategy.
"""
@@ -78,5 +81,13 @@ class Strategy(Enum):
model: str
strategy: Strategy
prompt: Optional[AgentPromptEntity] = None
- tools: list[AgentToolEntity] | None = None
+ tools: Optional[list[AgentToolEntity]] = None
max_iteration: int = 5
+
+
+class AgentInvokeMessage(ToolInvokeMessage):
+ """
+ Agent Invoke Message.
+ """
+
+ pass
diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py
index b862c96072aaa0..f45fa5c66e49ed 100644
--- a/api/core/agent/fc_agent_runner.py
+++ b/api/core/agent/fc_agent_runner.py
@@ -46,18 +46,20 @@ def run(self, message: Message, query: str, **kwargs: Any) -> Generator[LLMResul
# convert tools into ModelRuntime Tool format
tool_instances, prompt_messages_tools = self._init_prompt_tools()
+ assert app_config.agent
+
iteration_step = 1
max_iteration_steps = min(app_config.agent.max_iteration, 5) + 1
# continue to run until there is not any tool call
function_call_state = True
- llm_usage: dict[str, LLMUsage] = {"usage": LLMUsage.empty_usage()}
+ llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
final_answer = ""
# get tracing instance
trace_manager = app_generate_entity.trace_manager
- def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
+ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: LLMUsage):
if not final_llm_usage_dict["usage"]:
final_llm_usage_dict["usage"] = usage
else:
@@ -107,7 +109,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
current_llm_usage = None
- if self.stream_tool_call and isinstance(chunks, Generator):
+ if isinstance(chunks, Generator):
is_first_chunk = True
for chunk in chunks:
if is_first_chunk:
@@ -124,7 +126,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
tool_call_inputs = json.dumps(
{tool_call[1]: tool_call[2] for tool_call in tool_calls}, ensure_ascii=False
)
- except json.JSONDecodeError as e:
+ except json.JSONDecodeError:
# ensure ascii to avoid encoding error
tool_call_inputs = json.dumps({tool_call[1]: tool_call[2] for tool_call in tool_calls})
@@ -140,7 +142,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
current_llm_usage = chunk.delta.usage
yield chunk
- elif not self.stream_tool_call and isinstance(chunks, LLMResult):
+ else:
result = chunks
# check if there is any tool call
if self.check_blocking_tool_calls(result):
@@ -151,7 +153,7 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
tool_call_inputs = json.dumps(
{tool_call[1]: tool_call[2] for tool_call in tool_calls}, ensure_ascii=False
)
- except json.JSONDecodeError as e:
+ except json.JSONDecodeError:
# ensure ascii to avoid encoding error
tool_call_inputs = json.dumps({tool_call[1]: tool_call[2] for tool_call in tool_calls})
@@ -183,8 +185,6 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
usage=result.usage,
),
)
- else:
- raise RuntimeError(f"invalid chunks type: {type(chunks)}")
assistant_message = AssistantPromptMessage(content="", tool_calls=[])
if tool_calls:
@@ -243,15 +243,12 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
invoke_from=self.application_generate_entity.invoke_from,
agent_tool_callback=self.agent_callback,
trace_manager=trace_manager,
+ app_id=self.application_generate_entity.app_config.app_id,
+ message_id=self.message.id,
+ conversation_id=self.conversation.id,
)
# publish files
- for message_file_id, save_as in message_files:
- if save_as:
- if self.variables_pool:
- self.variables_pool.set_file(
- tool_name=tool_call_name, value=message_file_id, name=save_as
- )
-
+ for message_file_id in message_files:
# publish message file
self.queue_manager.publish(
QueueMessageFileEvent(message_file_id=message_file_id), PublishFrom.APPLICATION_MANAGER
@@ -303,8 +300,6 @@ def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
iteration_step += 1
- if self.variables_pool and self.db_variables_pool:
- self.update_db_variables(self.variables_pool, self.db_variables_pool)
# publish end event
self.queue_manager.publish(
QueueMessageEndEvent(
@@ -335,9 +330,7 @@ def check_blocking_tool_calls(self, llm_result: LLMResult) -> bool:
return True
return False
- def extract_tool_calls(
- self, llm_result_chunk: LLMResultChunk
- ) -> Union[None, list[tuple[str, str, dict[str, Any]]]]:
+ def extract_tool_calls(self, llm_result_chunk: LLMResultChunk) -> list[tuple[str, str, dict[str, Any]]]:
"""
Extract tool calls from llm result chunk
@@ -360,7 +353,7 @@ def extract_tool_calls(
return tool_calls
- def extract_blocking_tool_calls(self, llm_result: LLMResult) -> Union[None, list[tuple[str, str, dict[str, Any]]]]:
+ def extract_blocking_tool_calls(self, llm_result: LLMResult) -> list[tuple[str, str, dict[str, Any]]]:
"""
Extract blocking tool calls from llm result
@@ -383,9 +376,7 @@ def extract_blocking_tool_calls(self, llm_result: LLMResult) -> Union[None, list
return tool_calls
- def _init_system_message(
- self, prompt_template: str, prompt_messages: Optional[list[PromptMessage]] = None
- ) -> list[PromptMessage]:
+ def _init_system_message(self, prompt_template: str, prompt_messages: list[PromptMessage]) -> list[PromptMessage]:
"""
Initialize system message
"""
diff --git a/api/core/agent/plugin_entities.py b/api/core/agent/plugin_entities.py
new file mode 100644
index 00000000000000..92bd5500eff520
--- /dev/null
+++ b/api/core/agent/plugin_entities.py
@@ -0,0 +1,89 @@
+import enum
+from typing import Any, Optional
+
+from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
+
+from core.entities.parameter_entities import CommonParameterType
+from core.plugin.entities.parameters import (
+ PluginParameter,
+ as_normal_type,
+ cast_parameter_value,
+ init_frontend_parameter,
+)
+from core.tools.entities.common_entities import I18nObject
+from core.tools.entities.tool_entities import (
+ ToolIdentity,
+ ToolProviderIdentity,
+)
+
+
+class AgentStrategyProviderIdentity(ToolProviderIdentity):
+ """
+ Inherits from ToolProviderIdentity, without any additional fields.
+ """
+
+ pass
+
+
+class AgentStrategyParameter(PluginParameter):
+ class AgentStrategyParameterType(enum.StrEnum):
+ """
+ Keep all the types from PluginParameterType
+ """
+
+ STRING = CommonParameterType.STRING.value
+ NUMBER = CommonParameterType.NUMBER.value
+ BOOLEAN = CommonParameterType.BOOLEAN.value
+ SELECT = CommonParameterType.SELECT.value
+ SECRET_INPUT = CommonParameterType.SECRET_INPUT.value
+ FILE = CommonParameterType.FILE.value
+ FILES = CommonParameterType.FILES.value
+ APP_SELECTOR = CommonParameterType.APP_SELECTOR.value
+ MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR.value
+ TOOLS_SELECTOR = CommonParameterType.TOOLS_SELECTOR.value
+
+ # deprecated, should not use.
+ SYSTEM_FILES = CommonParameterType.SYSTEM_FILES.value
+
+ def as_normal_type(self):
+ return as_normal_type(self)
+
+ def cast_value(self, value: Any):
+ return cast_parameter_value(self, value)
+
+ type: AgentStrategyParameterType = Field(..., description="The type of the parameter")
+
+ def init_frontend_parameter(self, value: Any):
+ return init_frontend_parameter(self, self.type, value)
+
+
+class AgentStrategyProviderEntity(BaseModel):
+ identity: AgentStrategyProviderIdentity
+ plugin_id: Optional[str] = Field(None, description="The id of the plugin")
+
+
+class AgentStrategyIdentity(ToolIdentity):
+ """
+ Inherits from ToolIdentity, without any additional fields.
+ """
+
+ pass
+
+
+class AgentStrategyEntity(BaseModel):
+ identity: AgentStrategyIdentity
+ parameters: list[AgentStrategyParameter] = Field(default_factory=list)
+ description: I18nObject = Field(..., description="The description of the agent strategy")
+ output_schema: Optional[dict] = None
+
+ # pydantic configs
+ model_config = ConfigDict(protected_namespaces=())
+
+ @field_validator("parameters", mode="before")
+ @classmethod
+ def set_parameters(cls, v, validation_info: ValidationInfo) -> list[AgentStrategyParameter]:
+ return v or []
+
+
+class AgentProviderEntityWithPlugin(AgentStrategyProviderEntity):
+ strategies: list[AgentStrategyEntity] = Field(default_factory=list)
diff --git a/api/core/agent/strategy/base.py b/api/core/agent/strategy/base.py
new file mode 100644
index 00000000000000..ead81a7a0e28a0
--- /dev/null
+++ b/api/core/agent/strategy/base.py
@@ -0,0 +1,42 @@
+from abc import ABC, abstractmethod
+from collections.abc import Generator, Sequence
+from typing import Any, Optional
+
+from core.agent.entities import AgentInvokeMessage
+from core.agent.plugin_entities import AgentStrategyParameter
+
+
+class BaseAgentStrategy(ABC):
+ """
+ Agent Strategy
+ """
+
+ def invoke(
+ self,
+ params: dict[str, Any],
+ user_id: str,
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[AgentInvokeMessage, None, None]:
+ """
+ Invoke the agent strategy.
+ """
+ yield from self._invoke(params, user_id, conversation_id, app_id, message_id)
+
+ def get_parameters(self) -> Sequence[AgentStrategyParameter]:
+ """
+ Get the parameters for the agent strategy.
+ """
+ return []
+
+ @abstractmethod
+ def _invoke(
+ self,
+ params: dict[str, Any],
+ user_id: str,
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[AgentInvokeMessage, None, None]:
+ pass
diff --git a/api/core/agent/strategy/plugin.py b/api/core/agent/strategy/plugin.py
new file mode 100644
index 00000000000000..a4b25f46e6e404
--- /dev/null
+++ b/api/core/agent/strategy/plugin.py
@@ -0,0 +1,59 @@
+from collections.abc import Generator, Sequence
+from typing import Any, Optional
+
+from core.agent.entities import AgentInvokeMessage
+from core.agent.plugin_entities import AgentStrategyEntity, AgentStrategyParameter
+from core.agent.strategy.base import BaseAgentStrategy
+from core.plugin.manager.agent import PluginAgentManager
+from core.plugin.utils.converter import convert_parameters_to_plugin_format
+
+
+class PluginAgentStrategy(BaseAgentStrategy):
+ """
+ Agent Strategy
+ """
+
+ tenant_id: str
+ declaration: AgentStrategyEntity
+
+ def __init__(self, tenant_id: str, declaration: AgentStrategyEntity):
+ self.tenant_id = tenant_id
+ self.declaration = declaration
+
+ def get_parameters(self) -> Sequence[AgentStrategyParameter]:
+ return self.declaration.parameters
+
+ def initialize_parameters(self, params: dict[str, Any]) -> dict[str, Any]:
+ """
+ Initialize the parameters for the agent strategy.
+ """
+ for parameter in self.declaration.parameters:
+ params[parameter.name] = parameter.init_frontend_parameter(params.get(parameter.name))
+ return params
+
+ def _invoke(
+ self,
+ params: dict[str, Any],
+ user_id: str,
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[AgentInvokeMessage, None, None]:
+ """
+ Invoke the agent strategy.
+ """
+ manager = PluginAgentManager()
+
+ initialized_params = self.initialize_parameters(params)
+ params = convert_parameters_to_plugin_format(initialized_params)
+
+ yield from manager.invoke(
+ tenant_id=self.tenant_id,
+ user_id=user_id,
+ agent_provider=self.declaration.identity.provider,
+ agent_strategy=self.declaration.identity.name,
+ agent_params=params,
+ conversation_id=conversation_id,
+ app_id=app_id,
+ message_id=message_id,
+ )
diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py
index cdc82860c6cc4d..ecb045a30ad4a1 100644
--- a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py
+++ b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py
@@ -4,7 +4,8 @@
from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
from core.entities.model_entities import ModelStatus
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
-from core.model_runtime.entities.model_entities import ModelType
+from core.model_runtime.entities.llm_entities import LLMMode
+from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.provider_manager import ProviderManager
@@ -63,14 +64,14 @@ def convert(cls, app_config: EasyUIBasedAppConfig) -> ModelConfigWithCredentials
stop = completion_params["stop"]
del completion_params["stop"]
+ model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials)
+
# get model mode
model_mode = model_config.mode
if not model_mode:
- mode_enum = model_type_instance.get_model_mode(model=model_config.model, credentials=model_credentials)
-
- model_mode = mode_enum.value
-
- model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials)
+ model_mode = LLMMode.CHAT.value
+ if model_schema and model_schema.model_properties.get(ModelPropertyKey.MODE):
+ model_mode = LLMMode.value_of(model_schema.model_properties[ModelPropertyKey.MODE]).value
if not model_schema:
raise ValueError(f"Model {model_name} not exist.")
diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py
index 6426865115126f..54bca10fc3f698 100644
--- a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py
+++ b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py
@@ -3,7 +3,8 @@
from core.app.app_config.entities import ModelConfigEntity
from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
-from core.model_runtime.model_providers import model_provider_factory
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
+from core.plugin.entities.plugin import ModelProviderID
from core.provider_manager import ProviderManager
@@ -53,9 +54,16 @@ def validate_and_set_defaults(cls, tenant_id: str, config: Mapping[str, Any]) ->
raise ValueError("model must be of object type")
# model.provider
+ model_provider_factory = ModelProviderFactory(tenant_id)
provider_entities = model_provider_factory.get_providers()
model_provider_names = [provider.provider for provider in provider_entities]
- if "provider" not in config["model"] or config["model"]["provider"] not in model_provider_names:
+ if "provider" not in config["model"]:
+ raise ValueError(f"model.provider is required and must be in {str(model_provider_names)}")
+
+ if "/" not in config["model"]["provider"]:
+ config["model"]["provider"] = str(ModelProviderID(config["model"]["provider"]))
+
+ if config["model"]["provider"] not in model_provider_names:
raise ValueError(f"model.provider is required and must be in {str(model_provider_names)}")
# model.name
diff --git a/api/core/app/app_config/entities.py b/api/core/app/app_config/entities.py
index 15bd353484e372..16b69a44688311 100644
--- a/api/core/app/app_config/entities.py
+++ b/api/core/app/app_config/entities.py
@@ -17,8 +17,8 @@ class ModelConfigEntity(BaseModel):
provider: str
model: str
mode: Optional[str] = None
- parameters: dict[str, Any] = {}
- stop: list[str] = []
+ parameters: dict[str, Any] = Field(default_factory=dict)
+ stop: list[str] = Field(default_factory=list)
class AdvancedChatMessageEntity(BaseModel):
@@ -132,7 +132,7 @@ class ExternalDataVariableEntity(BaseModel):
variable: str
type: str
- config: dict[str, Any] = {}
+ config: dict[str, Any] = Field(default_factory=dict)
class DatasetRetrieveConfigEntity(BaseModel):
@@ -188,7 +188,7 @@ class SensitiveWordAvoidanceEntity(BaseModel):
"""
type: str
- config: dict[str, Any] = {}
+ config: dict[str, Any] = Field(default_factory=dict)
class TextToSpeechEntity(BaseModel):
diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py
index 36f71fd47879c9..5cc4b2b0e422df 100644
--- a/api/core/app/apps/advanced_chat/app_generator.py
+++ b/api/core/app/apps/advanced_chat/app_generator.py
@@ -45,8 +45,8 @@ def generate(
user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
- streaming: Literal[True],
- ) -> Generator[str, None, None]: ...
+ streaming: Literal[False],
+ ) -> Mapping[str, Any]: ...
@overload
def generate(
@@ -54,10 +54,10 @@ def generate(
app_model: App,
workflow: Workflow,
user: Union[Account, EndUser],
- args: Mapping[str, Any],
+ args: Mapping,
invoke_from: InvokeFrom,
- streaming: Literal[False],
- ) -> Mapping[str, Any]: ...
+ streaming: Literal[True],
+ ) -> Generator[Mapping | str, None, None]: ...
@overload
def generate(
@@ -65,20 +65,20 @@ def generate(
app_model: App,
workflow: Workflow,
user: Union[Account, EndUser],
- args: Mapping[str, Any],
+ args: Mapping,
invoke_from: InvokeFrom,
- streaming: bool = True,
- ) -> Union[Mapping[str, Any], Generator[str, None, None]]: ...
+ streaming: bool,
+ ) -> Mapping[str, Any] | Generator[str | Mapping, None, None]: ...
def generate(
self,
app_model: App,
workflow: Workflow,
user: Union[Account, EndUser],
- args: Mapping[str, Any],
+ args: Mapping,
invoke_from: InvokeFrom,
streaming: bool = True,
- ):
+ ) -> Mapping[str, Any] | Generator[str | Mapping, None, None]:
"""
Generate App response.
@@ -140,9 +140,7 @@ def generate(
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(
+ inputs=self._prepare_user_inputs(
user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id
),
query=query,
@@ -156,6 +154,8 @@ def generate(
workflow_run_id=workflow_run_id,
)
contexts.tenant_id.set(application_generate_entity.app_config.tenant_id)
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
return self._generate(
workflow=workflow,
@@ -167,8 +167,14 @@ def generate(
)
def single_iteration_generate(
- self, app_model: App, workflow: Workflow, node_id: str, user: Account, args: dict, streaming: bool = True
- ) -> Mapping[str, Any] | Generator[str, None, None]:
+ self,
+ app_model: App,
+ workflow: Workflow,
+ node_id: str,
+ user: Account | EndUser,
+ args: Mapping,
+ streaming: bool = True,
+ ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], Any, None]:
"""
Generate App response.
@@ -205,6 +211,8 @@ def single_iteration_generate(
),
)
contexts.tenant_id.set(application_generate_entity.app_config.tenant_id)
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
return self._generate(
workflow=workflow,
@@ -224,7 +232,7 @@ def _generate(
application_generate_entity: AdvancedChatAppGenerateEntity,
conversation: Optional[Conversation] = None,
stream: bool = True,
- ) -> Mapping[str, Any] | Generator[str, None, None]:
+ ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], Any, None]:
"""
Generate App response.
diff --git a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py
index a506447671abfb..4e6422e2df145b 100644
--- a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py
+++ b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py
@@ -56,7 +56,7 @@ def _process_future(
class AppGeneratorTTSPublisher:
- def __init__(self, tenant_id: str, voice: str):
+ def __init__(self, tenant_id: str, voice: str, language: Optional[str] = None):
self.logger = logging.getLogger(__name__)
self.tenant_id = tenant_id
self.msg_text = ""
@@ -67,7 +67,7 @@ def __init__(self, tenant_id: str, voice: str):
self.model_instance = self.model_manager.get_default_model_instance(
tenant_id=self.tenant_id, model_type=ModelType.TTS
)
- self.voices = self.model_instance.get_tts_voices()
+ self.voices = self.model_instance.get_tts_voices(language=language)
values = [voice.get("value") for voice in self.voices]
self.voice = voice
if not voice or voice not in values:
diff --git a/api/core/app/apps/advanced_chat/app_runner.py b/api/core/app/apps/advanced_chat/app_runner.py
index 6339d798984800..0e918e4929db1c 100644
--- a/api/core/app/apps/advanced_chat/app_runner.py
+++ b/api/core/app/apps/advanced_chat/app_runner.py
@@ -77,7 +77,7 @@ def run(self) -> None:
graph, variable_pool = self._get_graph_and_variable_pool_of_single_iteration(
workflow=workflow,
node_id=self.application_generate_entity.single_iteration_run.node_id,
- user_inputs=self.application_generate_entity.single_iteration_run.inputs,
+ user_inputs=dict(self.application_generate_entity.single_iteration_run.inputs),
)
else:
inputs = self.application_generate_entity.inputs
diff --git a/api/core/app/apps/advanced_chat/generate_response_converter.py b/api/core/app/apps/advanced_chat/generate_response_converter.py
index 5fbd3e9a94906f..b2bff432080162 100644
--- a/api/core/app/apps/advanced_chat/generate_response_converter.py
+++ b/api/core/app/apps/advanced_chat/generate_response_converter.py
@@ -1,4 +1,3 @@
-import json
from collections.abc import Generator
from typing import Any, cast
@@ -58,7 +57,7 @@ def convert_blocking_simple_response(cls, blocking_response: AppBlockingResponse
@classmethod
def convert_stream_full_response(
cls, stream_response: Generator[AppStreamResponse, None, None]
- ) -> Generator[str, Any, None]:
+ ) -> Generator[dict | str, Any, None]:
"""
Convert stream full response.
:param stream_response: stream response
@@ -84,12 +83,12 @@ def convert_stream_full_response(
response_chunk.update(data)
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
@classmethod
def convert_stream_simple_response(
cls, stream_response: Generator[AppStreamResponse, None, None]
- ) -> Generator[str, Any, None]:
+ ) -> Generator[dict | str, Any, None]:
"""
Convert stream simple response.
:param stream_response: stream response
@@ -123,4 +122,4 @@ def convert_stream_simple_response(
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py
index 6aad805034ba9c..3d6ba5ce374408 100644
--- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py
+++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py
@@ -17,6 +17,7 @@
)
from core.app.entities.queue_entities import (
QueueAdvancedChatMessageEndEvent,
+ QueueAgentLogEvent,
QueueAnnotationReplyEvent,
QueueErrorEvent,
QueueIterationCompletedEvent,
@@ -219,7 +220,9 @@ def _wrapper_process_stream_response(
and features_dict["text_to_speech"].get("enabled")
and features_dict["text_to_speech"].get("autoPlay") == "enabled"
):
- tts_publisher = AppGeneratorTTSPublisher(tenant_id, features_dict["text_to_speech"].get("voice"))
+ tts_publisher = AppGeneratorTTSPublisher(
+ tenant_id, features_dict["text_to_speech"].get("voice"), features_dict["text_to_speech"].get("language")
+ )
for response in self._process_stream_response(tts_publisher=tts_publisher, trace_manager=trace_manager):
while True:
@@ -247,7 +250,7 @@ def _wrapper_process_stream_response(
else:
start_listener_time = time.time()
yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id)
- except Exception as e:
+ except Exception:
logger.exception(f"Failed to listen audio message, task_id: {task_id}")
break
if tts_publisher:
@@ -640,6 +643,10 @@ def _process_stream_response(
session.commit()
yield self._message_end_to_stream_response()
+ elif isinstance(event, QueueAgentLogEvent):
+ yield self._workflow_cycle_manager._handle_agent_log(
+ task_id=self._application_generate_entity.task_id, event=event
+ )
else:
continue
diff --git a/api/core/app/apps/agent_chat/app_generator.py b/api/core/app/apps/agent_chat/app_generator.py
index e7f622263eb084..e47428b557b3bf 100644
--- a/api/core/app/apps/agent_chat/app_generator.py
+++ b/api/core/app/apps/agent_chat/app_generator.py
@@ -1,3 +1,4 @@
+import contextvars
import logging
import threading
import uuid
@@ -37,8 +38,8 @@ def generate(
user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
- streaming: Literal[True],
- ) -> Generator[str, None, None]: ...
+ streaming: Literal[False],
+ ) -> Mapping[str, Any]: ...
@overload
def generate(
@@ -48,8 +49,8 @@ def generate(
user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
- streaming: Literal[False],
- ) -> Mapping[str, Any]: ...
+ streaming: Literal[True],
+ ) -> Generator[Mapping | str, None, None]: ...
@overload
def generate(
@@ -60,7 +61,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool,
- ) -> Mapping[str, Any] | Generator[str, None, None]: ...
+ ) -> Union[Mapping, Generator[Mapping | str, None, None]]: ...
def generate(
self,
@@ -70,7 +71,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool = True,
- ):
+ ) -> Union[Mapping, Generator[Mapping | str, None, None]]:
"""
Generate App response.
@@ -148,9 +149,7 @@ def generate(
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(
+ inputs=self._prepare_user_inputs(
user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id
),
query=query,
@@ -182,6 +181,7 @@ def generate(
target=self._generate_worker,
kwargs={
"flask_app": current_app._get_current_object(), # type: ignore
+ "context": contextvars.copy_context(),
"application_generate_entity": application_generate_entity,
"queue_manager": queue_manager,
"conversation_id": conversation.id,
@@ -206,6 +206,7 @@ def generate(
def _generate_worker(
self,
flask_app: Flask,
+ context: contextvars.Context,
application_generate_entity: AgentChatAppGenerateEntity,
queue_manager: AppQueueManager,
conversation_id: str,
@@ -220,6 +221,9 @@ def _generate_worker(
:param message_id: message ID
:return:
"""
+ for var, val in context.items():
+ var.set(val)
+
with flask_app.app_context():
try:
# get conversation and message
diff --git a/api/core/app/apps/agent_chat/app_runner.py b/api/core/app/apps/agent_chat/app_runner.py
index c6705361407c89..72a17171124057 100644
--- a/api/core/app/apps/agent_chat/app_runner.py
+++ b/api/core/app/apps/agent_chat/app_runner.py
@@ -8,18 +8,16 @@
from core.app.apps.agent_chat.app_config_manager import AgentChatAppConfig
from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom
from core.app.apps.base_app_runner import AppRunner
-from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity, ModelConfigWithCredentialsEntity
+from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity
from core.app.entities.queue_entities import QueueAnnotationReplyEvent
from core.memory.token_buffer_memory import TokenBufferMemory
from core.model_manager import ModelInstance
-from core.model_runtime.entities.llm_entities import LLMMode, LLMUsage
+from core.model_runtime.entities.llm_entities import LLMMode
from core.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.moderation.base import ModerationError
-from core.tools.entities.tool_entities import ToolRuntimeVariablePool
from extensions.ext_database import db
-from models.model import App, Conversation, Message, MessageAgentThought
-from models.tools import ToolConversationVariables
+from models.model import App, Conversation, Message
logger = logging.getLogger(__name__)
@@ -64,8 +62,8 @@ def run(
app_record=app_record,
model_config=application_generate_entity.model_conf,
prompt_template_entity=app_config.prompt_template,
- inputs=inputs,
- files=files,
+ inputs=dict(inputs),
+ files=list(files),
query=query,
)
@@ -86,8 +84,8 @@ def run(
app_record=app_record,
model_config=application_generate_entity.model_conf,
prompt_template_entity=app_config.prompt_template,
- inputs=inputs,
- files=files,
+ inputs=dict(inputs),
+ files=list(files),
query=query,
memory=memory,
)
@@ -99,8 +97,8 @@ def run(
app_id=app_record.id,
tenant_id=app_config.tenant_id,
app_generate_entity=application_generate_entity,
- inputs=inputs,
- query=query,
+ inputs=dict(inputs),
+ query=query or "",
message_id=message.id,
)
except ModerationError as e:
@@ -156,9 +154,9 @@ def run(
app_record=app_record,
model_config=application_generate_entity.model_conf,
prompt_template_entity=app_config.prompt_template,
- inputs=inputs,
- files=files,
- query=query,
+ inputs=dict(inputs),
+ files=list(files),
+ query=query or "",
memory=memory,
)
@@ -173,16 +171,7 @@ def run(
return
agent_entity = app_config.agent
- if not agent_entity:
- raise ValueError("Agent entity not found")
-
- # load tool variables
- tool_conversation_variables = self._load_tool_variables(
- conversation_id=conversation.id, user_id=application_generate_entity.user_id, tenant_id=app_config.tenant_id
- )
-
- # convert db variables to tool variables
- tool_variables = self._convert_db_variables_to_tool_variables(tool_conversation_variables)
+ assert agent_entity is not None
# init model instance
model_instance = ModelInstance(
@@ -193,9 +182,9 @@ def run(
app_record=app_record,
model_config=application_generate_entity.model_conf,
prompt_template_entity=app_config.prompt_template,
- inputs=inputs,
- files=files,
- query=query,
+ inputs=dict(inputs),
+ files=list(files),
+ query=query or "",
memory=memory,
)
@@ -243,8 +232,6 @@ def run(
user_id=application_generate_entity.user_id,
memory=memory,
prompt_messages=prompt_message,
- variables_pool=tool_variables,
- db_variables=tool_conversation_variables,
model_instance=model_instance,
)
@@ -261,73 +248,3 @@ def run(
stream=application_generate_entity.stream,
agent=True,
)
-
- def _load_tool_variables(self, conversation_id: str, user_id: str, tenant_id: str) -> ToolConversationVariables:
- """
- load tool variables from database
- """
- tool_variables: ToolConversationVariables | None = (
- db.session.query(ToolConversationVariables)
- .filter(
- ToolConversationVariables.conversation_id == conversation_id,
- ToolConversationVariables.tenant_id == tenant_id,
- )
- .first()
- )
-
- if tool_variables:
- # save tool variables to session, so that we can update it later
- db.session.add(tool_variables)
- else:
- # create new tool variables
- tool_variables = ToolConversationVariables(
- conversation_id=conversation_id,
- user_id=user_id,
- tenant_id=tenant_id,
- variables_str="[]",
- )
- db.session.add(tool_variables)
- db.session.commit()
-
- return tool_variables
-
- def _convert_db_variables_to_tool_variables(
- self, db_variables: ToolConversationVariables
- ) -> ToolRuntimeVariablePool:
- """
- convert db variables to tool variables
- """
- return ToolRuntimeVariablePool(
- **{
- "conversation_id": db_variables.conversation_id,
- "user_id": db_variables.user_id,
- "tenant_id": db_variables.tenant_id,
- "pool": db_variables.variables,
- }
- )
-
- def _get_usage_of_all_agent_thoughts(
- self, model_config: ModelConfigWithCredentialsEntity, message: Message
- ) -> LLMUsage:
- """
- Get usage of all agent thoughts
- :param model_config: model config
- :param message: message
- :return:
- """
- agent_thoughts = (
- db.session.query(MessageAgentThought).filter(MessageAgentThought.message_id == message.id).all()
- )
-
- all_message_tokens = 0
- all_answer_tokens = 0
- for agent_thought in agent_thoughts:
- all_message_tokens += agent_thought.message_tokens
- all_answer_tokens += agent_thought.answer_tokens
-
- model_type_instance = model_config.provider_model_bundle.model_type_instance
- model_type_instance = cast(LargeLanguageModel, model_type_instance)
-
- return model_type_instance._calc_response_usage(
- model_config.model, model_config.credentials, all_message_tokens, all_answer_tokens
- )
diff --git a/api/core/app/apps/agent_chat/generate_response_converter.py b/api/core/app/apps/agent_chat/generate_response_converter.py
index ce331d904cc826..0eea1351674f51 100644
--- a/api/core/app/apps/agent_chat/generate_response_converter.py
+++ b/api/core/app/apps/agent_chat/generate_response_converter.py
@@ -1,9 +1,9 @@
-import json
from collections.abc import Generator
from typing import cast
from core.app.apps.base_app_generate_response_converter import AppGenerateResponseConverter
from core.app.entities.task_entities import (
+ AppStreamResponse,
ChatbotAppBlockingResponse,
ChatbotAppStreamResponse,
ErrorStreamResponse,
@@ -51,10 +51,9 @@ def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingR
return response
@classmethod
- def convert_stream_full_response( # type: ignore[override]
- cls,
- stream_response: Generator[ChatbotAppStreamResponse, None, None],
- ) -> Generator[str, None, None]:
+ def convert_stream_full_response(
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream full response.
:param stream_response: stream response
@@ -80,13 +79,12 @@ def convert_stream_full_response( # type: ignore[override]
response_chunk.update(data)
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
@classmethod
- def convert_stream_simple_response( # type: ignore[override]
- cls,
- stream_response: Generator[ChatbotAppStreamResponse, None, None],
- ) -> Generator[str, None, None]:
+ def convert_stream_simple_response(
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream simple response.
:param stream_response: stream response
@@ -118,4 +116,4 @@ def convert_stream_simple_response( # type: ignore[override]
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
diff --git a/api/core/app/apps/base_app_generate_response_converter.py b/api/core/app/apps/base_app_generate_response_converter.py
index be4027132ba903..29c1ad598e477b 100644
--- a/api/core/app/apps/base_app_generate_response_converter.py
+++ b/api/core/app/apps/base_app_generate_response_converter.py
@@ -14,21 +14,15 @@ class AppGenerateResponseConverter(ABC):
@classmethod
def convert(
- cls,
- response: Union[AppBlockingResponse, Generator[AppStreamResponse, Any, None]],
- invoke_from: InvokeFrom,
- ) -> Mapping[str, Any] | Generator[str, None, None]:
+ cls, response: Union[AppBlockingResponse, Generator[AppStreamResponse, Any, None]], invoke_from: InvokeFrom
+ ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], Any, None]:
if invoke_from in {InvokeFrom.DEBUGGER, InvokeFrom.SERVICE_API}:
if isinstance(response, AppBlockingResponse):
return cls.convert_blocking_full_response(response)
else:
- def _generate_full_response() -> Generator[str, Any, None]:
- for chunk in cls.convert_stream_full_response(response):
- if chunk == "ping":
- yield f"event: {chunk}\n\n"
- else:
- yield f"data: {chunk}\n\n"
+ def _generate_full_response() -> Generator[dict | str, Any, None]:
+ yield from cls.convert_stream_full_response(response)
return _generate_full_response()
else:
@@ -36,12 +30,8 @@ def _generate_full_response() -> Generator[str, Any, None]:
return cls.convert_blocking_simple_response(response)
else:
- def _generate_simple_response() -> Generator[str, Any, None]:
- for chunk in cls.convert_stream_simple_response(response):
- if chunk == "ping":
- yield f"event: {chunk}\n\n"
- else:
- yield f"data: {chunk}\n\n"
+ def _generate_simple_response() -> Generator[dict | str, Any, None]:
+ yield from cls.convert_stream_simple_response(response)
return _generate_simple_response()
@@ -59,14 +49,14 @@ def convert_blocking_simple_response(cls, blocking_response: AppBlockingResponse
@abstractmethod
def convert_stream_full_response(
cls, stream_response: Generator[AppStreamResponse, None, None]
- ) -> Generator[str, None, None]:
+ ) -> Generator[dict | str, None, None]:
raise NotImplementedError
@classmethod
@abstractmethod
def convert_stream_simple_response(
cls, stream_response: Generator[AppStreamResponse, None, None]
- ) -> Generator[str, None, None]:
+ ) -> Generator[dict | str, None, None]:
raise NotImplementedError
@classmethod
diff --git a/api/core/app/apps/base_app_generator.py b/api/core/app/apps/base_app_generator.py
index 85b7aced557c5e..00159c858d8bf9 100644
--- a/api/core/app/apps/base_app_generator.py
+++ b/api/core/app/apps/base_app_generator.py
@@ -1,5 +1,6 @@
-from collections.abc import Mapping, Sequence
-from typing import TYPE_CHECKING, Any, Optional
+import json
+from collections.abc import Generator, Mapping, Sequence
+from typing import TYPE_CHECKING, Any, Optional, Union
from core.app.app_config.entities import VariableEntityType
from core.file import File, FileUploadConfig
@@ -138,3 +139,21 @@ def _sanitize_value(self, value: Any) -> Any:
if isinstance(value, str):
return value.replace("\x00", "")
return value
+
+ @classmethod
+ def convert_to_event_stream(cls, generator: Union[Mapping, Generator[Mapping | str, None, None]]):
+ """
+ Convert messages into event stream
+ """
+ if isinstance(generator, dict):
+ return generator
+ else:
+
+ def gen():
+ for message in generator:
+ if isinstance(message, (Mapping, dict)):
+ yield f"data: {json.dumps(message)}\n\n"
+ else:
+ yield f"event: {message}\n\n"
+
+ return gen()
diff --git a/api/core/app/apps/base_app_queue_manager.py b/api/core/app/apps/base_app_queue_manager.py
index ce2222a14e77e5..0ba33fbe0d8a7c 100644
--- a/api/core/app/apps/base_app_queue_manager.py
+++ b/api/core/app/apps/base_app_queue_manager.py
@@ -2,7 +2,7 @@
import time
from abc import abstractmethod
from enum import Enum
-from typing import Any
+from typing import Any, Optional
from sqlalchemy.orm import DeclarativeMeta
@@ -115,7 +115,7 @@ def set_stop_flag(cls, task_id: str, invoke_from: InvokeFrom, user_id: str) -> N
Set task stop flag
:return:
"""
- result = redis_client.get(cls._generate_task_belong_cache_key(task_id))
+ result: Optional[Any] = redis_client.get(cls._generate_task_belong_cache_key(task_id))
if result is None:
return
diff --git a/api/core/app/apps/chat/app_generator.py b/api/core/app/apps/chat/app_generator.py
index dc7cf3667a0175..213fb79b7dcf2b 100644
--- a/api/core/app/apps/chat/app_generator.py
+++ b/api/core/app/apps/chat/app_generator.py
@@ -38,7 +38,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: Literal[True],
- ) -> Generator[str, None, None]: ...
+ ) -> Generator[Mapping | str, None, None]: ...
@overload
def generate(
@@ -58,7 +58,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool,
- ) -> Union[Mapping[str, Any], Generator[str, None, None]]: ...
+ ) -> Union[Mapping[str, Any], Generator[Mapping[str, Any] | str, None, None]]: ...
def generate(
self,
@@ -67,7 +67,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool = True,
- ):
+ ) -> Union[Mapping[str, Any], Generator[Mapping[str, Any] | str, None, None]]:
"""
Generate App response.
@@ -141,9 +141,7 @@ def generate(
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(
+ inputs=self._prepare_user_inputs(
user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id
),
query=query,
diff --git a/api/core/app/apps/chat/generate_response_converter.py b/api/core/app/apps/chat/generate_response_converter.py
index 9024c3a98273d1..13a6be167c07fa 100644
--- a/api/core/app/apps/chat/generate_response_converter.py
+++ b/api/core/app/apps/chat/generate_response_converter.py
@@ -1,9 +1,9 @@
-import json
from collections.abc import Generator
from typing import cast
from core.app.apps.base_app_generate_response_converter import AppGenerateResponseConverter
from core.app.entities.task_entities import (
+ AppStreamResponse,
ChatbotAppBlockingResponse,
ChatbotAppStreamResponse,
ErrorStreamResponse,
@@ -52,9 +52,8 @@ def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingR
@classmethod
def convert_stream_full_response(
- cls,
- stream_response: Generator[ChatbotAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream full response.
:param stream_response: stream response
@@ -80,13 +79,12 @@ def convert_stream_full_response(
response_chunk.update(data)
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
@classmethod
def convert_stream_simple_response(
- cls,
- stream_response: Generator[ChatbotAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream simple response.
:param stream_response: stream response
@@ -118,4 +116,4 @@ def convert_stream_simple_response(
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
diff --git a/api/core/app/apps/completion/app_generator.py b/api/core/app/apps/completion/app_generator.py
index be50d496d23694..ac9ad346ebd23a 100644
--- a/api/core/app/apps/completion/app_generator.py
+++ b/api/core/app/apps/completion/app_generator.py
@@ -37,7 +37,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: Literal[True],
- ) -> Generator[str, None, None]: ...
+ ) -> Generator[str | Mapping[str, Any], None, None]: ...
@overload
def generate(
@@ -56,8 +56,8 @@ def generate(
user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
- streaming: bool,
- ) -> Mapping[str, Any] | Generator[str, None, None]: ...
+ streaming: bool = False,
+ ) -> Union[Mapping[str, Any], Generator[str | Mapping[str, Any], None, None]]: ...
def generate(
self,
@@ -66,7 +66,7 @@ def generate(
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool = True,
- ):
+ ) -> Union[Mapping[str, Any], Generator[str | Mapping[str, Any], None, None]]:
"""
Generate App response.
@@ -231,7 +231,7 @@ def generate_more_like_this(
user: Union[Account, EndUser],
invoke_from: InvokeFrom,
stream: bool = True,
- ) -> Union[Mapping[str, Any], Generator[str, None, None]]:
+ ) -> Union[Mapping, Generator[Mapping | str, None, None]]:
"""
Generate App response.
diff --git a/api/core/app/apps/completion/generate_response_converter.py b/api/core/app/apps/completion/generate_response_converter.py
index 73f38c3d0bcb96..c2b78e81764f57 100644
--- a/api/core/app/apps/completion/generate_response_converter.py
+++ b/api/core/app/apps/completion/generate_response_converter.py
@@ -1,9 +1,9 @@
-import json
from collections.abc import Generator
from typing import cast
from core.app.apps.base_app_generate_response_converter import AppGenerateResponseConverter
from core.app.entities.task_entities import (
+ AppStreamResponse,
CompletionAppBlockingResponse,
CompletionAppStreamResponse,
ErrorStreamResponse,
@@ -51,9 +51,8 @@ def convert_blocking_simple_response(cls, blocking_response: CompletionAppBlocki
@classmethod
def convert_stream_full_response(
- cls,
- stream_response: Generator[CompletionAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream full response.
:param stream_response: stream response
@@ -78,13 +77,12 @@ def convert_stream_full_response(
response_chunk.update(data)
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
@classmethod
def convert_stream_simple_response(
- cls,
- stream_response: Generator[CompletionAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream simple response.
:param stream_response: stream response
@@ -115,4 +113,4 @@ def convert_stream_simple_response(
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
diff --git a/api/core/app/apps/message_based_app_generator.py b/api/core/app/apps/message_based_app_generator.py
index cccd62cd5ba375..909f76cc9a5aa1 100644
--- a/api/core/app/apps/message_based_app_generator.py
+++ b/api/core/app/apps/message_based_app_generator.py
@@ -42,7 +42,6 @@ def _handle_response(
ChatAppGenerateEntity,
CompletionAppGenerateEntity,
AgentChatAppGenerateEntity,
- AgentChatAppGenerateEntity,
],
queue_manager: AppQueueManager,
conversation: Conversation,
diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py
index 3e65fd56c808e0..f13cb530093ee2 100644
--- a/api/core/app/apps/workflow/app_generator.py
+++ b/api/core/app/apps/workflow/app_generator.py
@@ -36,13 +36,13 @@ def generate(
*,
app_model: App,
workflow: Workflow,
- user: Account | EndUser,
+ user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: Literal[True],
- call_depth: int = 0,
- workflow_thread_pool_id: Optional[str] = None,
- ) -> Generator[str, None, None]: ...
+ call_depth: int,
+ workflow_thread_pool_id: Optional[str],
+ ) -> Generator[Mapping | str, None, None]: ...
@overload
def generate(
@@ -50,12 +50,12 @@ def generate(
*,
app_model: App,
workflow: Workflow,
- user: Account | EndUser,
+ user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: Literal[False],
- call_depth: int = 0,
- workflow_thread_pool_id: Optional[str] = None,
+ call_depth: int,
+ workflow_thread_pool_id: Optional[str],
) -> Mapping[str, Any]: ...
@overload
@@ -64,26 +64,26 @@ def generate(
*,
app_model: App,
workflow: Workflow,
- user: Account | EndUser,
+ user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
- streaming: bool = True,
- call_depth: int = 0,
- workflow_thread_pool_id: Optional[str] = None,
- ) -> Mapping[str, Any] | Generator[str, None, None]: ...
+ streaming: bool,
+ call_depth: int,
+ workflow_thread_pool_id: Optional[str],
+ ) -> Union[Mapping[str, Any], Generator[Mapping | str, None, None]]: ...
def generate(
self,
*,
app_model: App,
workflow: Workflow,
- user: Account | EndUser,
+ user: Union[Account, EndUser],
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool = True,
call_depth: int = 0,
workflow_thread_pool_id: Optional[str] = None,
- ):
+ ) -> Union[Mapping[str, Any], Generator[Mapping | str, None, None]]:
files: Sequence[Mapping[str, Any]] = args.get("files") or []
# parse files
@@ -124,7 +124,10 @@ def generate(
trace_manager=trace_manager,
workflow_run_id=workflow_run_id,
)
+
contexts.tenant_id.set(application_generate_entity.app_config.tenant_id)
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
return self._generate(
app_model=app_model,
@@ -146,7 +149,18 @@ def _generate(
invoke_from: InvokeFrom,
streaming: bool = True,
workflow_thread_pool_id: Optional[str] = None,
- ) -> Mapping[str, Any] | Generator[str, None, None]:
+ ) -> Union[Mapping[str, Any], Generator[str | Mapping[str, Any], None, None]]:
+ """
+ Generate App response.
+
+ :param app_model: App
+ :param workflow: Workflow
+ :param user: account or end user
+ :param application_generate_entity: application generate entity
+ :param invoke_from: invoke from source
+ :param stream: is stream
+ :param workflow_thread_pool_id: workflow thread pool id
+ """
# init queue manager
queue_manager = WorkflowAppQueueManager(
task_id=application_generate_entity.task_id,
@@ -185,10 +199,10 @@ def single_iteration_generate(
app_model: App,
workflow: Workflow,
node_id: str,
- user: Account,
+ user: Account | EndUser,
args: Mapping[str, Any],
streaming: bool = True,
- ) -> Mapping[str, Any] | Generator[str, None, None]:
+ ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], None, None]:
"""
Generate App response.
@@ -224,6 +238,8 @@ def single_iteration_generate(
workflow_run_id=str(uuid.uuid4()),
)
contexts.tenant_id.set(application_generate_entity.app_config.tenant_id)
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
return self._generate(
app_model=app_model,
diff --git a/api/core/app/apps/workflow/generate_response_converter.py b/api/core/app/apps/workflow/generate_response_converter.py
index 5cdac6ad28fdaa..10ec73a7d23c65 100644
--- a/api/core/app/apps/workflow/generate_response_converter.py
+++ b/api/core/app/apps/workflow/generate_response_converter.py
@@ -1,9 +1,9 @@
-import json
from collections.abc import Generator
from typing import cast
from core.app.apps.base_app_generate_response_converter import AppGenerateResponseConverter
from core.app.entities.task_entities import (
+ AppStreamResponse,
ErrorStreamResponse,
NodeFinishStreamResponse,
NodeStartStreamResponse,
@@ -36,9 +36,8 @@ def convert_blocking_simple_response(cls, blocking_response: WorkflowAppBlocking
@classmethod
def convert_stream_full_response(
- cls,
- stream_response: Generator[WorkflowAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream full response.
:param stream_response: stream response
@@ -62,13 +61,12 @@ def convert_stream_full_response(
response_chunk.update(data)
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
@classmethod
def convert_stream_simple_response(
- cls,
- stream_response: Generator[WorkflowAppStreamResponse, None, None], # type: ignore[override]
- ) -> Generator[str, None, None]:
+ cls, stream_response: Generator[AppStreamResponse, None, None]
+ ) -> Generator[dict | str, None, None]:
"""
Convert stream simple response.
:param stream_response: stream response
@@ -94,4 +92,4 @@ def convert_stream_simple_response(
response_chunk.update(sub_stream_response.to_ignore_detail_dict())
else:
response_chunk.update(sub_stream_response.to_dict())
- yield json.dumps(response_chunk)
+ yield response_chunk
diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py
index f89f456916e72e..9837cf9975cec5 100644
--- a/api/core/app/apps/workflow/generate_task_pipeline.py
+++ b/api/core/app/apps/workflow/generate_task_pipeline.py
@@ -13,6 +13,7 @@
WorkflowAppGenerateEntity,
)
from core.app.entities.queue_entities import (
+ QueueAgentLogEvent,
QueueErrorEvent,
QueueIterationCompletedEvent,
QueueIterationNextEvent,
@@ -190,7 +191,9 @@ def _wrapper_process_stream_response(
and features_dict["text_to_speech"].get("enabled")
and features_dict["text_to_speech"].get("autoPlay") == "enabled"
):
- tts_publisher = AppGeneratorTTSPublisher(tenant_id, features_dict["text_to_speech"].get("voice"))
+ tts_publisher = AppGeneratorTTSPublisher(
+ tenant_id, features_dict["text_to_speech"].get("voice"), features_dict["text_to_speech"].get("language")
+ )
for response in self._process_stream_response(tts_publisher=tts_publisher, trace_manager=trace_manager):
while True:
@@ -527,6 +530,10 @@ def _process_stream_response(
yield self._text_chunk_to_stream_response(
delta_text, from_variable_selector=event.from_variable_selector
)
+ elif isinstance(event, QueueAgentLogEvent):
+ yield self._workflow_cycle_manager._handle_agent_log(
+ task_id=self._application_generate_entity.task_id, event=event
+ )
else:
continue
diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py
index 63f516bcc60682..6d3b8a996b30f1 100644
--- a/api/core/app/apps/workflow_app_runner.py
+++ b/api/core/app/apps/workflow_app_runner.py
@@ -5,6 +5,7 @@
from core.app.apps.base_app_runner import AppRunner
from core.app.entities.queue_entities import (
AppQueueEvent,
+ QueueAgentLogEvent,
QueueIterationCompletedEvent,
QueueIterationNextEvent,
QueueIterationStartEvent,
@@ -27,6 +28,7 @@
from core.workflow.entities.node_entities import NodeRunMetadataKey
from core.workflow.entities.variable_pool import VariablePool
from core.workflow.graph_engine.entities.event import (
+ AgentLogEvent,
GraphEngineEvent,
GraphRunFailedEvent,
GraphRunPartialSucceededEvent,
@@ -239,6 +241,7 @@ def _handle_event(self, workflow_entry: WorkflowEntry, event: GraphEngineEvent)
predecessor_node_id=event.predecessor_node_id,
in_iteration_id=event.in_iteration_id,
parallel_mode_run_id=event.parallel_mode_run_id,
+ agent_strategy=event.agent_strategy,
)
)
elif isinstance(event, NodeRunSucceededEvent):
@@ -373,6 +376,19 @@ def _handle_event(self, workflow_entry: WorkflowEntry, event: GraphEngineEvent)
retriever_resources=event.retriever_resources, in_iteration_id=event.in_iteration_id
)
)
+ elif isinstance(event, AgentLogEvent):
+ self._publish_event(
+ QueueAgentLogEvent(
+ id=event.id,
+ label=event.label,
+ node_execution_id=event.node_execution_id,
+ parent_id=event.parent_id,
+ error=event.error,
+ status=event.status,
+ data=event.data,
+ metadata=event.metadata,
+ )
+ )
elif isinstance(event, ParallelBranchRunStartedEvent):
self._publish_event(
QueueParallelBranchRunStartedEvent(
diff --git a/api/core/app/entities/app_invoke_entities.py b/api/core/app/entities/app_invoke_entities.py
index 7cb4e590326257..57beeaacc0fdac 100644
--- a/api/core/app/entities/app_invoke_entities.py
+++ b/api/core/app/entities/app_invoke_entities.py
@@ -63,9 +63,9 @@ class ModelConfigWithCredentialsEntity(BaseModel):
model_schema: AIModelEntity
mode: str
provider_model_bundle: ProviderModelBundle
- credentials: dict[str, Any] = {}
- parameters: dict[str, Any] = {}
- stop: list[str] = []
+ credentials: dict[str, Any] = Field(default_factory=dict)
+ parameters: dict[str, Any] = Field(default_factory=dict)
+ stop: list[str] = Field(default_factory=list)
# pydantic configs
model_config = ConfigDict(protected_namespaces=())
@@ -94,7 +94,7 @@ class AppGenerateEntity(BaseModel):
call_depth: int = 0
# extra parameters, like: auto_generate_conversation_name
- extras: dict[str, Any] = {}
+ extras: dict[str, Any] = Field(default_factory=dict)
# tracing instance
trace_manager: Optional[TraceQueueManager] = None
@@ -183,7 +183,7 @@ class SingleIterationRunEntity(BaseModel):
"""
node_id: str
- inputs: dict
+ inputs: Mapping
single_iteration_run: Optional[SingleIterationRunEntity] = None
diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py
index a93e533ff45d26..f1cc3ac2216b58 100644
--- a/api/core/app/entities/queue_entities.py
+++ b/api/core/app/entities/queue_entities.py
@@ -6,7 +6,7 @@
from pydantic import BaseModel
from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk
-from core.workflow.entities.node_entities import NodeRunMetadataKey
+from core.workflow.entities.node_entities import AgentNodeStrategyInit, NodeRunMetadataKey
from core.workflow.graph_engine.entities.graph_runtime_state import GraphRuntimeState
from core.workflow.nodes import NodeType
from core.workflow.nodes.base import BaseNodeData
@@ -41,6 +41,7 @@ class QueueEvent(StrEnum):
PARALLEL_BRANCH_RUN_STARTED = "parallel_branch_run_started"
PARALLEL_BRANCH_RUN_SUCCEEDED = "parallel_branch_run_succeeded"
PARALLEL_BRANCH_RUN_FAILED = "parallel_branch_run_failed"
+ AGENT_LOG = "agent_log"
ERROR = "error"
PING = "ping"
STOP = "stop"
@@ -280,6 +281,7 @@ class QueueNodeStartedEvent(AppQueueEvent):
start_at: datetime
parallel_mode_run_id: Optional[str] = None
"""iteratoin run in parallel mode run id"""
+ agent_strategy: Optional[AgentNodeStrategyInit] = None
class QueueNodeSucceededEvent(AppQueueEvent):
@@ -315,6 +317,22 @@ class QueueNodeSucceededEvent(AppQueueEvent):
iteration_duration_map: Optional[dict[str, float]] = None
+class QueueAgentLogEvent(AppQueueEvent):
+ """
+ QueueAgentLogEvent entity
+ """
+
+ event: QueueEvent = QueueEvent.AGENT_LOG
+ id: str
+ label: str
+ node_execution_id: str
+ parent_id: str | None
+ error: str | None
+ status: str
+ data: Mapping[str, Any]
+ metadata: Optional[Mapping[str, Any]] = None
+
+
class QueueNodeRetryEvent(QueueNodeStartedEvent):
"""QueueNodeRetryEvent entity"""
diff --git a/api/core/app/entities/task_entities.py b/api/core/app/entities/task_entities.py
index 5e845eba2da1d3..e64bd416e0cf9b 100644
--- a/api/core/app/entities/task_entities.py
+++ b/api/core/app/entities/task_entities.py
@@ -6,6 +6,7 @@
from core.model_runtime.entities.llm_entities import LLMResult
from core.model_runtime.utils.encoders import jsonable_encoder
+from core.workflow.entities.node_entities import AgentNodeStrategyInit
from models.workflow import WorkflowNodeExecutionStatus
@@ -60,6 +61,7 @@ class StreamEvent(Enum):
ITERATION_COMPLETED = "iteration_completed"
TEXT_CHUNK = "text_chunk"
TEXT_REPLACE = "text_replace"
+ AGENT_LOG = "agent_log"
class StreamResponse(BaseModel):
@@ -247,6 +249,7 @@ class Data(BaseModel):
parent_parallel_start_node_id: Optional[str] = None
iteration_id: Optional[str] = None
parallel_run_id: Optional[str] = None
+ agent_strategy: Optional[AgentNodeStrategyInit] = None
event: StreamEvent = StreamEvent.NODE_STARTED
workflow_run_id: str
@@ -696,3 +699,26 @@ class Data(BaseModel):
workflow_run_id: str
data: Data
+
+
+class AgentLogStreamResponse(StreamResponse):
+ """
+ AgentLogStreamResponse entity
+ """
+
+ class Data(BaseModel):
+ """
+ Data entity
+ """
+
+ node_execution_id: str
+ id: str
+ label: str
+ parent_id: str | None
+ error: str | None
+ status: str
+ data: Mapping[str, Any]
+ metadata: Optional[Mapping[str, Any]] = None
+
+ event: StreamEvent = StreamEvent.AGENT_LOG
+ data: Data
diff --git a/api/core/app/features/hosting_moderation/hosting_moderation.py b/api/core/app/features/hosting_moderation/hosting_moderation.py
index ba14b61201e72f..a5a54865819e46 100644
--- a/api/core/app/features/hosting_moderation/hosting_moderation.py
+++ b/api/core/app/features/hosting_moderation/hosting_moderation.py
@@ -24,6 +24,8 @@ def check(
if isinstance(prompt_message.content, str):
text += prompt_message.content + "\n"
- moderation_result = moderation.check_moderation(model_config, text)
+ moderation_result = moderation.check_moderation(
+ tenant_id=application_generate_entity.app_config.tenant_id, model_config=model_config, text=text
+ )
return moderation_result
diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
index c84f8ba3e450cc..8c9c26d36e003b 100644
--- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
+++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
@@ -215,7 +215,9 @@ def _wrapper_process_stream_response(
and text_to_speech_dict.get("autoPlay") == "enabled"
and text_to_speech_dict.get("enabled")
):
- publisher = AppGeneratorTTSPublisher(tenant_id, text_to_speech_dict.get("voice", None))
+ publisher = AppGeneratorTTSPublisher(
+ tenant_id, text_to_speech_dict.get("voice", None), text_to_speech_dict.get("language", None)
+ )
for response in self._process_stream_response(publisher=publisher, trace_manager=trace_manager):
while True:
audio_response = self._listen_audio_msg(publisher, task_id)
diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py
index dcc364d22766e6..4710bc32213fc1 100644
--- a/api/core/app/task_pipeline/workflow_cycle_manage.py
+++ b/api/core/app/task_pipeline/workflow_cycle_manage.py
@@ -10,6 +10,7 @@
from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom, WorkflowAppGenerateEntity
from core.app.entities.queue_entities import (
+ QueueAgentLogEvent,
QueueIterationCompletedEvent,
QueueIterationNextEvent,
QueueIterationStartEvent,
@@ -24,6 +25,7 @@
QueueParallelBranchRunSucceededEvent,
)
from core.app.entities.task_entities import (
+ AgentLogStreamResponse,
IterationNodeCompletedStreamResponse,
IterationNodeNextStreamResponse,
IterationNodeStartStreamResponse,
@@ -320,9 +322,8 @@ def _handle_workflow_node_execution_success(
inputs = WorkflowEntry.handle_special_values(event.inputs)
process_data = WorkflowEntry.handle_special_values(event.process_data)
outputs = WorkflowEntry.handle_special_values(event.outputs)
- execution_metadata = (
- json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
- )
+ execution_metadata_dict = dict(event.execution_metadata or {})
+ execution_metadata = json.dumps(jsonable_encoder(execution_metadata_dict)) if execution_metadata_dict else None
finished_at = datetime.now(UTC).replace(tzinfo=None)
elapsed_time = (finished_at - event.start_at).total_seconds()
@@ -540,6 +541,7 @@ def _workflow_node_start_to_stream_response(
parent_parallel_start_node_id=event.parent_parallel_start_node_id,
iteration_id=event.in_iteration_id,
parallel_run_id=event.parallel_mode_run_id,
+ agent_strategy=event.agent_strategy,
),
)
@@ -842,4 +844,25 @@ def _get_workflow_node_execution(self, session: Session, node_execution_id: str)
if node_execution_id not in self._workflow_node_executions:
raise ValueError(f"Workflow node execution not found: {node_execution_id}")
cached_workflow_node_execution = self._workflow_node_executions[node_execution_id]
- return cached_workflow_node_execution
+ return session.merge(cached_workflow_node_execution)
+
+ def _handle_agent_log(self, task_id: str, event: QueueAgentLogEvent) -> AgentLogStreamResponse:
+ """
+ Handle agent log
+ :param task_id: task id
+ :param event: agent log event
+ :return:
+ """
+ return AgentLogStreamResponse(
+ task_id=task_id,
+ data=AgentLogStreamResponse.Data(
+ node_execution_id=event.node_execution_id,
+ id=event.id,
+ parent_id=event.parent_id,
+ label=event.label,
+ error=event.error,
+ status=event.status,
+ data=event.data,
+ metadata=event.metadata,
+ ),
+ )
diff --git a/api/core/callback_handler/agent_tool_callback_handler.py b/api/core/callback_handler/agent_tool_callback_handler.py
index effc7eff9179ae..65d899a0023d2d 100644
--- a/api/core/callback_handler/agent_tool_callback_handler.py
+++ b/api/core/callback_handler/agent_tool_callback_handler.py
@@ -1,4 +1,4 @@
-from collections.abc import Mapping, Sequence
+from collections.abc import Iterable, Mapping
from typing import Any, Optional, TextIO, Union
from pydantic import BaseModel
@@ -57,7 +57,7 @@ def on_tool_end(
self,
tool_name: str,
tool_inputs: Mapping[str, Any],
- tool_outputs: Sequence[ToolInvokeMessage] | str,
+ tool_outputs: Iterable[ToolInvokeMessage] | str,
message_id: Optional[str] = None,
timer: Optional[Any] = None,
trace_manager: Optional[TraceQueueManager] = None,
diff --git a/api/core/callback_handler/workflow_tool_callback_handler.py b/api/core/callback_handler/workflow_tool_callback_handler.py
index 8ac12f72f29d6c..350b18772bc5e9 100644
--- a/api/core/callback_handler/workflow_tool_callback_handler.py
+++ b/api/core/callback_handler/workflow_tool_callback_handler.py
@@ -1,5 +1,26 @@
-from core.callback_handler.agent_tool_callback_handler import DifyAgentCallbackHandler
+from collections.abc import Generator, Iterable, Mapping
+from typing import Any, Optional
+
+from core.callback_handler.agent_tool_callback_handler import DifyAgentCallbackHandler, print_text
+from core.ops.ops_trace_manager import TraceQueueManager
+from core.tools.entities.tool_entities import ToolInvokeMessage
class DifyWorkflowCallbackHandler(DifyAgentCallbackHandler):
"""Callback Handler that prints to std out."""
+
+ def on_tool_execution(
+ self,
+ tool_name: str,
+ tool_inputs: Mapping[str, Any],
+ tool_outputs: Iterable[ToolInvokeMessage],
+ message_id: Optional[str] = None,
+ timer: Optional[Any] = None,
+ trace_manager: Optional[TraceQueueManager] = None,
+ ) -> Generator[ToolInvokeMessage, None, None]:
+ for tool_output in tool_outputs:
+ print_text("\n[on_tool_execution]\n", color=self.color)
+ print_text("Tool: " + tool_name + "\n", color=self.color)
+ print_text("Outputs: " + tool_output.model_dump_json()[:1000] + "\n", color=self.color)
+ print_text("\n")
+ yield tool_output
diff --git a/api/core/entities/__init__.py b/api/core/entities/__init__.py
index e69de29bb2d1d6..b848da366495c2 100644
--- a/api/core/entities/__init__.py
+++ b/api/core/entities/__init__.py
@@ -0,0 +1 @@
+DEFAULT_PLUGIN_ID = "langgenius"
diff --git a/api/core/entities/parameter_entities.py b/api/core/entities/parameter_entities.py
new file mode 100644
index 00000000000000..36800bc2634443
--- /dev/null
+++ b/api/core/entities/parameter_entities.py
@@ -0,0 +1,42 @@
+from enum import StrEnum
+
+
+class CommonParameterType(StrEnum):
+ SECRET_INPUT = "secret-input"
+ TEXT_INPUT = "text-input"
+ SELECT = "select"
+ STRING = "string"
+ NUMBER = "number"
+ FILE = "file"
+ FILES = "files"
+ SYSTEM_FILES = "system-files"
+ BOOLEAN = "boolean"
+ APP_SELECTOR = "app-selector"
+ MODEL_SELECTOR = "model-selector"
+ TOOLS_SELECTOR = "array[tools]"
+
+ # TOOL_SELECTOR = "tool-selector"
+
+
+class AppSelectorScope(StrEnum):
+ ALL = "all"
+ CHAT = "chat"
+ WORKFLOW = "workflow"
+ COMPLETION = "completion"
+
+
+class ModelSelectorScope(StrEnum):
+ LLM = "llm"
+ TEXT_EMBEDDING = "text-embedding"
+ RERANK = "rerank"
+ TTS = "tts"
+ SPEECH2TEXT = "speech2text"
+ MODERATION = "moderation"
+ VISION = "vision"
+
+
+class ToolSelectorScope(StrEnum):
+ ALL = "all"
+ CUSTOM = "custom"
+ BUILTIN = "builtin"
+ WORKFLOW = "workflow"
diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py
index bff5a0ec9c6be7..b1a155fea8f59e 100644
--- a/api/core/entities/provider_configuration.py
+++ b/api/core/entities/provider_configuration.py
@@ -2,11 +2,12 @@
import json
import logging
from collections import defaultdict
-from collections.abc import Iterator
+from collections.abc import Iterator, Sequence
from json import JSONDecodeError
from typing import Optional
-from pydantic import BaseModel, ConfigDict
+from pydantic import BaseModel, ConfigDict, Field
+from sqlalchemy import or_
from constants import HIDDEN_VALUE
from core.entities.model_entities import ModelStatus, ModelWithProviderEntity, SimpleModelProviderEntity
@@ -18,16 +19,16 @@
)
from core.helper import encrypter
from core.helper.model_provider_cache import ProviderCredentialsCache, ProviderCredentialsCacheType
-from core.model_runtime.entities.model_entities import FetchFrom, ModelType
+from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType
from core.model_runtime.entities.provider_entities import (
ConfigurateMethod,
CredentialFormSchema,
FormType,
ProviderEntity,
)
-from core.model_runtime.model_providers import model_provider_factory
from core.model_runtime.model_providers.__base.ai_model import AIModel
-from core.model_runtime.model_providers.__base.model_provider import ModelProvider
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
+from core.plugin.entities.plugin import ModelProviderID
from extensions.ext_database import db
from models.provider import (
LoadBalancingModelConfig,
@@ -99,9 +100,10 @@ def get_current_credentials(self, model_type: ModelType, model: str) -> Optional
continue
restrict_models = quota_configuration.restrict_models
- if self.system_configuration.credentials is None:
- return None
- copy_credentials = self.system_configuration.credentials.copy()
+
+ copy_credentials = (
+ self.system_configuration.credentials.copy() if self.system_configuration.credentials else {}
+ )
if restrict_models:
for restrict_model in restrict_models:
if (
@@ -140,6 +142,9 @@ def get_system_configuration_status(self) -> Optional[SystemConfigurationStatus]
if current_quota_configuration is None:
return None
+ if not current_quota_configuration:
+ return SystemConfigurationStatus.UNSUPPORTED
+
return (
SystemConfigurationStatus.ACTIVE
if current_quota_configuration.is_valid
@@ -153,7 +158,7 @@ def is_custom_configuration_available(self) -> bool:
"""
return self.custom_configuration.provider is not None or len(self.custom_configuration.models) > 0
- def get_custom_credentials(self, obfuscated: bool = False):
+ def get_custom_credentials(self, obfuscated: bool = False) -> dict | None:
"""
Get custom credentials.
@@ -175,7 +180,7 @@ def get_custom_credentials(self, obfuscated: bool = False):
else [],
)
- def custom_credentials_validate(self, credentials: dict) -> tuple[Optional[Provider], dict]:
+ def custom_credentials_validate(self, credentials: dict) -> tuple[Provider | None, dict]:
"""
Validate custom credentials.
:param credentials: provider credentials
@@ -186,8 +191,11 @@ def custom_credentials_validate(self, credentials: dict) -> tuple[Optional[Provi
db.session.query(Provider)
.filter(
Provider.tenant_id == self.tenant_id,
- Provider.provider_name == self.provider.provider,
Provider.provider_type == ProviderType.CUSTOM.value,
+ or_(
+ Provider.provider_name == ModelProviderID(self.provider.provider).plugin_name,
+ Provider.provider_name == self.provider.provider,
+ ),
)
.first()
)
@@ -219,6 +227,7 @@ def custom_credentials_validate(self, credentials: dict) -> tuple[Optional[Provi
if value == HIDDEN_VALUE and key in original_credentials:
credentials[key] = encrypter.decrypt_token(self.tenant_id, original_credentials[key])
+ model_provider_factory = ModelProviderFactory(self.tenant_id)
credentials = model_provider_factory.provider_credentials_validate(
provider=self.provider.provider, credentials=credentials
)
@@ -246,13 +255,13 @@ def add_or_update_custom_credentials(self, credentials: dict) -> None:
provider_record.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- provider_record = Provider(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- provider_type=ProviderType.CUSTOM.value,
- encrypted_config=json.dumps(credentials),
- is_valid=True,
- )
+ provider_record = Provider()
+ provider_record.tenant_id = self.tenant_id
+ provider_record.provider_name = self.provider.provider
+ provider_record.provider_type = ProviderType.CUSTOM.value
+ provider_record.encrypted_config = json.dumps(credentials)
+ provider_record.is_valid = True
+
db.session.add(provider_record)
db.session.commit()
@@ -274,7 +283,10 @@ def delete_custom_credentials(self) -> None:
db.session.query(Provider)
.filter(
Provider.tenant_id == self.tenant_id,
- Provider.provider_name == self.provider.provider,
+ or_(
+ Provider.provider_name == ModelProviderID(self.provider.provider).plugin_name,
+ Provider.provider_name == self.provider.provider,
+ ),
Provider.provider_type == ProviderType.CUSTOM.value,
)
.first()
@@ -327,7 +339,7 @@ def get_custom_model_credentials(
def custom_model_credentials_validate(
self, model_type: ModelType, model: str, credentials: dict
- ) -> tuple[Optional[ProviderModel], dict]:
+ ) -> tuple[ProviderModel | None, dict]:
"""
Validate custom model credentials.
@@ -370,6 +382,7 @@ def custom_model_credentials_validate(
if value == HIDDEN_VALUE and key in original_credentials:
credentials[key] = encrypter.decrypt_token(self.tenant_id, original_credentials[key])
+ model_provider_factory = ModelProviderFactory(self.tenant_id)
credentials = model_provider_factory.model_credentials_validate(
provider=self.provider.provider, model_type=model_type, model=model, credentials=credentials
)
@@ -400,14 +413,13 @@ def add_or_update_custom_model_credentials(self, model_type: ModelType, model: s
provider_model_record.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- provider_model_record = ProviderModel(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- model_name=model,
- model_type=model_type.to_origin_model_type(),
- encrypted_config=json.dumps(credentials),
- is_valid=True,
- )
+ provider_model_record = ProviderModel()
+ provider_model_record.tenant_id = self.tenant_id
+ provider_model_record.provider_name = self.provider.provider
+ provider_model_record.model_name = model
+ provider_model_record.model_type = model_type.to_origin_model_type()
+ provider_model_record.encrypted_config = json.dumps(credentials)
+ provider_model_record.is_valid = True
db.session.add(provider_model_record)
db.session.commit()
@@ -474,13 +486,12 @@ def enable_model(self, model_type: ModelType, model: str) -> ProviderModelSettin
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- model_setting = ProviderModelSetting(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- model_type=model_type.to_origin_model_type(),
- model_name=model,
- enabled=True,
- )
+ model_setting = ProviderModelSetting()
+ model_setting.tenant_id = self.tenant_id
+ model_setting.provider_name = self.provider.provider
+ model_setting.model_type = model_type.to_origin_model_type()
+ model_setting.model_name = model
+ model_setting.enabled = True
db.session.add(model_setting)
db.session.commit()
@@ -509,13 +520,12 @@ def disable_model(self, model_type: ModelType, model: str) -> ProviderModelSetti
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- model_setting = ProviderModelSetting(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- model_type=model_type.to_origin_model_type(),
- model_name=model,
- enabled=False,
- )
+ model_setting = ProviderModelSetting()
+ model_setting.tenant_id = self.tenant_id
+ model_setting.provider_name = self.provider.provider
+ model_setting.model_type = model_type.to_origin_model_type()
+ model_setting.model_name = model
+ model_setting.enabled = False
db.session.add(model_setting)
db.session.commit()
@@ -576,13 +586,12 @@ def enable_model_load_balancing(self, model_type: ModelType, model: str) -> Prov
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- model_setting = ProviderModelSetting(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- model_type=model_type.to_origin_model_type(),
- model_name=model,
- load_balancing_enabled=True,
- )
+ model_setting = ProviderModelSetting()
+ model_setting.tenant_id = self.tenant_id
+ model_setting.provider_name = self.provider.provider
+ model_setting.model_type = model_type.to_origin_model_type()
+ model_setting.model_name = model
+ model_setting.load_balancing_enabled = True
db.session.add(model_setting)
db.session.commit()
@@ -611,25 +620,17 @@ def disable_model_load_balancing(self, model_type: ModelType, model: str) -> Pro
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
db.session.commit()
else:
- model_setting = ProviderModelSetting(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- model_type=model_type.to_origin_model_type(),
- model_name=model,
- load_balancing_enabled=False,
- )
+ model_setting = ProviderModelSetting()
+ model_setting.tenant_id = self.tenant_id
+ model_setting.provider_name = self.provider.provider
+ model_setting.model_type = model_type.to_origin_model_type()
+ model_setting.model_name = model
+ model_setting.load_balancing_enabled = False
db.session.add(model_setting)
db.session.commit()
return model_setting
- def get_provider_instance(self) -> ModelProvider:
- """
- Get provider instance.
- :return:
- """
- return model_provider_factory.get_provider_instance(self.provider.provider)
-
def get_model_type_instance(self, model_type: ModelType) -> AIModel:
"""
Get current model type instance.
@@ -637,11 +638,19 @@ def get_model_type_instance(self, model_type: ModelType) -> AIModel:
:param model_type: model type
:return:
"""
- # Get provider instance
- provider_instance = self.get_provider_instance()
+ model_provider_factory = ModelProviderFactory(self.tenant_id)
# Get model instance of LLM
- return provider_instance.get_model_instance(model_type)
+ return model_provider_factory.get_model_type_instance(provider=self.provider.provider, model_type=model_type)
+
+ def get_model_schema(self, model_type: ModelType, model: str, credentials: dict) -> AIModelEntity | None:
+ """
+ Get model schema
+ """
+ model_provider_factory = ModelProviderFactory(self.tenant_id)
+ return model_provider_factory.get_model_schema(
+ provider=self.provider.provider, model_type=model_type, model=model, credentials=credentials
+ )
def switch_preferred_provider_type(self, provider_type: ProviderType) -> None:
"""
@@ -668,11 +677,10 @@ def switch_preferred_provider_type(self, provider_type: ProviderType) -> None:
if preferred_model_provider:
preferred_model_provider.preferred_provider_type = provider_type.value
else:
- preferred_model_provider = TenantPreferredModelProvider(
- tenant_id=self.tenant_id,
- provider_name=self.provider.provider,
- preferred_provider_type=provider_type.value,
- )
+ preferred_model_provider = TenantPreferredModelProvider()
+ preferred_model_provider.tenant_id = self.tenant_id
+ preferred_model_provider.provider_name = self.provider.provider
+ preferred_model_provider.preferred_provider_type = provider_type.value
db.session.add(preferred_model_provider)
db.session.commit()
@@ -737,13 +745,14 @@ def get_provider_models(
:param only_active: only active models
:return:
"""
- provider_instance = self.get_provider_instance()
+ model_provider_factory = ModelProviderFactory(self.tenant_id)
+ provider_schema = model_provider_factory.get_provider_schema(self.provider.provider)
- model_types = []
+ model_types: list[ModelType] = []
if model_type:
model_types.append(model_type)
else:
- model_types = list(provider_instance.get_provider_schema().supported_model_types)
+ model_types = list(provider_schema.supported_model_types)
# Group model settings by model type and model
model_setting_map: defaultdict[ModelType, dict[str, ModelSettings]] = defaultdict(dict)
@@ -752,11 +761,11 @@ def get_provider_models(
if self.using_provider_type == ProviderType.SYSTEM:
provider_models = self._get_system_provider_models(
- model_types=model_types, provider_instance=provider_instance, model_setting_map=model_setting_map
+ model_types=model_types, provider_schema=provider_schema, model_setting_map=model_setting_map
)
else:
provider_models = self._get_custom_provider_models(
- model_types=model_types, provider_instance=provider_instance, model_setting_map=model_setting_map
+ model_types=model_types, provider_schema=provider_schema, model_setting_map=model_setting_map
)
if only_active:
@@ -767,23 +776,26 @@ def get_provider_models(
def _get_system_provider_models(
self,
- model_types: list[ModelType],
- provider_instance: ModelProvider,
+ model_types: Sequence[ModelType],
+ provider_schema: ProviderEntity,
model_setting_map: dict[ModelType, dict[str, ModelSettings]],
) -> list[ModelWithProviderEntity]:
"""
Get system provider models.
:param model_types: model types
- :param provider_instance: provider instance
+ :param provider_schema: provider schema
:param model_setting_map: model setting map
:return:
"""
provider_models = []
for model_type in model_types:
- for m in provider_instance.models(model_type):
+ for m in provider_schema.models:
+ if m.model_type != model_type:
+ continue
+
status = ModelStatus.ACTIVE
- if m.model_type in model_setting_map and m.model in model_setting_map[m.model_type]:
+ if m.model in model_setting_map:
model_setting = model_setting_map[m.model_type][m.model]
if model_setting.enabled is False:
status = ModelStatus.DISABLED
@@ -804,7 +816,7 @@ def _get_system_provider_models(
if self.provider.provider not in original_provider_configurate_methods:
original_provider_configurate_methods[self.provider.provider] = []
- for configurate_method in provider_instance.get_provider_schema().configurate_methods:
+ for configurate_method in provider_schema.configurate_methods:
original_provider_configurate_methods[self.provider.provider].append(configurate_method)
should_use_custom_model = False
@@ -825,18 +837,22 @@ def _get_system_provider_models(
]:
# only customizable model
for restrict_model in restrict_models:
- if self.system_configuration.credentials is not None:
- copy_credentials = self.system_configuration.credentials.copy()
- if restrict_model.base_model_name:
- copy_credentials["base_model_name"] = restrict_model.base_model_name
-
- try:
- custom_model_schema = provider_instance.get_model_instance(
- restrict_model.model_type
- ).get_customizable_model_schema_from_credentials(restrict_model.model, copy_credentials)
- except Exception as ex:
- logger.warning(f"get custom model schema failed, {ex}")
- continue
+ copy_credentials = (
+ self.system_configuration.credentials.copy()
+ if self.system_configuration.credentials
+ else {}
+ )
+ if restrict_model.base_model_name:
+ copy_credentials["base_model_name"] = restrict_model.base_model_name
+
+ try:
+ custom_model_schema = self.get_model_schema(
+ model_type=restrict_model.model_type,
+ model=restrict_model.model,
+ credentials=copy_credentials,
+ )
+ except Exception as ex:
+ logger.warning(f"get custom model schema failed, {ex}")
if not custom_model_schema:
continue
@@ -881,15 +897,15 @@ def _get_system_provider_models(
def _get_custom_provider_models(
self,
- model_types: list[ModelType],
- provider_instance: ModelProvider,
+ model_types: Sequence[ModelType],
+ provider_schema: ProviderEntity,
model_setting_map: dict[ModelType, dict[str, ModelSettings]],
) -> list[ModelWithProviderEntity]:
"""
Get custom provider models.
:param model_types: model types
- :param provider_instance: provider instance
+ :param provider_schema: provider schema
:param model_setting_map: model setting map
:return:
"""
@@ -903,8 +919,10 @@ def _get_custom_provider_models(
if model_type not in self.provider.supported_model_types:
continue
- models = provider_instance.models(model_type)
- for m in models:
+ for m in provider_schema.models:
+ if m.model_type != model_type:
+ continue
+
status = ModelStatus.ACTIVE if credentials else ModelStatus.NO_CONFIGURE
load_balancing_enabled = False
if m.model_type in model_setting_map and m.model in model_setting_map[m.model_type]:
@@ -936,10 +954,10 @@ def _get_custom_provider_models(
continue
try:
- custom_model_schema = provider_instance.get_model_instance(
- model_configuration.model_type
- ).get_customizable_model_schema_from_credentials(
- model_configuration.model, model_configuration.credentials
+ custom_model_schema = self.get_model_schema(
+ model_type=model_configuration.model_type,
+ model=model_configuration.model,
+ credentials=model_configuration.credentials,
)
except Exception as ex:
logger.warning(f"get custom model schema failed, {ex}")
@@ -967,7 +985,7 @@ def _get_custom_provider_models(
label=custom_model_schema.label,
model_type=custom_model_schema.model_type,
features=custom_model_schema.features,
- fetch_from=custom_model_schema.fetch_from,
+ fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
model_properties=custom_model_schema.model_properties,
deprecated=custom_model_schema.deprecated,
provider=SimpleModelProviderEntity(self.provider),
@@ -985,7 +1003,7 @@ class ProviderConfigurations(BaseModel):
"""
tenant_id: str
- configurations: dict[str, ProviderConfiguration] = {}
+ configurations: dict[str, ProviderConfiguration] = Field(default_factory=dict)
def __init__(self, tenant_id: str):
super().__init__(tenant_id=tenant_id)
@@ -1040,6 +1058,9 @@ def to_list(self) -> list[ProviderConfiguration]:
return list(self.values())
def __getitem__(self, key):
+ if "/" not in key:
+ key = str(ModelProviderID(key))
+
return self.configurations[key]
def __setitem__(self, key, value):
@@ -1051,8 +1072,11 @@ def __iter__(self):
def values(self) -> Iterator[ProviderConfiguration]:
return iter(self.configurations.values())
- def get(self, key, default=None):
- return self.configurations.get(key, default)
+ def get(self, key, default=None) -> ProviderConfiguration | None:
+ if "/" not in key:
+ key = str(ModelProviderID(key))
+
+ return self.configurations.get(key, default) # type: ignore
class ProviderModelBundle(BaseModel):
@@ -1061,7 +1085,6 @@ class ProviderModelBundle(BaseModel):
"""
configuration: ProviderConfiguration
- provider_instance: ModelProvider
model_type_instance: AIModel
# pydantic configs
diff --git a/api/core/entities/provider_entities.py b/api/core/entities/provider_entities.py
index 44725623dc4bd4..e04e2a42fda5e3 100644
--- a/api/core/entities/provider_entities.py
+++ b/api/core/entities/provider_entities.py
@@ -1,10 +1,34 @@
from enum import Enum
-from typing import Optional
+from typing import Optional, Union
-from pydantic import BaseModel, ConfigDict
+from pydantic import BaseModel, ConfigDict, Field
+from core.entities.parameter_entities import (
+ AppSelectorScope,
+ CommonParameterType,
+ ModelSelectorScope,
+ ToolSelectorScope,
+)
from core.model_runtime.entities.model_entities import ModelType
-from models.provider import ProviderQuotaType
+from core.tools.entities.common_entities import I18nObject
+
+
+class ProviderQuotaType(Enum):
+ PAID = "paid"
+ """hosted paid quota"""
+
+ FREE = "free"
+ """third-party free quota"""
+
+ TRIAL = "trial"
+ """hosted trial quota"""
+
+ @staticmethod
+ def value_of(value):
+ for member in ProviderQuotaType:
+ if member.value == value:
+ return member
+ raise ValueError(f"No matching enum found for value '{value}'")
class QuotaUnit(Enum):
@@ -108,3 +132,55 @@ class ModelSettings(BaseModel):
# pydantic configs
model_config = ConfigDict(protected_namespaces=())
+
+
+class BasicProviderConfig(BaseModel):
+ """
+ Base model class for common provider settings like credentials
+ """
+
+ class Type(Enum):
+ SECRET_INPUT = CommonParameterType.SECRET_INPUT.value
+ TEXT_INPUT = CommonParameterType.TEXT_INPUT.value
+ SELECT = CommonParameterType.SELECT.value
+ BOOLEAN = CommonParameterType.BOOLEAN.value
+ APP_SELECTOR = CommonParameterType.APP_SELECTOR.value
+ MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR.value
+
+ @classmethod
+ def value_of(cls, value: str) -> "ProviderConfig.Type":
+ """
+ Get value of given mode.
+
+ :param value: mode value
+ :return: mode
+ """
+ for mode in cls:
+ if mode.value == value:
+ return mode
+ raise ValueError(f"invalid mode value {value}")
+
+ type: Type = Field(..., description="The type of the credentials")
+ name: str = Field(..., description="The name of the credentials")
+
+
+class ProviderConfig(BasicProviderConfig):
+ """
+ Model class for common provider settings like credentials
+ """
+
+ class Option(BaseModel):
+ value: str = Field(..., description="The value of the option")
+ label: I18nObject = Field(..., description="The label of the option")
+
+ scope: AppSelectorScope | ModelSelectorScope | ToolSelectorScope | None = None
+ required: bool = False
+ default: Optional[Union[int, str]] = None
+ options: Optional[list[Option]] = None
+ label: Optional[I18nObject] = None
+ help: Optional[I18nObject] = None
+ url: Optional[str] = None
+ placeholder: Optional[I18nObject] = None
+
+ def to_basic_provider_config(self) -> BasicProviderConfig:
+ return BasicProviderConfig(type=self.type, name=self.name)
diff --git a/api/core/file/helpers.py b/api/core/file/helpers.py
index 12123cf3f74630..73fabdb11b52da 100644
--- a/api/core/file/helpers.py
+++ b/api/core/file/helpers.py
@@ -20,6 +20,41 @@ def get_signed_file_url(upload_file_id: str) -> str:
return f"{url}?timestamp={timestamp}&nonce={nonce}&sign={encoded_sign}"
+def get_signed_file_url_for_plugin(filename: str, mimetype: str, tenant_id: str, user_id: str) -> str:
+ url = f"{dify_config.FILES_URL}/files/upload/for-plugin"
+
+ if user_id is None:
+ user_id = "DEFAULT-USER"
+
+ timestamp = str(int(time.time()))
+ nonce = os.urandom(16).hex()
+ key = dify_config.SECRET_KEY.encode()
+ msg = f"upload|{filename}|{mimetype}|{tenant_id}|{user_id}|{timestamp}|{nonce}"
+ sign = hmac.new(key, msg.encode(), hashlib.sha256).digest()
+ encoded_sign = base64.urlsafe_b64encode(sign).decode()
+
+ return f"{url}?timestamp={timestamp}&nonce={nonce}&sign={encoded_sign}&user_id={user_id}&tenant_id={tenant_id}"
+
+
+def verify_plugin_file_signature(
+ *, filename: str, mimetype: str, tenant_id: str, user_id: str | None, timestamp: str, nonce: str, sign: str
+) -> bool:
+ if user_id is None:
+ user_id = "DEFAULT-USER"
+
+ data_to_sign = f"upload|{filename}|{mimetype}|{tenant_id}|{user_id}|{timestamp}|{nonce}"
+ secret_key = dify_config.SECRET_KEY.encode()
+ recalculated_sign = hmac.new(secret_key, data_to_sign.encode(), hashlib.sha256).digest()
+ recalculated_encoded_sign = base64.urlsafe_b64encode(recalculated_sign).decode()
+
+ # verify signature
+ if sign != recalculated_encoded_sign:
+ return False
+
+ current_time = int(time.time())
+ return current_time - int(timestamp) <= dify_config.FILES_ACCESS_TIMEOUT
+
+
def verify_image_signature(*, upload_file_id: str, timestamp: str, nonce: str, sign: str) -> bool:
data_to_sign = f"image-preview|{upload_file_id}|{timestamp}|{nonce}"
secret_key = dify_config.SECRET_KEY.encode()
diff --git a/api/core/file/models.py b/api/core/file/models.py
index 0de0089430ef32..2f0026a20398a2 100644
--- a/api/core/file/models.py
+++ b/api/core/file/models.py
@@ -1,5 +1,5 @@
from collections.abc import Mapping, Sequence
-from typing import Optional
+from typing import Any, Optional
from pydantic import BaseModel, Field, model_validator
@@ -124,6 +124,17 @@ def generate_url(self) -> Optional[str]:
tool_file_id=self.related_id, extension=self.extension
)
+ def to_plugin_parameter(self) -> dict[str, Any]:
+ return {
+ "dify_model_identity": FILE_MODEL_IDENTITY,
+ "mime_type": self.mime_type,
+ "filename": self.filename,
+ "extension": self.extension,
+ "size": self.size,
+ "type": self.type,
+ "url": self.generate_url(),
+ }
+
@model_validator(mode="after")
def validate_after(self):
match self.transfer_method:
diff --git a/api/core/file/upload_file_parser.py b/api/core/file/upload_file_parser.py
new file mode 100644
index 00000000000000..062a0b6d225952
--- /dev/null
+++ b/api/core/file/upload_file_parser.py
@@ -0,0 +1,69 @@
+import base64
+import logging
+import time
+from typing import Optional
+
+from configs import dify_config
+from core.helper.url_signer import UrlSigner
+from extensions.ext_storage import storage
+
+IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "gif", "svg"]
+IMAGE_EXTENSIONS.extend([ext.upper() for ext in IMAGE_EXTENSIONS])
+
+
+class UploadFileParser:
+ @classmethod
+ def get_image_data(cls, upload_file, force_url: bool = False) -> Optional[str]:
+ if not upload_file:
+ return None
+
+ if upload_file.extension not in IMAGE_EXTENSIONS:
+ return None
+
+ if dify_config.MULTIMODAL_SEND_FORMAT == "url" or force_url:
+ return cls.get_signed_temp_image_url(upload_file.id)
+ else:
+ # get image file base64
+ try:
+ data = storage.load(upload_file.key)
+ except FileNotFoundError:
+ logging.exception(f"File not found: {upload_file.key}")
+ return None
+
+ encoded_string = base64.b64encode(data).decode("utf-8")
+ return f"data:{upload_file.mime_type};base64,{encoded_string}"
+
+ @classmethod
+ def get_signed_temp_image_url(cls, upload_file_id) -> str:
+ """
+ get signed url from upload file
+
+ :param upload_file: UploadFile object
+ :return:
+ """
+ base_url = dify_config.FILES_URL
+ image_preview_url = f"{base_url}/files/{upload_file_id}/image-preview"
+
+ return UrlSigner.get_signed_url(url=image_preview_url, sign_key=upload_file_id, prefix="image-preview")
+
+ @classmethod
+ def verify_image_file_signature(cls, upload_file_id: str, timestamp: str, nonce: str, sign: str) -> bool:
+ """
+ verify signature
+
+ :param upload_file_id: file id
+ :param timestamp: timestamp
+ :param nonce: nonce
+ :param sign: signature
+ :return:
+ """
+ result = UrlSigner.verify(
+ sign_key=upload_file_id, timestamp=timestamp, nonce=nonce, sign=sign, prefix="image-preview"
+ )
+
+ # verify signature
+ if not result:
+ return False
+
+ current_time = int(time.time())
+ return current_time - int(timestamp) <= dify_config.FILES_ACCESS_TIMEOUT
diff --git a/api/core/helper/download.py b/api/core/helper/download.py
new file mode 100644
index 00000000000000..96400e8ba5879a
--- /dev/null
+++ b/api/core/helper/download.py
@@ -0,0 +1,17 @@
+from core.helper import ssrf_proxy
+
+
+def download_with_size_limit(url, max_download_size: int, **kwargs):
+ response = ssrf_proxy.get(url, follow_redirects=True, **kwargs)
+ if response.status_code == 404:
+ raise ValueError("file not found")
+
+ total_size = 0
+ chunks = []
+ for chunk in response.iter_bytes():
+ total_size += len(chunk)
+ if total_size > max_download_size:
+ raise ValueError("Max file size reached")
+ chunks.append(chunk)
+ content = b"".join(chunks)
+ return content
diff --git a/api/core/helper/marketplace.py b/api/core/helper/marketplace.py
new file mode 100644
index 00000000000000..f4129b88eda869
--- /dev/null
+++ b/api/core/helper/marketplace.py
@@ -0,0 +1,35 @@
+from collections.abc import Sequence
+
+import requests
+from yarl import URL
+
+from configs import dify_config
+from core.helper.download import download_with_size_limit
+from core.plugin.entities.marketplace import MarketplacePluginDeclaration
+
+
+def get_plugin_pkg_url(plugin_unique_identifier: str):
+ return (URL(str(dify_config.MARKETPLACE_API_URL)) / "api/v1/plugins/download").with_query(
+ unique_identifier=plugin_unique_identifier
+ )
+
+
+def download_plugin_pkg(plugin_unique_identifier: str):
+ url = str(get_plugin_pkg_url(plugin_unique_identifier))
+ return download_with_size_limit(url, dify_config.PLUGIN_MAX_PACKAGE_SIZE)
+
+
+def batch_fetch_plugin_manifests(plugin_ids: list[str]) -> Sequence[MarketplacePluginDeclaration]:
+ if len(plugin_ids) == 0:
+ return []
+
+ url = str(URL(str(dify_config.MARKETPLACE_API_URL)) / "api/v1/plugins/batch")
+ response = requests.post(url, json={"plugin_ids": plugin_ids})
+ response.raise_for_status()
+ return [MarketplacePluginDeclaration(**plugin) for plugin in response.json()["data"]["plugins"]]
+
+
+def record_install_plugin_event(plugin_unique_identifier: str):
+ url = str(URL(str(dify_config.MARKETPLACE_API_URL)) / "api/v1/stats/plugins/install_count")
+ response = requests.post(url, json={"unique_identifier": plugin_unique_identifier})
+ response.raise_for_status()
diff --git a/api/core/helper/moderation.py b/api/core/helper/moderation.py
index 543444463b9f1a..6a5982eca49625 100644
--- a/api/core/helper/moderation.py
+++ b/api/core/helper/moderation.py
@@ -1,28 +1,35 @@
import logging
import random
+from typing import cast
from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
+from core.entities import DEFAULT_PLUGIN_ID
+from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.errors.invoke import InvokeBadRequestError
-from core.model_runtime.model_providers.openai.moderation.moderation import OpenAIModerationModel
+from core.model_runtime.model_providers.__base.moderation_model import ModerationModel
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
from extensions.ext_hosting_provider import hosting_configuration
from models.provider import ProviderType
logger = logging.getLogger(__name__)
-def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str) -> bool:
+def check_moderation(tenant_id: str, model_config: ModelConfigWithCredentialsEntity, text: str) -> bool:
moderation_config = hosting_configuration.moderation_config
+ openai_provider_name = f"{DEFAULT_PLUGIN_ID}/openai/openai"
if (
moderation_config
and moderation_config.enabled is True
- and "openai" in hosting_configuration.provider_map
- and hosting_configuration.provider_map["openai"].enabled is True
+ and openai_provider_name in hosting_configuration.provider_map
+ and hosting_configuration.provider_map[openai_provider_name].enabled is True
):
using_provider_type = model_config.provider_model_bundle.configuration.using_provider_type
provider_name = model_config.provider
if using_provider_type == ProviderType.SYSTEM and provider_name in moderation_config.providers:
- hosting_openai_config = hosting_configuration.provider_map["openai"]
- assert hosting_openai_config is not None
+ hosting_openai_config = hosting_configuration.provider_map[openai_provider_name]
+
+ if hosting_openai_config.credentials is None:
+ return False
# 2000 text per chunk
length = 2000
@@ -34,15 +41,20 @@ def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str)
text_chunk = random.choice(text_chunks)
try:
- model_type_instance = OpenAIModerationModel()
- # FIXME, for type hint using assert or raise ValueError is better here?
+ model_provider_factory = ModelProviderFactory(tenant_id)
+
+ # Get model instance of LLM
+ model_type_instance = model_provider_factory.get_model_type_instance(
+ provider=openai_provider_name, model_type=ModelType.MODERATION
+ )
+ model_type_instance = cast(ModerationModel, model_type_instance)
moderation_result = model_type_instance.invoke(
- model="text-moderation-stable", credentials=hosting_openai_config.credentials or {}, text=text_chunk
+ model="omni-moderation-latest", credentials=hosting_openai_config.credentials, text=text_chunk
)
if moderation_result is True:
return True
- except Exception as ex:
+ except Exception:
logger.exception(f"Fails to check moderation, provider_name: {provider_name}")
raise InvokeBadRequestError("Rate limit exceeded, please try again later.")
diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py
index af1e527eb55a12..c8243b29d05e1e 100644
--- a/api/core/helper/ssrf_proxy.py
+++ b/api/core/helper/ssrf_proxy.py
@@ -36,7 +36,6 @@ def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs):
)
retries = 0
- stream = kwargs.pop("stream", False)
while retries <= max_retries:
try:
if dify_config.SSRF_PROXY_ALL_URL:
diff --git a/api/core/helper/tool_provider_cache.py b/api/core/helper/tool_provider_cache.py
index 6de5e704abf4f5..2e4a04c579d4fb 100644
--- a/api/core/helper/tool_provider_cache.py
+++ b/api/core/helper/tool_provider_cache.py
@@ -8,6 +8,7 @@
class ToolProviderCredentialsCacheType(Enum):
PROVIDER = "tool_provider"
+ ENDPOINT = "endpoint"
class ToolProviderCredentialsCache:
diff --git a/api/core/helper/url_signer.py b/api/core/helper/url_signer.py
new file mode 100644
index 00000000000000..dfb143f4c4a88b
--- /dev/null
+++ b/api/core/helper/url_signer.py
@@ -0,0 +1,52 @@
+import base64
+import hashlib
+import hmac
+import os
+import time
+
+from pydantic import BaseModel, Field
+
+from configs import dify_config
+
+
+class SignedUrlParams(BaseModel):
+ sign_key: str = Field(..., description="The sign key")
+ timestamp: str = Field(..., description="Timestamp")
+ nonce: str = Field(..., description="Nonce")
+ sign: str = Field(..., description="Signature")
+
+
+class UrlSigner:
+ @classmethod
+ def get_signed_url(cls, url: str, sign_key: str, prefix: str) -> str:
+ signed_url_params = cls.get_signed_url_params(sign_key, prefix)
+ return (
+ f"{url}?timestamp={signed_url_params.timestamp}"
+ f"&nonce={signed_url_params.nonce}&sign={signed_url_params.sign}"
+ )
+
+ @classmethod
+ def get_signed_url_params(cls, sign_key: str, prefix: str) -> SignedUrlParams:
+ timestamp = str(int(time.time()))
+ nonce = os.urandom(16).hex()
+ sign = cls._sign(sign_key, timestamp, nonce, prefix)
+
+ return SignedUrlParams(sign_key=sign_key, timestamp=timestamp, nonce=nonce, sign=sign)
+
+ @classmethod
+ def verify(cls, sign_key: str, timestamp: str, nonce: str, sign: str, prefix: str) -> bool:
+ recalculated_sign = cls._sign(sign_key, timestamp, nonce, prefix)
+
+ return sign == recalculated_sign
+
+ @classmethod
+ def _sign(cls, sign_key: str, timestamp: str, nonce: str, prefix: str) -> str:
+ if not dify_config.SECRET_KEY:
+ raise Exception("SECRET_KEY is not set")
+
+ data_to_sign = f"{prefix}|{sign_key}|{timestamp}|{nonce}"
+ secret_key = dify_config.SECRET_KEY.encode()
+ sign = hmac.new(secret_key, data_to_sign.encode(), hashlib.sha256).digest()
+ encoded_sign = base64.urlsafe_b64encode(sign).decode()
+
+ return encoded_sign
diff --git a/api/core/hosting_configuration.py b/api/core/hosting_configuration.py
index f9fb7275f3624f..20d98562de019e 100644
--- a/api/core/hosting_configuration.py
+++ b/api/core/hosting_configuration.py
@@ -4,9 +4,9 @@
from pydantic import BaseModel
from configs import dify_config
-from core.entities.provider_entities import QuotaUnit, RestrictModel
+from core.entities import DEFAULT_PLUGIN_ID
+from core.entities.provider_entities import ProviderQuotaType, QuotaUnit, RestrictModel
from core.model_runtime.entities.model_entities import ModelType
-from models.provider import ProviderQuotaType
class HostingQuota(BaseModel):
@@ -41,19 +41,23 @@ class HostedModerationConfig(BaseModel):
class HostingConfiguration:
- provider_map: dict[str, HostingProvider] = {}
+ provider_map: dict[str, HostingProvider]
moderation_config: Optional[HostedModerationConfig] = None
+ def __init__(self) -> None:
+ self.provider_map = {}
+ self.moderation_config = None
+
def init_app(self, app: Flask) -> None:
if dify_config.EDITION != "CLOUD":
return
- self.provider_map["azure_openai"] = self.init_azure_openai()
- self.provider_map["openai"] = self.init_openai()
- self.provider_map["anthropic"] = self.init_anthropic()
- self.provider_map["minimax"] = self.init_minimax()
- self.provider_map["spark"] = self.init_spark()
- self.provider_map["zhipuai"] = self.init_zhipuai()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/azure_openai/azure_openai"] = self.init_azure_openai()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/openai/openai"] = self.init_openai()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/anthropic/anthropic"] = self.init_anthropic()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/minimax/minimax"] = self.init_minimax()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/spark/spark"] = self.init_spark()
+ self.provider_map[f"{DEFAULT_PLUGIN_ID}/zhipuai/zhipuai"] = self.init_zhipuai()
self.moderation_config = self.init_moderation_config()
@@ -240,7 +244,14 @@ def init_zhipuai() -> HostingProvider:
@staticmethod
def init_moderation_config() -> HostedModerationConfig:
if dify_config.HOSTED_MODERATION_ENABLED and dify_config.HOSTED_MODERATION_PROVIDERS:
- return HostedModerationConfig(enabled=True, providers=dify_config.HOSTED_MODERATION_PROVIDERS.split(","))
+ providers = dify_config.HOSTED_MODERATION_PROVIDERS.split(",")
+ hosted_providers = []
+ for provider in providers:
+ if "/" not in provider:
+ provider = f"{DEFAULT_PLUGIN_ID}/{provider}/{provider}"
+ hosted_providers.append(provider)
+
+ return HostedModerationConfig(enabled=True, providers=hosted_providers)
return HostedModerationConfig(enabled=False)
diff --git a/api/core/indexing_runner.py b/api/core/indexing_runner.py
index 1bc4baf9c0e9e7..8206a8d3ecbbbd 100644
--- a/api/core/indexing_runner.py
+++ b/api/core/indexing_runner.py
@@ -30,7 +30,7 @@
FixedRecursiveCharacterTextSplitter,
)
from core.rag.splitter.text_splitter import TextSplitter
-from core.tools.utils.web_reader_tool import get_image_upload_file_ids
+from core.tools.utils.rag_web_reader import get_image_upload_file_ids
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from extensions.ext_storage import storage
@@ -618,10 +618,8 @@ def _process_chunk(
tokens = 0
if embedding_model_instance:
- tokens += sum(
- embedding_model_instance.get_text_embedding_num_tokens([document.page_content])
- for document in chunk_documents
- )
+ page_content_list = [document.page_content for document in chunk_documents]
+ tokens += sum(embedding_model_instance.get_text_embedding_num_tokens(page_content_list))
# load index
index_processor.load(dataset, chunk_documents, with_keywords=False)
diff --git a/api/core/llm_generator/llm_generator.py b/api/core/llm_generator/llm_generator.py
index 9fe3f68f2a8af5..75687f9ae3c9f6 100644
--- a/api/core/llm_generator/llm_generator.py
+++ b/api/core/llm_generator/llm_generator.py
@@ -48,7 +48,7 @@ def generate_conversation_name(
response = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=prompts, model_parameters={"max_tokens": 100, "temperature": 1}, stream=False
+ prompt_messages=list(prompts), model_parameters={"max_tokens": 100, "temperature": 1}, stream=False
),
)
answer = cast(str, response.message.content)
@@ -101,7 +101,7 @@ def generate_suggested_questions_after_answer(cls, tenant_id: str, histories: st
response = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=prompt_messages,
+ prompt_messages=list(prompt_messages),
model_parameters={"max_tokens": 256, "temperature": 0},
stream=False,
),
@@ -110,7 +110,7 @@ def generate_suggested_questions_after_answer(cls, tenant_id: str, histories: st
questions = output_parser.parse(cast(str, response.message.content))
except InvokeError:
questions = []
- except Exception as e:
+ except Exception:
logging.exception("Failed to generate suggested questions after answer")
questions = []
@@ -150,7 +150,7 @@ def generate_rule_config(
response = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False
+ prompt_messages=list(prompt_messages), model_parameters=model_parameters, stream=False
),
)
@@ -200,7 +200,7 @@ def generate_rule_config(
prompt_content = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False
+ prompt_messages=list(prompt_messages), model_parameters=model_parameters, stream=False
),
)
except InvokeError as e:
@@ -236,7 +236,7 @@ def generate_rule_config(
parameter_content = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=parameter_messages, model_parameters=model_parameters, stream=False
+ prompt_messages=list(parameter_messages), model_parameters=model_parameters, stream=False
),
)
rule_config["variables"] = re.findall(r'"\s*([^"]+)\s*"', cast(str, parameter_content.message.content))
@@ -248,7 +248,7 @@ def generate_rule_config(
statement_content = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=statement_messages, model_parameters=model_parameters, stream=False
+ prompt_messages=list(statement_messages), model_parameters=model_parameters, stream=False
),
)
rule_config["opening_statement"] = cast(str, statement_content.message.content)
@@ -301,7 +301,7 @@ def generate_code(
response = cast(
LLMResult,
model_instance.invoke_llm(
- prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False
+ prompt_messages=list(prompt_messages), model_parameters=model_parameters, stream=False
),
)
diff --git a/api/core/model_manager.py b/api/core/model_manager.py
index d1e71148cd6023..0d5e8a3e4b3de9 100644
--- a/api/core/model_manager.py
+++ b/api/core/model_manager.py
@@ -1,6 +1,6 @@
import logging
from collections.abc import Callable, Generator, Iterable, Sequence
-from typing import IO, Any, Optional, Union, cast
+from typing import IO, Any, Literal, Optional, Union, cast, overload
from configs import dify_config
from core.entities.embedding_type import EmbeddingInputType
@@ -98,6 +98,42 @@ def _get_load_balancing_manager(
return None
+ @overload
+ def invoke_llm(
+ self,
+ prompt_messages: list[PromptMessage],
+ model_parameters: Optional[dict] = None,
+ tools: Sequence[PromptMessageTool] | None = None,
+ stop: Optional[list[str]] = None,
+ stream: Literal[True] = True,
+ user: Optional[str] = None,
+ callbacks: Optional[list[Callback]] = None,
+ ) -> Generator: ...
+
+ @overload
+ def invoke_llm(
+ self,
+ prompt_messages: list[PromptMessage],
+ model_parameters: Optional[dict] = None,
+ tools: Sequence[PromptMessageTool] | None = None,
+ stop: Optional[list[str]] = None,
+ stream: Literal[False] = False,
+ user: Optional[str] = None,
+ callbacks: Optional[list[Callback]] = None,
+ ) -> LLMResult: ...
+
+ @overload
+ def invoke_llm(
+ self,
+ prompt_messages: list[PromptMessage],
+ model_parameters: Optional[dict] = None,
+ tools: Sequence[PromptMessageTool] | None = None,
+ stop: Optional[list[str]] = None,
+ stream: bool = True,
+ user: Optional[str] = None,
+ callbacks: Optional[list[Callback]] = None,
+ ) -> Union[LLMResult, Generator]: ...
+
def invoke_llm(
self,
prompt_messages: Sequence[PromptMessage],
@@ -192,7 +228,7 @@ def invoke_text_embedding(
),
)
- def get_text_embedding_num_tokens(self, texts: list[str]) -> int:
+ def get_text_embedding_num_tokens(self, texts: list[str]) -> list[int]:
"""
Get number of tokens for text embedding
@@ -204,7 +240,7 @@ def get_text_embedding_num_tokens(self, texts: list[str]) -> int:
self.model_type_instance = cast(TextEmbeddingModel, self.model_type_instance)
return cast(
- int,
+ list[int],
self._round_robin_invoke(
function=self.model_type_instance.get_num_tokens,
model=self.model,
@@ -397,7 +433,7 @@ def get_model_instance(self, tenant_id: str, provider: str, model_type: ModelTyp
return ModelInstance(provider_model_bundle, model)
- def get_default_provider_model_name(self, tenant_id: str, model_type: ModelType) -> tuple[str, str]:
+ def get_default_provider_model_name(self, tenant_id: str, model_type: ModelType) -> tuple[str | None, str | None]:
"""
Return first provider and the first model in the provider
:param tenant_id: tenant id
diff --git a/api/core/model_runtime/entities/model_entities.py b/api/core/model_runtime/entities/model_entities.py
index edc6eac5175006..3225f03fbddd12 100644
--- a/api/core/model_runtime/entities/model_entities.py
+++ b/api/core/model_runtime/entities/model_entities.py
@@ -18,7 +18,6 @@ class ModelType(Enum):
SPEECH2TEXT = "speech2text"
MODERATION = "moderation"
TTS = "tts"
- TEXT2IMG = "text2img"
@classmethod
def value_of(cls, origin_model_type: str) -> "ModelType":
@@ -37,8 +36,6 @@ def value_of(cls, origin_model_type: str) -> "ModelType":
return cls.SPEECH2TEXT
elif origin_model_type in {"tts", cls.TTS.value}:
return cls.TTS
- elif origin_model_type in {"text2img", cls.TEXT2IMG.value}:
- return cls.TEXT2IMG
elif origin_model_type == cls.MODERATION.value:
return cls.MODERATION
else:
@@ -62,8 +59,6 @@ def to_origin_model_type(self) -> str:
return "tts"
elif self == self.MODERATION:
return "moderation"
- elif self == self.TEXT2IMG:
- return "text2img"
else:
raise ValueError(f"invalid model type {self}")
diff --git a/api/core/model_runtime/entities/provider_entities.py b/api/core/model_runtime/entities/provider_entities.py
index bfe861a97ffbf8..85321bed947a02 100644
--- a/api/core/model_runtime/entities/provider_entities.py
+++ b/api/core/model_runtime/entities/provider_entities.py
@@ -2,10 +2,10 @@
from enum import Enum
from typing import Optional
-from pydantic import BaseModel, ConfigDict
+from pydantic import BaseModel, ConfigDict, Field, field_validator
from core.model_runtime.entities.common_entities import I18nObject
-from core.model_runtime.entities.model_entities import ModelType, ProviderModel
+from core.model_runtime.entities.model_entities import AIModelEntity, ModelType
class ConfigurateMethod(Enum):
@@ -101,7 +101,7 @@ class SimpleProviderEntity(BaseModel):
icon_small: Optional[I18nObject] = None
icon_large: Optional[I18nObject] = None
supported_model_types: Sequence[ModelType]
- models: list[ProviderModel] = []
+ models: list[AIModelEntity] = []
class ProviderHelpEntity(BaseModel):
@@ -127,13 +127,21 @@ class ProviderEntity(BaseModel):
help: Optional[ProviderHelpEntity] = None
supported_model_types: Sequence[ModelType]
configurate_methods: list[ConfigurateMethod]
- models: list[ProviderModel] = []
+ models: list[AIModelEntity] = Field(default_factory=list)
provider_credential_schema: Optional[ProviderCredentialSchema] = None
model_credential_schema: Optional[ModelCredentialSchema] = None
# pydantic configs
model_config = ConfigDict(protected_namespaces=())
+ @field_validator("models", mode="before")
+ @classmethod
+ def validate_models(cls, v):
+ # returns EmptyList if v is empty
+ if not v:
+ return []
+ return v
+
def to_simple_provider(self) -> SimpleProviderEntity:
"""
Convert to simple provider.
diff --git a/api/core/model_runtime/model_providers/__base/ai_model.py b/api/core/model_runtime/model_providers/__base/ai_model.py
index 4cac66ac4ab54a..abc8c83e19fe56 100644
--- a/api/core/model_runtime/model_providers/__base/ai_model.py
+++ b/api/core/model_runtime/model_providers/__base/ai_model.py
@@ -1,53 +1,48 @@
import decimal
-import os
-from abc import ABC, abstractmethod
from typing import Optional
-from pydantic import ConfigDict
+from pydantic import BaseModel, ConfigDict, Field
-from core.helper.position_helper import get_position_map, sort_by_position_map
from core.model_runtime.entities.common_entities import I18nObject
from core.model_runtime.entities.defaults import PARAMETER_RULE_TEMPLATE
from core.model_runtime.entities.model_entities import (
AIModelEntity,
DefaultParameterName,
- FetchFrom,
ModelType,
PriceConfig,
PriceInfo,
PriceType,
)
-from core.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError
+from core.model_runtime.errors.invoke import (
+ InvokeAuthorizationError,
+ InvokeBadRequestError,
+ InvokeConnectionError,
+ InvokeError,
+ InvokeRateLimitError,
+ InvokeServerUnavailableError,
+)
from core.model_runtime.model_providers.__base.tokenizers.gpt2_tokenzier import GPT2Tokenizer
-from core.tools.utils.yaml_utils import load_yaml_file
+from core.plugin.entities.plugin_daemon import PluginDaemonInnerError, PluginModelProviderEntity
+from core.plugin.manager.model import PluginModelManager
-class AIModel(ABC):
+class AIModel(BaseModel):
"""
Base class for all models.
"""
- model_type: ModelType
- model_schemas: Optional[list[AIModelEntity]] = None
- started_at: float = 0
+ tenant_id: str = Field(description="Tenant ID")
+ model_type: ModelType = Field(description="Model type")
+ plugin_id: str = Field(description="Plugin ID")
+ provider_name: str = Field(description="Provider")
+ plugin_model_provider: PluginModelProviderEntity = Field(description="Plugin model provider")
+ started_at: float = Field(description="Invoke start time", default=0)
# pydantic configs
model_config = ConfigDict(protected_namespaces=())
- @abstractmethod
- def validate_credentials(self, model: str, credentials: dict) -> None:
- """
- Validate model credentials
-
- :param model: model name
- :param credentials: model credentials
- :return:
- """
- raise NotImplementedError
-
@property
- @abstractmethod
- def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]:
+ def _invoke_error_mapping(self) -> dict[type[Exception], list[type[Exception]]]:
"""
Map model invoke error to unified error
The key is the error type thrown to the caller
@@ -56,29 +51,37 @@ def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]
:return: Invoke error mapping
"""
- raise NotImplementedError
+ return {
+ InvokeConnectionError: [InvokeConnectionError],
+ InvokeServerUnavailableError: [InvokeServerUnavailableError],
+ InvokeRateLimitError: [InvokeRateLimitError],
+ InvokeAuthorizationError: [InvokeAuthorizationError],
+ InvokeBadRequestError: [InvokeBadRequestError],
+ PluginDaemonInnerError: [PluginDaemonInnerError],
+ ValueError: [ValueError],
+ }
- def _transform_invoke_error(self, error: Exception) -> InvokeError:
+ def _transform_invoke_error(self, error: Exception) -> Exception:
"""
Transform invoke error to unified error
:param error: model invoke error
:return: unified error
"""
- provider_name = self.__class__.__module__.split(".")[-3]
-
for invoke_error, model_errors in self._invoke_error_mapping.items():
if isinstance(error, tuple(model_errors)):
if invoke_error == InvokeAuthorizationError:
- return invoke_error(
+ return InvokeAuthorizationError(
description=(
- f"[{provider_name}] Incorrect model credentials provided, please check and try again."
+ f"[{self.provider_name}] Incorrect model credentials provided, please check and try again."
)
)
+ elif isinstance(invoke_error, InvokeError):
+ return invoke_error(description=f"[{self.provider_name}] {invoke_error.description}, {str(error)}")
+ else:
+ return error
- return invoke_error(description=f"[{provider_name}] {invoke_error.description}, {str(error)}")
-
- return InvokeError(description=f"[{provider_name}] Error: {str(error)}")
+ return InvokeError(description=f"[{self.provider_name}] Error: {str(error)}")
def get_price(self, model: str, credentials: dict, price_type: PriceType, tokens: int) -> PriceInfo:
"""
@@ -127,92 +130,6 @@ def get_price(self, model: str, credentials: dict, price_type: PriceType, tokens
currency=price_config.currency,
)
- def predefined_models(self) -> list[AIModelEntity]:
- """
- Get all predefined models for given provider.
-
- :return:
- """
- if self.model_schemas:
- return self.model_schemas
-
- model_schemas = []
-
- # get module name
- model_type = self.__class__.__module__.split(".")[-1]
-
- # get provider name
- provider_name = self.__class__.__module__.split(".")[-3]
-
- # get the path of current classes
- current_path = os.path.abspath(__file__)
- # get parent path of the current path
- provider_model_type_path = os.path.join(
- os.path.dirname(os.path.dirname(current_path)), provider_name, model_type
- )
-
- # get all yaml files path under provider_model_type_path that do not start with __
- model_schema_yaml_paths = [
- os.path.join(provider_model_type_path, model_schema_yaml)
- for model_schema_yaml in os.listdir(provider_model_type_path)
- if not model_schema_yaml.startswith("__")
- and not model_schema_yaml.startswith("_")
- and os.path.isfile(os.path.join(provider_model_type_path, model_schema_yaml))
- and model_schema_yaml.endswith(".yaml")
- ]
-
- # get _position.yaml file path
- position_map = get_position_map(provider_model_type_path)
-
- # traverse all model_schema_yaml_paths
- for model_schema_yaml_path in model_schema_yaml_paths:
- # read yaml data from yaml file
- yaml_data = load_yaml_file(model_schema_yaml_path)
-
- new_parameter_rules = []
- for parameter_rule in yaml_data.get("parameter_rules", []):
- if "use_template" in parameter_rule:
- try:
- default_parameter_name = DefaultParameterName.value_of(parameter_rule["use_template"])
- default_parameter_rule = self._get_default_parameter_rule_variable_map(default_parameter_name)
- copy_default_parameter_rule = default_parameter_rule.copy()
- copy_default_parameter_rule.update(parameter_rule)
- parameter_rule = copy_default_parameter_rule
- except ValueError:
- pass
-
- if "label" not in parameter_rule:
- parameter_rule["label"] = {"zh_Hans": parameter_rule["name"], "en_US": parameter_rule["name"]}
-
- new_parameter_rules.append(parameter_rule)
-
- yaml_data["parameter_rules"] = new_parameter_rules
-
- if "label" not in yaml_data:
- yaml_data["label"] = {"zh_Hans": yaml_data["model"], "en_US": yaml_data["model"]}
-
- yaml_data["fetch_from"] = FetchFrom.PREDEFINED_MODEL.value
-
- try:
- # yaml_data to entity
- model_schema = AIModelEntity(**yaml_data)
- except Exception as e:
- model_schema_yaml_file_name = os.path.basename(model_schema_yaml_path).rstrip(".yaml")
- raise Exception(
- f"Invalid model schema for {provider_name}.{model_type}.{model_schema_yaml_file_name}: {str(e)}"
- )
-
- # cache model schema
- model_schemas.append(model_schema)
-
- # resort model schemas by position
- model_schemas = sort_by_position_map(position_map, model_schemas, lambda x: x.model)
-
- # cache model schemas
- self.model_schemas = model_schemas
-
- return model_schemas
-
def get_model_schema(self, model: str, credentials: Optional[dict] = None) -> Optional[AIModelEntity]:
"""
Get model schema by model name and credentials
@@ -221,18 +138,16 @@ def get_model_schema(self, model: str, credentials: Optional[dict] = None) -> Op
:param credentials: model credentials
:return: model schema
"""
- # Try to get model schema from predefined models
- for predefined_model in self.predefined_models():
- if model == predefined_model.model:
- return predefined_model
-
- # Try to get model schema from credentials
- if credentials:
- model_schema = self.get_customizable_model_schema_from_credentials(model, credentials)
- if model_schema:
- return model_schema
-
- return None
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.get_model_schema(
+ tenant_id=self.tenant_id,
+ user_id="unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model_type=self.model_type.value,
+ model=model,
+ credentials=credentials or {},
+ )
def get_customizable_model_schema_from_credentials(self, model: str, credentials: dict) -> Optional[AIModelEntity]:
"""
diff --git a/api/core/model_runtime/model_providers/__base/audio.mp3 b/api/core/model_runtime/model_providers/__base/audio.mp3
deleted file mode 100644
index 7c86e02e160909..00000000000000
Binary files a/api/core/model_runtime/model_providers/__base/audio.mp3 and /dev/null differ
diff --git a/api/core/model_runtime/model_providers/__base/large_language_model.py b/api/core/model_runtime/model_providers/__base/large_language_model.py
index b45b2ca02568db..ed67fef7682136 100644
--- a/api/core/model_runtime/model_providers/__base/large_language_model.py
+++ b/api/core/model_runtime/model_providers/__base/large_language_model.py
@@ -1,7 +1,5 @@
import logging
-import re
import time
-from abc import abstractmethod
from collections.abc import Generator, Sequence
from typing import Optional, Union
@@ -10,31 +8,21 @@
from configs import dify_config
from core.model_runtime.callbacks.base_callback import Callback
from core.model_runtime.callbacks.logging_callback import LoggingCallback
-from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage
+from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMUsage
from core.model_runtime.entities.message_entities import (
AssistantPromptMessage,
PromptMessage,
- PromptMessageContentType,
PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
)
from core.model_runtime.entities.model_entities import (
- ModelPropertyKey,
ModelType,
- ParameterRule,
- ParameterType,
PriceType,
)
from core.model_runtime.model_providers.__base.ai_model import AIModel
+from core.plugin.manager.model import PluginModelManager
logger = logging.getLogger(__name__)
-HTML_THINKING_TAG = (
- ' '
- " Thinking... "
-)
-
class LargeLanguageModel(AIModel):
"""
@@ -76,8 +64,6 @@ def invoke(
if model_parameters is None:
model_parameters = {}
- model_parameters = self._validate_and_filter_model_parameters(model, model_parameters, credentials)
-
self.started_at = time.perf_counter()
callbacks = callbacks or []
@@ -98,29 +84,83 @@ def invoke(
callbacks=callbacks,
)
+ result: Union[LLMResult, Generator[LLMResultChunk, None, None]]
+
try:
- if "response_format" in model_parameters and model_parameters["response_format"] in {"JSON", "XML"}:
- result = self._code_block_mode_wrapper(
- model=model,
- credentials=credentials,
- prompt_messages=prompt_messages,
- model_parameters=model_parameters,
- tools=tools,
- stop=stop,
- stream=stream,
- user=user,
- callbacks=callbacks,
- )
- else:
- result = self._invoke(
+ plugin_model_manager = PluginModelManager()
+ result = plugin_model_manager.invoke_llm(
+ tenant_id=self.tenant_id,
+ user_id=user or "unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ model_parameters=model_parameters,
+ prompt_messages=prompt_messages,
+ tools=tools,
+ stop=list(stop) if stop else None,
+ stream=stream,
+ )
+
+ if not stream:
+ content = ""
+ content_list = []
+ usage = LLMUsage.empty_usage()
+ system_fingerprint = None
+ tools_calls: list[AssistantPromptMessage.ToolCall] = []
+
+ def increase_tool_call(new_tool_calls: list[AssistantPromptMessage.ToolCall]):
+ def get_tool_call(tool_name: str):
+ if not tool_name:
+ return tools_calls[-1]
+
+ tool_call = next(
+ (tool_call for tool_call in tools_calls if tool_call.function.name == tool_name), None
+ )
+ if tool_call is None:
+ tool_call = AssistantPromptMessage.ToolCall(
+ id="",
+ type="",
+ function=AssistantPromptMessage.ToolCall.ToolCallFunction(name=tool_name, arguments=""),
+ )
+ tools_calls.append(tool_call)
+
+ return tool_call
+
+ for new_tool_call in new_tool_calls:
+ # get tool call
+ tool_call = get_tool_call(new_tool_call.function.name)
+ # update tool call
+ if new_tool_call.id:
+ tool_call.id = new_tool_call.id
+ if new_tool_call.type:
+ tool_call.type = new_tool_call.type
+ if new_tool_call.function.name:
+ tool_call.function.name = new_tool_call.function.name
+ if new_tool_call.function.arguments:
+ tool_call.function.arguments += new_tool_call.function.arguments
+
+ for chunk in result:
+ if isinstance(chunk.delta.message.content, str):
+ content += chunk.delta.message.content
+ elif isinstance(chunk.delta.message.content, list):
+ content_list.extend(chunk.delta.message.content)
+ if chunk.delta.message.tool_calls:
+ increase_tool_call(chunk.delta.message.tool_calls)
+
+ usage = chunk.delta.usage or LLMUsage.empty_usage()
+ system_fingerprint = chunk.system_fingerprint
+ break
+
+ result = LLMResult(
model=model,
- credentials=credentials,
prompt_messages=prompt_messages,
- model_parameters=model_parameters,
- tools=tools,
- stop=stop,
- stream=stream,
- user=user,
+ message=AssistantPromptMessage(
+ content=content or content_list,
+ tool_calls=tools_calls,
+ ),
+ usage=usage,
+ system_fingerprint=system_fingerprint,
)
except Exception as e:
self._trigger_invoke_error_callbacks(
@@ -136,6 +176,7 @@ def invoke(
callbacks=callbacks,
)
+ # TODO
raise self._transform_invoke_error(e)
if stream and isinstance(result, Generator):
@@ -167,278 +208,6 @@ def invoke(
return result
- def _code_block_mode_wrapper(
- self,
- model: str,
- credentials: dict,
- prompt_messages: list[PromptMessage],
- model_parameters: dict,
- tools: Optional[list[PromptMessageTool]] = None,
- stop: Optional[Sequence[str]] = None,
- stream: bool = True,
- user: Optional[str] = None,
- callbacks: Optional[list[Callback]] = None,
- ) -> Union[LLMResult, Generator]:
- """
- Code block mode wrapper, ensure the response is a code block with output markdown quote
-
- :param model: model name
- :param credentials: model credentials
- :param prompt_messages: prompt messages
- :param model_parameters: model parameters
- :param tools: tools for tool calling
- :param stop: stop words
- :param stream: is stream response
- :param user: unique user id
- :param callbacks: callbacks
- :return: full response or stream response chunk generator result
- """
-
- block_prompts = """You should always follow the instructions and output a valid {{block}} object.
-The structure of the {{block}} object you can found in the instructions, use {"answer": "$your_answer"} as the default structure
-if you are not sure about the structure.
-
-
-{{instructions}}
-
-""" # noqa: E501
-
- code_block = model_parameters.get("response_format", "")
- if not code_block:
- return self._invoke(
- model=model,
- credentials=credentials,
- prompt_messages=prompt_messages,
- model_parameters=model_parameters,
- tools=tools,
- stop=stop,
- stream=stream,
- user=user,
- )
-
- model_parameters.pop("response_format")
- stop = list(stop) if stop is not None else []
- stop.extend(["\n```", "```\n"])
- block_prompts = block_prompts.replace("{{block}}", code_block)
-
- # check if there is a system message
- if len(prompt_messages) > 0 and isinstance(prompt_messages[0], SystemPromptMessage):
- # override the system message
- prompt_messages[0] = SystemPromptMessage(
- content=block_prompts.replace("{{instructions}}", str(prompt_messages[0].content))
- )
- else:
- # insert the system message
- prompt_messages.insert(
- 0,
- SystemPromptMessage(
- content=block_prompts.replace("{{instructions}}", f"Please output a valid {code_block} object.")
- ),
- )
-
- if len(prompt_messages) > 0 and isinstance(prompt_messages[-1], UserPromptMessage):
- # add ```JSON\n to the last text message
- if isinstance(prompt_messages[-1].content, str):
- prompt_messages[-1].content += f"\n```{code_block}\n"
- elif isinstance(prompt_messages[-1].content, list):
- for i in range(len(prompt_messages[-1].content) - 1, -1, -1):
- if prompt_messages[-1].content[i].type == PromptMessageContentType.TEXT:
- prompt_messages[-1].content[i].data += f"\n```{code_block}\n"
- break
- else:
- # append a user message
- prompt_messages.append(UserPromptMessage(content=f"```{code_block}\n"))
-
- response = self._invoke(
- model=model,
- credentials=credentials,
- prompt_messages=prompt_messages,
- model_parameters=model_parameters,
- tools=tools,
- stop=stop,
- stream=stream,
- user=user,
- )
-
- if isinstance(response, Generator):
- first_chunk = next(response)
-
- def new_generator():
- yield first_chunk
- yield from response
-
- if first_chunk.delta.message.content and first_chunk.delta.message.content.startswith("`"):
- return self._code_block_mode_stream_processor_with_backtick(
- model=model, prompt_messages=prompt_messages, input_generator=new_generator()
- )
- else:
- return self._code_block_mode_stream_processor(
- model=model, prompt_messages=prompt_messages, input_generator=new_generator()
- )
-
- return response
-
- def _code_block_mode_stream_processor(
- self, model: str, prompt_messages: list[PromptMessage], input_generator: Generator[LLMResultChunk, None, None]
- ) -> Generator[LLMResultChunk, None, None]:
- """
- Code block mode stream processor, ensure the response is a code block with output markdown quote
-
- :param model: model name
- :param prompt_messages: prompt messages
- :param input_generator: input generator
- :return: output generator
- """
- state = "normal"
- backtick_count = 0
- for piece in input_generator:
- if piece.delta.message.content:
- content = piece.delta.message.content
- piece.delta.message.content = ""
- yield piece
- content_piece = content
- else:
- yield piece
- continue
- new_piece: str = ""
- for char in content_piece:
- char = str(char)
- if state == "normal":
- if char == "`":
- state = "in_backticks"
- backtick_count = 1
- else:
- new_piece += char
- elif state == "in_backticks":
- if char == "`":
- backtick_count += 1
- if backtick_count == 3:
- state = "skip_content"
- backtick_count = 0
- else:
- new_piece += "`" * backtick_count + char
- state = "normal"
- backtick_count = 0
- elif state == "skip_content":
- if char.isspace():
- state = "normal"
-
- if new_piece:
- yield LLMResultChunk(
- model=model,
- prompt_messages=prompt_messages,
- delta=LLMResultChunkDelta(
- index=0,
- message=AssistantPromptMessage(content=new_piece, tool_calls=[]),
- ),
- )
-
- def _code_block_mode_stream_processor_with_backtick(
- self, model: str, prompt_messages: list, input_generator: Generator[LLMResultChunk, None, None]
- ) -> Generator[LLMResultChunk, None, None]:
- """
- Code block mode stream processor, ensure the response is a code block with output markdown quote.
- This version skips the language identifier that follows the opening triple backticks.
-
- :param model: model name
- :param prompt_messages: prompt messages
- :param input_generator: input generator
- :return: output generator
- """
- state = "search_start"
- backtick_count = 0
-
- for piece in input_generator:
- if piece.delta.message.content:
- content = piece.delta.message.content
- # Reset content to ensure we're only processing and yielding the relevant parts
- piece.delta.message.content = ""
- # Yield a piece with cleared content before processing it to maintain the generator structure
- yield piece
- content_piece = content
- else:
- # Yield pieces without content directly
- yield piece
- continue
-
- if state == "done":
- continue
-
- new_piece: str = ""
- for char in content_piece:
- if state == "search_start":
- if char == "`":
- backtick_count += 1
- if backtick_count == 3:
- state = "skip_language"
- backtick_count = 0
- else:
- backtick_count = 0
- elif state == "skip_language":
- # Skip everything until the first newline, marking the end of the language identifier
- if char == "\n":
- state = "in_code_block"
- elif state == "in_code_block":
- if char == "`":
- backtick_count += 1
- if backtick_count == 3:
- state = "done"
- break
- else:
- if backtick_count > 0:
- # If backticks were counted but we're still collecting content, it was a false start
- new_piece += "`" * backtick_count
- backtick_count = 0
- new_piece += str(char)
-
- elif state == "done":
- break
-
- if new_piece:
- # Only yield content collected within the code block
- yield LLMResultChunk(
- model=model,
- prompt_messages=prompt_messages,
- delta=LLMResultChunkDelta(
- index=0,
- message=AssistantPromptMessage(content=new_piece, tool_calls=[]),
- ),
- )
-
- def _wrap_thinking_by_reasoning_content(self, delta: dict, is_reasoning: bool) -> tuple[str, bool]:
- """
- If the reasoning response is from delta.get("reasoning_content"), we wrap
- it with HTML details tag.
-
- :param delta: delta dictionary from LLM streaming response
- :param is_reasoning: is reasoning
- :return: tuple of (processed_content, is_reasoning)
- """
-
- content = delta.get("content") or ""
- reasoning_content = delta.get("reasoning_content")
-
- if reasoning_content:
- if not is_reasoning:
- content = HTML_THINKING_TAG + reasoning_content
- is_reasoning = True
- else:
- content = reasoning_content
- elif is_reasoning:
- content = " " + content
- is_reasoning = False
- return content, is_reasoning
-
- def _wrap_thinking_by_tag(self, content: str) -> str:
- """
- if the reasoning response is a ... block from delta.get("content"),
- we replace to .
-
- :param content: delta.get("content")
- :return: processed_content
- """
- return content.replace("", HTML_THINKING_TAG).replace(" ", "")
-
def _invoke_result_generator(
self,
model: str,
@@ -459,7 +228,7 @@ def _invoke_result_generator(
:return: result generator
"""
callbacks = callbacks or []
- prompt_message = AssistantPromptMessage(content="")
+ assistant_message = AssistantPromptMessage(content="")
usage = None
system_fingerprint = None
real_model = model
@@ -481,7 +250,7 @@ def _invoke_result_generator(
callbacks=callbacks,
)
- prompt_message.content += chunk.delta.message.content
+ assistant_message.content += chunk.delta.message.content
real_model = chunk.model
if chunk.delta.usage:
usage = chunk.delta.usage
@@ -496,7 +265,7 @@ def _invoke_result_generator(
result=LLMResult(
model=real_model,
prompt_messages=prompt_messages,
- message=prompt_message,
+ message=assistant_message,
usage=usage or LLMUsage.empty_usage(),
system_fingerprint=system_fingerprint,
),
@@ -510,34 +279,6 @@ def _invoke_result_generator(
callbacks=callbacks,
)
- @abstractmethod
- def _invoke(
- self,
- model: str,
- credentials: dict,
- prompt_messages: list[PromptMessage],
- model_parameters: dict,
- tools: Optional[list[PromptMessageTool]] = None,
- stop: Optional[Sequence[str]] = None,
- stream: bool = True,
- user: Optional[str] = None,
- ) -> Union[LLMResult, Generator]:
- """
- Invoke large language model
-
- :param model: model name
- :param credentials: model credentials
- :param prompt_messages: prompt messages
- :param model_parameters: model parameters
- :param tools: tools for tool calling
- :param stop: stop words
- :param stream: is stream response
- :param user: unique user id
- :return: full response or stream response chunk generator result
- """
- raise NotImplementedError
-
- @abstractmethod
def get_num_tokens(
self,
model: str,
@@ -554,41 +295,18 @@ def get_num_tokens(
:param tools: tools for tool calling
:return:
"""
- raise NotImplementedError
-
- def enforce_stop_tokens(self, text: str, stop: list[str]) -> str:
- """Cut off the text as soon as any stop words occur."""
- return re.split("|".join(stop), text, maxsplit=1)[0]
-
- def get_parameter_rules(self, model: str, credentials: dict) -> list[ParameterRule]:
- """
- Get parameter rules
-
- :param model: model name
- :param credentials: model credentials
- :return: parameter rules
- """
- model_schema = self.get_model_schema(model, credentials)
- if model_schema:
- return model_schema.parameter_rules
-
- return []
-
- def get_model_mode(self, model: str, credentials: Optional[dict] = None) -> LLMMode:
- """
- Get model mode
-
- :param model: model name
- :param credentials: model credentials
- :return: model mode
- """
- model_schema = self.get_model_schema(model, credentials)
-
- mode = LLMMode.CHAT
- if model_schema and model_schema.model_properties.get(ModelPropertyKey.MODE):
- mode = LLMMode.value_of(model_schema.model_properties[ModelPropertyKey.MODE])
-
- return mode
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.get_llm_num_tokens(
+ tenant_id=self.tenant_id,
+ user_id="unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model_type=self.model_type.value,
+ model=model,
+ credentials=credentials,
+ prompt_messages=prompt_messages,
+ tools=tools,
+ )
def _calc_response_usage(
self, model: str, credentials: dict, prompt_tokens: int, completion_tokens: int
@@ -820,98 +538,3 @@ def _trigger_invoke_error_callbacks(
raise e
else:
logger.warning(f"Callback {callback.__class__.__name__} on_invoke_error failed with error {e}")
-
- def _validate_and_filter_model_parameters(self, model: str, model_parameters: dict, credentials: dict) -> dict:
- """
- Validate model parameters
-
- :param model: model name
- :param model_parameters: model parameters
- :param credentials: model credentials
- :return:
- """
- parameter_rules = self.get_parameter_rules(model, credentials)
-
- # validate model parameters
- filtered_model_parameters = {}
- for parameter_rule in parameter_rules:
- parameter_name = parameter_rule.name
- parameter_value = model_parameters.get(parameter_name)
- if parameter_value is None:
- if parameter_rule.use_template and parameter_rule.use_template in model_parameters:
- # if parameter value is None, use template value variable name instead
- parameter_value = model_parameters[parameter_rule.use_template]
- else:
- if parameter_rule.required:
- if parameter_rule.default is not None:
- filtered_model_parameters[parameter_name] = parameter_rule.default
- continue
- else:
- raise ValueError(f"Model Parameter {parameter_name} is required.")
- else:
- continue
-
- # validate parameter value type
- if parameter_rule.type == ParameterType.INT:
- if not isinstance(parameter_value, int):
- raise ValueError(f"Model Parameter {parameter_name} should be int.")
-
- # validate parameter value range
- if parameter_rule.min is not None and parameter_value < parameter_rule.min:
- raise ValueError(
- f"Model Parameter {parameter_name} should be greater than or equal to {parameter_rule.min}."
- )
-
- if parameter_rule.max is not None and parameter_value > parameter_rule.max:
- raise ValueError(
- f"Model Parameter {parameter_name} should be less than or equal to {parameter_rule.max}."
- )
- elif parameter_rule.type == ParameterType.FLOAT:
- if not isinstance(parameter_value, float | int):
- raise ValueError(f"Model Parameter {parameter_name} should be float.")
-
- # validate parameter value precision
- if parameter_rule.precision is not None:
- if parameter_rule.precision == 0:
- if parameter_value != int(parameter_value):
- raise ValueError(f"Model Parameter {parameter_name} should be int.")
- else:
- if parameter_value != round(parameter_value, parameter_rule.precision):
- raise ValueError(
- f"Model Parameter {parameter_name} should be round to {parameter_rule.precision}"
- f" decimal places."
- )
-
- # validate parameter value range
- if parameter_rule.min is not None and parameter_value < parameter_rule.min:
- raise ValueError(
- f"Model Parameter {parameter_name} should be greater than or equal to {parameter_rule.min}."
- )
-
- if parameter_rule.max is not None and parameter_value > parameter_rule.max:
- raise ValueError(
- f"Model Parameter {parameter_name} should be less than or equal to {parameter_rule.max}."
- )
- elif parameter_rule.type == ParameterType.BOOLEAN:
- if not isinstance(parameter_value, bool):
- raise ValueError(f"Model Parameter {parameter_name} should be bool.")
- elif parameter_rule.type == ParameterType.STRING:
- if not isinstance(parameter_value, str):
- raise ValueError(f"Model Parameter {parameter_name} should be string.")
-
- # validate options
- if parameter_rule.options and parameter_value not in parameter_rule.options:
- raise ValueError(f"Model Parameter {parameter_name} should be one of {parameter_rule.options}.")
- elif parameter_rule.type == ParameterType.TEXT:
- if not isinstance(parameter_value, str):
- raise ValueError(f"Model Parameter {parameter_name} should be text.")
-
- # validate options
- if parameter_rule.options and parameter_value not in parameter_rule.options:
- raise ValueError(f"Model Parameter {parameter_name} should be one of {parameter_rule.options}.")
- else:
- raise ValueError(f"Model Parameter {parameter_name} type {parameter_rule.type} is not supported.")
-
- filtered_model_parameters[parameter_name] = parameter_value
-
- return filtered_model_parameters
diff --git a/api/core/model_runtime/model_providers/__base/model_provider.py b/api/core/model_runtime/model_providers/__base/model_provider.py
deleted file mode 100644
index 36e3e7bd557163..00000000000000
--- a/api/core/model_runtime/model_providers/__base/model_provider.py
+++ /dev/null
@@ -1,121 +0,0 @@
-import os
-from abc import ABC, abstractmethod
-from typing import Optional
-
-from core.helper.module_import_helper import get_subclasses_from_module, import_module_from_source
-from core.model_runtime.entities.model_entities import AIModelEntity, ModelType
-from core.model_runtime.entities.provider_entities import ProviderEntity
-from core.model_runtime.model_providers.__base.ai_model import AIModel
-from core.tools.utils.yaml_utils import load_yaml_file
-
-
-class ModelProvider(ABC):
- provider_schema: Optional[ProviderEntity] = None
- model_instance_map: dict[str, AIModel] = {}
-
- @abstractmethod
- def validate_provider_credentials(self, credentials: dict) -> None:
- """
- Validate provider credentials
- You can choose any validate_credentials method of model type or implement validate method by yourself,
- such as: get model list api
-
- if validate failed, raise exception
-
- :param credentials: provider credentials, credentials form defined in `provider_credential_schema`.
- """
- raise NotImplementedError
-
- def get_provider_schema(self) -> ProviderEntity:
- """
- Get provider schema
-
- :return: provider schema
- """
- if self.provider_schema:
- return self.provider_schema
-
- # get dirname of the current path
- provider_name = self.__class__.__module__.split(".")[-1]
-
- # get the path of the model_provider classes
- base_path = os.path.abspath(__file__)
- current_path = os.path.join(os.path.dirname(os.path.dirname(base_path)), provider_name)
-
- # read provider schema from yaml file
- yaml_path = os.path.join(current_path, f"{provider_name}.yaml")
- yaml_data = load_yaml_file(yaml_path)
-
- try:
- # yaml_data to entity
- provider_schema = ProviderEntity(**yaml_data)
- except Exception as e:
- raise Exception(f"Invalid provider schema for {provider_name}: {str(e)}")
-
- # cache schema
- self.provider_schema = provider_schema
-
- return provider_schema
-
- def models(self, model_type: ModelType) -> list[AIModelEntity]:
- """
- Get all models for given model type
-
- :param model_type: model type defined in `ModelType`
- :return: list of models
- """
- provider_schema = self.get_provider_schema()
- if model_type not in provider_schema.supported_model_types:
- return []
-
- # get model instance of the model type
- model_instance = self.get_model_instance(model_type)
-
- # get predefined models (predefined_models)
- models = model_instance.predefined_models()
-
- # return models
- return models
-
- def get_model_instance(self, model_type: ModelType) -> AIModel:
- """
- Get model instance
-
- :param model_type: model type defined in `ModelType`
- :return:
- """
- # get dirname of the current path
- provider_name = self.__class__.__module__.split(".")[-1]
-
- if f"{provider_name}.{model_type.value}" in self.model_instance_map:
- return self.model_instance_map[f"{provider_name}.{model_type.value}"]
-
- # get the path of the model type classes
- base_path = os.path.abspath(__file__)
- model_type_name = model_type.value.replace("-", "_")
- model_type_path = os.path.join(os.path.dirname(os.path.dirname(base_path)), provider_name, model_type_name)
- model_type_py_path = os.path.join(model_type_path, f"{model_type_name}.py")
-
- if not os.path.isdir(model_type_path) or not os.path.exists(model_type_py_path):
- raise Exception(f"Invalid model type {model_type} for provider {provider_name}")
-
- # Dynamic loading {model_type_name}.py file and find the subclass of AIModel
- parent_module = ".".join(self.__class__.__module__.split(".")[:-1])
- mod = import_module_from_source(
- module_name=f"{parent_module}.{model_type_name}.{model_type_name}", py_file_path=model_type_py_path
- )
- # FIXME "type" has no attribute "__abstractmethods__" ignore it for now fix it later
- model_class = next(
- filter(
- lambda x: x.__module__ == mod.__name__ and not x.__abstractmethods__, # type: ignore
- get_subclasses_from_module(mod, AIModel),
- ),
- None,
- )
- if not model_class:
- raise Exception(f"Missing AIModel Class for model type {model_type} in {model_type_py_path}")
-
- model_instance_map = model_class()
- self.model_instance_map[f"{provider_name}.{model_type.value}"] = model_instance_map
-
- return model_instance_map
diff --git a/api/core/model_runtime/model_providers/__base/moderation_model.py b/api/core/model_runtime/model_providers/__base/moderation_model.py
index d04414ccb87a63..f98d7572c727d0 100644
--- a/api/core/model_runtime/model_providers/__base/moderation_model.py
+++ b/api/core/model_runtime/model_providers/__base/moderation_model.py
@@ -1,11 +1,11 @@
import time
-from abc import abstractmethod
from typing import Optional
from pydantic import ConfigDict
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.model_providers.__base.ai_model import AIModel
+from core.plugin.manager.model import PluginModelManager
class ModerationModel(AIModel):
@@ -31,19 +31,15 @@ def invoke(self, model: str, credentials: dict, text: str, user: Optional[str] =
self.started_at = time.perf_counter()
try:
- return self._invoke(model, credentials, text, user)
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.invoke_moderation(
+ tenant_id=self.tenant_id,
+ user_id=user or "unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ text=text,
+ )
except Exception as e:
raise self._transform_invoke_error(e)
-
- @abstractmethod
- def _invoke(self, model: str, credentials: dict, text: str, user: Optional[str] = None) -> bool:
- """
- Invoke large language model
-
- :param model: model name
- :param credentials: model credentials
- :param text: text to moderate
- :param user: unique user id
- :return: false if text is safe, true otherwise
- """
- raise NotImplementedError
diff --git a/api/core/model_runtime/model_providers/__base/rerank_model.py b/api/core/model_runtime/model_providers/__base/rerank_model.py
index 5fb96047425592..e905cb18d46282 100644
--- a/api/core/model_runtime/model_providers/__base/rerank_model.py
+++ b/api/core/model_runtime/model_providers/__base/rerank_model.py
@@ -1,10 +1,9 @@
-import time
-from abc import abstractmethod
from typing import Optional
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.entities.rerank_entities import RerankResult
from core.model_runtime.model_providers.__base.ai_model import AIModel
+from core.plugin.manager.model import PluginModelManager
class RerankModel(AIModel):
@@ -36,34 +35,19 @@ def invoke(
:param user: unique user id
:return: rerank result
"""
- self.started_at = time.perf_counter()
-
try:
- return self._invoke(model, credentials, query, docs, score_threshold, top_n, user)
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.invoke_rerank(
+ tenant_id=self.tenant_id,
+ user_id=user or "unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ query=query,
+ docs=docs,
+ score_threshold=score_threshold,
+ top_n=top_n,
+ )
except Exception as e:
raise self._transform_invoke_error(e)
-
- @abstractmethod
- def _invoke(
- self,
- model: str,
- credentials: dict,
- query: str,
- docs: list[str],
- score_threshold: Optional[float] = None,
- top_n: Optional[int] = None,
- user: Optional[str] = None,
- ) -> RerankResult:
- """
- Invoke rerank model
-
- :param model: model name
- :param credentials: model credentials
- :param query: search query
- :param docs: docs for reranking
- :param score_threshold: score threshold
- :param top_n: top n
- :param user: unique user id
- :return: rerank result
- """
- raise NotImplementedError
diff --git a/api/core/model_runtime/model_providers/__base/speech2text_model.py b/api/core/model_runtime/model_providers/__base/speech2text_model.py
index b6b0b737436d9c..97ff322f09c2d2 100644
--- a/api/core/model_runtime/model_providers/__base/speech2text_model.py
+++ b/api/core/model_runtime/model_providers/__base/speech2text_model.py
@@ -1,11 +1,10 @@
-import os
-from abc import abstractmethod
from typing import IO, Optional
from pydantic import ConfigDict
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.model_providers.__base.ai_model import AIModel
+from core.plugin.manager.model import PluginModelManager
class Speech2TextModel(AIModel):
@@ -20,7 +19,7 @@ class Speech2TextModel(AIModel):
def invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str:
"""
- Invoke large language model
+ Invoke speech to text model
:param model: model name
:param credentials: model credentials
@@ -29,31 +28,15 @@ def invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[
:return: text for given audio file
"""
try:
- return self._invoke(model, credentials, file, user)
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.invoke_speech_to_text(
+ tenant_id=self.tenant_id,
+ user_id=user or "unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ file=file,
+ )
except Exception as e:
raise self._transform_invoke_error(e)
-
- @abstractmethod
- def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str:
- """
- Invoke large language model
-
- :param model: model name
- :param credentials: model credentials
- :param file: audio file
- :param user: unique user id
- :return: text for given audio file
- """
- raise NotImplementedError
-
- def _get_demo_file_path(self) -> str:
- """
- Get demo file for given model
-
- :return: demo file
- """
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Construct the path to the audio file
- return os.path.join(current_dir, "audio.mp3")
diff --git a/api/core/model_runtime/model_providers/__base/text2img_model.py b/api/core/model_runtime/model_providers/__base/text2img_model.py
deleted file mode 100644
index a5810e2f0e4b09..00000000000000
--- a/api/core/model_runtime/model_providers/__base/text2img_model.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from abc import abstractmethod
-from typing import IO, Optional
-
-from pydantic import ConfigDict
-
-from core.model_runtime.entities.model_entities import ModelType
-from core.model_runtime.model_providers.__base.ai_model import AIModel
-
-
-class Text2ImageModel(AIModel):
- """
- Model class for text2img model.
- """
-
- model_type: ModelType = ModelType.TEXT2IMG
-
- # pydantic configs
- model_config = ConfigDict(protected_namespaces=())
-
- def invoke(
- self, model: str, credentials: dict, prompt: str, model_parameters: dict, user: Optional[str] = None
- ) -> list[IO[bytes]]:
- """
- Invoke Text2Image model
-
- :param model: model name
- :param credentials: model credentials
- :param prompt: prompt for image generation
- :param model_parameters: model parameters
- :param user: unique user id
-
- :return: image bytes
- """
- try:
- return self._invoke(model, credentials, prompt, model_parameters, user)
- except Exception as e:
- raise self._transform_invoke_error(e)
-
- @abstractmethod
- def _invoke(
- self, model: str, credentials: dict, prompt: str, model_parameters: dict, user: Optional[str] = None
- ) -> list[IO[bytes]]:
- """
- Invoke Text2Image model
-
- :param model: model name
- :param credentials: model credentials
- :param prompt: prompt for image generation
- :param model_parameters: model parameters
- :param user: unique user id
-
- :return: image bytes
- """
- raise NotImplementedError
diff --git a/api/core/model_runtime/model_providers/__base/text_embedding_model.py b/api/core/model_runtime/model_providers/__base/text_embedding_model.py
index 33135129082b1d..c4c1f9217795aa 100644
--- a/api/core/model_runtime/model_providers/__base/text_embedding_model.py
+++ b/api/core/model_runtime/model_providers/__base/text_embedding_model.py
@@ -1,5 +1,3 @@
-import time
-from abc import abstractmethod
from typing import Optional
from pydantic import ConfigDict
@@ -8,6 +6,7 @@
from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
from core.model_runtime.model_providers.__base.ai_model import AIModel
+from core.plugin.manager.model import PluginModelManager
class TextEmbeddingModel(AIModel):
@@ -38,36 +37,22 @@ def invoke(
:param input_type: input type
:return: embeddings result
"""
- self.started_at = time.perf_counter()
-
try:
- return self._invoke(model, credentials, texts, user, input_type)
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.invoke_text_embedding(
+ tenant_id=self.tenant_id,
+ user_id=user or "unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ texts=texts,
+ input_type=input_type.value,
+ )
except Exception as e:
raise self._transform_invoke_error(e)
- @abstractmethod
- def _invoke(
- self,
- model: str,
- credentials: dict,
- texts: list[str],
- user: Optional[str] = None,
- input_type: EmbeddingInputType = EmbeddingInputType.DOCUMENT,
- ) -> TextEmbeddingResult:
- """
- Invoke text embedding model
-
- :param model: model name
- :param credentials: model credentials
- :param texts: texts to embed
- :param user: unique user id
- :param input_type: input type
- :return: embeddings result
- """
- raise NotImplementedError
-
- @abstractmethod
- def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int:
+ def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> list[int]:
"""
Get number of tokens for given prompt messages
@@ -76,7 +61,16 @@ def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int
:param texts: texts to embed
:return:
"""
- raise NotImplementedError
+ plugin_model_manager = PluginModelManager()
+ return plugin_model_manager.get_text_embedding_num_tokens(
+ tenant_id=self.tenant_id,
+ user_id="unknown",
+ plugin_id=self.plugin_id,
+ provider=self.provider_name,
+ model=model,
+ credentials=credentials,
+ texts=texts,
+ )
def _get_context_size(self, model: str, credentials: dict) -> int:
"""
diff --git a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/merges.txt b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/merges.txt
deleted file mode 100644
index 226b0752cac778..00000000000000
--- a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/merges.txt
+++ /dev/null
@@ -1,50001 +0,0 @@
-#version: 0.2
-Ġ t
-Ġ a
-h e
-i n
-r e
-o n
-Ġt he
-e r
-Ġ s
-a t
-Ġ w
-Ġ o
-e n
-Ġ c
-i t
-i s
-a n
-o r
-e s
-Ġ b
-e d
-Ġ f
-in g
-Ġ p
-o u
-Ġa n
-a l
-a r
-Ġt o
-Ġ m
-Ġo f
-Ġ in
-Ġ d
-Ġ h
-Ġan d
-i c
-a s
-l e
-Ġt h
-i on
-o m
-l l
-en t
-Ġ n
-Ġ l
-s t
-Ġ re
-v e
-Ġ e
-r o
-l y
-Ġb e
-Ġ g
-Ġ T
-c t
-Ġ S
-i d
-o t
-Ġ I
-u t
-e t
-Ġ A
-Ġ is
-Ġ on
-i m
-a m
-o w
-a y
-a d
-s e
-Ġth at
-Ġ C
-i g
-Ġf or
-a c
-Ġ y
-v er
-u r
-Ġ u
-l d
-Ġs t
-Ġ M
-' s
-Ġ he
-Ġ it
-at ion
-it h
-i r
-c e
-Ġy ou
-i l
-Ġ B
-Ġw h
-o l
-Ġ P
-Ġw ith
-Ġ 1
-t er
-c h
-Ġa s
-Ġw e
-Ġ (
-n d
-i ll
-Ġ D
-i f
-Ġ 2
-a g
-er s
-k e
-Ġ "
-Ġ H
-e m
-Ġc on
-Ġ W
-Ġ R
-he r
-Ġw as
-Ġ r
-o d
-Ġ F
-u l
-at e
-Ġa t
-r i
-p p
-o re
-ĠT he
-Ġs e
-u s
-Ġp ro
-Ġh a
-u m
-Ġa re
-Ġd e
-a in
-an d
-Ġo r
-ig h
-es t
-is t
-a b
-r om
-Ġ N
-t h
-Ġc om
-Ġ G
-u n
-o p
-0 0
-Ġ L
-Ġn ot
-es s
-Ġe x
-Ġ v
-re s
-Ġ E
-e w
-it y
-an t
-Ġb y
-e l
-o s
-or t
-o c
-q u
-Ġf rom
-Ġha ve
-Ġs u
-i ve
-ou ld
-Ġs h
-Ġth is
-n t
-r a
-p e
-igh t
-ar t
-m ent
-Ġa l
-u st
-en d
-- -
-al l
-Ġ O
-ac k
-Ġc h
-Ġ le
-i es
-re d
-ar d
-â Ģ
-ou t
-Ġ J
-Ġa b
-e ar
-i v
-al ly
-ou r
-o st
-g h
-p t
-Ġp l
-as t
-Ġc an
-a k
-om e
-u d
-T he
-Ġh is
-Ġd o
-Ġg o
-Ġh as
-g e
-' t
-Ġ U
-r ou
-Ġs a
-Ġ j
-Ġb ut
-Ġw or
-Ġa ll
-e ct
-Ġ k
-am e
-Ġw ill
-o k
-Ġw he
-Ġthe y
-id e
-0 1
-f f
-ic h
-p l
-t her
-Ġt r
-. .
-Ġin t
-i e
-u re
-ag e
-Ġn e
-i al
-a p
-in e
-ic e
-Ġm e
-Ġo ut
-an s
-on e
-on g
-ion s
-Ġwh o
-Ġ K
-Ġu p
-Ġthe ir
-Ġa d
-Ġ 3
-Ġu s
-at ed
-ou s
-Ġm ore
-u e
-o g
-ĠS t
-in d
-i ke
-Ġs o
-im e
-p er
-. "
-b er
-i z
-a ct
-Ġon e
-Ġsa id
-Ġ -
-a re
-Ġyou r
-c c
-ĠT h
-Ġc l
-e p
-a ke
-ab le
-i p
-Ġcon t
-Ġwh ich
-i a
-Ġ im
-Ġab out
-Ġwe re
-ver y
-u b
-Ġh ad
-Ġ en
-Ġcom p
-, "
-ĠI n
-Ġu n
-Ġa g
-i re
-ac e
-a u
-ar y
-Ġw ould
-as s
-r y
-Ġ âĢ
-c l
-o ok
-e re
-s o
-Ġ V
-ig n
-i b
-Ġof f
-Ġt e
-v en
-Ġ Y
-i le
-o se
-it e
-or m
-Ġ2 01
-Ġre s
-Ġm an
-Ġp er
-Ġo ther
-or d
-ul t
-Ġbe en
-Ġl ike
-as e
-an ce
-k s
-ay s
-ow n
-en ce
-Ġd is
-ct ion
-Ġan y
-Ġa pp
-Ġs p
-in t
-res s
-ation s
-a il
-Ġ 4
-ic al
-Ġthe m
-Ġhe r
-ou nt
-ĠC h
-Ġa r
-Ġ if
-Ġthe re
-Ġp e
-Ġy ear
-a v
-Ġm y
-Ġs ome
-Ġwhe n
-ou gh
-ac h
-Ġth an
-r u
-on d
-ic k
-Ġo ver
-ve l
-Ġ qu
-Ċ Ċ
-Ġs c
-re at
-re e
-ĠI t
-ou nd
-p ort
-Ġal so
-Ġp art
-f ter
-Ġk n
-Ġbe c
-Ġt ime
-en s
-Ġ 5
-op le
-Ġwh at
-Ġn o
-d u
-m er
-an g
-Ġn ew
--- --
-Ġg et
-or y
-it ion
-ing s
-Ġj ust
-Ġint o
-Ġ 0
-ent s
-o ve
-t e
-Ġpe ople
-Ġp re
-Ġit s
-Ġre c
-Ġt w
-i an
-ir st
-ar k
-or s
-Ġwor k
-ad e
-o b
-Ġs he
-Ġo ur
-w n
-in k
-l ic
-Ġ1 9
-ĠH e
-is h
-nd er
-au se
-Ġh im
-on s
-Ġ [
-Ġ ro
-f orm
-i ld
-at es
-ver s
-Ġon ly
-o ll
-Ġs pe
-c k
-e ll
-am p
-Ġa cc
-Ġb l
-i ous
-ur n
-f t
-o od
-Ġh ow
-he d
-Ġ '
-Ġa fter
-a w
-Ġat t
-o v
-n e
-Ġpl ay
-er v
-ic t
-Ġc ould
-it t
-Ġa m
-Ġf irst
-Ġ 6
-Ġa ct
-Ġ $
-e c
-h ing
-u al
-u ll
-Ġcom m
-o y
-o ld
-c es
-at er
-Ġf e
-Ġbe t
-w e
-if f
-Ġtw o
-oc k
-Ġb ack
-) .
-id ent
-Ġu nder
-rou gh
-se l
-x t
-Ġm ay
-rou nd
-Ġp o
-p h
-is s
-Ġd es
-Ġm ost
-Ġd id
-Ġad d
-j ect
-Ġin c
-f ore
-Ġp ol
-on t
-Ġag ain
-cl ud
-ter n
-Ġkn ow
-Ġne ed
-Ġcon s
-Ġc o
-Ġ .
-Ġw ant
-Ġse e
-Ġ 7
-n ing
-i ew
-ĠTh is
-c ed
-Ġe ven
-Ġin d
-t y
-ĠW e
-at h
-Ġthe se
-Ġp r
-Ġu se
-Ġbec ause
-Ġf l
-n g
-Ġn ow
-ĠâĢ ĵ
-c om
-is e
-Ġm ake
-Ġthe n
-ow er
-Ġe very
-ĠU n
-Ġse c
-os s
-u ch
-Ġe m
-Ġ =
-ĠR e
-i ed
-r it
-Ġin v
-le ct
-Ġsu pp
-at ing
-Ġl ook
-m an
-pe ct
-Ġ 8
-ro w
-Ġb u
-Ġwhe re
-if ic
-Ġyear s
-i ly
-Ġd iff
-Ġsh ould
-Ġre m
-T h
-I n
-Ġe v
-d ay
-' re
-ri b
-Ġre l
-s s
-Ġde f
-Ġr ight
-Ġs y
-) ,
-l es
-00 0
-he n
-Ġth rough
-ĠT r
-_ _
-Ġw ay
-Ġd on
-Ġ ,
-Ġ1 0
-as ed
-Ġas s
-ub lic
-Ġre g
-ĠA nd
-i x
-Ġ very
-Ġin clud
-ot her
-Ġim p
-ot h
-Ġsu b
-ĠâĢ Ķ
-Ġbe ing
-ar g
-ĠW h
-= =
-ib le
-Ġdo es
-an ge
-r am
-Ġ 9
-er t
-p s
-it ed
-ation al
-Ġb r
-Ġd own
-Ġman y
-ak ing
-Ġc all
-ur ing
-it ies
-Ġp h
-ic s
-al s
-Ġde c
-at ive
-en er
-Ġbe fore
-il ity
-Ġwe ll
-Ġm uch
-ers on
-Ġth ose
-Ġsu ch
-Ġ ke
-Ġ end
-ĠB ut
-as on
-t ing
-Ġl ong
-e f
-Ġth ink
-y s
-Ġbe l
-Ġs m
-it s
-a x
-Ġo wn
-Ġpro v
-Ġs et
-if e
-ment s
-b le
-w ard
-Ġsh ow
-Ġp res
-m s
-om et
-Ġo b
-Ġs ay
-ĠS h
-t s
-f ul
-Ġe ff
-Ġg u
-Ġin st
-u nd
-re n
-c ess
-Ġ ent
-ĠY ou
-Ġgo od
-Ġst art
-in ce
-Ġm ade
-t t
-st em
-ol og
-u p
-Ġ |
-um p
-Ġhe l
-ver n
-ul ar
-u ally
-Ġa c
-Ġm on
-Ġl ast
-Ġ2 00
-1 0
-Ġst ud
-u res
-ĠA r
-sel f
-ar s
-mer ic
-u es
-c y
-Ġm in
-oll ow
-Ġc ol
-i o
-Ġm od
-Ġc ount
-ĠC om
-he s
-Ġf in
-a ir
-i er
-âĢ Ķ
-re ad
-an k
-at ch
-e ver
-Ġst r
-Ġpo int
-or k
-ĠN ew
-Ġs ur
-o ol
-al k
-em ent
-Ġus ed
-ra ct
-we en
-Ġs ame
-ou n
-ĠA l
-c i
-Ġdiff ere
-Ġwh ile
----- ----
-Ġg ame
-ce pt
-Ġs im
-.. .
-Ġin ter
-e k
-Ġre port
-Ġpro du
-Ġst ill
-l ed
-a h
-Ġhe re
-Ġwor ld
-Ġth ough
-Ġn um
-ar ch
-im es
-al e
-ĠS e
-ĠI f
-/ /
-ĠL e
-Ġre t
-Ġre f
-Ġtr ans
-n er
-ut ion
-ter s
-Ġt ake
-ĠC l
-Ġcon f
-w ay
-a ve
-Ġgo ing
-Ġs l
-u g
-ĠA meric
-Ġspe c
-Ġh and
-Ġbet ween
-ist s
-ĠD e
-o ot
-I t
-Ġe ar
-Ġagain st
-Ġh igh
-g an
-a z
-at her
-Ġex p
-Ġo p
-Ġin s
-Ġg r
-Ġhel p
-Ġre qu
-et s
-in s
-ĠP ro
-is m
-Ġf ound
-l and
-at a
-us s
-am es
-Ġp erson
-Ġg reat
-p r
-Ġs ign
-ĠA n
-' ve
-Ġs omet
-Ġs er
-h ip
-Ġr un
-Ġ :
-Ġt er
-ire ct
-Ġf ollow
-Ġd et
-ic es
-Ġf ind
-1 2
-Ġm em
-Ġc r
-e red
-e x
-Ġex t
-ut h
-en se
-c o
-Ġte am
-v ing
-ou se
-as h
-at t
-v ed
-Ġsy stem
-ĠA s
-d er
-iv es
-m in
-Ġle ad
-ĠB l
-c ent
-Ġa round
-Ġgo vern
-Ġc ur
-vel op
-an y
-Ġc our
-al th
-ag es
-iz e
-Ġc ar
-od e
-Ġl aw
-Ġre ad
-' m
-c on
-Ġre al
-Ġsupp ort
-Ġ1 2
-.. ..
-Ġre ally
-n ess
-Ġf act
-Ġd ay
-Ġb oth
-y ing
-Ġs erv
-ĠF or
-Ġth ree
-Ġw om
-Ġm ed
-od y
-ĠThe y
-5 0
-Ġex per
-t on
-Ġe ach
-ak es
-Ġc he
-Ġc re
-in es
-Ġre p
-1 9
-g g
-ill ion
-Ġg rou
-ut e
-i k
-W e
-g et
-E R
-Ġm et
-Ġs ays
-o x
-Ġd uring
-er n
-iz ed
-a red
-Ġf am
-ic ally
-Ġha pp
-ĠI s
-Ġch ar
-m ed
-v ent
-Ġg ener
-i ent
-p le
-i et
-re nt
-1 1
-v es
-pt ion
-Ġ2 0
-form ation
-Ġc or
-Ġoff ic
-ie ld
-Ġto o
-is ion
-Ġin f
-Ġ Z
-t he
-o ad
-Ġp ublic
-Ġpro g
-r ic
-* *
-Ġw ar
-Ġp ower
-v iew
-Ġf ew
-Ġl oc
-Ġdiffere nt
-Ġst ate
-Ġhe ad
-' ll
-Ġp oss
-Ġst at
-re t
-ant s
-Ġv al
-Ġis s
-Ġc le
-i vers
-an c
-Ġex pl
-Ġan other
-Ġ Q
-Ġa v
-th ing
-n ce
-W h
-Ġch ild
-Ġs ince
-i red
-l ess
-Ġl ife
-Ġde velop
-itt le
-Ġde p
-Ġp ass
-ã ĥ
-Ġt urn
-or n
-Th is
-b ers
-ro ss
-ĠA d
-Ġf r
-Ġres p
-Ġsec ond
-o h
-Ġ /
-Ġdis c
-Ġ &
-Ġsomet hing
-Ġcomp le
-Ġ ed
-Ġf il
-Ġmon th
-a j
-u c
-Ġgovern ment
-Ġwith out
-Ġle g
-Ġd ist
-Ġp ut
-Ġqu est
-an n
-Ġpro t
-2 0
-Ġne ver
-i ence
-Ġle vel
-Ġar t
-Ġth ings
-Ġm ight
-Ġeff ect
-Ġcont ro
-Ġc ent
-Ġ1 8
-Ġall ow
-Ġbel ie
-ch ool
-ot t
-Ġinc re
-Ġfe el
-Ġres ult
-Ġl ot
-Ġf un
-ot e
-Ġt y
-ere st
-Ġcont in
-Ġus ing
-Ġb ig
-2 01
-Ġas k
-Ġb est
-Ġ )
-I N
-Ġo pp
-3 0
-Ġnum ber
-in ess
-S t
-le ase
-Ġc a
-Ġm ust
-Ġd irect
-Ġg l
-Ġ <
-Ġop en
-Ġp ost
-Ġcom e
-Ġse em
-ord ing
-Ġwe ek
-ate ly
-it al
-Ġe l
-ri end
-Ġf ar
-Ġt ra
-in al
-Ġp ri
-ĠU S
-Ġpl ace
-Ġfor m
-Ġto ld
-" :
-ain s
-at ure
-ĠTr ump
-Ġst and
-Ġ #
-id er
-ĠF r
-Ġne xt
-Ġs oc
-Ġp ur
-Ġle t
-Ġl ittle
-Ġh um
-Ġ i
-r on
-1 5
-Ġ1 5
-Ġcomm un
-Ġm ark
-ĠThe re
-Ġw r
-ĠTh at
-Ġin formation
-w ays
-Ġb us
-a pp
-Ġinv est
-m e
-Ġh ard
-ain ed
-e ad
-Ġim port
-Ġapp ro
-Ġt est
-Ġt ri
-Ġre st
-os ed
-Ġf ull
-Ġc are
-ĠS p
-Ġc ase
-O N
-Ġs k
-Ġl ess
-Ġ +
-Ġpart ic
-ĠP l
-ab ly
-u ck
-is hed
-ch n
-b e
-Ġl ist
-at or
-Ġto p
-Ġad v
-ĠB e
-ru ct
-Ġd em
-r ation
-l ing
-g y
-re en
-g er
-Ġh ome
-Ġle ft
-Ġbet ter
-Ġd ata
-Ġ1 1
-Ġatt ack
-Ġpro ble
-l ine
-ard s
-Ġbe h
-r al
-ĠH ow
-ĠS he
-ar ge
-Ġ --
-: //
-Ġb ro
-ĠP h
-at s
-Ġbu ild
-w w
-id ed
-a im
-as es
-en cy
-Ġm ain
-in ed
-Ġinclud ing
-Ġ {
-Ġg ot
-Ġint erest
-Ġke ep
-Ġ X
-Ġe as
-ain ing
-Ġcl ass
-âĢ ¦
-ĠN o
-Ġv ar
-Ġsm all
-amp le
-A T
-Ġ ide
-ĠS o
-Ġre ce
-Ġpol it
-Ġm ov
-Ġpl an
-Ġper cent
-iv ing
-Ġc amp
-Ġp ay
-1 4
-s c
-is ed
-Ġu nt
-one y
-pl oy
-== ==
-Ġdid n
-ĠI nd
-el s
-ert ain
-Ġp os
-__ __
-i ver
-Ġpro cess
-Ġprog ram
-if ied
-ĠR ep
-1 6
-u ro
-olog y
-at ter
-in a
-Ġn ame
-ĠA ll
-Ġf our
-Ġret urn
-v ious
-b s
-Ġcall ed
-Ġm ove
-ĠS c
-ir d
-Ġgrou p
-Ġb re
-Ġm en
-Ġc ap
-t en
-e e
-Ġd ri
-le g
-he re
-uth or
-Ġp at
-Ġcur rent
-id es
-Ġp op
-t o
-ent ion
-Ġal ways
-Ġm il
-Ġwom en
-Ġ1 6
-Ġo ld
-iv en
-ra ph
-ĠO r
-r or
-ent ly
-Ġn ear
-ĠE x
-re am
-s h
-Ġ1 4
-Ġf ree
-iss ion
-st and
-ĠC on
-al ity
-us ed
-1 3
-Ġdes ign
-Ġch ange
-Ġch ang
-Ġb o
-Ġv is
-em ber
-Ġb ook
-read y
-Ġk ill
-2 5
-pp ed
-Ġa way
-Ġab le
-Ġcount ry
-Ġcon st
-ar n
-Ġor der
-A R
-i or
-i um
-or th
-1 8
-ail able
-Ġs w
-Ġm illion
-Ġ1 3
-at ic
-t ed
-ĠG o
-Ġo per
-en g
-Ġth ing
-aj or
-con om
-ĠCom m
-Ġwh y
-u red
-ur al
-Ġs chool
-b y
-ĠM ar
-Ġa ff
-Ġd ays
-Ġan n
-us h
-an e
-I f
-e g
-Ġpro f
-Ġhe alth
-ou th
-B ut
-ion al
-. ,
-Ġs ol
-Ġal ready
-Ġ3 0
-Ġchar act
-H e
-Ġf riend
-E S
-i ans
-ic le
-' d
-ĠO n
-Ġle ast
-Ġp rom
-Ġd r
-Ġh ist
-it her
-Ġ est
-i qu
-1 7
-s on
-Ġte ll
-Ġt alk
-oh n
-o int
-le ction
-A N
-Ġunt il
-au gh
-Ġl ater
-Ġ ve
-Ġv iew
-end ing
-iv ed
-Ġwor d
-w are
-Ġc ost
-Ġen ough
-Ġg ive
-ĠUn ited
-Ġte chn
-are nt
-O R
-Ġp ar
-ĠD r
-Ġ201 6
-r ist
-er ing
-Ġ Â
-Ġl arge
-s ide
-ac y
-cc ess
-Ġw in
-Ġimport ant
-Ġ19 9
-Ġdoes n
-Ġ1 7
-Ġbus iness
-Ġcle ar
-Ġre se
-" ,
-ur y
-Ġe qu
-as ter
-al f
-ĠAmeric an
-n ect
-Ġex pect
-ivers ity
-Ġo cc
-ĠF l
-Ġk ind
-Ġme an
-Ġp ast
-Ġde v
-Ġb as
-le t
-ra ft
-Ġor gan
-Ġde l
-Ġper form
-Ġst ory
-Ġse ason
-ĠC ol
-Ġcl aim
-Ġc ame
-Ġwith in
-Ġl ine
-Ġpro ject
-ĠA t
-Ġcontro l
-end ed
-ĠS y
-Ġa ir
-iz ation
-Ġ *
-le y
-Ġm oney
-id d
-Y ou
-f or
-Ġfam ily
-Ġm aking
-Ġb it
-Ġpol ice
-Ġhapp en
-Ġ vers
-on y
-u ff
-ĠW hen
-Ġs it
-ide o
-l f
-is on
-Ġsu re
-g in
-Ġapp ear
-Ġl ight
-Ġ es
-o f
-Ġw ater
-Ġt imes
-n ot
-Ġg row
-Ġcomp any
-ĠT e
-ow s
-Ġm ar
-our ce
-i ol
-ar m
-b r
-Ġex ample
-Ġcon c
-Ġf ore
-ĠT o
-p ro
-E N
-ri es
-Ġ2 5
-ĠC an
-ne y
-Ġact ually
-Ġe ver
-ur ity
-ak en
-ap s
-Ġt ax
-Ġm ajor
-am a
-Ġof ten
-er al
-Ġhum an
-Ġj ob
-is ter
-Ġav ailable
-oc r
-en n
-a id
-iv id
-Ġrec ord
-? "
-Ġs ing
-ĠA m
-id ence
-Ġnew s
-st er
-Ġe conom
-Ġfollow ing
-ĠB r
-is ing
-Ġh our
-m ost
-um ent
-Ġse x
-Ġdes c
-Ġbec ome
-ĠE d
-Ġto ok
-Ġha ving
-Ġprodu ct
-a ult
-A s
-ar ing
-Ġme ans
-Ġh op
-un e
-Ġch o
-Ġc ertain
-Ġn on
-Ġde al
-2 4
-le ment
-oc i
-en e
-Ġs ide
-ĠP r
-ĠM ay
-Ġre ason
-u ed
-c hed
-ul ation
-Ġe lect
-Ġoffic ial
-Ġposs ible
-Ġh old
-and s
-ot s
-Ġc ity
-or ies
-Ġse ver
-Ġchild ren
-Ġon ce
-Ġact iv
-l er
-Ġn ight
-it ions
-ĠJ ohn
-a pe
-pl ay
-Ġd one
-Ġl im
-Ġwork ing
-ĠP res
-or ld
-e b
-ĠC o
-Ġb ody
-ail s
-ut es
-ĠM r
-Ġwhe ther
-Ġa uthor
-ro p
-Ġpro per
-Ġse en
-) ;
-Ġf ac
-ĠS u
-Ġcon d
-it ing
-Ġcour se
-Ġ }
--------- --------
-a ign
-Ġev ent
-Ġen g
-Ġp ot
-Ġin tern
-i am
-Ġsh ort
-em pt
-ã Ĥ
-ĠG od
-il ar
-8 0
-Ġor ig
-I S
-our n
-ab ility
-it ive
-Ġd am
-Ġ1 00
-Ġp ress
-Ġdo ing
-Ġprot ect
-r ing
-Ġthough t
-Ġquest ion
-re w
-ĠW ar
-Ġsever al
-ĠSt ate
-Ġg iven
-Ġf und
-ĠT w
-Ġw ent
-an ces
-w ork
-p or
-m y
-4 0
-Ġar g
-art ment
-ust om
-Ġpol ic
-Ġme et
-Ġc reat
-2 2
-ĠSt ates
-Ġg ames
-ra w
-ut ure
-Ġunder stand
-ur s
-ĠO b
-l ish
-s y
-Ġm akes
-Ġw on
-ag on
-Ġh tt
-Ġl ove
-ent ial
-Ġcomple te
-p ar
-ĠI m
-A L
-Ġacc ount
-Â ł
-ore d
-ver t
-Ġ ident
-Ġ201 5
-Ġother s
-ĠM in
-i ber
-ver age
-The re
-ition al
-d d
-Ġpro b
-Ġyou ng
-Ġal ong
-Ġacc ording
-Ġy et
-Ġmem bers
-ĠWh at
-o id
-ĠM an
-A nd
-Ġam ong
-a i
-Ġem ploy
-ĠR es
-Ġ >
-Ġinv ol
-Ġl ow
-a f
-ĠC ar
-Ġh ig
-ĠO ne
-ĠS ec
-in ation
-Ġlike ly
-Ġan t
-ag ed
-ĠR uss
-Ġb en
-Ġre le
-F or
-b ack
-ĠN ot
-Ġpres ident
-b all
-Ġacc ess
-ivid ual
-ĠD em
-ĠE uro
-6 0
-Ġkn own
-ir l
-ĠG r
-Ġear ly
-u se
-iet y
-âĢ ĵ
-Ġf ight
-Ġs ent
-Ġto day
-Ġmark et
-" .
-Ġb ased
-Ġstr ong
-ur ther
-Ġde b
-m ber
-Ġproble m
-Ġde ath
-Ġsoc ial
-im ate
-A S
-ort un
-Ġcamp aign
-er y
-C h
-Ġe y
-i ally
-Ġm us
-w h
-p os
-Ġ er
-Ġsa f
-Ġmonth s
-ir on
-Ġv iol
-Ġf ive
-Ġst re
-Ġplay ers
-in c
-al d
-y ear
-a un
-Ġsu ccess
-Ġpres ent
-ere nce
-Ġ201 4
-Ġsu gg
-Ġpartic ular
-Ġtr y
-Ġsugg est
-ĠCh rist
-on es
-Ġpri v
-2 3
-Ġc rit
-Ġl and
-Ġloc al
-if y
-2 9
-Ġa ut
-E D
-ĠG u
-Ġm ult
-Ġpolit ical
-Ġask ed
-Ġfor mer
-it ter
-ri pt
-Ġcl ose
-Ġp ract
-ĠY ork
-Ġget ting
-Ġac ross
-Ġcom b
-Ġbelie ve
-Ġ z
-Ġto get
-Ġtoget her
-ĠC ent
-ir c
-Ġind ividual
-ĠM c
-2 7
-is k
-ĠE ng
-Ġf ace
-Ġ2 4
-Ġval ue
-Ġare a
-e v
-Ġw rit
-ĠPres ident
-Ġv ot
-Ġke y
-Ġm om
-p ut
-Ġany thing
-Ġexper ience
-att le
-Ġm ind
-a ff
-om m
-Ġf uture
-g ed
-Ġc ut
-Ġto t
-it ch
-Ġv ideo
-Ġinvest ig
-Ġn et
-ĠM y
-r ict
-i en
-. )
-Ġimp ro
-th ough
-ward s
-Ġcon nect
-ĠM ed
-sel ves
-ens ive
-m b
-o ber
-at ors
-A n
-Ġ5 0
-Ġre du
-res ent
-Ġab ove
-Ġf re
-ĠEuro pe
-s w
-Ġam ount
-ĠA pp
-Ġe ither
-Ġmil it
-Ġan al
-Ġf ail
-ĠE n
-al es
-Ġspec ial
-Ġbl ack
-I T
-c her
-Ġlook ing
-Ġf ire
-y n
-Ġal most
-o on
-Ġstud y
-Ġm iss
-c hes
-ro wn
-Ġt re
-Ġcommun ity
-Ġmed ia
-Ġf ood
-Ġcom es
-ĠUn iversity
-Ġsing le
-Wh at
-u ly
-Ġh alf
-ag ue
-h od
-ĠRep ublic
-Ġstart ed
-Ġqu ick
-ot o
-b ook
-Ġiss ue
-it or
-Ġel se
-Ġcons ider
-2 6
-ro du
-Ġt aken
-2 8
-9 9
-ĠW ith
-Ġtr ue
-Ġw a
-Ġtr ad
-Ġag o
-Ġm ess
-ie f
-Ġadd ed
-o ke
-Ġb ad
-Ġf av
-3 3
-Ġsim ilar
-as k
-ĠD on
-Ġcharact er
-ort s
-ĠH ouse
-Ġreport ed
-Ġty pe
-v al
-i od
-ĠHow ever
-Ġt arg
-Ġent ire
-pp ing
-Ġhist ory
-Ġl ive
-ff ic
-.... ....
-ed eral
-Ġtr ying
-Ġdisc uss
-ĠH ar
-ac es
-l ished
-Ġse lf
-os p
-re st
-Ġro om
-el t
-Ġf all
-ol ution
-Ġe t
-Ġ x
-Ġis n
-Ġide a
-b o
-Ġs ound
-ĠD ep
-Ġsome one
-ci ally
-ull y
-Ġf oc
-Ġob ject
-if t
-ap er
-Ġplay er
-Ġr ather
-Ġserv ice
-as hing
-ĠD o
-ĠP art
-ru g
-m on
-p ly
-Ġm or
-Ġnot hing
-Ġprov ide
-I C
-un g
-Ġpart y
-Ġex ist
-Ġm ag
-7 0
-Ġr ul
-Ġh ouse
-Ġbeh ind
-Ġhow ever
-ĠW orld
-Ġs um
-Ġapp lic
-Ġ ;
-Ġfun ction
-g r
-ĠP ol
-Ġfr ont
-2 00
-Ġser ies
-Ġt em
-Ġty p
-ill s
-Ġo pt
-Ġpoint s
-Ġbel ow
-itt ed
-Ġspec ific
-Ġ201 7
-um b
-Ġr a
-Ġpre vious
-Ġpre t
-re me
-Ġc ustom
-Ġcour t
-ĠM e
-Ġre pl
-Ġwho le
-g o
-c er
-Ġt reat
-ĠA ct
-Ġprob ably
-Ġle arn
-end er
-ĠA ss
-Ġvers ion
-n ow
-Ġche ck
-ĠC al
-R E
-min ist
-O n
-our ces
-Ġben ef
-Ġd oc
-Ġdet er
-Ġen c
-Ġsu per
-Ġadd ress
-Ġv ict
-Ġ201 3
-Ġme as
-t r
-Ġf ield
-W hen
-Ġsign ific
-u ge
-Ġfe at
-Ġcomm on
-l oad
-Ġbe gin
-Ġbr ing
-Ġa ction
-er man
-Ġdesc rib
-Ġind ust
-Ġwant ed
-ri ed
-m ing
-Ġatt empt
-4 5
-f er
-Ġd ue
-ress ion
-# #
-Ġsh all
-Ġs ix
-o o
-Ġst ep
-Ġp ub
-Ġhim self
-Ġ2 3
-Ġc op
-Ġd est
-Ġst op
-A C
-ib ility
-Ġl ab
-ic ult
-Ġhour s
-Ġcre ate
-Ġf urther
-ĠAmeric a
-ĠC ity
-Ġd ou
-he ad
-S T
-ĠN orth
-c ing
-Ġn ational
-u le
-ĠIn st
-Ġt aking
-ĠQ u
-ir t
-Ġre d
-Ġrese arch
-v iron
-ĠG e
-Ġbre ak
-an a
-Ġsp ace
-ater ial
-Ġrec ent
-ĠA b
-Ġgener al
-Ġh it
-Ġper iod
-Ġevery thing
-ive ly
-Ġph ys
-Ġsay ing
-an ks
-Ġc ou
-Ġc ult
-ac ed
-e al
-u ation
-Ġc oun
-l u
-Ġinclud e
-Ġpos ition
-ĠA fter
-ĠCan ad
-ĠE m
-Ġim m
-ĠR ed
-Ġp ick
-Ġcom pl
-Ġm atter
-re g
-e xt
-ang u
-is c
-o le
-a ut
-Ġcomp et
-e ed
-f ect
-Ġ2 1
-ĠS en
-ĠThe se
-as ing
-Ġcan not
-Ġin it
-Ġrel ations
-ac hed
-Ġb ar
-Ġ4 0
-ĠT H
-Ġ201 2
-Ġv ol
-Ġg round
-Ġsec urity
-Ġup d
-il t
-3 5
-Ġconc ern
-ĠJ ust
-Ġwh ite
-Ġseem s
-ĠH er
-pe cially
-i ents
-Ġann oun
-Ġf ig
-ight s
-Ġst ri
-l ike
-id s
-Ġs us
-Ġw atch
-Ġ â
-Ġw ind
-ĠC ont
-Ġit self
-Ġm ass
-A l
-y le
-iqu e
-ĠN ational
-Ġab s
-Ġp ack
-Ġout side
-Ġan im
-Ġp ain
-et er
-Ġman ag
-du ct
-og n
-Ġ ]
-ĠSe pt
-se c
-o ff
-ĠJ an
-Ġf oot
-ad es
-Ġth ird
-Ġm ot
-Ġev idence
-int on
-Ġth reat
-a pt
-pl es
-c le
-Ġl o
-Ġde cl
-Ġit em
-med i
-Ġrep resent
-om b
-am er
-Ġsignific ant
-og raph
-s u
-Ġc al
-i res
-00 00
-I D
-A M
-Ġsim ply
-Ġlong er
-Ġf ile
-O T
-c he
-S o
-ate g
-or g
-ĠH is
-Ġen er
-Ġd om
-Ġup on
-il i
-": "
-Ġthem selves
-Ġcom ing
-Ġqu ite
-Ġdiff icult
-ĠB ar
-il ities
-re l
-end s
-c ial
-6 4
-Ġwom an
-ra p
-y r
-Ġne cess
-ip s
-Ġte xt
-Ġrequ ire
-Ġmilit ary
-Ġre view
-Ġresp ons
-7 5
-Ġsub ject
-Ġinst ead
-Ġiss ues
-Ġg en
-" ,"
-Ġmin utes
-Ġwe ap
-r ay
-am ed
-t ime
-b l
-H ow
-Ġc ode
-ĠS m
-Ġhig her
-ĠSt e
-r is
-Ġp age
-Ġstud ents
-ĠIn tern
-Ġmet hod
-ĠA ug
-ĠP er
-ĠA g
-Ġpolic y
-ĠS w
-Ġex ec
-Ġac cept
-um e
-rib ut
-Ġword s
-Ġfin al
-Ġchang es
-ĠDem ocr
-Ġfriend s
-Ġres pect
-Ġe p
-Ġcomp an
-iv il
-Ġdam age
-** **
-og le
-viron ment
-Ġne g
-ent al
-Ġa p
-Ġtot al
-iv al
-! "
-l im
-Ġneed s
-Ġag re
-Ġdevelop ment
-Ġa ge
-ip le
-2 1
-Ġresult s
-ĠA f
-S h
-Ġg un
-ĠOb ama
-ro ll
-Ġ @
-Ġright s
-ĠB rit
-Ġrun ning
-Ġwas n
-Ġp ort
-Ġr ate
-Ġpret ty
-Ġtarg et
-Ġsa w
-Ġc irc
-Ġwor ks
-ic ro
-al t
-o ver
-ww w
-Th at
-l ier
-Ġevery one
-ud e
-Ġp ie
-idd le
-ra el
-Ġr ad
-Ġbl ock
-Ġw alk
-T o
-ã ģ
-n es
-ĠA ust
-a ul
-ro te
-ĠS outh
-ess ion
-op h
-Ġshow s
-Ġs ite
-Ġj o
-Ġr isk
-cl us
-l t
-Ġin j
-id ing
-ĠS pe
-Ġch all
-ir m
-Ġ2 2
-itt ing
-st r
-Ġh y
-L E
-ke y
-Ġbe gan
-at ur
-ashing ton
-l am
-ĠD av
-b it
-Ġs ize
-ĠP ar
-3 8
-ourn al
-f ace
-Ġdec ision
-Ġl arg
-Ġj ud
-re ct
-Ġcontin ue
-ĠO ct
-ove red
-ĠI nt
-==== ====
-Ġp arent
-ĠW ill
-Ġeas y
-Ġd rug
-ang er
-Ġs ense
-Ġd i
-id ay
-Ġener gy
-ist ic
-Ġass oci
-ar ter
-ob al
-e ks
-ĠE l
-ur ch
-Ġg irl
-o e
-it le
-Ġ2 8
-ĠC he
-Ġrequ est
-Ġso on
-Ġh ost
-k y
-Ġst ates
-om es
-Ġm aterial
-le x
-Ġmom ent
-Ġan sw
-on se
-Ġes pecially
-Ġn orm
-Ġserv ices
-p ite
-r an
-Ġro le
-4 4
-) :
-Ġc red
-C l
-____ ____
-Ġm at
-Ġl og
-ĠCl inton
-O U
-Ġoff ice
-Ġ2 6
-Ġch arg
-Ġtr ack
-m a
-Ġhe art
-Ġb all
-Ġperson al
-Ġbuild ing
-n a
-s et
-b ody
-ĠBl ack
-Ġincre ase
-itt en
-Ġneed ed
-3 6
-3 2
-= "
-Ġl ost
-Ġbec ame
-Ġgrou ps
-ĠM us
-Ġw rote
-ĠP e
-Ġpro p
-j oy
-Ã ©
-ĠWh ite
-Ġde ad
-. '
-Ġhtt p
-Ġwe bs
-O S
-Ġins ide
-Ġwr ong
-Ġstat ement
-Ġ ...
-y l
-Ġfil m
-Ġmus ic
-Ġsh are
-ific ation
-Ġre lease
-Ġfor ward
-Ġst ay
-Ġcomp ut
-it te
-s er
-Ġorig inal
-Ġc ard
-Ġc and
-Ġd iv
-at ural
-Ġfav or
-O M
-Ġc ases
-us es
-Ġse ction
-Ġle ave
-g ing
-ov ed
-ĠW ashington
-3 9
-ĠG l
-Ġrequ ired
-act ion
-ap an
-o or
-it er
-ĠK ing
-Ġcount ries
-ĠG erman
-ll ing
-Ġ2 7
-3 4
-Ġquest ions
-Ġpr im
-Ġc ell
-Ġsh oot
-Ġany one
-ĠW est
-Ġaff ect
-ep end
-Ġon line
-ĠIs rael
-ĠSept ember
-Ġab ility
-Ġcont ent
-is es
-Ġre ve
-Ġl aun
-Ġind ic
-Ġfor ce
-c ast
-Ġso ld
-av ing
-f l
-Ġso ft
-Ġcompan ies
-ce ed
-Ġart icle
-Ġa ud
-Ġre v
-Ġed uc
-Ġplay ing
-0 5
-Ġhe ld
-ct or
-Ġrele ased
-Ġf ederal
-3 7
-Ġad minist
-Ġinter view
-Ġinst all
-Ġrece ived
-Ġs ource
-u k
-P h
-Ġser ious
-Ġcre ated
-Ġc ause
-Ġim medi
-Ġdef in
-u el
-ĠDep artment
-ct ions
-ĠC our
-ĠN ow
-z e
-it es
-it ution
-Ġl ate
-Ġspe ak
-n ers
-Ġleg al
-ar i
-ĠC or
-Ġwe eks
-Ġmod el
-Ġp red
-Ġex act
-B C
-ĠB y
-IN G
-os ing
-Ġt akes
-Ġreg ard
-Ġopp ortun
-Ġpr ice
-Ġ19 8
-ĠA pr
-f ully
-Ġor d
-Ġproble ms
-ru ction
-h am
-ĠC ount
-le ge
-Ġlead ers
-E T
-le v
-Ġde ep
-olog ical
-es e
-h aps
-ĠS ome
-Ġp ers
-Ġcont ract
-Ġrelations hip
-s p
-ou d
-Ġb ase
-4 8
-m it
-A d
-anc ial
-Ġcons um
-Ġpot ential
-Ġl angu
-re m
-et h
-Ġrel ig
-ress ed
-6 6
-Ġl ink
-Ġl ower
-ay er
-ĠJ une
-Ġf em
-un t
-er c
-ur d
-Ġcont act
-Ġ ill
-Ġm other
-Ġest ab
-h tt
-ĠM arch
-ĠB ro
-ĠCh ina
-Ġ2 9
-Ġs qu
-Ġprov ided
-Ġa verage
-as ons
-Ġ201 1
-Ġex am
-l in
-5 5
-n ed
-Ġper fect
-Ġt ou
-al se
-u x
-Ġbu y
-Ġsh ot
-Ġcol lect
-Ġph ot
-Ġplay ed
-Ġsur pr
-Ġofficial s
-Ġsim ple
-av y
-Ġindust ry
-Ġhand s
-g round
-Ġp ull
-Ġr ound
-Ġus er
-Ġr ange
-u ary
-Ġpriv ate
-op s
-e es
-Ġw ays
-ĠM ich
-Ġve h
-Ġex cept
-Ġter ms
-im um
-pp er
-I ON
-ore s
-ĠDr agon
-ou l
-Ġd en
-Ġperform ance
-Ġb ill
-c il
-4 7
-Ġen vironment
-Ġex c
-ad d
-Ġwor th
-Ġp ict
-Ġch ance
-Ġ201 8
-b or
-Ġspe ed
-ict ion
-Ġal leg
-ĠJ apan
-at ory
-re et
-Ġm atch
-ĠI I
-Ġst ru
-ord er
-Ġst e
-Ġl iving
-Ġst ruct
-in o
-Ġse par
-her n
-Ġresp onse
-Ġen joy
-Ġv ia
-A D
-um ents
-ace book
-Ġmem ber
-ib r
-iz ing
-Ġto ol
-ĠM on
-ĠWh ile
-h ood
-ĠA ng
-ĠD ef
-Ġoff er
-T r
-a ur
-Ġturn ed
-ĠJ uly
-d own
-an ced
-Ġrec ently
-ĠE ar
-Ġc e
-ĠSt ar
-ĠC ong
-rough t
-Ġbl ood
-Ġhop e
-Ġcom ment
-ain t
-Ġar ri
-il es
-Ġpartic ip
-ough t
-ri ption
-0 8
-4 9
-Ġg ave
-Ġse lect
-Ġkill ed
-sy ch
-Ġgo es
-i j
-Ġc oll
-Ġimp act
-at ives
-ĠS er
-0 9
-ĠAug ust
-Ġb oy
-d e
-ĠD es
-Ġf elt
-U S
-Ġexpect ed
-Ġim age
-ĠM ark
-cc ording
-o ice
-E C
-ĠM ag
-en ed
-h old
-ĠP ost
-Ġpre vent
-N o
-Ġinvol ved
-Ġey es
-Ġquick ly
-A t
-un k
-Ġbeh av
-Ġ ur
-Ġl ed
-c ome
-e y
-Ġcand id
-Ġear lier
-Ġfoc us
-et y
-P ro
-led ge
-ix ed
-ill ed
-Ġpop ular
-A P
-Ġset t
-l ight
-Ġvar ious
-in ks
-Ġlevel s
-Ġro ad
-ell ig
-ab les
-he l
-itte e
-ĠG ener
-y pe
-Ġhe ard
-ic les
-Ġm is
-Ġus ers
-ĠS an
-Ġimpro ve
-Ġf ather
-Ġse arch
-The y
-v il
-Ġprof ess
-Ġkn ew
-Ġl oss
-Ġev ents
-6 5
-Ġb illion
-0 7
-0 2
-ĠNew s
-ĠA M
-Ġco ver
-w here
-ens ion
-Ġb ott
-Ġare as
-en ces
-op e
-ĠTw itter
-a el
-Ġget s
-ĠGo ogle
-Ġs n
-i ant
-Ġv ote
-Ġnear ly
-Ġinclud ed
-Ġrec ogn
-z z
-m m
-al ed
-Ġhappen ed
-0 4
-Ġh ot
-Ġwho se
-Ġc ivil
-Ġsu ff
-o es
-it iz
-ĠSy ri
-Ġresp ond
-Ġh on
-Ġfeat ures
-Ġeconom ic
-ĠApr il
-r im
-Ġtechn ology
-Ġo ption
-ag ing
-Ġpur ch
-R e
-Ġl at
-ch ie
-is l
-Ġrec omm
-u f
-Ġtr aining
-Ġeffect s
-Ġf ast
-Ġ201 0
-Ġocc ur
-Ġwebs ite
-Ġem ail
-Ġs ens
-e ch
-Ġo il
-Ġinf lu
-Ġcurrent ly
-ĠS ch
-ĠAd d
-Ġgo al
-Ġsc ient
-Ġcon v
-1 00
-em y
-Ġdec ided
-Ġtra vel
-Ġm ention
-L L
-0 3
-Ġe lection
-Ġph one
-Ġlook s
-Ġsit uation
-Ġc y
-Ġh or
-b ed
-ĠCour t
-a ily
-av es
-Ġqu ality
-ĠCom p
-w ise
-Ġt able
-Ġst aff
-ĠW ind
-et t
-Ġtri ed
-ide red
-Ġadd ition
-Ġb ox
-Ġl ack
-ar ily
-Ġw ide
-Ġm id
-Ġbo ard
-ys is
-Ġant i
-h a
-Ġd ig
-en ing
-Ġd ro
-C on
-6 8
-Ġsl ow
-b ased
-se qu
-Ġp ath
-E x
-ak er
-Ġwork ed
-Ġp en
-Ġeng ine
-Ġlook ed
-ĠSu per
-ĠS erv
-Ġvict im
-U n
-Ġproper ty
-Ġint rodu
-Ġexec ut
-ĠP M
-L e
-Ġcol or
-ĠM ore
-Ġ6 0
-Ġnet work
-Ġd ate
-c ul
-id ge
-Ġext ra
-3 1
-Ġs le
-6 7
-Ġw ond
-Ġreport s
-j ust
-ĠAust ral
-Ġcap ital
-Ġen s
-Ġcomm and
-Ġallow ed
-Ġpre p
-Ġca pt
-h ib
-Ġnum bers
-ch an
-Ġf air
-m p
-om s
-Ġre ach
-W ith
-t ain
-Ġbro ad
-Ġcou ple
-ec ause
-ly ing
-ĠF eb
-Ġsc reen
-Ġl ives
-Ġpri or
-ĠCong ress
-A r
-Ġappro ach
-Ġe mer
-ar ies
-ĠD is
-s erv
-ĠN e
-Ġbu ilt
-c ies
-Ġre pe
-Ġrul es
-for ce
-ĠP al
-Ġfin ancial
-Ġcons idered
-ĠCh ar
-n ces
-ĠI S
-Ġb rought
-Ġb i
-i ers
-ĠS im
-O P
-Ġproduct s
-Ġvis it
-Ġdoc ument
-Ġcon duct
-Ġcomplete ly
-in ing
-ĠCal if
-ib ly
-Ġwr itten
-ĠT V
-em ents
-Ġd raw
-O ne
-Ġpub lished
-Ġsec ret
-r ain
-he t
-ĠF acebook
-ond ay
-ĠU p
-Ġsex ual
-Ġth ous
-ĠP at
-Ġ ess
-Ġstand ard
-Ġar m
-g es
-ect ion
-Ġf ell
-Ġfore ign
-an i
-ĠFr iday
-Ġreg ular
-in ary
-Ġincre ased
-Ġus ually
-Ġdem on
-Ġd ark
-Ġadd itional
-ro l
-ĠO f
-Ġprodu ction
-! !
-und red
-Ġintern ational
-id ents
-ĠF ree
-rou p
-Ġr ace
-Ġm ach
-Ġh uge
-A ll
-le ar
-ove mber
-Ġto wn
-Ġatt ention
-ĠO ff
-y ond
-ĠThe n
-f ield
-Ġter ror
-ra z
-ĠB o
-Ġmeet ing
-ĠP ark
-Ġar rest
-Ġf ear
-Ġa w
-ĠV al
-or ing
-' ,
-Ġext reme
-ar r
-Ġwork ers
-A fter
-Ġ3 1
-n et
-am ent
-Ġdirect ly
-Ġpop ulation
-ub e
-ĠOct ober
-ĠI N
-ĠJan uary
-5 9
-ĠDav id
-Ġc ross
-ce mber
-ĠF irst
-Ġmess age
-ir it
-Ġn ation
-Ġp oll
-is ions
-Ġansw er
-n y
-is ode
-Ġcar ry
-ĠRuss ia
-Ġhe ar
-eng th
-ro y
-Ġn atural
-in ally
-Ġdo g
-m itted
-Ġtr ade
-Ġsub st
-Ġmult iple
-ĠAf ric
-Ġf ans
-Ġs ort
-Ġgl obal
-ic ation
-ĠW ed
-ar a
-Ġa chie
-Ġlangu age
-ve y
-Ġt al
-Ġnecess ary
-Ġdet ails
-Ġs en
-ĠS und
-ĠRe g
-ĠR ec
-0 6
-Ġs il
-ress ive
-Ġmed ical
-un ch
-orn ia
-Ġu nd
-f ort
-oc ks
-ĠM onday
-ues day
-c raft
-7 7
-ur t
-Ġ ver
-ĠH ill
-Ġrece ive
-Ġmor ning
-es tern
-Ġb ank
-Ġs at
-ir th
-ĠH igh
-Ġdev ice
-ĠTH E
-ĠCent er
-Ġsaf e
-Ġp le
-ĠCanad a
-Ġsystem s
-Ġass ist
-Ġsur v
-Ġb attle
-ĠS oc
-vert is
-S he
-Ġp aper
-Ġgrow th
-Ġc ast
-S c
-Ġpl ans
-ll ed
-Ġpart s
-Ġw all
-Ġmove ment
-Ġpract ice
-im ately
-Ġdis play
-Ġsomet imes
-om p
-ĠP aul
-ĠY es
-k ing
-5 8
-o ly
-Ġs on
-Ġav oid
-ok es
-ĠJ ew
-Ġto wards
-as c
-Ġ //
-ĠK ore
-Ġtalk ing
-Ġcor rect
-Ġsp ent
-ic ks
-i able
-e ared
-Ġter m
-Ġwant s
-om ing
-Ġ ut
-Ġdou b
-Ġfor ces
-Ġp lease
-6 9
-ĠN ovember
-at form
-ond on
-Ġon es
-Ġimmedi ately
-ĠRuss ian
-ĠM et
-Ġde g
-Ġparent s
-C H
-ĠAmeric ans
-al y
-ĠM od
-Ġsh own
-Ġcond itions
-Ġst uff
-Ġre b
-ĠY our
-Ġinclud es
-n own
-ĠS am
-Ġexper ien
-m ission
-ĠE ven
-augh t
-Ġannoun ced
-ĠRepublic an
-Ġdeter min
-Ġdescrib ed
-ĠCount y
-( )
-Ġdo or
-Ġchang ed
-Ġne igh
-ĠH ere
-Ġcle an
-Ġp an
-ĠDe cember
-ĠEurope an
-ir ing
-ap ter
-Ġcl ub
-ĠT uesday
-Ġp aid
-ĠN et
-Ġattack s
-Ġcharact ers
-Ġal one
-Ġdirect or
-d om
-Ġ3 5
-Ġl oad
-Ġr out
-ĠCalif ornia
-Ġfin ally
-Ġr ac
-Ġcont r
-Ġexact ly
-res h
-p ri
-ĠIs lam
-Ġn ature
-Ġcare er
-Ġlat est
-Ġcon vers
-ĠS l
-p ose
-ci ent
-ĠIn c
-iv ity
-8 8
-ĠA tt
-ĠM or
-nes day
-Ġwe ight
-k en
-Ġnot e
-Ġteam s
-Ġ \
-air s
-ĠG reen
-Ġh undred
-on ent
-Ġstre ng
-Ġcons ist
-ic ated
-Ġreg ul
-Ġl ic
-ast ic
-Ġt en
-urs day
-ellig ence
-ous ly
-ĠU K
-B I
-Ġcost s
-Ġind epend
-ĠA P
-Ġnorm al
-Ġh om
-Ġob vious
-Ġs we
-Ġst ar
-Ġread y
-ac her
-Ġimp lement
-g est
-Ġs ong
-ĠG et
-ĠL ab
-Ġinterest ing
-us ing
-Ġg iving
-ĠSund ay
-Ġet c
-Ġm iddle
-Ġrem ember
-r ight
-os ition
-ut ions
-Ġm ax
-4 6
-Ġyour self
-Ġdem and
-Ġtreat ment
-Ġd anger
-ĠC ons
-Ġgu y
-ĠBrit ish
-Ġphys ical
-Ġrel ated
-Ġrem ain
-Ġcould n
-Ġref er
-Ġc itiz
-b ox
-EN T
-bo ard
-Ġin n
-I G
-er o
-ĠSt reet
-osp ital
-ren ch
-cher s
-Ġst ra
-O L
-ag er
-ĠA N
-Ġeas ily
-I A
-en ge
-in y
-Ġcl os
-ock ed
-Ġus es
-ĠC oun
-I m
-u ild
-? ?
-m ore
-Ġan g
-Ġwr ite
-ol ute
-5 7
-Ġlead er
-Ġread ing
-< /
-Ġaut om
-est s
-4 3
-Ġleg isl
-ĠG old
-Ġdesign ed
-ĠS T
-ĠLe g
-a res
-Ġbe aut
-ĠT ex
-Ġappear s
-Ġstru gg
-ĠR om
-Ġ 00
-Ġcho ice
-Ġparticular ly
-ĠF rom
-op er
-ĠL ondon
-ann ed
-Ġallow s
-ob ile
-Ġdiffere nce
-âĢ ¢
-ĠV iew
-ĠWed nesday
-Ġal though
-Ġrel ative
-Ġapplic ation
-ate ver
-Ġare n
-Ġmy self
-Ġim ag
-Ġdis e
-Ġsoc iety
-Ġfre qu
-ĠEng lish
-Ġpo or
-ĠD ay
-Ġwrit ing
-Ġse ven
-Ġstart ing
-Ġb ud
-Ġpr int
-ĠTr ans
-uf act
-ĠSt ud
-n ew
-Ġcr im
-Ġg ives
-Ġco ol
-a e
-i ance
-ĠGener al
-Ġthink ing
-Ġsa ve
-Ġlim ited
-ĠPart y
-Ġmean ing
-p en
-ow ers
-ĠJ ack
-E M
-Ġn ice
-ru pt
-Ġg as
-Ġe ight
-Ġfe et
-Ġeff ort
-Ġ ign
-ic it
-B l
-co in
-Ġop in
-Ġbr ain
-Wh ile
-he st
-ĠTh ursday
-Ġwould n
-augh ter
-Ġtou ch
-le ments
-Ġstud ies
-Ġcent er
-c ont
-or ge
-Ġcomput er
-Ġinvestig ation
-P l
-or ks
-Ġ200 8
-Ġincre asing
-Ġst ore
-Ġcom ments
-Ġb al
-m en
-Ġdo ll
-Ġl iber
-Ġw ife
-Ġlaw s
-atur day
-it ness
-Ġmod ern
-ĠS k
-Ġadminist ration
-Ġopportun ity
-Ġs al
-Ġpower ful
-M y
-Ġclaim s
-ĠEar th
-ord s
-Ġt itle
-Ġes c
-n ame
-N ot
-om en
-Ġbe yond
-Ġc amer
-Ġse ll
-it ute
-ear ch
-Ġapp l
-im ent
-4 2
-ĠAr t
-Ġun f
-Ġviol ence
-ur g
-ĠE ast
-Ġcomp ared
-Ġopt ions
-Ġthrough out
-Ġv s
-ig r
-. [
-ac hes
-7 8
-Ġfil es
-F L
-E L
-ar ian
-ĠJ ames
-ĠA ir
-an ch
-Ġdet ail
-Ġpie ce
-P S
-Ġn amed
-Ġeduc ation
-Ġdri ve
-Ġitem s
-Ġstud ent
-ic ed
-: :
-ic o
-Ġth row
-Ġsc ene
-Ġcomple x
-Ġ200 9
-Ġpre c
-ĠB re
-7 9
-Ġcon cept
-Ġstat us
-am ing
-Ġd ied
-Ġknow ledge
-Ġbegin ning
-O D
-ru ary
-Ġcertain ly
-Ġgu ys
-Ġsl ight
-in n
-ound s
-Ġf ine
-Ġf at
-ic ations
-Ġper haps
-ĠA nt
-Ġinc ome
-Ġhtt ps
-Ġmajor ity
-port s
-st on
-Ġgreat er
-Ġfe ed
-ent ially
-Ġsaf ety
-Ġun ique
-and om
-Ġg one
-Ġshow ed
-Ġhist or
-Ġcoun ter
-i us
-id a
-Ġlead ing
-i pe
-Ġs end
-ĠDon ald
-er ve
-Ġdef ense
-ines e
-Ġy es
-ĠF ire
-ĠMus lim
-ra q
-Ġcontin ued
-os h
-Ġprov ides
-Ġpr ison
-ĠP re
-Ġhapp y
-Ġeconom y
-Ġtr ust
-ag s
-ĠG ame
-Ġweap ons
-um an
-ĠC le
-it ation
-Ġanal ysis
-ĠT imes
-Ġsc ience
-- >
-Ġfig ure
-Ġdis app
-ent y
-Ġsoft ware
-Ġu lt
-Ġoffic ers
-N ew
-I s
-Ġrem ains
-ĠInd ia
-Ġp sych
-ri ef
-Ġc at
-es c
-Ġob serv
-Ġst age
-ĠD ark
-Ġent er
-ch ange
-Ġpass ed
-Ġdes pite
-ĠO ut
-Ġmov ie
-r s
-Ġv oice
-m ine
-ĠPl ay
-Ġto ward
-ĠT er
-Ġreg ion
-Ġval ues
-or ters
-Ġm ount
-Ġoffic er
-ĠO ther
-b an
-Ġh ous
-w ood
-ro om
-I V
-ĠS un
-se e
-ĠO ver
-ro g
-9 0
-Ġl ay
-ĠT ur
-a wn
-Ġpress ure
-ĠS ub
-Ġbook s
-ed om
-ĠS and
-A A
-ag o
-Ġre asons
-f ord
-Ġactiv ity
-U T
-N ow
-ĠSen ate
-ce ll
-n ight
-Ġcall s
-in ter
-Ġlet ter
-ĠR ob
-ĠJ e
-Ġcho ose
-ĠL aw
-G et
-B e
-Ġro b
-Ġtyp es
-Ġpl atform
-Ġqu arter
-R A
-ĠT ime
-Ġmay be
-ĠC r
-9 5
-p re
-Ġmov ing
-Ġl if
-Ġgo ld
-Ġs om
-Ġpat ients
-Ġtr uth
-ĠK e
-ur ance
-ant ly
-m ar
-Ġchar ge
-ĠG reat
-Ġce le
----------------- ----------------
-Ġro ck
-ro id
-an cy
-Ġcred it
-a ud
-B y
-ĠE very
-Ġmov ed
-ing er
-rib ution
-Ġn ames
-Ġstra ight
-ĠHe alth
-ĠW ell
-Ġfe ature
-Ġr ule
-Ġsc he
-in ated
-ĠMich ael
-ber g
-4 1
-il ed
-b and
-Ġcl ick
-ĠAng el
-on ents
-Â Ń
-ĠI raq
-ĠS aturday
-Ġa ware
-p art
-Ġpat tern
-O W
-ĠL et
-Ġgr ad
-ign ed
-Ġassoci ated
-Ġst yle
-n o
-i ation
-a ith
-il ies
-Ġst ories
-ur ation
-Ġindividual s
-ĠâĢ ¦
-m iss
-ĠAss oci
-ish ing
-ab y
-Ġsum mer
-ĠB en
-Ġ3 2
-Ġar ch
-ut y
-ĠTex as
-h ol
-Ġfull y
-Ġm ill
-Ġfollow ed
-ĠB ill
-ĠInd ian
-ĠSec ret
-ĠB el
-ĠFeb ruary
-Ġjob s
-Ġseem ed
-ĠGo vern
-i pped
-Ġreal ity
-Ġl ines
-Ġp ark
-Ġmeas ure
-ĠO ur
-I M
-Ġbro ther
-Ġgrow ing
-Ġb an
-Ġest im
-Ġc ry
-ĠS chool
-Ġme chan
-ĠO F
-ĠWind ows
-Ġr ates
-ĠO h
-Ġpos itive
-Ġcult ure
-ist ics
-ic a
-Ġh ar
-y a
-ite ly
-i pp
-Ġm ap
-en cies
-ĠWill iam
-I I
-ak ers
-5 6
-ĠM art
-ĠR em
-Ġal tern
-it ude
-Ġco ach
-row d
-D on
-Ġk ids
-Ġj ournal
-Ġcor por
-Ġf alse
-Ġwe b
-Ġsle ep
-Ġcont ain
-Ġst o
-Ġb ed
-iver se
-ĠR ich
-ĠCh inese
-Ġp un
-Ġme ant
-k nown
-Ġnot ice
-Ġfavor ite
-a ven
-Ġcond ition
-Ġpur pose
-) )
-Ġorgan ization
-Ġchall eng
-Ġman ufact
-Ġsus p
-ĠA c
-Ġcrit ic
-un es
-uc lear
-Ġm er
-vent ion
-Ġ8 0
-Ġm ist
-ĠU s
-ĠT or
-htt p
-ol f
-Ġlarg er
-Ġadv ant
-Ġrese ar
-Ġact ions
-m l
-Ġke pt
-Ġa im
-, '
-c ol
-Ġbenef its
-if ying
-Ġact ual
-ĠIntern ational
-Ġveh icle
-Ġch ief
-Ġeff orts
-ĠLe ague
-ĠM ost
-Ġwa it
-Ġad ult
-Ġover all
-Ġspe ech
-Ġhigh ly
-Ġfem ale
-Ġer ror
-Ġeffect ive
-5 4
-Ġenc our
-w ell
-Ġfail ed
-Ġcons erv
-Ġprogram s
-Ġt rou
-Ġa head
-5 00
-vertis ement
-I P
-ĠF ound
-p ir
-Ġ %
-Ġcr ime
-and er
-Ġloc ation
-ĠI ran
-Ġbehav ior
-az ing
-Ġr are
-Ġem b
-Ġca used
-Ġsh ip
-Ġact ive
-Ġcont ribut
-Ġg reen
-Ġac qu
-Ġref lect
-ven ue
-Ġf irm
-Ġb irth
-] .
-Ġclear ly
-Ġem ot
-Ġag ency
-ri age
-Ġmem ory
-9 8
-S A
-ĠSe e
-ac ing
-C C
-Ġbig gest
-Ġr ap
-Ġbas ic
-Ġb and
-e at
-Ġsus pect
-ĠM ac
-Ġ9 0
-m ark
-ist an
-Ġsp read
-am s
-k i
-as y
-ra v
-ĠR ober
-Ġdemon str
-r ated
-Ġabs olute
-Ġpl aces
-Ġim pl
-ibr ary
-Ġc ards
-Ġdest roy
-Ġv irt
-ve re
-Ġapp eared
-y an
-p oint
-Ġbe g
-Ġtem per
-s pe
-ant ed
-ear s
-ĠD irect
-Ġl ength
-Ġbl og
-am b
-Ġint eg
-Ġres ources
-ac c
-if ul
-Ġsp ot
-Ġfor ced
-Ġthous ands
-ĠMin ister
-Ġqu al
-ĠF rench
-at ically
-Ġgener ally
-Ġdr ink
-Ġth us
-I L
-od es
-Ġappro pri
-ĠRe ad
-Ġwh om
-Ġey e
-Ġcol lege
-Ġ4 5
-ire ction
-Ġens ure
-Ġapp arent
-id ers
-Ġrelig ious
-Ġmin or
-ol ic
-Ġt ro
-ĠWh y
-rib ute
-m et
-Ġprim ary
-Ġdevelop ed
-Ġpe ace
-Ġsk in
-st e
-av a
-Ġbl ue
-Ġfam ilies
-Ġ ir
-Ġapp ly
-Ġin form
-ĠSm ith
-C T
-i i
-Ġlim it
-Ġres ist
-........ ........
-um n
-Ġconf lic
-Ġtw e
-ud d
-ĠT om
-Ġl iter
-qu e
-b on
-Ġha ir
-Ġevent ually
-Ġp us
-Ġhelp ed
-Ġag g
-or ney
-ĠApp le
-Ġf it
-ĠS ur
-Ġpre m
-Ġs ales
-Ġsecond s
-Ġstreng th
-Ġfeel ing
-¿ ½
-Ġt our
-Ġknow s
-o om
-Ġex erc
-Ġsom ew
-ï ¿½
-> >
-Ġsp okes
-Ġide as
-Ġreg ist
-so ft
-ĠD el
-ĠP C
-Ġpro pos
-Ġlaun ch
-Ġbott om
-T H
-ĠP lease
-v est
-it z
-ĠIn ter
-Ġsc ript
-Ġr at
-ar ning
-Ġ il
-ĠJ er
-ĠA re
-Ġwh atever
-ok en
-ci ence
-Ġmod e
-Ġag ree
-Ġs ources
-Ġinit ial
-Ġrest rict
-Ġwond er
-us ion
-## ##
-ĠS il
-vil le
-Ġb urn
-t w
-as ion
-ĠÂ £
-Ġn or
-u ing
-Ġre ached
-Ġs un
-Ġc ateg
-ig ration
-Ġc ook
-Ġprom ot
-Ġm ale
-Ġcl imate
-Ġf ix
-Ġalleg ed
-U R
-all ed
-Ġim ages
-C ont
-ot a
-Ġschool s
-i os
-Ġd rop
-Ġst ream
-ĠM o
-Ġprevious ly
-al ing
-Ġp et
-Ġdou ble
-Ġ( @
-ann el
-Ġdef ault
-t ies
-Ġr ank
-ĠD ec
-ĠCoun cil
-Ġweap on
-Ġst ock
-Ġanal y
-ĠSt r
-Ġpict ure
-ĠPol ice
-f erence
-Ġcent ury
-Ġcitiz ens
-Ġon to
-Ġexp and
-Ġhe ro
-ĠS ol
-Ġw ild
-Ġupd ate
-Ġcustom ers
-r ont
-d ef
-Ġl ik
-Ġcrim inal
-ĠChrist ian
-S P
-7 6
-Ġle aving
-Ġother wise
-ĠD ist
-Ġbas is
-5 2
-5 3
-ic ip
-ĠB er
-Ġrecomm end
-Ġfl oor
-Ġc rowd
-ol es
-Ġ7 0
-Ġcent ral
-ĠE v
-Ġd ream
-Ġdown load
-Ġconf ir
-ĠTh om
-Ġwind ow
-Ġhapp ens
-Ġun it
-Ġt end
-Ġs pl
-Ġbec omes
-Ġfight ing
-Ġpred ict
-ĠP ress
-ĠP ower
-Ġhe avy
-ak ed
-Ġf an
-or ter
-ate gy
-B A
-iz es
-Ġsp end
-H ere
-Ġ200 7
-Ġad op
-ĠH am
-Ġfoot ball
-ĠP ort
-od ay
-5 1
-amp ions
-Ġtrans fer
-h t
-Ġ3 8
-ter m
-ac ity
-Ġb ur
-] ,
-tern al
-r ig
-b ut
-Ġthere fore
-ĠB ecause
-res p
-re y
-Ġm ission
-S ome
-Ġnot ed
-Ġass um
-Ġdise ase
-Ġed it
-Ġprog ress
-r d
-ĠB rown
-oc al
-Ġadd ing
-Ġra ised
-ĠAn y
-Ġt ick
-Ġsee ing
-ĠPe ople
-Ġagre ement
-Ġser ver
-Ġw at
-Ġdeb ate
-Ġsupp osed
-il ing
-Ġlarg est
-Ġsuccess ful
-ĠP ri
-ĠDemocr atic
-Ġj ump
-ĠSyri a
-Ġown ers
-Ġoff ers
-Ġshoot ing
-Ġeff ic
-se y
-Ġha ven
-ver se
-te red
-ĠL ight
-im al
-ĠB ig
-Ġdef end
-Ġbe at
-Ġrecord s
-% )
-Ġsc en
-Ġemploy ees
-Ġdev ices
-he m
-Ġcom mer
-ĠM ex
-Ġbenef it
-ĠPro f
-Ġil leg
-Ġsur face
-ĠAl so
-Ġh arm
-ing ly
-w ide
-ĠA lex
-Ġsh ut
-ĠC ur
-Ġl ose
-p m
-Ġchall enge
-se mb
-Ġst ation
-Ġint elligence
-Ġacc ur
-ĠFl or
-Ġrequ ires
-ĠM al
-b um
-Ġh ospital
-Ġsp irit
-Ġoff ered
-Ġprodu ce
-ĠComm un
-Ġcreat ing
-Ġcr is
-s pect
-Ġend ed
-Ġd aily
-Ġvot ers
-land s
-i as
-i h
-on a
-Ġsm art
-ĠOff ice
-ĠL ord
-ri al
-ĠIntern et
-Ġcirc um
-Ġextreme ly
-' .
-Ġopin ion
-ĠM il
-Ġg ain
-B S
-ĠF in
-y p
-Ġuse ful
-Ġbud get
-Ġcom fort
-is f
-Ġback ground
-el ine
-Ġep isode
-Ġen emy
-Ġtri al
-Ġestab lish
-d ate
-ĠC ap
-Ġcontin ues
-Ġshow ing
-ĠUn ion
-w ith
-Ġpost ed
-ĠSy stem
-Ġe at
-ri an
-Ġr ise
-ĠGerman y
-il s
-Ġsign ed
-Ġv ill
-Ġgr and
-m or
-ĠEng land
-Ġproject s
-um ber
-Ġconf erence
-z a
-Ġrespons ible
-ĠAr ab
-Ġlearn ed
-âĢĶ âĢĶ
-i pping
-ĠGe orge
-O C
-Ġreturn ed
-ĠAustral ia
-Ġb rief
-Q u
-Ġbr and
-ill ing
-ab led
-Ġhig hest
-Ġtr ain
-ĠComm ission
-wh ile
-Ġn om
-cept ion
-Ġm ut
-ĠBl ue
-Ġinc ident
-v ant
-8 6
-ĠI D
-Ġn uclear
-7 4
-ĠL ike
-ĠR E
-ĠM icro
-l i
-m ail
-Ġcharg es
-8 9
-Ġad just
-ad o
-Ġear th
-N A
-Ġpr ices
-P A
-Ġd raft
-Ġrun s
-Ġcandid ate
-ens es
-Ġmanag ement
-ĠPh il
-ĠM iss
-Ġte ach
-g ram
-Ġunderstand ing
-a it
-ic ago
-A dd
-ĠE p
-sec ut
-Ġsepar ate
-Ġinst ance
-Ġe th
-Ġun less
-**** ****
-ĠF ore
-in ate
-Ġoper ations
-S p
-Ġf aith
-g ar
-ĠCh urch
-ron ic
-Ġconf ig
-os ure
-Ġactiv ities
-Ġtrad itional
-Ġ3 6
-Ġd irection
-Ġmach ine
-Ġsur round
-Ġp ush
-un ction
-ĠE U
-Ġeas ier
-Ġarg ument
-G B
-Ġm icro
-Ġsp ending
-iz ations
-Ġthe ory
-ad ow
-Ġcall ing
-ĠL ast
-Ġd er
-Ġinflu ence
-Ġcomm it
-Ġph oto
-Ġun c
-ist ry
-g n
-ast e
-ack s
-Ġdis p
-ad y
-d o
-ĠG ood
-Ġ `
-Ġw ish
-Ġreve aled
-Âł Âł
-l ig
-Ġen force
-ĠComm ittee
-Ġche m
-Ġmil es
-Ġinterest ed
-Ġsol ution
-ic y
-in ct
-Ġ- >
-ĠD et
-Ġrem oved
-Ġcomp ar
-e ah
-Ġpl ant
-ĠS ince
-Ġachie ve
-Ġadvant age
-Ġslight ly
-b ing
-Ġpl aced
-u nder
-201 5
-ĠM ad
-Ġt im
-os es
-Ġc ru
-ĠR ock
-Ġmost ly
-Ġneg ative
-Ġset ting
-Ġprodu ced
-Ġm ur
-Ġconnect ion
-ĠM er
-Ġdri ver
-Ġexecut ive
-Ġass ault
-Ġb orn
-ĠV er
-t ained
-Ġstruct ure
-Ġredu ce
-Ġdec ades
-Ġd ed
-u ke
-ĠM any
-idd en
-Ġle ague
-S e
-Ġjo in
-Ġdis co
-Ġd ie
-c ks
-act ions
-Ġass ess
-ag n
-Ġgo als
-our s
-I R
-Ġsen ior
-ill er
-m od
-ip ment
-oc ol
-u y
-ĠQ ue
-Ġpart ies
-ir gin
-Ġle arning
-it able
-Ġstre et
-Ġcamer a
-A pp
-Ġsk ills
-b re
-c ious
-Ġcele br
-ĠFr anc
-Ġexist ing
-Ġwill ing
-l or
-Ġ id
-ĠSp ace
-Ġcrit ical
-ĠL a
-ortun ately
-Ġser ve
-Ġc old
-Ġspec ies
-T S
-Ġanim als
-ĠB ay
-Ġold er
-ĠU nder
-est ic
-ĠT re
-Ġte acher
-Ġpre fer
-v is
-Ġth read
-ĠM att
-Ġmanag er
-ãĥ »
-Ġprofess ional
-ĠV ol
-Ġnot es
-The se
-ul a
-Ġf resh
-ent ed
-u zz
-ed y
-clus ion
-ĠR el
-Ġdoub t
-E O
-Ġopen ed
-ĠB it
-Ad vertisement
-Ġgu ess
-ĠU N
-Ġse qu
-Ġexpl ain
-ott en
-Ġatt ract
-ak s
-Ġstr ing
-Ġcont ext
-oss ible
-ĠRepublic ans
-Ġsol id
-Ġc ities
-Ġask ing
-Ġr andom
-u ps
-ur ies
-ar ant
-dd en
-g l
-ĠFlor ida
-Ġdep end
-ĠSc ott
-Ġ3 3
-Ġi T
-ic on
-Ġmention ed
-Ġ2 000
-Ġclaim ed
-Ġdefin itely
-ul f
-Ġc ore
-Ġopen ing
-ĠCon st
-wh ich
-ĠT ra
-A G
-7 2
-Ġbelie ved
-ad a
-Ġ4 8
-ĠSec urity
-yr ight
-ĠP et
-ĠL ou
-Ġhold ing
-======== ========
-Ġ ice
-Ġb row
-Ġauthor ities
-h ost
-w ord
-Ġsc ore
-ĠD iv
-Ġcell s
-Ġtrans l
-Ġneigh bor
-Ġrem ove
-u ct
-Ġdist rict
-ĠA ccording
-Ġwor se
-Ġconcern s
-Ġpresident ial
-Ġpolic ies
-ĠH all
-7 3
-Ġh us
-A Y
-Ġ200 6
-ĠJ ud
-Ġindepend ent
-ĠJust ice
-ili ar
-pr int
-igh ter
-Ġprotect ion
-z en
-Ġsu dden
-h ouse
-ĠJ es
-P R
-ĠIn f
-Ġb ul
-Ġ _
-ĠServ ice
-ĠP R
-Ġstr ategy
-ff ect
-Ġgirl s
-Ġmiss ing
-oy al
-ĠTe am
-ul ated
-Ġd at
-Ġpolit ics
-ab or
-A ccording
-Ġspe ll
-Ġg raph
-ort hern
-T C
-A b
-Ġlab or
-is her
-Ġk ick
-ĠiT unes
-Ġstep s
-pos es
-Ġsmall er
-E n
-ber t
-Ġro ll
-Ġresear chers
-Ġcl osed
-Ġtrans port
-Ġlaw y
-________ ________
-ĠCh icago
-Ġas pect
-Ġn one
-Ġmar riage
-9 6
-Ġe lements
-ĠF re
-ĠS al
-Ġd ram
-F C
-t op
-e qu
-Ġhe aring
-Ġsupport ed
-Ġtest ing
-co hol
-Ġmass ive
-Ġst ick
-Ġgu ard
-is co
-ph one
-F rom
-How ever
-Ġb order
-Ġcop y
-ograph y
-l ist
-7 1
-Ġown er
-cl ass
-ru it
-r ate
-ĠO nce
-Ġdig ital
-Ġt ask
-ER S
-Ġinc red
-t es
-+ +
-ĠFr ance
-Ġb reat
-ow l
-Ġiss ued
-ĠW estern
-Ġdet ect
-Ġpart ners
-Ġsh ared
-ĠC all
-Ġcan cer
-ac he
-rib e
-Ġexpl ained
-Ġhe at
-{ "
-Ġinvest ment
-ĠB ook
-Ġw ood
-Ġtool s
-ĠAl though
-Ġbelie f
-Ġcris is
-Ġg e
-ĠM P
-Ġoper ation
-ty pe
-~ ~
-g a
-Ġcont ains
-ant a
-Ġexp ress
-ĠG roup
-ĠJ ournal
-k a
-Ġam b
-ĠUS A
-Ġfind ing
-Ġfund ing
-h ow
-Ġestab lished
-ide os
-Ġdeg ree
-Ġdanger ous
-ang ing
-Ġfre edom
-pp ort
-out hern
-Ġch urch
-Ġc atch
-ĠTw o
-Ġpres ence
-ĠGu ard
-U p
-Ġauthor ity
-ĠPro ject
-Ġbut ton
-Ġcon sequ
-Ġval id
-Ġwe ak
-Ġstart s
-Ġref erence
-ĠM em
-" )
-U N
-or age
-ĠO pen
-Ġcol lection
-y m
-g ency
-Ġbeaut iful
-ro s
-Ġtell s
-Ġwa iting
-n el
-Ġprov iding
-ĠDemocr ats
-Ġd aughter
-Ġm aster
-Ġpur poses
-ĠJapan ese
-Ġequ al
-Ġturn s
-Ġdoc uments
-Ġwatch ing
-R es
-Ġr an
-201 4
-Ġre ject
-ĠKore a
-Ġvictim s
-Le vel
-ere nces
-Ġw itness
-Ġ3 4
-Ġre form
-com ing
-Ġocc up
-Ġc aught
-Ġtra ffic
-ad ing
-Ġmod els
-ar io
-Ġserv ed
-Ġb atter
-u ate
-ĠSecret ary
-Ġagre ed
-Ġtr uly
-yn am
-ĠR et
-Ġun its
-ĠRes earch
-h and
-az ine
-ĠM ike
-Ġvar iety
-ot al
-Ġam azing
-Ġconfir med
-Ġentire ly
-Ġpurch ase
-Ġe lement
-Ġc ash
-Ġdeter mine
-D e
-Ġc ars
-ĠW all
-â ĸ
-Ġview s
-Ġdrug s
-Ġdep artment
-ĠSt ep
-u it
-Ġ3 9
-as ure
-ĠCl ass
-Ġc overed
-ĠB ank
-Ġme re
-u ana
-Ġmult i
-Ġm ix
-Ġun like
-lev ision
-Ġsto pped
-Ġs em
-ĠG al
-ul es
-Ġwe l
-ĠJohn son
-l a
-Ġsk ill
-Ġbec oming
-ri e
-Ġappropri ate
-f e
-ell ow
-ĠPro t
-ul ate
-oc ation
-Ġweek end
-od ies
-Ġsit es
-Ġanim al
-ĠT im
-Ġsc ale
-Ġcharg ed
-Ġinst ruct
-ill a
-Ġmethod s
-Ġc ert
-Ġjud ge
-ĠH el
-Ġdoll ars
-Ġstand ing
-ĠS qu
-Ġdeb t
-l iam
-Ġdri ving
-ĠS um
-ĠEd ition
-Ġal bum
-and on
-I F
-ĠU k
-6 3
-ad er
-Ġcommer cial
-es h
-ĠGovern ment
-Ġdisc overed
-Ġout put
-ĠHill ary
-ĠCar ol
-Ġ200 5
-Ġab use
-anc ing
-Ġsw itch
-Ġann ual
-T w
-Ġst ated
-ag ement
-in ner
-Ġdem ocr
-Ġres idents
-Ġallow ing
-Ġfact ors
-od d
-Ġf uck
-em ies
-Ġoccur red
-ot i
-Ġn orth
-ĠP ublic
-Ġinj ury
-Ġins urance
-C L
-oll y
-ã Ģ
-Ġrepe ated
-Ġar ms
-ang ed
-Ġconst ruction
-Ġf le
-P U
-ic ians
-Ġfor ms
-ĠMc C
-ant ic
-Ġm ental
-p ire
-Ġequ ipment
-Ġf ant
-Ġdiscuss ion
-Ġregard ing
-k in
-ar p
-Ġch air
-og ue
-Ġpro ceed
-ĠI d
-O ur
-Ġmur der
-M an
-Ġ4 9
-as p
-Ġsupp ly
-Ġin put
-Ġwe alth
-liam ent
-Ġpro ced
-or ial
-ĠSt at
-ĠN FL
-hen s
-ĠInst itute
-Ġput ting
-ourn ament
-et ic
-Ġloc ated
-Ġk id
-er ia
-r un
-Ġpr inc
-Ġ !
-go ing
-ĠB et
-Ġcl ot
-Ġtell ing
-Ġprop osed
-i ot
-or ry
-Ġfund s
-g ment
-ĠL ife
-Ġb aby
-ĠB ack
-Ġsp oke
-Im age
-Ġear n
-ĠA T
-g u
-Ġex change
-ĠL in
-ov ing
-Ġp air
-M ore
-az on
-Ġarrest ed
-Ġkill ing
-c an
-ĠC ard
-y d
-Ġident ified
-Ġm obile
-Ġthan ks
-ony m
-ĠF orm
-Ġhundred s
-ĠCh ris
-ĠC at
-Ġtre nd
-h at
-ĠA v
-om an
-Ġelect ric
-ĠW il
-S E
-O f
-Ġrest aur
-ot ed
-Ġtr ig
-Ġn ine
-Ġb omb
-Wh y
-Â ¯
-Ġco verage
-Ġapp eal
-ĠRober t
-ĠS up
-Ġfin ished
-Ġfl ow
-Ġdel iver
-Ġcal cul
-Ġphot os
-Ġph il
-Ġpie ces
-Ġapp re
-k es
-Ġr ough
-D o
-Ġpart ner
-Ġconcern ed
-Ġ3 7
-ĠG en
-C ol
-ct ors
-Ġ= >
-st ate
-Ġsuggest ed
-ĠFor ce
-C E
-Ġher self
-ĠPl an
-w orks
-o oth
-ren cy
-Ġcor ner
-Ġhus band
-Ġintern et
-ĠA ut
-em s
-os en
-ĠAt l
-g en
-Ġbal ance
-6 2
-Ġsound s
-te xt
-Ġar r
-ov es
-Ġmill ions
-Ġrad io
-Ġsat isf
-ĠD am
-M r
-G o
-S pe
-Ġcomb at
-r ant
-ĠG ree
-Ġf uel
-Ġdist ance
-Ġtest s
-Ġdec re
-ĠE r
-Ġman aged
-D S
-Ġt it
-Ġmeas ures
-ĠL iber
-Ġatt end
-as hed
-ĠJ ose
-ĠN ight
-d it
-ĠN ov
-ĠE nd
-out s
-Ġgener ation
-Ġadv oc
-y th
-Ġconvers ation
-ĠS ky
-act ive
-ce l
-ri er
-ĠFr ank
-Ġg ender
-Ġcon cent
-Ġcar ried
-and a
-ĠV irgin
-Ġarri ved
-ic ide
-ad ed
-Ġfail ure
-Ġmin imum
-le ts
-Ġwor st
-Ġkeep ing
-Ġint ended
-Ġilleg al
-Ġsub sc
-Ġdetermin ed
-Ġtri p
-Y es
-Ġra ise
-Ġ ~
-Ġfeel s
-Ġpack age
-ĠJ o
-h i
-201 6
-re al
-Ġf ra
-Ġsy mb
-M e
-uck y
-p ret
-ĠK h
-ĠEd it
-ĠWe b
-em ic
-ĠCol or
-Ġjust ice
-I nt
-Ġfar m
-ck now
-" >
-el ess
-Ġredu ced
-Ġ5 00
-x x
-ĠR ad
-ĠW ood
-Ġcl in
-Ġhy p
-il er
-ur a
-k ins
-8 5
-6 1
-ĠThe ir
-ĠM ary
-Ġs an
-Ġno vel
-ĠWh o
-Ġcap acity
-Ġimp ossible
-Ġpl ays
-Ġmin ister
-ij uana
-ic ate
-ĠS et
-Ġf ram
-Ġ ing
-Ġcommun ities
-ĠF BI
-it a
-Ġb on
-Ġstr ateg
-Ġinterest s
-l ock
-g ers
-m as
-ĠAN D
-Ġconflic t
-Ġrequire ments
-Ġs ac
-Ġoper ating
-in i
-rel ated
-Ġcomm itted
-Ġrelative ly
-Ġs outh
-¯ ¯
-Ġaff ord
-Ġident ity
-Ġdec isions
-Ġacc used
-pl ace
-Ġvict ory
-o ch
-i at
-N ame
-C om
-t ion
-ed s
-Ġsee k
-Ġt ight
-ĠIm ages
-Ġinit i
-Ġhum ans
-Ġfam iliar
-Ġaud ience
-Ġintern al
-vent ure
-Ġs ides
-ĠT O
-Ġd im
-Ġcon clud
-Ġapp oint
-Ġenforce ment
-ĠJ im
-ĠAssoci ation
-Ġcircum st
-ĠCanad ian
-Ġjo ined
-Ġdiffere nces
-ĠL os
-Ġprot est
-Ġtw ice
-w in
-Ġgl ass
-ars h
-ĠAr my
-Ġexp ression
-Ġdec ide
-Ġplan ning
-an ia
-Ġhand le
-ĠMicro soft
-ĠN or
-Ġmax imum
-ĠRe v
-Ġse a
-Ġev al
-Ġhel ps
-re f
-Ġb ound
-Ġm outh
-Ġstand ards
-Ġcl im
-ĠC amp
-ĠF ox
-cl es
-Ġar my
-ĠTe chn
-ack ing
-x y
-S S
-Ġ4 2
-Ġbu g
-ĠUk rain
-ĠM ax
-ĠJ ones
-ĠSh ow
-l o
-Ġplan et
-Ġ7 5
-Ġwin ning
-Ġf aster
-Ġspe ct
-Ġbro ken
-T R
-Ġdef ined
-Ġhealth y
-Ġcompet ition
-htt ps
-ĠIs land
-ĠF e
-Ġannoun ce
-ĠC up
-ĠInst ead
-Ġcl ient
-Ġposs ibly
-se ction
-ock et
-l ook
-Ġfin ish
-Ġcre w
-Ġres erv
-Ġed itor
-Ġh ate
-Ġs ale
-Ġcontro vers
-Ġp ages
-w ing
-Ġnum er
-Ġopp osition
-Ġ200 4
-Ġref uge
-Ġfl ight
-Ġap art
-ĠL at
-A meric
-ĠAfric a
-Ġapplic ations
-ĠPal est
-ĠB ur
-Ġg ar
-ĠSoc ial
-Ġup gr
-Ġsh ape
-Ġspe aking
-ans ion
-a o
-ĠS n
-Ġwor ry
-ĠBrit ain
-P lease
-rou d
-Ġh un
-Ġintrodu ced
-Ġd iet
-I nd
-ĠSec ond
-Ġfun ctions
-ut s
-ĠE ach
-ĠJe ff
-Ġst ress
-Ġaccount s
-Ġgu arant
-ĠAn n
-ed ia
-Ġhon est
-Ġt ree
-ĠAfric an
-ĠB ush
-} ,
-Ġs ch
-ĠOn ly
-Ġf if
-ig an
-Ġexerc ise
-ĠEx p
-Ġscient ists
-Ġlegisl ation
-ĠW ork
-ĠS pr
-Ã Ĥ
-ĠH uman
-Ġ è
-Ġsur vey
-Ġr ich
-ri p
-Ġmain tain
-Ġfl o
-Ġleaders hip
-st ream
-ĠIslam ic
-Ġ 01
-ĠCol lege
-Ġmag ic
-ĠPr ime
-Ġfig ures
-201 7
-ind er
-x ual
-ĠDe ad
-Ġabsolute ly
-Ġfour th
-Ġpresent ed
-resp ond
-rib le
-Ġal cohol
-at o
-ĠD E
-por ary
-Ġgr ab
-Ġvar i
-Ġqu ant
-ĠPh oto
-Ġpl us
-r ick
-ar ks
-Ġaltern ative
-Ġp il
-Ġappro x
-th at
-Ġobject s
-ĠR o
-ĠAnd roid
-Ġsignificant ly
-ĠR oad
-k ay
-R ead
-av or
-Ġa cknow
-ĠH D
-ĠS ing
-O r
-ĠM ont
-Ġun s
-pro f
-Ġneg oti
-ĠAr ch
-ik i
-Ġte levision
-ĠJew ish
-Ġcomm ittee
-Ġmot or
-Ġappear ance
-Ġs itting
-Ġstri ke
-ĠD own
-com p
-ĠH ist
-Ġf old
-ac ement
-ĠLou is
-Ġbel ong
-ĠâĢ ¢
-Ġm ort
-Ġprep ared
-Ġ6 4
-ĠM aster
-Ġind eed
-ĠD en
-Ġre nt
-T A
-our ney
-ar c
-S u
-9 7
-Ġadv ice
-Ġchang ing
-Ġlist ed
-Ġlaun ched
-is ation
-ĠP eter
-is hes
-Ġl ived
-ĠM el
-ĠSup reme
-ĠF ederal
-Ġ) ;
-ruct ure
-Ġset s
-Ġphil os
-u ous
-ĠÂ ł
-Ġappl ied
-ĠN OT
-Ġhous ing
-ĠM ount
-Ġo dd
-Ġsu st
-D A
-ffic ient
-Ġ ?
-ol ved
-Ġp owers
-Ġth r
-Ġrem aining
-ĠW ater
-L C
-Ġca uses
-ãģ ®
-Ġman ner
-ad s
-Ġsuggest s
-Ġend s
-stand ing
-f ig
-ĠD un
-id th
-Ġg ay
-Ġter min
-ĠAngel es
-M S
-Ġscient ific
-Ġco al
-ap ers
-b ar
-ĠThom as
-Ġsy m
-ĠR un
-th is
-P C
-igr ants
-Ġmin ute
-ĠDist rict
-cell ent
-Ġle aves
-Ġcomple ted
-am in
-Ġfoc used
-Ġmon itor
-Ġveh icles
-M A
-ĠM ass
-ĠGr and
-Ġaffect ed
-itution al
-Ġconst ruct
-Ġfollow s
-Ġt on
-re ens
-Ġh omes
-ĠE xt
-ĠLe vel
-r ast
-ĠI r
-Ġel im
-Ġlarge ly
-ĠJ oe
-Ġvot es
-all s
-Ġbusiness es
-ĠFound ation
-ĠCent ral
-Ġy ards
-Ġmaterial s
-ul ner
-Ġgu ide
-Ġclos er
-um s
-Ġsp orts
-ed er
-J ust
-Ġtax es
-8 4
-ĠO ld
-Ġdec ade
-ol a
-Ġv ir
-Ġdro pped
-Ġdel ay
-it ect
-Ġsec ure
-ste in
-le vel
-Ġtre ated
-Ġfil ed
-ain e
-Ġv an
-Ġm ir
-Ġcol umn
-ict ed
-e per
-Ġro t
-Ġcons ult
-Ġent ry
-Ġmar ijuana
-ĠD ou
-Ġapparent ly
-ok ing
-clus ive
-Ġincre ases
-an o
-Ġspecific ally
-Ġte le
-ens ions
-Ġrelig ion
-ab ilities
-Ġfr ame
-ĠN ote
-ĠLe e
-Ġhelp ing
-Ġed ge
-ost on
-Ġorgan izations
-Ã ĥ
-ĠB oth
-hip s
-Ġbig ger
-Ġbo ost
-ĠSt and
-Ġro w
-ul s
-ab ase
-Ġr id
-L et
-are n
-ra ve
-Ġst ret
-P D
-Ġv ision
-Ġwe aring
-Ġappre ci
-Ġa ward
-ĠU se
-Ġfact or
-w ar
-ul ations
-) (
-Ġg od
-Ġter rit
-Ġpar am
-ast s
-8 7
-Ġen emies
-ĠG ames
-F F
-Ġacc ident
-W ell
-ĠMart in
-T ER
-Ġat h
-ĠHe ll
-Ġfor g
-Ġve ter
-ĠMed ic
-f ree
-Ġst ars
-Ġexp ensive
-Ġac ad
-ra wn
-ĠW he
-Ġl ock
-Ġform at
-Ġsold iers
-s m
-Ġag ent
-Ġrespons ibility
-or a
-ĠS cience
-Ġrap id
-Ġt ough
-ĠJes us
-Ġbelie ves
-M L
-Ġwe ar
-le te
-Ãĥ ÃĤ
-ĠD ri
-Ġcomm ission
-ĠB ob
-O h
-ap ed
-Ġwar m
-ÃĥÃĤ ÃĥÃĤ
-Ġ200 3
-ort ion
-Ġhas n
-ust er
-Ġun ivers
-ĠI ll
-Ġk ing
-olog ies
-9 4
-ĠT em
-ĠM os
-Ġpat ient
-ĠMex ico
-ce an
-ĠDe ath
-ĠSand ers
-y ou
-ĠC ast
-ĠComp any
-pt y
-Ġhappen ing
-F P
-ĠB attle
-Ġb ought
-A m
-M od
-U s
-ut ers
-ĠC re
-ĠTh ose
-Ġ4 4
-is er
-Ġs oul
-ĠT op
-ĠHar ry
-ĠA w
-Ġse at
-ff ee
-Ġrev olution
-Ġ( "
-ĠD uring
-et te
-Ġr ing
-Ġoff ensive
-Ġreturn s
-Ġv ideos
-Ġdis cl
-Ġfam ous
-en ced
-ĠS ign
-ĠR iver
-Ġ3 00
-P M
-ĠB us
-ĠC H
-Ġcandid ates
-ard en
-Ġpercent age
-Ġvis ual
-Ġthan k
-Ġtrou ble
-ner gy
-Ġ200 1
-Ġpro ve
-ash ion
-Ġen h
-ĠL ong
-U M
-Ġconnect ed
-Ġposs ibility
-O ver
-Ġexper t
-Ġl ibrary
-art s
-ĠDirect or
-Ġfell ow
-9 2
-ir ty
-Ġd ry
-Ġsign s
-ĠL ove
-Ġqu iet
-f oot
-Ġp ure
-ĠH un
-Ġf illed
-ph as
-ĠE lect
-end ment
-ĠEx pl
-Ġun able
-n s
-m o
-Ġv ast
-ob e
-Ġident ify
-app ing
-ĠCarol ina
-g ress
-Ġpro te
-Ġf ish
-Ġcircumst ances
-raz y
-ĠPh ot
-Ġb odies
-ĠM ur
-Ġdevelop ing
-ĠA R
-Ġexperien ced
-Ġsubst ant
-ĠBo ard
-es ome
-Ġdom estic
-Ġcomb ined
-ĠP ut
-Ġchem ical
-ĠCh ild
-Ġpo ol
-ĠC y
-Ġe gg
-c ons
-st ers
-Ġh urt
-Ġmark ets
-Ġconserv ative
-Ġsupp orters
-Ġag encies
-id el
-O b
-ur b
-Ġ4 3
-ĠDef ense
-y e
-ĠA p
-du le
-Ġtemper ature
-Ġconduct ed
-ĠCh ief
-Ġpull ed
-Ġf ol
-L ast
-ont o
-os is
-V ER
-D es
-ĠP an
-F irst
-Ġadv ance
-Ġlic ense
-r ors
-ĠJ on
-Ġimag ine
-Ġhe ll
-Ġf ixed
-Ġinc or
-os ite
-ĠL og
-ick en
-] :
-Ġsurpr ise
-h ab
-Ġc raft
-ol t
-ĠJ ul
-Ġd ial
-Ġrele vant
-Ġent ered
-Ġlead s
-ĠA D
-ĠCle an
-Ġpict ures
-ess or
-Ġal t
-Ġpay ing
-P er
-ĠMark et
-Ġupd ates
-am ily
-ĠT ype
-ĠH ome
-Ġ5 5
-semb ly
-rom e
-8 3
-Ġgreat est
-Ġhe ight
-Ġhe av
-ain ts
-Ġlist en
-as er
-ĠS H
-Ġcap able
-ac le
-Ġpers pect
-in ating
-Ġoff ering
-ry pt
-ĠDe velop
-ab in
-r c
-Ġbr ight
-al ty
-ar row
-Ġsupp l
-ind ing
-ack ed
-gy pt
-ĠAn other
-p g
-ĠVirgin ia
-ĠL u
-Ġpl anned
-Ġp it
-Ġswe et
-T ype
-ĠD i
-Ġtyp ically
-ĠFranc isco
-Ġpro spect
-ĠD an
-Ġte en
-re es
-Ġsc hed
-Ġh ol
-Ġsc r
-Ġlot s
-l ife
-Ġnews p
-Ġfor get
-ĠN one
-ĠM iddle
-ĠR yan
-ed d
-Ġse vere
-Ġsu it
-ll er
-9 3
-Ġcor respond
-Ġexpl os
-u ations
-Ġfl ag
-g ame
-r id
-Ġpr in
-ĠD ata
-Ġde ploy
-ĠEn ter
-su it
-gh an
-ĠM en
-Ġthough ts
-Ġmat ters
-Ġad apt
-ĠA ri
-Ġf ill
-Ġfor th
-Ġs am
-Ġ4 1
-Ġpay ment
-ĠH or
-Ġsp ring
-du c
-Ġl osing
-Ġbring ing
-F O
-al a
-Ġdist ribution
-he red
-b our
-ĠIsrael i
-om a
-Ġcomb ination
-Ġpl enty
-V E
-C an
-ĠH aw
-Ġper man
-ĠSpe cial
-Ġto w
-Ġsee king
-Ġexam ples
-Ġclass es
-c r
-Ġbe er
-Ġmov es
-ĠI P
-ĠK n
-Ġpan el
-E ven
-Ġproper ly
-Ġr is
-Ġpl ug
-Ġestim ated
-E very
-Ġdef ensive
-ag raph
-Ġpre gn
-Ġinst it
-ĠV ict
-Ġvol ume
-Ġpos itions
-Ġl inks
-ĠPro gram
-ĠWe ek
-ag ues
-Ġtrans form
-k er
-ĠC EO
-Ġc as
-Ġopp onent
-Ġtwe et
-ĠC ode
-Ġsh op
-Ġf ly
-Ġtal ks
-Ġb ag
-Ph one
-Ġa id
-Ġpl ants
-Ġ6 5
-Ġatt orney
-ar ters
-qu est
-ĠMag ic
-Ġbeg ins
-Ġmy ster
-Ġenvironment al
-Ġst orage
-N N
-Ġm arg
-Ġs ke
-Ġmet al
-ell y
-Ġord ered
-Ġrem ained
-Ġl oved
-Ġprom pt
-Ġupd ated
-Ġexper ts
-Ġwalk ing
-Ġan cient
-Ġperform ed
-AT E
-Ġne ither
-i ency
-Ġmanufact ure
-ĠP ak
-Ġselect ed
-Ġm ine
-Ġult imately
-Ġexpl an
-Ġlab el
-ĠServ ices
-ribut ed
-Tr ump
-Ġsy n
-ĠU lt
-S C
-Ġme at
-Ġg iant
-ĠW ars
-ĠO N
-Ġad m
-Ġinter pret
-Ġeven ing
-Ġev il
-ĠB oston
-ĠW ild
-Ġ Ã
-ĠBit coin
-ĠAm azon
-D r
-ĠIn formation
-Ġobvious ly
-Ġadv anced
-Ph oto
-ol ar
-Ġwe ather
-Ġsymb ol
-Ġso le
-Ġpot entially
-ost er
-Ġorig inally
-m un
-3 00
-az e
-ess ions
-Ġde ck
-Ġst ood
-Ġyou th
-ĠB ern
-R ep
-ĠT est
-Ġbas ically
-ot ic
-Ġinvol ve
-ol it
-ly n
-S ee
-Ġair craft
-Ġconf irm
-E W
-Ġmess ages
-ĠRich ard
-Ġk it
-Ġpro hib
-Ġv ulner
-is ters
-Ġexist ence
-Ġturn ing
-ĠS P
-Ġdes ire
-Ġfl at
-Ġm ent
-se ason
-ang es
-Ġneighbor hood
-ĠL ake
-AT ION
-Ġpoint ed
-b ur
-Ġinn ov
-uc ks
-U L
-Ġprofess or
-Ġexp ressed
-A B
-ic ious
-Ġ200 2
-ĠDe v
-Ġs ession
-Ġb are
-s en
-Ġdis s
-ĠC ath
-ĠP ass
-ĠP oint
-Ġdo ctor
-or row
-ail ed
-ĠR ub
-ĠD C
-ĠChar l
-p erson
-Ġwrit er
-igh ters
-ure au
-Ġob lig
-Ġrecord ed
-Ġbro ke
-Ġord ers
-il ty
-Ġmot ion
-in ity
-l aw
-ad ium
-Ġimm igration
-Ġcontr ast
-Ġb att
-Ġex cellent
-Ġtechn ical
-am i
-Ġt un
-Ġcl oud
-ĠY ear
-ge on
-Ġcre ation
-Ġstr ange
-Ġa uth
-Ġfor t
-b orn
-Ġext ent
-ĠT oday
-ĠCl ub
-Ġr ain
-Ġs ample
-Ġaccept ed
-Ġt act
-Ġf ired
-ĠS on
-Ġstand s
-Ġb oot
-Ġ4 7
-Ġstat ements
-Ġvers ions
-Ġse lling
-ound ed
-Ġ199 0
-Ġwere n
-ĠW atch
-Ġexper iment
-P ost
-Ġret ail
-ul ed
-In st
-un te
-ãĥ ¼
-Ġdep art
-Ġb ond
-i very
-om pl
-Ġre action
-ĠSyri an
-ĠP ac
-app ed
-ani el
-D P
-Ġres olution
-Ġre act
-Ġappro ved
-on om
-m ond
-ĠO ffic
--- -
-Ġrepl ace
-Ġt ack
-Ġsp ort
-Ġch ain
-Ġemer gency
-r ad
-ĠPalest in
-Ġ4 6
-Ġautom atically
-Ġrout e
-Ġp al
-Ġb anks
-ĠPar is
-ĠMed ia
-ro ad
-ic ing
-i xt
-ist ed
-Ġg rew
-Ġco ord
-ĠW here
-om in
-Ġsub s
-� �
-ĠÂ ±
-Ġcorpor ate
-Ġse lection
-n oon
-ĠRep ort
-c s
-clud ing
-ord ers
-anc he
-ĠIt s
-Ġslow ly
-ĠE gypt
-ĠA cc
-Ġcol le
-iqu es
-E X
-Ġattempt s
-ur l
-ĠC ross
-Ġfind ings
-ĠS C
-ĠO R
-Ġind ex
-ens ity
-ĠW ay
-ĠL and
-Ġsh ock
-d is
-Ġd ynam
-Ġc art
-m osp
-S ince
-i est
-ĠB oy
-Ġst orm
-ĠCont in
-201 3
-he w
-il it
-Ġess ential
-iqu id
-O ther
-ive red
-Ġreason able
-A ct
-Ġsub sequ
-ĠP ack
-ĠF ort
-Ġconsider ing
-Ġun iversity
-l og
-Ġmar ried
-Ġill ust
-ĠTr ue
-£ ı
-Ġnumer ous
-rast ructure
-Ġserious ly
-Ġrefer red
-u a
-Ġconsist ent
-on na
-ĠRe al
-ru ption
-ci ples
-Ġfact s
-9 1
-ot es
-er g
-The n
-Ġacc ompl
-N ote
-Ġre venue
-Ġpass ing
-Ġm al
-e en
-ĠY et
-Ġg ather
-ter day
-ew ork
-ĠA uthor
-P e
-Ġopt im
-Ġr ub
-Ġè £ı
-Ġun known
-st one
-Ġun ion
-ol ve
-Ġopportun ities
-Ġbrow ser
-ĠW al
-ĠC ost
-Ġreport ing
-st s
-p et
-Ġs and
-Ġsudden ly
-Ġsurpr ising
-ĠV R
-Ġsomew hat
-ĠB as
-ult ure
-iz z
-ĠC D
-Ġchalleng es
-Ġsett ings
-Ġexperien ces
-ĠF ull
-Ġcan n
-Ġrece iving
-ES T
-Ġj oint
-Ġcult ural
-Ġa st
-8 2
-as tern
-ce ived
-ĠC ru
-Ġb ull
-p ired
-am m
-Ġfac ing
-p ower
-Ġb oss
-ĠH ol
-Ġinst r
-Ġincreasing ly
-Ġsh ift
-Ġstre ets
-ĠWilliam s
-ab b
-Ġl ie
-Ġl augh
-ĠC a
-P L
-Ġadult s
-Ġcustom er
-Ġob tained
-Ġsupport ing
-ht ml
-f ire
-Ġdetail ed
-Ġpick ed
-ĠR ight
-ld er
-E E
-st ood
-ĠK im
-Ġw ire
-Ġs ight
-Ġdevelop ers
-Ġpers ons
-Ġs ad
-Ġc up
-Ġwar ning
-Ġboy s
-l ong
-Ġb ird
-f o
-Ġw al
-Ġobserv ed
-Ġz one
-iven ess
-Ġch annel
-c ript
-Ġref used
-ĠAg ain
-Ġsu c
-Ġspokes man
-ĠRe f
-r ite
-ou ston
-ãĥ ³
-ĠS her
-Ġact s
-ĠN ame
-Ġstrugg le
-ar ry
-omet imes
-Ġdisc rim
-H T
-Ġcateg ory
-Ġreal ize
-Ġemploy ee
-ĠAf ghan
-en ger
-Ġgun s
-ĠSte ve
-ĠM ot
-ĠO l
-ok ed
-Ġth ick
-Ġfair ly
-ill y
-Ġsur ve
-ĠM at
-we ight
-â Ķ
-Ġtro ops
-Ġag ents
-Ġbatter y
-Ġmot iv
-Ã ¡
-S ec
-d en
-o very
-L S
-Ġfl u
-Ġconf ident
-ĠO per
-Ġem pty
-Ġp hen
-Ġse ctor
-Ġexc ited
-Ġrem ote
-ap h
-o en
-Ġdestroy ed
-Ġmor al
-ĠH P
-ĠR on
-Ġd ress
-ĠB at
-Ġl it
-ĠM S
-Ġa f
-H L
-r um
-is ms
-Ġshould n
-Ġsym pt
-ĠTor onto
-het ic
-Ġcar bon
-Ġinstall ed
-Ġviol ent
-Ġsol ar
-j a
-Ġpract ices
-Ġr ide
-ĠP enn
-Ġimpro ved
-Ġaud io
-Ġbehav i
-ĠP S
-Ġe ating
-D ata
-ĠRe view
-p ass
-cl aim
-u ated
-ang ers
-c hen
-Ġproper ties
-Ġany where
-An other
-Ġbl ow
-ĠJack son
-Ġp roud
-Ġplan e
-l ines
-Ġsqu are
-Ġpro of
-ans as
-Ġtalk ed
-m akers
-Ġs ister
-Ġhold s
-Ġres ident
-Ġ= =
-Ġresist ance
-Ġspl it
-Ġpro secut
-Ġconf idence
-res ents
-Ġcut s
-Ġexcept ion
-Ġz ero
-Get ty
-Ġcop yright
-Ġtot ally
-orm al
-ific ations
-ĠAustral ian
-Ġs ick
-Ġ1 50
-Ġhouse hold
-Ġfe es
-Ġdri vers
-og en
-ĠN Y
-Ġnecess arily
-Ġregul ations
-ear ing
-s l
-Ġperspect ive
-c are
-ic ial
-H is
-Ġesc ape
-Ġsurpr ised
-ĠV an
-ur rent
-Ġv ac
-8 1
-ĠTh us
-Ġem phas
-ĠCh ampions
-ĠI ce
-Ġn arr
-Ġhead s
-Ġca using
-b el
-f ortunately
-ĠM a
-Ġtarg ets
-ci pl
-Ġafter noon
-Ġadd s
-ĠMay be
-ĠF our
-ess ed
-ple te
-Ġus ual
-ch o
-ing u
-Ġwith d
-ĠE nergy
-ĠE conom
-O O
-Ġart icles
-Ġinj ured
-Ġman age
-Ġexpl ains
-Ġdi agn
-R ec
-at ures
-Ġlink ed
-Ġdiscuss ed
-Ġexpl o
-Ġocc asion
-ath an
-Ġopp osite
-Ġfac es
-Ġden ied
-ĠK night
-Ġn ut
-Ġapprox imately
-Ġdisapp oint
-onym ous
-ĠB est
-ĠL o
-ĠH y
-ĠA ff
-Ġvot ing
-an while
-ĠII I
-Ġinstit utions
-ag ram
-ĠD aily
-Ġdr ag
-Ġnear by
-Ġgu ilty
-Ġcon ver
-P re
-s hip
-Ġre ward
-Ġphilos oph
-ĠS S
-u gh
-Ġapp s
-f riend
-Ġu pper
-Ġad vert
-Ġs now
-Ġfr ust
-Ġour selves
-F r
-ĠD ie
-amp ion
-Ġdis miss
-Ġc ere
-Ġsign al
-f rom
-Ġ ).
-Ġ5 2
-Ġcr imes
-it ors
-est ival
-use um
-Ġcoun cil
-ĠS aud
-M ay
-ĠG un
-ic ian
-et her
-Ġsu fficient
-ĠH en
-so le
-Ġhistor ical
-ĠF ar
-ĠT urn
-Ġp in
-Ġsuc ceed
-m at
-ly mp
-Ġtrad ition
-ĠO k
-Ġc ro
-Ġdesc ription
-al le
-Ġsk y
-T e
-Ġwide ly
-Ġw ave
-Ġdefin ition
-ĠJew s
-Ġcy cle
-Ġref ere
-Ġbr ings
-us al
-Ġal ive
-Ġfrequ ently
-Ġint ention
-ĠCont rol
-l v
-y stem
-Ġpriv acy
-g ent
-ren ce
-ĠQu est
-ĠChrist mas
-Ġr ail
-Ġco oper
-Ġtest ed
-ĠC apt
-as ks
-Ġcomfort able
-Ġdel ivered
-sc ape
-Ġdep th
-ĠG OP
-Ġwrit es
-Ġass ets
-Ġsa v
-im ents
-Ġtrans ition
-Ġart ist
-ĠL ook
-Ġl ob
-Ġcomp onents
-ar ity
-Ġwalk ed
-Ġro ot
-Ġparticip ants
-Ġnot iced
-Ġres c
-Ġn av
-ĠAd minist
-d a
-ut ral
-pl ate
-Ġimport ance
-Ġass ert
-ious ly
-c ription
-Ġinj uries
-ĠChe ck
-Ġregist ered
-Ġint ent
-Ġmiss ed
-ograph ic
-Ġsent ence
-oun ter
-Ġassist ance
-ev in
-Ġdat abase
-Ġbuild ings
-Ġclass ic
-Ġth inks
-ĠOh io
-P r
-ug g
-Ġfe e
-p an
-Ġeffect ively
-Ġfac ility
-Ġbe ar
-Ġch apter
-Ġdog s
-ĠCol umb
-Ġl atter
-it ial
-Ġad mitted
-T V
-ĠGe org
-Ġpost s
-\ \
-Ġlawy er
-Ġequ ival
-Ġm and
-Ġcontro lled
-ĠW alk
-ĠAnd rew
-Ġmen u
-am ental
-Ġprotect ed
-v a
-Ġadminist r
-or al
-Ġre in
-ĠS ar
-Ġamount s
-Ġn ative
-ĠM oon
-Ġrep resents
-Ġab andon
-Ġcarry ing
-Ġt ank
-m ary
-Ġdecl ared
-T ube
-Ġh at
-Ġpun ish
-el lect
-m es
-Ġun iverse
-ĠR od
-ph y
-Ġinf rastructure
-Ġ5 1
-Ġopp osed
-ow nt
-c a
-ĠM ake
-Ġhard ware
-Ġco ffee
-R el
-b al
-w orld
-ĠS af
-ĠSe a
-in als
-Ġown ed
-Ġh all
-ers ion
-Ġdescrib e
-ĠP ot
-Ġport ion
-Ġat mosp
-Ġgovern ments
-Ġdep ending
-Ġoff ense
-Ġtr ick
-aw a
-ĠL ine
-ĠV is
-ĠH ard
-ĠOr ig
-ĠCl ick
-Ġdes k
-ĠVal ley
-ĠS ov
-Ġmov ies
-Ġrem ark
-Ġm ail
-Ġcons cious
-Ġrul ing
-ĠR ights
-Ġmed ic
-he nt
-ĠW omen
-> <
-Ġrepl aced
-ĠP rem
-ĠTh anks
-Ġre new
-ĠB all
-if orm
-Ġsh ots
-C omm
-Ġar med
-Ġconst ant
-Ġt aste
-Ġreal ized
-Ġbu ff
-Ġm o
-Ġeffic ient
-M ost
-or ation
-if ies
-Ġcommun ication
-Ġfl ood
-Ġconsequ ences
-Ġany way
-ig g
-ĠG M
-ĠTh ank
-Ġ iron
-Ġev olution
-ĠC op
-tw itter
-Ġ9 5
-Ġrelationship s
-ad el
-ĠYou ng
-Ġpropos al
-ay ers
-uild ing
-ĠH ot
-OR E
-c os
-Ġcoll abor
-P G
-ax y
-Ġknow ing
-Ġsupport s
-ow ed
-Ġcontrol s
-Ġmere ly
-um er
-Ġath let
-Ġf ashion
-p ath
-Ġg ift
-Ġer a
-AN D
-Ġkind s
-ĠKore an
-Ġleg it
-ul ous
-Ġess entially
-Ġthe rap
-n ic
-Ġsuff ered
-Ġh ur
-Ġprom ise
-Ġex cess
-Ġover w
-Ġpr ime
-ĠH ouston
-er ry
-ĠM s
-R S
-201 2
-Ġst ores
-ĠO lymp
-Ġj ourney
-Al though
-S ub
-ĠE duc
-ĠCh apter
-Ġrequest s
-Ġconsum ers
-Ġt iny
-Ġis ol
-ĠF air
-b a
-ĠY OU
-Ġcr ash
-ce ler
-Ġemot ional
-Ġgood s
-Ġelect ed
-Ġmod er
-ĠLin ux
-Ġbl ocks
-Ġis land
-ĠSoc iety
-Ġelect ions
-Ġbroad cast
-Ġche ap
-Ġn ations
-Ġse asons
-4 00
-Ġwas te
-ĠS at
-Ġfield s
-em ploy
-Ġprof ile
-Ġauth ors
-AL L
-ĠG ra
-w est
-ĠT y
-Ġdeath s
-Ġv acc
-Ġfor med
-Ġd u
-Ġon going
-ĠMuslim s
-el f
-ig ure
-Ġass ume
-ĠUkrain e
-w ater
-Ġco ast
-Ġvot ed
-g or
-ĠA S
-ĠMich igan
-az a
-ĠAr m
-i ro
-Ġf lex
-as ters
-' '
-Ġwel come
-ar l
-Ġloc ations
-ig ation
-ĠF il
-Ġbu ying
-Ġarch itect
-Ġhard er
-ĠC ub
-Ġinter face
-Ġrestaur ant
-Ġdisco ver
-Ġex ceed
-Ġfav our
-ger y
-Ġd uty
-Ġp itch
-ad or
-ĠM ach
-b oy
-Ġrespond ed
-Ġext ended
-her s
-M any
-ra id
-if er
-ĠIn s
-S er
-Ġmed ium
-s he
-ĠS ports
-Ġmag azine
-ut ation
-Ġlim its
-ĠG all
-Ġex ternal
-raz il
-Ġyoung er
-t le
-Ġrem ind
-ĠC ON
-Ġimmedi ate
-Ġh idden
-Ġvol unte
-Ġsim pl
-od cast
-Ġph ase
-d r
-Ġpl ot
-Ġexp osure
-R I
-og rap
-v in
-an ish
-ĠAc ad
-ĠEng ine
-Ġexp ansion
-ĠP ay
-Y our
-Ġpus hed
-ĠE ll
-ĠHe ad
-Ġmarket ing
-ĠA C
-k et
-Ġh its
-Ġg ro
-ĠA ge
-ĠSc ot
-] [
-Ġst im
-Ġi Phone
-Ī Ĵ
-Ġn arrow
-ĠGet ty
-ĠTur key
-Ġperfect ly
-Ġen able
-ut ch
-Ġprec ise
-Ġreg ime
-Ġsh if
-Ġcomp ens
-g un
-d iv
-Ġch osen
-ĠK en
-An y
-Ġtre es
-Ġrecomm ended
-ĠR en
-u able
-ĠH T
-F ollow
-E G
-ĠH and
-ĠK enn
-Ġarg uments
-Ġex ists
-Ġb ike
-ĠCons erv
-Ġbre aking
-ĠG ar
-Ġc razy
-Ġvirt ual
-ay lor
-ix el
-Ġ19 80
-Ġper mission
-ĠSer ies
-Ġconsum er
-Ġclose ly
-c alled
-Ġ5 4
-Ġhop es
-Ġar ray
-ĠW in
-ĠLab our
-Ġsp ons
-ĠI re
-Ġp ow
-Ġread ers
-Ġemploy ment
-Ġcreat ure
-Ġresult ing
-Ġaccur ate
-Ġmom ents
-Ġarg ued
-Ġp ed
-D uring
-Ġ5 3
-ĠT al
-Ġs ought
-Ġsuff ering
-Ġ icon
-le e
-Ġ( $
-al ian
-Â °
-Ġp ra
-Ġbon us
-( "
-k o
-Ġact ing
-D E
-f all
-Ġcompar ison
-Ġsm ooth
-ĠN AS
-u pp
-ĠJose ph
-ep ing
-ĠT ake
-ĠM id
-Ġs ending
-f ast
-ĠF all
-Ġdeal ing
-us er
-ĠOr gan
-C o
-Ġatt ached
-Ġse es
-% .
-Ġtyp ical
-AR T
-Ġfind s
-ĠAs ia
-um in
-ĠC ore
-ĠE nt
-in ent
-u ce
-ĠBl ood
-ĠN ever
-Ġem ails
-Ġhigh light
-Ġconf ront
-at us
-ut ed
-Ġun us
-Ġtop ic
-ĠAd am
-Ġb le
-at i
-Ġunder stood
-S et
-st ruct
-T P
-Ġm ob
-a a
-ĠSt art
-pect ed
-se ll
-Ġded icated
-ĠC A
-u an
-Ġsong s
-esc ription
-Ġte ch
-Ġr ape
-Ġas ide
-Ġgr ant
-Ġ5 6
-s ub
-Ġarg ue
-Ġcont aining
-Ġsche dule
-Ġliber al
-Ġpublic ly
-Ġheav ily
-ĠU t
-in er
-ĠS ection
-ĠC are
-we et
-l s
-D is
-âĶ Ģ
-ĠF ollow
-B ack
-ĠI T
-Ġb es
-j i
-ĠH it
-est ed
-Ġevery body
-ĠSw ed
-Ġfem in
-Ġfac ilities
-Ġcon ven
-C omp
-ĠO S
-c ore
-Ġan x
-Ġdiv ision
-ĠC am
-ĠSt an
-m ates
-Ġexpl ore
-pl om
-Ġsh ares
-pl oad
-an es
-Ġide al
-et ers
-ĠB ase
-Ġpl astic
-Ġdist inct
-ĠNet work
-ĠSe attle
-Ġtrad ing
-ens us
-int end
-Ġex hib
-Ġinit ially
-ĠF ood
-Ġthous and
-ĠBus iness
-act er
-Ġpar agraph
-Ġrough ly
-Ġw ww
-Ġcreat ive
-ĠCon f
-Ġconsum ption
-Ġfil ms
-ag an
-Ġob tain
-Ġt all
-Ġt or
-Ġacknow led
-Ġg rown
-al o
-K E
-Ġ4 00
-end ers
-t aining
-U G
-Ġsu icide
-Ġwat ched
-ĠL ist
-al i
-re hens
-Ġsurround ing
-Ġp ip
-Ġf lying
-ĠJ ava
-ord an
-Ġserv ing
-in ations
-p ost
-Ġsh o
-A v
-Ġj ail
-z y
-Ġ199 9
-Ġ< /
-Ġliter ally
-ĠS ir
-Ġexp osed
-Ġl ies
-st ar
-Ġb at
-Ġear ned
-ĠD ig
-Ġspec ified
-ĠSe ason
-Ġdeg rees
-Don ald
-Ġcent re
-Ġsh aring
-Ġwin ter
-ĠC O
-C he
-Ġ Î
-M P
-Ġun w
-Ġfew er
-ĠM ir
-Ġsomew here
-ĠK ey
-Ġattack ed
-ĠK ir
-Ġdom ain
-Ġstrong er
-Ġ9 9
-Ġpen alty
-I d
-Sc ript
-Ġdecl ined
-Ġne ck
-Ġfra ud
-Ġcur rency
-Ġr ising
-R C
-âĢ¦ âĢ¦
-H z
-Ġt ab
-Ġtal ent
-n am
-ĠN BA
-Ġvill age
-Ġleg s
-ĠN ext
-E d
-Ġac id
-Ġhy d
-8 00
-Ġinvol ving
-ĠIm age
-ĠBe fore
-F l
-Ġyes terday
-S ource
-Ġterror ist
-Ġsu p
-Ġsy nt
-ĠSaud i
-Ġw est
-Ġr u
-b urg
-Ġvis ible
-Ġstru ck
-r ison
-Ġaw esome
-Ġd rawn
-Ġansw ers
-ĠG irl
-ĠR am
-Ġthreat s
-Ġdef eat
-os it
-Ġv ent
-atur ally
-Americ an
-end a
-ĠH oly
-Ġr um
-% ,
-c ase
-ĠHist ory
-ĠYou Tube
-Ġsit uations
-ĠD NA
-S te
-Ġsa ved
-It em
-Ġrec ip
-olog ist
-Ġfac ed
-Ġel ig
-O nce
-ĠL i
-u h
-Ġmist ake
-ĠDiv ision
-ĠB ell
-Ġsympt oms
-Â ®
-Ġdom in
-Ġfall ing
-Ġend ing
-as hes
-Ġmat ches
-ĠOn line
-Ġexplan ation
-D ef
-red it
-Ġany more
-ĠT otal
-ĠF OR
-us hed
-Ġlet ters
-Ġris ks
-ĠO K
-Ġreported ly
-: \
-Ġpl ate
-Ġsubject s
-Ġattempt ed
-if ier
-ian a
-Ġunlike ly
-ĠTh ough
-um a
-ĠIn vest
-ĠPr in
-ic an
-ĠD ar
-ĠColor ado
-au g
-Ġve get
-a os
-ri a
-Ġshe l
-Ġmark ed
-Ġ( )
-Ġsp r
-p o
-ĠL ink
-Ġdef e
-ĠJ r
-Ġthem e
-Ġpass ion
-ĠP en
-Ġinf o
-iz er
-Ġsh it
-ĠC ivil
-ap se
-c re
-Ġpo ly
-Ġcomp onent
-ĠChar les
-ĠIre land
-ĠPro v
-Ġdo ctors
-Ġgr anted
-Ġpain t
-Ġhon or
-Ġsm oke
-Ġpay ments
-Ġprim arily
-ĠKing dom
-r ich
-ate ll
-Ġde als
-Ġsched uled
-Ġfund amental
-Ġprote in
-Ġnewsp aper
-Ġcl ients
-yth on
-ĠD ate
-h us
-Ġfeed back
-Ġstret ch
-Ġc ock
-Ġhot el
-ĠQue en
-Ġsu gar
-Ġj u
-Ġmil k
-Ġappro val
-ĠL ive
-Ġequival ent
-ef ully
-Ġins ert
-z ona
-Ġext ension
-d ri
-J ohn
-Ġacc omp
-S m
-ĠF und
-Ġconst antly
-Ġ` `
-Ġgener ated
-ĠA ction
-ĠP sych
-ĠT ri
-Ġrecogn ize
-Ġv ary
-ph a
-ĠR a
-d f
-et ch
-ĠSov iet
-Tw o
-Ġpattern s
-Ġprof ession
-an ing
-T ime
-ĠL im
-Ġcol ors
-ĠA z
-ĠT R
-Ġinf ect
-Ġphen omen
-Ġshe ll
-Al so
-Ġput s
-Ġdel ivery
-Ġbro wn
-Ġprocess ing
-Ġlight s
-ess age
-ĠBro ok
-ĠA ud
-l ation
-Ġindust rial
-L ike
-ĠB razil
-rou s
-ES S
-ĠL uc
-Ġsome how
-Ġ8 5
-Ġpro port
-Ġpolit icians
-Ġindic ate
-Ġh ole
-Ġtechn iques
-Ġcompet itive
-Ġph r
-Ġv o
-ist ent
-ĠD ream
-Ġcamp us
-Ġaspect s
-Ġhelp ful
-Ġsh ield
-or se
-Ġtrig ger
-m al
-Ġ5 8
-Ġt ort
-Ġperson ally
-Ġt ag
-Ġkeep s
-ĠV ideo
-Ġben ch
-Ġg ap
-a ire
-Ġe ast
-Ġrec overy
-per ial
-Ġprof it
-ĠM ic
-Ġ5 7
-Ġcol on
-Ġstrong ly
-st yle
-Ġalleg ations
-h an
-Ġrep orters
-j o
-r ine
-arg et
-and al
-Ġ0 3
-Ġfl ash
-tr ans
-Ġstr ict
-Ġpark ing
-ĠPak istan
-Ġl i
-Ġwe ird
-ĠE ric
-Ġreg ions
-ĠJ un
-Ġint ellect
-ĠW H
-od ing
-rib utes
-up id
-ĠT it
-Ġf inger
-or ia
-Ġe lev
-ĠF ield
-Ġcon clusion
-; ;
-Ġfeel ings
-Ġext ensive
-Ġm ixed
-Ġne uro
-v y
-Ġhar ass
-ĠC irc
-ou ch
-Ġterrit ory
-Ġsuccess fully
-M ar
-Ġing red
-Ġoverw hel
-Ġl ayer
-V iew
-Ġall ies
-ill ance
-ĠTh ree
-Ġb unch
-Ġnorm ally
-Ġnet works
-Ġsac r
-ĠC IA
-b les
-Ġch ose
-Ġopp onents
-Ġregard less
-Ġfr anch
-Ġpre f
-ĠP o
-Ġbr idge
-ann a
-ĠSil ver
-Ġw age
-p age
-ri or
-Ġrad ical
-ĠL ittle
-Ġman ip
-Ġsecret ary
-Ġg ang
-D R
-F A
-Ġdec ent
-ĠSp irit
-Ġun cle
-ĠDevelop ment
-Ġinvest ors
-Ġwall s
-Ġpub lish
-Ġgener ate
-iss ions
-c ar
-Ġprom ote
-Ġcut ting
-Ġche st
-Ġdrink ing
-Ġcollect ed
-Ġ7 2
-Ġhop ing
-Ġem br
-gor ith
-Ġwar ned
-Ġinstruct ions
-O G
-ĠD id
-ĠAg ency
-Ġg ear
-Ġcritic ism
-ĠF urther
-Ġut il
-ann y
-R ed
-Ġcoun sel
-ĠAs ian
-Ġredu ction
-p ool
-Ġteach ing
-Ġdeep ly
-i y
-Ġestim ates
-Ġcho ices
-Ġperman ent
-in em
-ke l
-Ġf asc
-p se
-f ile
-ĠL ow
-ĠP erson
-Ġt ournament
-st al
-Ġm el
-U ST
-ĠR ay
-az i
-V al
-Ġcont ained
-ĠH olly
-Ġw ake
-Ġreve al
-Ġprocess es
-ĠIS IS
-Ġ0 9
-Ġbl ind
-Ġste el
-ĠB ad
-Ġcare fully
-app y
-ro it
-Ġg aming
-Ġhous es
-ĠC oll
-Ġtr uck
-er m
-Ġsc ored
-Ġocc as
-ret urn
-b ound
-v ar
-Ġsh arp
-Ġaf raid
-ĠE X
-am ber
-c ific
-Ġsche me
-N C
-ĠPol it
-Ġdecl ine
-Ġ199 8
-Ġpus hing
-Ġposs ession
-Ġpriv ile
-Ġteacher s
-Ġy ield
-H A
-ĠDav is
-it led
-#### ####
-Ġr ig
-ĠD aniel
-ac on
-Ġh ide
-ut en
-Ġcolle agues
-Ġprin ciples
-Ġl oud
-Ġs in
-ĠDem on
-Ġst one
-Ġ0 2
-Ġt aught
-Ġter rible
-Ġst uck
-ĠPol icy
-te en
-Ġimplement ation
-ĠB BC
-ĠAP I
-Ġwhe el
-all as
-Ġch ampions
-ol ars
-play er
-Ġrepeated ly
-ĠSt ill
-Ġlik es
-ast y
-es ter
-ĠCath olic
-R L
-Ġb ath
-Ġno ise
-t itle
-Ġn orthern
-P art
-Ġmag n
-Ġf ab
-ĠAs h
-Ġdis pl
-Ġtick et
-Ġm urd
-Ġalong side
-ĠMus ic
-Ġr iver
-ĠSte el
-ĠC L
-ĠPl ayer
-ĠM ult
-ow ing
-re p
-s ize
-Ġt ur
-ĠGeorg ia
-isc al
-ra ction
-Ġc able
-Ġ5 9
-Ġw ins
-Ġup coming
-Ġsurv ive
-Ġins pired
-ĠEduc ation
-Ġstat istics
-ĠF oot
-iam i
-Ġy ellow
-ĠP age
-. -
-ĠH as
-Ġur ban
-Ġa x
-es sel
-\ "
-Ġquarter back
-Ġreg ister
-ĠLab or
-Ġab ilities
-ĠF amily
-Ġvar iable
-ĠPr ice
-Ġcont em
-Ġth in
-ĠE qu
-d ata
-Ġg otten
-Ġconst it
-Ġas ks
-Ġt ail
-Ġexc iting
-ĠE ffect
-ĠSp anish
-Ġencour age
-ins on
-ĠA h
-Ġcommit ment
-C S
-Ġr ally
-Ġ: :
-Ġsubs id
-Ġsp in
-Ġcapt ured
-201 8
-Ġinn oc
-Ġalleged ly
-ĠC ome
-Ġart ists
-ĠN umber
-Ġelect ronic
-Ġreg ional
-ap es
-Ġw ra
-Ġmy th
-pr ise
-ĠM iller
-ĠC reat
-ĠEp isode
-b ell
-Ġdirect ed
-Ġext ract
-Ġs orry
-Ġv ice
-ag ger
-ĠSu pport
-Ġ6 6
-ĠI ron
-Ġwonder ful
-Ġg ra
-N et
-ion e
-E ng
-Ġsh ips
-ik es
-ĠK evin
-it ar
-Ġactiv ists
-tr ue
-ĠAri zona
-ent h
-ĠDes pite
-ĠS E
-Ġha bit
-ern el
-Ġin qu
-Ġab ortion
-Ġv oid
-Ġexpl icit
-Ġeng aged
-Ġang ry
-Ġr ating
-Ġfr ag
-b ro
-ick ing
-d ev
-Ġwor ried
-Ġob ser
-Ġap artment
-ĠG T
-Ġest ate
-ĠConst itution
-em on
-ĠS now
-Ġcount y
-Ġdis ag
-ĠStep hen
-Ġimm igrants
-w ind
-ĠN ations
-Ġfol ks
-O ut
-Ġg all
-Ġtarget ed
-Ġst ead
-ĠB on
-ĠL ib
-Ġinform ed
-Ġ12 0
-ch ain
-idel ines
-or ough
-Ġdri ven
-Ġregular ly
-Ġbas ket
-Ġprinc iple
-oc ument
-Ġst un
-ib ilities
-ĠRom an
-ĠAb out
-Ġal ert
-Ġdemocr acy
-Ġrepresent ed
-H S
-c ers
-p arent
-Ar t
-p ack
-Ġdi plom
-re ts
-ĠN O
-Ġcapt ure
-ĠAd v
-Ħ ¢
-Ġannounce ment
-ĠL ear
-Ġh ook
-Ġpur s
-ĠS uch
-ĠC amer
-Ġrefuge es
-ĠV e
-P ol
-Ġrecogn ized
-l ib
-Ġhad n
-A ss
-Ġpil ot
-us hing
-Ġreturn ing
-Ġtra il
-ĠSt one
-Ġrout ine
-Ġcour ts
-Ġdes per
-Ġfriend ly
-ĠIt aly
-Ġpl ed
-Ġbreat h
-Ġstud io
-N S
-Ġimp ressive
-ĠAfghan istan
-Ġf ing
-Ġd ownt
-ink ing
-ĠR og
-i ary
-col or
-se x
-ar on
-Ġf ault
-ĠN ick
-D own
-ĠR ose
-ĠS outhern
-X X
-is odes
-L ist
-6 00
-Ġout come
-er r
-Ġelse where
-Ġret ire
-Ġp ounds
-ĠGl obal
-Pe ople
-Ġcommun ications
-Ġlo an
-Ġrat io
-ĠEm pire
-Ġg onna
-Ġinv ent
-D F
-Ġ19 70
-ĠComm on
-p at
-Ġprom ised
-Ġd inner
-ĠH om
-Ġcreat es
-Ġoper ate
-ver ty
-ĠJ ordan
-et ime
-Ġsust ain
-R eg
-Ġincred ible
-im a
-Ġwar rant
-Ġm m
-A tt
-Ġlaw suit
-Ġreview s
-it ure
-ĠS ource
-l ights
-ĠF ord
-Ġ6 3
-g roup
-st ore
-Ġfeat ured
-Ġfore ver
-Ġpo verty
-ĠP op
-ĠC NN
-az z
-ab is
-ach ing
-Ġl aid
-ĠSu pp
-Ġfil ter
-en a
-ĠCommun ity
-Ġcreat ures
-u ction
-ĠR oyal
-Ġassoci ation
-ĠCon nect
-ĠBr ad
-âĸ Ī
-l ers
-the re
-ĠG i
-Ġval uable
-AC K
-ĠT aylor
-Ġl iquid
-ĠAtt orney
-ĠCar l
-ĠF inal
-ag a
-ĠWil son
-B ecause
-ĠProf essor
-ak a
-Ġincred ibly
-r ance
-! )
-R ef
-s k
-Ġsol utions
-Ġatmosp here
-Ġbl ame
-um es
-ĠN ob
-C A
-um ps
-r ical
-ĠPut in
-ĠD est
-or ic
-ĠP A
-Ġrespect ively
-w an
-Ġfif th
-â Ħ¢
-ĠC ry
-Ġgovern or
-res ident
-Ġpurch ased
-Ġh ack
-Ġint ense
-ob s
-Ġorig in
-Ġdef ine
-Ġcare ful
-** *
-Ġshould er
-Cl ick
-Ġt ied
-Ġdest ruction
-ou red
-Ġno body
-Ġh o
-ĠEx per
-Ġt ip
-" ;
-Ġtechn ique
-Ġj ur
-ĠP ok
-b ow
-Ġleg end
-Ġacc ord
-Ġbus y
-ĠInt el
-Ġh ang
-ak i
-. ]
-âĢĶâĢĶ âĢĶâĢĶ
-Ġsur gery
-Ġrep rodu
-Ġun iform
-Ġscen es
-c ode
-Ġ6 2
-l isher
-ĠH ave
-ph ia
-Ġcry pt
-Ġrec on
-Ġsc ream
-Ġadop ted
-Ġsc ores
-N e
-ĠIt alian
-in cluding
-B O
-Ġindic ated
-Ġent ertain
-G u
-T ext
-i el
-Ġtw enty
-Ġeng age
-off s
-ĠPac ific
-Ġsm ile
-Ġperson nel
-Ġto ler
-Ġdo ors
-Ġt one
-Ġmach ines
-Ġent ering
-ten ance
-C O
-ĠJer sey
-Ġfore st
-Ġhor se
-Ġcompl aint
-ĠSpr ing
-y o
-ĠPl us
-ed ing
-ĠRet urn
-qu arters
-ial s
-c ow
-Ġacad emic
-Ġf ruit
-Ġ199 6
-og ether
-Ġw ine
-Ġpur su
-ĠSte ven
-Ġlic ens
-Wh o
-Ġclot hes
-re ction
-Ġsqu ad
-Ġst able
-Ġr aw
-z ens
-St ar
-ut ies
-anc er
-Ġke ys
-ĠM u
-Ġcompl icated
-ig er
-ĠTe xt
-Ġabs or
-Ġ6 8
-Ġfun ny
-Ġrel ief
-ĠL ew
-ĠC ook
-Ġch art
-Ġdraw ing
-G E
-Ġmod ule
-ĠB ull
-I LL
-Ġs alt
-0000 0000
-il le
-Ġres ource
-aw ay
-adel phia
-ĠB ru
-Ġ6 7
-Ġsome body
-Ġparticip ate
-Ġro se
-we red
-Ġmus cle
-Ġcons ent
-Ġcontin uing
-ĠGuard ian
-ĠOr der
-reg on
-Ġre ar
-Ġprov ision
-Ġlik ed
-ri ent
-Ġb ra
-Tr ans
-Ġmeet ings
-Ġto x
-Ġcon vent
-Ġaut o
-Ġrec ording
-ĠSo ft
-00 1
-ĠR oll
-Ġprogram ming
-Ġp ic
-Ġprov ed
-Ġst ab
-ĠA st
-Ġca ption
-ul ating
-ĠAtt ack
-Ġnew ly
-Ġ199 7
-f r
-Ġdis cipl
-ĠGree k
-Ġed ition
-ĠDo es
-ĠB ox
-if le
-ack et
-Ġpass es
-Ġgu est
-Ġac celer
-it als
-U D
-Ġaut hent
-ĠR est
-ov al
-t a
-u ine
-Ġarm or
-ĠT own
-Ġcomp at
-Ġinc hes
-Des pite
-Ġass ign
-he rent
-Ġprep are
-ĠM eg
-oc key
-Ġdep ends
-Ġtrack s
-w atch
-Ġl ists
-ĠN orthern
-Ġal ter
-re c
-ĠE astern
-Ġcond em
-Ġevery where
-? '
-Ġaff ili
-Ġf ought
-": {"
-Ġm ac
-it arian
-Ġsc ope
-ĠA L
-aw s
-ar ms
-Ġqu e
-Ġenjoy ed
-nes ota
-Ġagg ressive
-ĠSt ory
-ĠI V
-Ġrec ipe
-Ġrare ly
-ĠMed ical
-val ue
-ang el
-ay ing
-omet hing
-Ġsub section
-Ġs outhern
-Ġfrequ ency
-re te
-roll ed
-ult s
-ĠN ic
-Ġbeh alf
-Ġsequ ence
-ab et
-Ġcontrovers ial
-Ġcomp rom
-Ġwork er
-Ġmain ly
-Ġal gorith
-ĠM ajor
-or ce
-g ender
-Ġorgan ized
-Ġf ake
-Ġconclud ed
-ĠE D
-ĠEx ec
-r age
-Ġch ances
-ber ry
-ĠTr ad
-Ġconfig uration
-Ġwithd raw
-Ġf ro
-ud es
-ĠBro ther
-ĠB rian
-Ġtri es
-Ġsam ples
-Ġb id
-ĠGold en
-Ġphot ograph
-if est
-ĠD O
-ĠPar liament
-******** ********
-R em
-Ġcont est
-Ġsign ing
-p x
-ĠZ eal
-âĶĢ âĶĢ
-E ar
-Ġex it
-Be fore
-ĠCor por
-n ull
-mon th
-Ġrac ial
-ott ed
-ĠV eg
-ĠRe uters
-Ġsw ord
-ps on
-ĠRom ney
-a ed
-Ġt rib
-Ġin ner
-Ġprot ocol
-ĠB i
-ĠM iami
-ever al
-p ress
-Ġsh ipping
-ĠAm endment
-ĠHow ard
-con nect
-ĠD isc
-ĠJ ac
-iam ond
-ĠThere fore
-s es
-ĠPrin cess
-ĠUS B
-ĠAn th
-Ġsurve illance
-Ġap olog
-Ġ6 1
-ow a
-Ġf ulf
-j s
-Ġl uck
-ust ed
-ĠÂ §
-n i
-Ġant icip
-em an
-Ġwin ner
-Ġsil ver
-ll a
-ic ity
-Ġunus ual
-Ġcr ack
-Ġt ies
-e z
-Ġpract ical
-Ġprov ince
-ĠPl ace
-Ġprior ity
-IC E
-Ġdescrib es
-Ġbr anch
-F orm
-ask a
-miss ions
-b i
-Ġp orn
-ĠTur k
-Ġent hus
-Ġf ighters
-Ġ0 8
-ĠDet roit
-Ġfound ation
-av id
-A re
-Ġjud gment
-cl ing
-Ġsol ve
-ĠDes ign
-W here
-hes is
-ĠT ro
-a fter
-Ġne utral
-ĠPalestin ian
-ĠHolly wood
-Ġadv is
-ĠN on
-y es
-ol is
-Ġrep utation
-Ġsm ell
-Ġb read
-ĠB ul
-ĠBe ach
-Ġclaim ing
-Ġgen etic
-Ġtechn ologies
-Ġupgr ade
-row s
-Ġdevelop er
-ĠJ osh
-ĠDis ney
-erv ed
-ip al
-Ġun ex
-Ġbare ly
-t hen
-ĠP ub
-Ġill ness
-et ary
-ĠB al
-Ġp atch
-Ġbut t
-Ġst upid
-ĠD og
-ĠD allas
-f ront
-ie ce
-Ġprot ests
-Ġch at
-oen ix
-Ġw ing
-Ġpar liament
-Ġ7 7
-ose xual
-Ġre nder
-pt ions
-ĠCo ast
-os a
-ĠG reg
-h op
-ĠMan agement
-Ġbit coin
-Ġrec over
-Ġincor por
-or ne
-ĠUs ing
-Ġpre ced
-Ġthreat ened
-Ġspirit ual
-ĠE vent
-ĠF red
-Ġadvert ising
-Ġimprove ments
-ĠC ustom
-Ġer rors
-Ġsens itive
-ĠN avy
-Ġcre am
-L ook
-Ġex clusive
-Ġcomp rehens
-Ġde leg
-Ġcon ce
-Ġrem em
-Ġstruct ures
-Ġst ored
-N D
-Ġ1 000
-U P
-ĠB udd
-A F
-w oman
-ĠAcad emy
-ð Ł
-se a
-Ġtem porary
-Ab out
-es ters
-Ġtick ets
-Ġposs ess
-in ch
-o z
-Ġl a
-Ġcontract s
-Ġun p
-Ġc ig
-ĠK at
-ult ural
-as m
-Ġmount ain
-ĠCapt ain
-St ep
-m aking
-ĠSp ain
-Ġequ ally
-Ġl ands
-at ers
-Ġreject ed
-er a
-im m
-ri x
-C D
-Ġtrans action
-g ener
-less ly
-Ġ| |
-Ġc os
-ĠHen ry
-Ġprov isions
-Ġg ained
-Ġdirect ory
-Ġra ising
-ĠS ep
-ol en
-ond er
-Ġcon sole
-in st
-Ġb om
-Ġunc ertain
-1 50
-ock ing
-Ġmeas ured
-Ġpl ain
-Ġse ats
-Ġd ict
-S L
-af e
-Ġest imate
-iz on
-at hered
-Ġcontribut ed
-Ġep isodes
-omm od
-G r
-AN T
-Ġ6 9
-G ener
-Ġ2 50
-vious ly
-rog en
-Ġterror ism
-Ġmove ments
-ent le
-oun ce
-ĠS oul
-Ġpre v
-ĠT able
-act s
-ri ors
-t ab
-Ġsuff er
-Ġn erv
-Ġmain stream
-ĠW olf
-Ġfranch ise
-b at
-Ġdem ands
-Ġag enda
-Ġdo zen
-Ġclin ical
-iz ard
-ĠO p
-t d
-Ġvis ited
-ĠPer haps
-Ġact or
-Ġde lic
-Ġcont ribute
-Ġin ject
-ĠE s
-ac co
-Ġlist ening
-Ġcon gress
-epend ent
-Ġprem ium
-Ġ7 6
-ĠIr ish
-Ġass igned
-ĠPh ys
-Ġworld wide
-Ġnarr ative
-ot ype
-m ont
-b ase
-ĠB owl
-ĠAdminist ration
-Ġrel ation
-ĠE V
-C P
-Ġco vers
-Ġ7 8
-Ġcert ific
-Ġgr ass
-Ġ0 4
-pir acy
-ir a
-Ġengine ering
-ĠM ars
-Ġun employ
-ĠFore ign
-st ract
-Ġv en
-Ġst eal
-Ġrepl ied
-Ġult imate
-Ġtit les
-d ated
-Ġj oy
-a us
-Ġhy per
-ak u
-Ġoffic ially
-ĠPro duct
-Ġdifficult y
-per or
-Ġresult ed
-rib ed
-l ink
-wh o
-~~ ~~
-ĠSpe ed
-ĠV iet
-W ind
-ĠBar ack
-Ġrestrict ions
-ĠSh are
-Ġ199 5
-ition ally
-Ġbeaut y
-op t
-Ġm aps
-ĠC R
-ĠN ation
-ĠCru z
-W ill
-Ġelectric ity
-Ġor g
-Ġb urd
-Ġviol ation
-Ġus age
-Ġper mit
-ĠCh ron
-ĠF ant
-Ġn aturally
-Ġ0 7
-Ġth rown
-ĠAw oken
-Ġal ien
-ĠHer o
-ĠK ent
-ĠR ick
-ri ke
-Ġp ace
-}, {"
-G L
-Ġpo ison
-ĠT ower
-Ġform al
-al ysis
-Ġgen uine
-Ġk il
-a ver
-Ġproced ure
-ĠPro p
-intend o
-ĠM ain
-as ant
-Ġtr ained
-G ame
-ĠL oad
-ĠM A
-Ġcru cial
-Ġle ts
-ĠF R
-Ġch ampion
-1 01
-ĠCon ference
-Ġwrit ers
-Ġconnect ions
-Ġo kay
-ir ms
-ĠR and
-Ġenc ounter
-ĠB uff
-Ġachie ved
-Ġche cks
-isc ons
-Ġassist ant
-Ġwhen ever
-ĠA ccess
-ĠU r
-b in
-Ġcl ock
-is p
-op her
-Ġb orrow
-Ġm ad
-Ġperson ality
-on ly
-IS T
-ab ama
-Ġg ains
-Ġcommon ly
-Ġter r
-Ġhyp ot
-Ġre ly
-Ġt iss
-iscons in
-Ġrid ic
-f unction
-ĠO regon
-Ġun com
-r ating
-el and
-ĠN C
-Ġm oon
-ann on
-Ġvulner able
-ut ive
-³³ ³³
-ĠRad io
-Ġw estern
-se ct
-ĠT ony
-Ġocc urs
-ĠO s
-ĠH on
-Ã Ń
-Ġv essel
-ĠScot land
-Ġdiscrim ination
-Ġsubsequ ent
-st ring
-Ġfant asy
-ĠSh adow
-Ġtest im
-W E
-it i
-r as
-Ġbo at
-Ġmar ks
-Ġord inary
-Ġre n
-Ġrepresent ative
-Ġpet ition
-Ġ7 3
-Ġad venture
-Ġign ore
-ĠPhil adelphia
-ĠS av
-V P
-Ġfact ory
-Ġt asks
-Ġdep ression
-z ed
-................ ................
-ĠSt orm
-Ġc ogn
-Ġelig ible
-Ġredu cing
-v ia
-Ġ0 5
-Ġstri king
-Ġdoll ar
-h o
-O V
-Ġinstr ument
-Ġphilosoph y
-ĠMo ore
-ĠA venue
-Ġrul ed
-ĠFr ont
-IN E
-ĠM ah
-Ġscen ario
-ĠNAS A
-Ġen orm
-Ġdeb ut
-Ġte a
-T oday
-Ġabs ence
-S im
-Ġh am
-le ep
-Ġt ables
-ĠHe art
-M I
-K e
-re qu
-V D
-m ap
-Ġchair man
-Ġp ump
-Ġrapid ly
-v i
-Ġsubstant ial
-E P
-d es
-ch ant
-ili pp
-ĠS anta
-ri ers
-anche ster
-L oad
-ĠC ase
-Ġsa ving
-Ġ7 4
-ĠA FP
-er ning
-oun ced
-ĠMin nesota
-ĠW as
-Ġrec ru
-Ġassess ment
-ĠB ron
-U E
-Ġdynam ic
-Ġf urn
-ul ator
-Ġprop ag
-h igh
-Ġacc ommod
-Ġst ack
-ĠS us
-w rit
-Ġre ven
-ĠGod d
-ĠZeal and
-ab s
-Ġbr ut
-Ġper pet
-h ot
-Ġhard ly
-ĠB urn
-ãĤ ¹
-Ġst y
-Ġtrans actions
-Ġg ate
-Ġsc reens
-Ġsub mitted
-Ġ1 01
-Ġlangu ages
-ugh t
-em en
-Ġfall s
-Ġc oc
-Ĥ ¬
-Ġstri kes
-p a
-Ġdel iber
-ĠI M
-Ġrel ax
-ann els
-ĠSen ator
-Ġext rem
-Ġ} ,
-ĠDe b
-Ġbe ll
-Ġdis order
-c ut
-Ġi OS
-Ġl ocked
-Ġem issions
-Ġshort ly
-" ]
-ĠJud ge
-ĠS ometimes
-Ġr ival
-Ġd ust
-Ġreach ing
-F ile
-¯¯ ¯¯
-ino is
-ĠJ ason
-Ġs atell
-are t
-Ġst ations
-Ġag ric
-ĠTechn ology
-com es
-ĠUn fortunately
-ĠChild ren
-Ġappl ies
-ast ed
-Ġan ger
-ail ability
-ĠDam age
-Ġcomp are
-ĠStand ard
-Ġaim ed
-ĠB a
-angu age
-Ġreg ulation
-Ġj ury
-Ġair port
-Ġse ctions
-ĠPr ince
-em ed
-Ġmedic ine
-Ġh itting
-Ġsp ark
-ol ves
-Ġad s
-St ate
-Ġfood s
-Ġrepl acement
-Ġch icken
-Ġlow est
-Ġmind s
-Ġinvol ves
-u i
-Ġarr ang
-Ġproced ures
-ĠWh ich
-ivers ary
-Ġb ills
-Ġimprove ment
-Ġin ev
-Ġexpect ations
-Ġintellect ual
-Ġsp aces
-Ġmechan ism
-2 50
-bre ak
-ĠZ e
-ĠT enn
-ĠB alt
-Ġbar rel
-Ġstat ic
-man n
-Pol ice
-Ġt ips
-Ġhand ling
-c us
-od ed
-il ton
-ir y
-Ġjournal ists
-our se
-Ġcom ic
-Ġnom ine
-IT Y
-Ġvers us
-Ġlo op
-Ġsur f
-ĠInd ust
-ĠHun ter
-Ġbelief s
-is an
-Ġset up
-Ġbre w
-im age
-Ġcomput ers
-f ol
-} ,"
-ĠMed al
-Ġtax p
-Ġdisplay ed
-Ġg rav
-Ġf iscal
-M on
-ĠMos cow
-ĠK ong
-ĠCent re
-Ġcamer as
-ĠMr s
-ĠH ay
-Ġa ver
-ĠK elly
-p y
-Ġrequire ment
-Ġent itled
-omb ie
-Ġsh adow
-ag ic
-ĠA k
-Ġel ite
-Ġdiv ided
-Ġhead ing
-Ġcop ies
-Ġloss es
-Ġv it
-k ed
-ĠB ry
-Ġan s
-ĠSte am
-Ġrep orter
-he im
-ĠIt em
-Ġsuper ior
-d on
-ere nt
-Ã ¶
-Ġtherap y
-Ġpe ak
-ĠMod el
-Ġl ying
-Ġg am
-z er
-r itten
-Ġrespons es
-Ġconsider ation
-ĠB ible
-Ġl oyal
-Ġinst ant
-Ġp m
-ĠFore st
-Ã ¼
-Ġext end
-Ġconv icted
-Ġfound er
-Ġconv in
-ĠO ak
-che ck
-Ġsch olars
-p ed
-Ġover se
-T op
-c ount
-ĠAr k
-Â ·
-Ġ0 6
-ĠL A
-m d
-ĠLat in
-im ental
-ĠC PU
-Ġsubst ance
-Ġminor ity
-Ġmanufact uring
-E r
-ocol ate
-Ġatt ended
-ĠMan ager
-r ations
-Ġappreci ate
-om y
-GB T
-id ency
-B L
-Ġguarant ee
-pos ition
-Ġo cean
-clud e
-Ġhead ed
-Ġt ape
-Ġlo ose
-Ġlog ic
-Ġpro ven
-Ġsp ir
-Ġad mit
-is a
-Ġinvestig ate
-Ġ199 4
-sy lv
-ĠL ost
-c est
-Ġ7 1
-Ġrequest ed
-Ġwind ows
-ĠPok é
-ĠWith out
-M et
-Ġbehavi our
-Ġread er
-Ġh ung
-ĠKe ep
-Ġro les
-Ġimplement ed
-Ġbl ank
-Ġserv es
-ĠJ ay
-Ġc ited
-ĠF riend
-prof it
-ap on
-Ġrep air
-it em
-arr ass
-Ġcrit ics
-ad i
-ĠF ather
-Ġsh out
-Ġf ool
-Ġ8 8
-Ġprodu cing
-Ġl ib
-Ġround s
-Ġcirc le
-Ġpre par
-Ġsub mit
-Ġn ic
-mor row
-ãĥ «
-U nder
-Ġv ital
-ater n
-Ġpass word
-Ġpublic ation
-Ġprom inent
-Ġspeak s
-Ġb ars
-Ġde eper
-ĠM ill
-port ed
-Ġw id
-Ġbut ter
-Ġsm oking
-Ġindic ates
-K ey
-rop ri
-ĠF ile
-all ing
-ast ing
-ĠR us
-Ġad j
-Ġ7 9
-av al
-Ġpres um
-bur gh
-on ic
-Ġf ur
-Ġpoll s
-ik a
-Ġsecond ary
-Ġmon ster
-ig s
-ĠCur rent
-E vent
-Ġowners hip
-end ar
-Ġarri ve
-ĠT ax
-Ġn ull
-ĠPri v
-Ġth ro
-Ġk iss
-c at
-Ġup set
-ang le
-it ches
-ect or
-olog ists
-ĠGal axy
-Ġcor ruption
-Ġh int
-ent er
-ĠH ospital
-Ġgreat ly
-Ġbeg un
-es y
-Ġso il
-ĠAnt on
-Ġmain tenance
-ãĥ ©
-Ġdo zens
-Ġhuman ity
-ĠAl abama
-Ġr om
-w orth
-ap ing
-sylv ania
-l ah
-Ġg athered
-G A
-Ġattack ing
-f ound
-ĠSqu are
-Ġar bit
-ict ions
-ĠW isconsin
-Ġd ance
-ĠS aint
-arch y
-Ġbase ball
-Ġcontribut ions
-Ġliter ature
-Ġex ha
-per ty
-t est
-Ġb ab
-Ġcontain er
-let ter
-Ġfall en
-Ġwebs ites
-Ġbott le
-ĠS ac
-Ġbre ast
-ĠP L
-Ġveter an
-Ġinterview s
-ĠA le
-Ġb anned
-eng ers
-ĠRev olution
-in th
-Ġconc erning
-IV E
-Ġexp enses
-ĠMatt hew
-ĠColumb ia
-d s
-ist ance
-Ġent ity
-.. ."
-Ġrel iable
-Ġpar alle
-ĠChrist ians
-Ġopin ions
-Ġin du
-l ow
-Ġcompet e
-Ġth orough
-Ġemploy ed
-Ġestablish ment
-ig en
-ĠC ro
-Ġlawy ers
-ĠSt ation
-T E
-ĠL ind
-ĠP ur
-it ary
-Ġeffic iency
-âĢ IJ
-ĠL y
-Ġm ask
-Ġdis aster
-Ġag es
-ER E
-es is
-ĠH old
-Ġcas ual
-b led
-Ġen abled
-ĠEn vironment
-ĠInt elligence
-i per
-ĠM ap
-ĠB E
-Ġemer ged
-is dom
-Ġc abin
-Ġregist ration
-Ġfing ers
-Ġro ster
-Ġfram ework
-ĠDo ctor
-et ts
-Ġtransport ation
-Ġaware ness
-H er
-Ġattempt ing
-O ff
-ĠSt ore
-ÃĥÃĤÃĥÃĤ ÃĥÃĤÃĥÃĤ
-ĠK now
-Ġdef ence
-Ġsc an
-ĠT en
-ĠCh air
-ĠP H
-ĠAtl anta
-Ġfuck ing
-Ġans wered
-b n
-ĠK ar
-Ġcateg ories
-Ġr ational
-Ġc ust
-Ġrob ot
-Ġcorrect ly
-Ġg if
-Ġgraph ics
-m ic
-Ġground s
-ĠO pp
-i ate
-Ġdist ributed
-Ġsan ctions
-Ġchalleng ing
-ut o
-Ġingred ients
-Ġinv ited
-Ġfound ed
-ĠRe qu
-d ed
-Ġb owl
-Ġbrother s
-ĠH a
-I O
-Ġw ages
-im ore
-oc ial
-Ġse ed
-ative ly
-Ġaddress es
-ĠI owa
-ab eth
-Ġatt itude
-is d
-ch ild
-Ġm ole
-Ġdisco very
-y ard
-B r
-Ġ8 2
-Ġsuppl ies
-ell ing
-Ġdist ingu
-C R
-Ġre cept
-Ġ vert
-Ġsw im
-b ec
-d oor
-ĠY eah
-Ġg al
-Ġinter act
-ĠE SP
-ĠC S
-amp s
-Ġconvin ced
-Ġobject ive
-Ġdis h
-ĠPhot os
-l ad
-Ġdownt own
-o il
-in ction
-Ġto morrow
-ĠC OM
-Ġsurv ival
-sh ot
-Ġsett lement
-C ons
-ĠX box
-int erest
-ĠS M
-arg o
-en ess
-Ġeth nic
-b ered
-M in
-ĠT ok
-Ġinc ent
-ĠComm and
-Ġmain tained
-Ġbreak s
-br idge
-at ar
-ag g
-ĠF inally
-un icip
-ĠO nt
-le ft
-Ġrecogn ition
-Ġ* /
-ĠP ers
-Ġwe lf
-Ġaddress ed
-ĠK ansas
-Ġvir us
-Ġwhere as
-Ġp apers
-ram s
-ĠMin istry
-Ġple asure
-Ġacqu ired
-Ġd uration
-j pg
-Ġcal m
-ĠN HL
-Ġburn ing
-Ġfold er
-ick ed
-ĠP y
-ĠIll inois
-Cl ass
-ĠGodd ess
-Ġperform ing
-Ġwelf are
-j ar
-In ter
-Ġl in
-Ġenh ance
-Ġnot ion
-f are
-yp es
-ĠAre a
-Ġcann abis
-ĠDie go
-f s
-ĠM anchester
-com m
-in ite
-Ġcover ing
-ĠS ound
-Ġ19 60
-Ġ8 4
-e lect
-z ing
-Ġcitiz en
-Ġph ones
-Ġr aid
-Ġign ored
-ĠOb ject
-Ġu pload
-c ard
-Ġmod ified
-Ġroom s
-ia h
-r ange
-he ast
-ach us
-Ġsuggest ing
-âĢ ĭ
-gr ade
-E l
-Ġclot hing
-Ġr h
-ĠH an
-un ity
-en cing
-ĠAust in
-sec ution
-t ra
-d em
-ĠQ ual
-Ġhe aven
-Ġst ages
-Ġw edd
-pl us
-ific ial
-ĠIm m
-ĠH o
-iet ies
-Ġphr ase
-Ġbr ill
-act ory
-Ġprov iders
-Ġsil ence
-Ġa er
-ĠA I
-ĠAd venture
-Ġplatform s
-Ġdemonstr ated
-Ġinter f
-ing ton
-Ġr aces
-Ġgr ade
-ult ane
-ĠTh rough
-f alse
-Ġb ow
-ĠA B
-Ġfl avor
-Ġhistor ic
-g ov
-Ġcol our
-Ġview ed
-ĠEm ail
-el come
-Ġinter vention
-Ġd iversity
-Ġperiod s
-Ġre verse
-ĠV ery
-Ġqu ote
-ĠLe ft
-th rough
-Ġsc rew
-Ġland ing
-Ġp ill
-Ġw et
-Ġprot esters
-Ġrepe at
-av ed
-er k
-Ġsal ary
-ĠPenn sylvania
-St ill
-Ġmay or
-Ġkit chen
-Ġfeat uring
-ĠM useum
-ĠT ournament
-ĠF al
-Ġser vers
-U C
-Ġany body
-im g
-ĠTr ade
-ixt ure
-the less
-Ġfin ance
-Ġcl osing
-ĠPat ri
-i ac
-ab el
-Ġ> >
-or ous
-Ġf irms
-sc reen
-un a
-Ġemb arrass
-ul se
-Ġlet ting
-Ġth rew
-ile y
-Ġch annels
-l an
-ĠVeg as
-Ġse ar
-Ġfant astic
-ar re
-uzz le
-ĠD er
-Th ose
-Ġsw ing
-Ġshe et
-ind ex
-co ver
-og an
-Ġvari ables
-ĠTe ch
-Ġsp oken
-ac hel
-ĠD a
-ĠMount ain
-Ġload ed
-Ġfoot age
-vers ion
-Ġun l
-ĠPh oenix
-Ġthrow ing
-Ġf iring
-Ġtrack ing
-Ġw idth
-Ġstrugg ling
-ro oms
-ot ion
-Ġmonth ly
-ĠSer ver
-Ġegg s
-op en
-M C
-Ġ199 3
-Ġh ired
-Ġstay ed
-ĠAll en
-Ġst ro
-Ġ9 8
-st ep
-ĠTurk ish
-Ġfab ric
-ist ing
-ĠD om
-Ġd ates
-Ġpr on
-Ġbasket ball
-Ġl ucky
-ĠArab ia
-Ġassum ed
-est y
-Ġaff airs
-Ġgl ad
-ĠInd eed
-ĠF A
-ĠW ord
-Ġjo ining
-if ice
-p read
-ir ts
-ĠSe lect
-Ġpop ulations
-aw are
-Ġn ose
-Ġcompl aints
-st art
-Ġsc oring
-Th anks
-Ġmin ing
-Ġvisit ors
-S H
-Ġdam aged
-Ġcharacter istics
-ĠP ent
-D C
-Ġ8 3
-ĠS ix
-r ates
-Ġfl ags
-ĠB rew
-d og
-M ark
-// //
-Ġexec ution
-Ġj oke
-ph ones
-Ġtestim ony
-Ġob st
-Q L
-ĠC ut
-Ġstud ied
-ĠN intendo
-ick et
-ĠN BC
-Ġl ad
-ĠB ra
-ĠM oh
-Ġk ernel
-Ġoverwhel ming
-Ġag ed
-Ġapplic able
-ĠC ond
-Ġroad s
-ĠBl ock
-m ade
-od ge
-Ġcomm ands
-Ġoff ices
-vel and
-Ġt ut
-Ġrece iver
-ĠF ro
-Ġsho pping
-Ġi P
-ĠSt re
-ĠA BC
-Ġentertain ment
-ĠB ow
-ort ed
-M c
-Ġread s
-gr ad
-ĠCol lect
-Ġâ ĪĴ
-ĠCap ital
-eder ation
-Ġemploy er
-Ġinvolve ment
-Ġanx iety
-al ia
-Ġro of
-ĠAm ong
-ĠDemocr at
-Ġstat s
-ĠV ill
-Ġconst itutional
-Ġrefer ring
-itt y
-Ġtack le
-out ube
-Ġback ed
-ĠH ong
-ĠBro ad
-Ġe le
-ĠO tt
-Ġ199 2
-h our
-achus etts
-C al
-Ġdefe ated
-Ġ8 1
-es p
-Ġseem ingly
-w as
-ĠJ enn
-ĠK urd
-Ġg ene
-Ġdisc ount
-R et
-EC T
-( );
-Ġclub s
-Ġs id
-ĠM arsh
-Che ck
-Ġp p
-ĠE ag
-ides pread
-Ġbe ings
-F T
-Ġintrodu ction
-ĠCh ange
-AR D
-Ġ1 10
-ad ows
-ier ce
-Ġme al
-a uthor
-ĠB ang
-lah oma
-Ġr anks
-201 1
-?? ??
-m ax
-Ġcoll apse
-Ġop ens
-Ġe cho
-Ġs oph
-Ġrac ist
-Ġenorm ous
-Ġw aves
-Ġt ap
-Ġcomprehens ive
-. --
-ĠR oy
-Ġfarm ers
-Rel ated
-a ired
-ron es
-ĠC rim
-Ġproport ion
-Ġdesign s
-Ġnegoti ations
-Ġvirt ually
-ĠBat man
-Ġwar n
-Ġlegit imate
-m ate
-Ġcon vention
-, ,
-net ic
-ĠS D
-Ġconsist ently
-Ġcompens ation
-Ġpunish ment
-Ġy e
-Ġt ie
-ĠB ureau
-ir lf
-ĠB u
-ĠA ren
-ĠPh ilipp
-Ġkn ife
-Ġmem ories
-ĠR oss
-Ġang le
-Ġ8 6
-ĠTh under
-Ġre nd
-ĠT our
-Ġcount s
-s ung
-ĠIm p
-Ġeduc ational
-Ġaccess ible
-C OM
-Ġd rew
-y er
-G l
-am ine
-OR T
-O B
-I B
-m aster
-Ġtri als
-og y
-h ar
-ĠTr ust
-Ġprefer red
-irlf riend
-ĠN ev
-Ġb in
-Ġc ow
-P age
-Ġsign ature
-ĠB L
-7 00
-Ġret ired
-Ġby tes
-Ġneigh b
-ĠLeg end
-Ġdev ast
-Ġsuspect ed
-is ons
-ĠPoké mon
-sc ale
-Ġcap abilities
-Ġre vel
-Ġche ese
-d y
-igr ant
-Ġfail ing
-b its
-ĠHer oes
-ĠG host
-ĠS cient
-Ġappoint ed
-ur i
-Ġinst itution
-Ġexpand ed
-g reg
-Ġmonitor ing
-Ġp odcast
-Ġcoal ition
-Ġ9 6
-J o
-Ġst olen
-ĠS ab
-Ġstop s
-Ġhol iday
-Ġint r
-C ar
-Bl ack
-ĠL GBT
-Ġwar ming
-ĠAnd erson
-Ġ8 9
-Ġprodu cer
-M ed
-Ġaccur acy
-ĠMar vel
-iz abeth
-ĠPat rick
-m ony
-Ġmin i
-ac les
-Ġover t
-the y
-Ġmembers hip
-ĠV en
-Ġex ch
-Ġrem oval
-ĠD ave
-T Y
-m ad
-ĠF ind
-Ġad equ
-Ġe c
-Ġte eth
-Ġemot ion
-Ġper m
-Ġsole ly
-d b
-Ġextra ord
-IG HT
-c al
-Ġgu idelines
-Ġd ying
-Ġsusp ended
-ĠPrem ier
-ĠAnth ony
-el ve
-Ġd ad
-ĠE th
-ĠFoot ball
-Ġabandon ed
-Ġ< <
-Ġm arch
-Ġhor ror
-âĢ¦ "
-Ġchild hood
-Ġcampaign s
-Ġl unch
-ĠAl bert
-bl ock
-âĸĪ âĸĪ
-ound ing
-Ġb one
-or gan
-ad ers
-ĠFl ash
-ĠDri ve
-Ġton ight
-Ġw ars
-ĠF L
-Ġform ation
-con st
-New s
-Ġcom pe
-or ious
-ĠSt aff
-Ġdiscuss ions
-ĠProt ection
-ĠJ am
-Ġcrit eria
-Ġinstall ation
-Ġaccompl ish
-iz za
-Ġpub lisher
-Ġresc ue
-ĠT ry
-U LL
-ĠS om
-ĠH op
-ore t
-th s
-ord on
-Ġp ocket
-ĠIn v
-Down load
-ĠCr ime
-Ġb ene
-ĠGu ide
-ĠAs sembly
-Ġparam eters
-I E
-ĠAlex ander
-Ġconc ert
-ĠSc he
-Ġsh oes
-Ġvis iting
-Ġrec all
-Ġb ub
-Ġr ural
-Ġconc rete
-ĠR os
-N ext
-R uss
-Ġlo ans
-ĠSh ield
-Ġtre m
-hem at
-k g
-ĠHar ris
-is ition
-ĠM ove
-ĠF C
-Ġf ate
-ĠCh o
-Ġt ired
-Ġprinc ipal
-h ist
-ien ces
-ath y
-Ġse vent
-Ġm ood
-Ġstrateg ic
-Ġdise ases
-Ġfor um
-Ġtem por
-Ġhead quarters
-P ar
-ig e
-fl ix
-Ġgu itar
-Ġ9 4
-On ly
-Ġrele ases
-ro ph
-================ ================
-Ġ6 00
-ĠContin ue
-ig ate
-ĠC rit
-sy stem
-Ġdis abled
-Ġunex pected
-ith ub
-Ġuncle ar
-ĠE st
-Ġcontr ad
-Ġstrateg ies
-vent ures
-Ġpass age
-AM E
-Ġimpro ving
-Ġreve als
-Ġdecre ase
-ov a
-Ġann oy
-ĠSh ort
-ĠL ibrary
-Ġcy ber
-n ell
-ĠH ur
-ĠC B
-Ġphot ograp
-U I
-Ġs ed
-G e
-Ġ8 7
-Ġd iverse
-Ġencour aged
-Ġcons piracy
-Ġbird s
-Ġoper ator
-Ġhand ful
-Ġclass ified
-? )
-Ġdram atic
-Ġinvestig ators
-it o
-Ġw idespread
-ĠR oom
--------------------------------- --------------------------------
-Ġcollect ive
-Ġjournal ist
-St ring
-Ġtemper atures
-il a
-Ġgu id
-Ġins pect
-Ġmiss ile
-ĠMay or
-Ġman ual
-Ġsim ultane
-Ġrat ings
-Ġsu ck
-Ġ9 7
-Ġunivers al
-Ġph arm
-Ġdis rupt
-ian o
-A V
-Ġf t
-Ġstat ist
-old s
-ĠWalk er
-ph p
-Ġunder t
-ĠL as
-ish op
-nt il
-res hold
-ĠWhe ther
-M s
-Ġden y
-ĠCl oud
-Ġprov ider
-Ġsurv iv
-ĠUp date
-h as
-Ġmist akes
-ch arge
-pl ed
-r ity
-Ġn ode
-ĠMass achusetts
-ool s
-lic ation
-Ġf ails
-em ale
-or i
-back s
-Ġsh irt
-Ġ' '
-ĠN AT
-Ġwat ers
-els on
-Ġe ase
-Ġsc ar
-Ġcont ents
-m ind
-Ġcont ribution
-Ġsh r
-Ġhand ed
-Ġst ability
-Ġtra ve
-E m
-Ġmir ror
-12 3
-Ġwe igh
-Ġf iction
-ou ver
-ist ant
-r ition
-ĠF ed
-Ġphys ically
-Ġst ake
-ĠArt icle
-ĠAr c
-ĠLew is
-ĠM ind
-Ġdemonstr ate
-Ġprof its
-v ision
-om ic
-ol id
-Ġbatt les
-Ġdri ves
-Ġeas tern
-ĠS ony
-!! !
-ar ation
-v ard
-ĠG L
-port ation
-Ġ9 2
-Ġlaw makers
-Ġprotect ing
-ĠE PA
-Ġy eah
-Ġsh ame
-ol ph
-e ven
-x it
-Ġatt ach
-Ġrepresent ing
-Ġob s
-ĠUt ah
-iff s
-ĠFre edom
-Ã ³
-A K
-Ġinc idents
-it age
-Ġview ers
-c d
-Ġm ouse
-Ġcl ar
-Ġaccord ance
-Ġb ot
-c or
-ĠSum mer
-he ld
-Ġinnoc ent
-Ġiniti ative
-ol s
-________________ ________________
-Ġsp ots
-p ace
-Ġconvent ional
-Ġcorpor ations
-Ġblock ed
-H D
-at tered
-Ġref ers
-Ġbu ck
-ĠDig ital
-12 0
-Ġtop ics
-T F
-Ä ģ
-br id
-re ement
-Ġunder lying
-ĠM ember
-Ġinvestig ating
-Ġpregn ancy
-Ġtouch down
-ĠB and
-ĠCall er
-Ġinst ances
-P P
-w a
-G ood
-Ġ199 1
-ĠC old
-Ġfear s
-Ġrem arks
-Ĩ Ĵ
-at al
-Ġm it
-Ġexper iments
-i pt
-Col or
-ind u
-Up date
-Ġ9 3
-A g
-Ġ å
-anc ouver
-B oth
-Ġjud ges
-Ob ject
-Ġst ere
-umb n
-Ġparticip ation
-ĠSt ars
-ĠJ ere
-Ġweek ly
-ĠB an
-Ġconvers ations
-ĠP itt
-u z
-ĠIndian a
-ĠK ick
-Ġinf ection
-Ġhero es
-Ġsett led
-Ġstri p
-Ġh al
-Ġd ump
-ĠS ci
-Ġl es
-Ġref erences
-ĠU RL
-ĠBr idge
-Ġwant ing
-For ce
-Ġex clus
-Me anwhile
-m n
-Ġg entle
-m aker
-sen al
-ĠG ro
-ou ri
-ĠR ain
-ĠAll iance
-Ġl ift
-el a
-S D
-ĠCle veland
-Ġrank ed
-Ġst adium
-Ġdead ly
-ä ¸
-Ġr iding
-ar ia
-ĠAr mor
-Ġdocument ation
-ĠGree ce
-ree k
-Ġl ens
-ĠS a
-Ġg ross
-ĠE mer
-ag ers
-ĠD ub
-ĠR h
-ĠAM D
-Ġarri val
-Ġdes ert
-Ġsupp lement
-ĠRes p
-Ġkn ee
-Ġmarg in
-f ont
-og g
-201 0
-ĠP ir
-ĠP rom
-iv als
-Ġint ake
-Ġdifferent ly
-ug s
-Ġb its
-clud ed
-Ġsearch ing
-ĠD u
-um ble
-Ġfunction al
-ĠBalt imore
-ĠC ould
-Ġdes ired
-Ġcirc uit
-ĠL yn
-ĠG O
-ĠF alse
-re pre
-' :
-alt ies
-Ġmin im
-Ġdro ve
-ĠSh ould
-Ġh ip
-Ġpro s
-Ġut ility
-ĠN ature
-ĠM ode
-P resident
-o pp
-r at
-form ance
-Ġconcent ration
-Ġf ont
-ĠB ud
-Ġam id
-Ġre vers
-ĠM L
-B ar
-Ġinter action
-Ġjur isd
-Ġspell s
-d ep
-f il
-Ġcivil ians
-ut ter
-ĠCo oper
-ĠBel ow
-Ġent rance
-Ġcon vert
-Ġcontrovers y
-ow ered
-Ġcontr ary
-Ġar c
-ĠExec utive
-ĠOffic er
-Ġpack ages
-Ġprog ressive
-w idth
-Ġreserv ed
-v ol
-ĠSam sung
-Ġprint ed
-Ġcent ers
-Ġintrodu ce
-ĠKenn edy
-Ġodd s
-Ġsure ly
-Ġindepend ence
-Ġpass engers
-repre ne
-ĠBe h
-Ġl oves
-ĠESP N
-Ġfac ilit
-Ġident ical
-Ġdo ct
-Ġpartners hip
-con f
-ĠH ide
-Ġconf used
-ĠC ow
-M en
-Ġw rest
-ĠIraq i
-Ġh oles
-ĠStud ies
-Ġpregn ant
-h ard
-Ġsign als
-I X
-Ġpull ing
-Ġgrad uate
-Ġnomine e
-D ate
-Ġper mitted
-Ġâ Ĥ¬
-ĠOk lahoma
-St art
-Ġauthor ized
-Ġal arm
-ĠC os
-v an
-Ġgener ations
-c ular
-Ġdr agon
-ĠSoft ware
-ĠEd ward
-Ġcontro ller
-S en
-ge red
-ĠV ik
-Ġappro ached
-Th ank
-Ġcan ce
-Ġform ula
-ĠSm all
-Ġweak ness
-Ġr amp
-it udes
-j ud
-Ġbrill iant
-Ġacc us
-s ource
-Ġ8 00
-ĠE vil
-S w
-Ġhom eless
-we ek
-i ens
-r ics
-ĠTh ird
-T O
-Ġorgan ic
-Ġpresent ation
-ag h
-ĠDown load
-v ation
-Ġas sembly
-or able
-hold ers
-ĠBern ie
-ĠHel p
-Ġt ong
-ĠF ight
-Ġbe ach
-B ook
-ĠL ic
-Ġr ush
-ĠR ound
-ou p
-ĠMar x
-Ġcalcul ated
-ĠDe vil
-ĠSar ah
-Ġoccasion ally
-Ġbul let
-Av ailable
-g ate
-Ġ9 1
-Ġh osp
-Ġprom ises
-ĠH IV
-ĠSt adium
-ĠSt ock
-ĠCorpor ation
-g age
-N G
-ĠC redit
-Ġs ne
-ib l
-Ġacc um
-s uch
-Ġterror ists
-Ġconscious ness
-ĠZ h
-Ġdram a
-ool a
-pir ation
-Ġlab our
-ĠN in
-Ġut ter
-Ġdemocr atic
-Ġass ass
-il ation
-Ġg est
-Ġab road
-Ġmet ab
-Ġs orts
-Ġfl av
-U B
-Ġm g
-ĠNot hing
-ĠO d
-Ġmus ical
-200 9
-Ġdro ps
-oc ated
-ater al
-0000 00
-Ġg re
-Ġequ ality
-Ġburd en
-Ġv ig
-ĠLe ader
--------- ----
-Ġcere mony
-Ġf ighter
-Ġact ors
-Ġ æ
-am an
-F i
-Ġal ign
-put er
-Ġe lder
-ĠN SA
-Ġrepresent ation
-ĠOnt ario
-IT H
-usal em
-Ġharass ment
-itz er
-Ġsy mp
-Ġbox es
-ĠD R
-Ġman ifest
-at re
-Ġ ^
-Ġd ies
-le ton
-Ġmiss ions
-et he
-Ġres olve
-Ġfollow ers
-Ġas c
-Ġk m
-l ord
-am med
-Ġsil ent
-ĠAssoci ated
-Ġtim ing
-Ġprison ers
-ĠK ings
-ĠF ive
-Ġtow er
-Ġappro aches
-Ġprecise ly
-Ġb ureau
-ĠM other
-ĠI ss
-Ġkey board
-it ual
-Ġfund ed
-Ġstay ing
-Ġpsych ological
-Ġm ile
-ĠLe on
-ĠBar b
-w ill
-Ġw ider
-ĠAtl antic
-Ġt ill
-ĠR ome
-ro t
-Ġaccomp an
-Ġfl our
-ac o
-W orld
-ĠExp ress
-ĠY u
-C or
-Ġple ased
-part y
-Ġpoint ing
-Ġinf lation
-Ġro y
-Ġ ),
-ain er
-Ġwedd ing
-orm on
-Ġrequ iring
-Ġqual ified
-Ġse gment
-EN D
-Ġs izes
-e als
-Ġcor rupt
-ass ador
-Ġcele b
-Ġdream s
-ĠM ess
-Ġcheck ing
-ĠV ersion
-Ġprep aring
-Ġact ively
-ĠD iff
-Ġl ux
-ĠW inter
-act eria
-ĠN E
-Ġdep uty
-Ġtrans gender
-Ġsum mary
-Ġin her
-er ies
-ch ar
-ĠY an
-Ġkn ock
-ĠP ath
-Ġl ip
-roll er
-Ġimp ression
-Ġcelebr ate
-Ġsl ide
-Ġgu ests
-Ġcl ip
-F S
-Ġsav ings
-Ġcapt ain
-Ġleg acy
-ĠDen ver
-Ġw ounded
-tab oola
-AC T
-Ġpurs ue
-Ġo xy
-Ġ q
-Ġsem i
-ĠN eed
-ĠAff airs
-Ġob sc
-Ġcheck ed
-Ġd ual
-C ode
-ĠM D
-le m
-ult y
-ĠÂ ©
-ĠEl izabeth
-Ġcent uries
-ard ed
-s rc
-Ġev ident
-enn is
-at in
-Ġunemploy ment
-ĠMar io
-Ġint im
-Ch rist
-Ġbi ological
-Ġsold ier
-ĠAdd ed
-Ġm ath
-ĠG il
-Ġbi as
-Ġd ating
-ĠO cean
-Ġm ice
-M us
-h ire
-ĠT es
-Ser ver
-lim ited
-S ize
-Ġmet ers
-Ġrock et
-es see
-Ġcertific ate
-ĠIran ian
-AS S
-Ġgr id
-D ec
-Ġro lling
-com mun
-ĠSwed en
-b ury
-Ġtiss ue
-Ġrac ism
-ĠL ocal
-Ġmyster y
-Ġexam ine
-Ġst em
-Ġs its
-Ġhop ed
-ot ing
-Ġdial ogue
-Ġpers u
-W atch
-l ay
-M AN
-Ġch ronic
-ĠPort land
-mark et
-ĠS EC
-Ġparalle l
-Ġsc andal
-Ġcar ries
-Ġphenomen on
-h uman
-ack er
-ĠO x
-Ġretire ment
-tain ment
-ov ie
-ĠG ear
-Ġd uties
-Ġdo se
-Ġsc roll
-M B
-in f
-Ġsa uce
-Ġland scape
-red dit
-ĠChampions hip
-ĠRed dit
-al id
-Ġco in
-Ġover s
-Ġpost ing
-ab out
-Ġf el
-and y
-Ġb old
-Ġfocus ing
-e ffect
-G R
-Ġde emed
-Ġrecommend ations
-Ġste pped
-Ġvot er
-ĠDe ep
-ĠInst agram
-Ġmoder ate
-ĠMary land
-Ġrestrict ed
-ĠM B
-ĠCh all
-Ġto b
-Ġc ir
-ĠO cc
-ĠE ver
-Ġcoll aps
-IN FO
-= -
-ĠP ict
-ĠAcc ount
-n c
-Ġo ught
-Ġex port
-Ġdr unk
-( '
-Ġw ise
-ĠM ort
-ne cess
-Ġan cest
-ĠInc re
-Ġfrequ ent
-m ir
-Ġinterpret ation
-Ġdepend ent
-Ġco ins
-ĠB ol
-V ideo
-ĠJust in
-Ġfat al
-Ġcook ing
-Ġconf usion
-ip her
-Ġcust ody
-ĠMor gan
-om ach
-ĠGovern or
-Ġrestaur ants
-el ing
-Ġacknowled ged
-Ġthe r
-Ġgen es
-ch ing
-He y
-Ġtact ics
-ĠMex ican
-Ġv end
-Ġhe s
-qu er
-Ġnot ing
-ĠCamer on
-Ġtarget ing
-ro ck
-Ġcred its
-Ġemot ions
-Ġrepresent atives
-new s
-Ġlegisl ative
-Ġrem oving
-Ġtweet ed
-ĠCar ter
-ĠF ixed
-Ġfor cing
-Ġspeak er
-Ġm ales
-ĠViet nam
-l ined
-Ġconcept s
-Ġvo ices
-o ir
-ĠT rib
-W he
-ĠJer usalem
-ĠS ant
-Ġc ul
-Ġl ady
-ĠHaw ai
-Ġar ts
-ĠIn n
-ĠMach ine
-ĠEm peror
-Ġsl ot
-g ly
-ĠPro cess
-II I
-Ġathlet es
-ĠTem ple
-ĠRep resent
-Ġpres c
-Ġt ons
-Ġgold en
-Ġp unch
-ĠG R
-iver pool
-Ġen act
-Ġlob by
-Ġm os
-Ġpick ing
-Ġlif etime
-Ġcogn itive
-E ach
-z o
-Ġd ub
-Ġcons ists
-ol n
-Ġf estival
-am ous
-Ġint ellig
-w ords
-ĠSm art
-Ġde le
-Ġl apt
-Ġmag ical
-ĠS in
-b us
-ur ities
-igh th
-ĠRub y
-ĠS ure
-ol ving
-Ġj un
-O ST
-Ġimp osed
-Ġast ron
-Ġcor rel
-ĠN S
-ĠK it
-ĠF uture
-b urn
-Ġimm une
-oc us
-Ġcour ses
-ĠSt ring
-Ġle an
-Ġg host
-Ġout comes
-Ġexp ense
-Ġevery day
-Ġaccept able
-A h
-Ġequ ipped
-Ġor ange
-F R
-ĠD utch
-Th ough
-ĠR ank
-Q U
-ĠRober ts
-wh at
-re nd
-Ġdisapp ear
-Ġsp awn
-ĠL am
-o is
-Ġdes erve
-Ġmin imal
-Ġnerv ous
-ĠW ould
-Ġro ok
-ĠV ancouver
-Ġres ign
-sh ire
-ĠW orks
-ĠB uild
-Ġafford able
-ĠG ary
-ĠAren a
-Ġh anging
-Ġimpl ications
-ĠS ong
-Ġmain taining
-Ġgu ards
-C ON
-Ġder ived
-Ġexecut ed
-Ġthe ories
-Ġqu oted
-ĠAnd re
-og a
-sel ess
-in fo
-ĠBel g
-Ġt ears
-ĠSur v
-Ġbirth day
-ig ious
-im mer
-Ġspect rum
-Ġarchitect ure
-Ġrec ruit
-arm a
-T able
-Ġmon sters
-ĠG ov
-Ġdest ination
-Ġattract ive
-Ġf oss
-ĠMore over
-Ġpres ents
-TH E
-Ġrep ly
-pt on
-Ġc um
-Ġdel ight
-Ġaffect s
-Ġdon ations
-ĠT oy
-ĠH im
-M ENT
-Ġover come
-it ched
-ĠFant asy
-ĠH at
-ĠBe ast
-b ott
-Ġinvestig ations
-R un
-Ġhun ting
-d i
-f und
-Ġs essions
-est yle
-Ġport ray
-oid s
-Y eah
-Ġcommun icate
-Ġcom edy
-ĠY ang
-Ġbel t
-ĠMar ine
-Ġpredict ed
-Pl ay
-Ġimportant ly
-Ġremark able
-Ġelim inate
-D avid
-Ġb ind
-V ID
-Ġadvoc ates
-ĠG aza
-im p
-D B
-ĠN a
-ĠSim ilar
-I ES
-Ġchar ity
-v as
-m ath
-Ġâ ĸ
-ok er
-nd um
-Ġcap s
-ĠH al
-2 000
-e an
-Ġfle et
-Ġrec re
-R ight
-Ġsleep ing
-ij ing
-k ind
-Ġdesign ated
-Ã ¤
-Ġanim ation
-ke e
-ĠInt rodu
-Ġ/ >
-Ġdelay ed
-Ġtrem end
-Ġcur ious
-U se
-Ġle ct
-d am
-Ġinnov ation
-ĠPoint s
-Ġload ing
-Ġdisp ute
-ct ic
-ird s
-ĠB Y
-Ġn urs
-ĠVal ue
-ION S
-ĠH um
-Ġtem plate
-m ers
-Ġappear ances
-ĠEnter tainment
-Ġtransl ation
-Ġsa ke
-Ġbene ath
-Ġin hib
-Ġe uro
-abet es
-Ġstud ying
-ĠM as
-Ġper ceived
-Ġexam ined
-Ġe ager
-Ġco aches
-Ġim per
-ch i
-Ġprodu ces
-" ).
-ĠEvery one
-Ġm unicip
-Ġg irlfriend
-Ġh ire
-ĠV ice
-Ġsu itable
-op y
-Ġin equ
-ĠD uke
-f ish
-f irst
-ĠO bs
-Ġinter ior
-ĠBru ce
-ĠR y
-Ġanal ys
-Ġconsider able
-Ġfore cast
-Ġf ert
-ors hip
-ĠD rug
-ĠA LL
-: "
-th ur
-ĠM ail
-Ġball ot
-Ġinst antly
-ĠCh annel
-Ġp icks
-Ġ198 9
-Ġt ent
-ol i
-Ġcivil ian
-b ling
-ell o
-b u
-Ġin ch
-Ġlog o
-Ġcooper ation
-Ġwal ks
-Ġinvest ments
-Ġimp rison
-ĠF estival
-ĠK y
-Ġleg ally
-Ġg ri
-ch arg
-S l
-Ġthreat ening
-du ction
-fl ow
-Ġdismiss ed
-ibr aries
-c ap
-e le
-ĠMc G
-ĠHar vard
-ĠConserv ative
-ĠC BS
-p ng
-Ġro ots
-ĠH aving
-umb led
-ĠF un
-\ /
-ĠS earch
-ple x
-Ġdiscuss ing
-Ġcontin u
-ĠT ai
-ĠW ik
-F ree
-f it
-Ġref use
-Ġmanag ing
-Ġsy nd
-ip edia
-w alk
-Ġprofession als
-Ġguid ance
-Ġunivers ities
-Ġas semb
-unt u
-F inally
-AS E
-ĠAut o
-ĠH ad
-Ġann iversary
-L D
-ĠD ur
-ĠUlt imate
-ih ad
-pro duct
-Ġtrans it
-Ġrest ore
-Ġexpl aining
-Ġass et
-Ġtransfer red
-Ġbur st
-ap olis
-ĠMag azine
-ĠC ra
-ĠB R
-gg ed
-ĠH E
-M ich
-b et
-ĠL ady
-yl um
-erv es
-Ġme ets
-wh ite
-L og
-Ġcorrespond ing
-Ġins isted
-G G
-Ġsurround ed
-Ġt ens
-Ġl ane
-Ġco inc
-h ome
-Ġexist ed
-ect ed
-ĠDou ble
-lam m
-Ġske pt
-ex p
-Ġper ception
-ie v
-ĠBe ing
-o ft
-Ġadop t
-. :
-] ;
-Wind ows
-Ġsatell ite
-AS H
-Ġinf ant
-d escription
-ĠMe anwhile
-c m
-oc a
-ĠT reat
-act or
-Ġtob acco
-ĠN orm
-em ption
-Ġfl esh
-Ġj e
-o op
-ĠHe aven
-Ġbe ating
-an im
-Ġgather ing
-Ġcult iv
-G O
-ab e
-ĠJon athan
-ĠSaf ety
-Ġbad ly
-pro t
-Ġcho osing
-Ġcontact ed
-Ġqu it
-Ġdist ur
-Ġst ir
-Ġto ken
-D et
-ĠP a
-Ġfunction ality
-00 3
-s ome
-Ġlimit ations
-Ġmet h
-b uild
-con fig
-N T
-re ll
-ble m
-ĠM om
-Ġveter ans
-ĠH u
-Ġtrend s
-are r
-ĠG iven
-ĠCa ption
-m ay
-AS T
-Ġwond ering
-ĠCl ark
-n ormal
-Ġsepar ated
-Ġdes p
-st ic
-b rew
-Ġrel ating
-ĠN ik
-ĠF arm
-Ġenthus i
-g ood
-d eb
-Ġactiv ist
-Ġm art
-Ġexplos ion
-ĠEconom ic
-L ink
-Ġins ight
-Ġconven ient
-Ġcounter part
-su pport
-ĠV irt
-ag en
-ĠTenn essee
-ĠSim on
-ĠA ward
-OC K
-ĠF igure
-Ġoverse as
-Ġpr ide
-ĠC as
-n ote
-m g
-C urrent
-Ġdispl ays
-cont ent
-Ġtravel ing
-Ġhosp itals
-ĠFin ancial
-ĠP ast
-Ġdefend ant
-Ġstream ing
-m ble
-ĠBer lin
-uk i
-Ġdist ribut
-Ġant ib
-Ġch ocolate
-ĠCast le
-Ġinter rupt
-ĠR ow
-Ġconvers ion
-Ġbug s
-ĠR ather
-li est
-L Y
-ĠJe an
-com mon
-ak h
-Ġ1 30
-ot ton
-ĠDe an
-Ġam endment
-Ġgame play
-ĠWar ren
-od a
-Ġhigh lights
-Ġir re
-ĠNAT O
-Ġball s
-Ġdemand ing
-U RE
-ĠL uke
-F igure
-st op
-on ia
-z one
-iz ers
-ĠW R
-Ġaward ed
-Ġregul atory
-ĠH art
-ĠS N
-pl ing
-Ġs our
-ĠP ixel
-us ive
-Ġf et
-ĠS ent
-Ġautom atic
-Ġf er
-vern ment
-ĠKh an
-T ON
-f ather
-Ġextraord inary
-th rop
-ĠP ython
-ĠG PU
-Ġsex ually
-Ġdesk top
-it ivity
-ĠAnton io
-Ġo rient
-Ġe ars
-ob by
-ous es
-vertis ements
-Ġmanufacture rs
-ic ient
-min ute
-Ġconv iction
-Ġg arden
-p ublic
-Ġsatisf ied
-f old
-O K
-Ġin hab
-ĠTh ink
-Ġprogram me
-Ġst omach
-Ġcoord in
-Ġh oly
-Ġth reshold
-Ġr het
-Ġser ial
-Ġemploy ers
-ĠEvery thing
-ra h
-Ġb other
-Ġbr ands
-Val ue
-ĠT ed
-ĠPlan et
-Ġp ink
-ĠFurther more
-s a
-P E
-re ck
-ĠUS D
-ot te
-Ġ& &
-Ġland ed
-g ets
-Ġprodu cers
-Ġhealth care
-Ġdomin ant
-Ġdest ro
-Ġam ended
-ch ron
-Ġf its
-ĠSy d
-ĠAuthor ity
-AT CH
-Ġfight s
-ĠL LC
-Ġ-- -
-ĠCor p
-Ġtox ic
-spe cific
-ĠC orn
-ĠChe l
-Ġtele phone
-ĠP ant
-Ġmyster ious
-aun ch
-od ox
-med ia
-Ġwitness es
-ag u
-Ġquestion ed
-ĠBre xit
-ĠRem ember
-ene z
-Ġend orse
-iat ric
-ĠId ent
-Ġridic ulous
-1 10
-Ġpr ayer
-Ġscient ist
-Ġ19 50
-ĠA qu
-Ġunder ground
-ĠU FC
-m are
-ĠL ater
-w ich
-Ġsubsc rib
-Ġhost s
-Ġer r
-Ġgr ants
-ant om
-Ġsum mon
-ear ly
-ĠC lear
-ĠPr im
-Ġsusp ension
-Ġguarant eed
-app er
-Ġr ice
-ĠSe an
-ĠSh in
-Ġrefere ndum
-Ġfl ed
-r ust
-Ġ3 60
-ter y
-Ġsh ocked
-B R
-ĠO il
-ĠAll ah
-Ġpart ly
-Ġign or
-Ġtrans mission
-Ġhom osexual
-ivers al
-Ġhop efully
-ãĤ ¤
-Ġless on
-L eg
-Ġ ..
-Y et
-t able
-app ropri
-re tt
-Ġbo ards
-Ġincor rect
-Ġb acteria
-ar u
-am ac
-Ġsn ap
-.' "
-Ġpar ad
-t em
-he art
-Ġav ailability
-Ġw isdom
-Ġ( +
-Ġpri est
-ĠÂł ĠÂł
-O pen
-Ġsp an
-Ġparam eter
-Ġconv ince
-Ġ( %)
-r ac
-Ġf o
-Ġsafe ly
-Ġconver ted
-ĠOlymp ic
-Ġres erve
-Ġhe aling
-ĠM ine
-M ax
-Ġin herent
-ĠGra ham
-Ġinteg rated
-D em
-Ġpip eline
-Ġapp lying
-Ġem bed
-ĠCharl ie
-Ġc ave
-200 8
-Ġcons ensus
-Ġre wards
-P al
-ĠHT ML
-Ġpopular ity
-look ing
-ĠSw ord
-ĠAr ts
-' )
-Ġelect ron
-clus ions
-Ġinteg rity
-Ġexclus ively
-Ġgr ace
-Ġtort ure
-Ġburn ed
-tw o
-Ġ18 0
-P rodu
-Ġent reprene
-raph ics
-Ġg ym
-ric ane
-ĠT am
-Ġadministr ative
-Ġmanufacture r
-Ġ vel
-ĠN i
-Ġisol ated
-ĠMedic ine
-Ġback up
-Ġpromot ing
-Ġcommand er
-Ġfle e
-ĠRus sell
-Ġforg otten
-ĠMiss ouri
-Ġres idence
-m ons
-Ġrese mb
-Ġw and
-Ġmeaning ful
-P T
-Ġb ol
-Ġhe lic
-Ġwealth y
-Ġr ifle
-str ong
-row ing
-pl an
-as ury
-âĢ¦ .
-Ġexpand ing
-ĠHam ilton
-Ġrece ives
-S I
-eat ures
-ĠAn im
-RE E
-P ut
-Ġbrief ly
-ri ve
-Ġstim ul
-Ġ`` (
-Ġ __
-Ġch ip
-Ġha z
-Ġpri ze
-ĠTh ings
-AC E
-ul in
-d ict
-ok u
-Ġassoci ate
-ock ets
-y outube
-St ory
-ateg ory
-Ġm ild
-ail ing
-ĠY e
-O rig
-ĠK a
-or ig
-Ġpropag anda
-Ġan onymous
-Ġstrugg led
-Ġout rage
-AT ED
-ĠBe ijing
-r ary
-Ġle ather
-Ġworld s
-Ġbroad er
-12 5
-id al
-ĠBet ter
-Ġt ear
-E xt
-Ġpropos als
-Ġit er
-ĠSqu ad
-Ġvol unt
-m i
-D id
-ĠP u
-p in
-Ġspeak ers
-Ġb orders
-Ġfig ured
-= '
-Ġsimultane ously
-aed a
-Ġcharg ing
-Ġur ged
-Ġcon j
-25 6
-ĠG ordon
-mer ce
-Ġdocument ary
-Sh are
-it ol
-ON E
-ĠG arden
-h att
-ĠThom pson
-ane ous
-ap ore
-Ġt anks
-Ġless ons
-tr ack
-Ġout standing
-Ġvolunte ers
-Ġsp ray
-Ġmanag ers
-l arge
-Ġcamp s
-Ġart ificial
-ĠR u
-Ġb ags
-th al
-Ġcompat ible
-ĠBl ade
-Ġf ed
-Ġarg ues
-F I
-Ġunf air
-Ġcor n
-Ġoff set
-Ġdirect ions
-Ġdisappoint ed
-ĠCon vention
-Ġview ing
-M E
-oc ity
-Ġtown s
-Ġlay ers
-Ġro lled
-Ġjump ed
-Ġatt ribute
-Ġun necess
-inc oln
-Ġsupp ose
-ĠNet her
-ch a
-Ġbur ied
-Ġsix th
-B en
-ress ing
-OU R
-Ġw ound
-Ġcy cl
-Ġmechan isms
-Ġcongress ional
-ĠE lement
-Ġagre ements
-Ġdec or
-Ġclos est
-ĠM it
-Go ogle
-} }
-Ġm ixture
-Ġflu id
-S ign
-ĠSch olar
-Ġp ist
-ask et
-ab ling
-Ġrac ing
-he ro
-ri el
-ass y
-Ġche aper
-b en
-Ġvert ical
-amac are
-ĠRead ing
-g ments
-Ġhelic op
-Ġsacr ifice
-ay a
-p aren
-V A
-ĠL es
-ĠStud io
-Ġviol ations
-ĠAn na
-ac er
-é ¾
-ĠR at
-ĠBe ck
-ĠD ick
-ĠA CT
-Ġcomp osition
-Ġtext ure
-ĠO wn
-Ġsmart phone
-ĠN A
-Ġfor b
-im port
-Ġdef ending
-il st
-re r
-Ġo h
-ĠJere my
-Ġbank ing
-cept ions
-Ġrespect ive
-/ .
-Ġdr inks
-ĠW i
-Ġb ands
-ĠL iverpool
-Ġg rip
-ĠB uy
-Ġopen ly
-Ġreview ed
-per t
-Ġver ify
-ĠCo le
-ĠW ales
-M O
-Ġun pre
-Ġshel ter
-ĠIm perial
-Ġgu i
-ĠD ak
-Ġsuggest ions
-Ġexplicit ly
-Ġsl ave
-Ġblock chain
-Ġcompet ing
-Ġprom ising
-S ON
-Ġsoc cer
-Ġconst itution
-4 29
-Ġdist ract
-ĠU ser
-es ides
-ĠMet hod
-ĠTok yo
-Ġaccompan ied
-Cl ient
-s ur
-al og
-Ġident ification
-Ġinv asion
-as ma
-Ġindust ries
-pp ers
-Ġsub tle
-ĠUn it
-n atural
-Ġsurv ived
-Ġfl aw
-ĺ ħ
-ĠH oll
-Ġdef icit
-Ġtut orial
-ĠCh ance
-Ġarg uing
-Ġcontem porary
-Ġinteg ration
-for ward
-Ġt um
-it is
-Ġh iding
-ĠD omin
-ĠT an
-ĠB uilding
-ĠV in
-Ġspokes person
-ĠNot es
-Ġemer ging
-Ġprepar ation
-Ġpro st
-Ġsuspect s
-Ġaut onom
-D escription
-Ġdeal t
-ĠP ear
-Ġstead y
-Ġdecre ased
-Ġso vere
-ĠCl in
-Ġgrad ually
-ors es
-ĠW AR
-S erv
-ãĤ ¢
-h r
-Ġd irty
-ĠB arn
-ĠB C
-Ġd il
-Ġcal endar
-Ġcompl iance
-Ġch amber
-b b
-Ġpass enger
-ate ful
-ĠT itle
-ĠSyd ney
-ĠG ot
-Ġdark ness
-Ġdef ect
-Ġpack ed
-ass ion
-Ġgod s
-Ġh arsh
-IC K
-le ans
-Ġalgorith m
-Ġoxy gen
-Ġvis its
-Ġbl ade
-Ġkil omet
-ĠKent ucky
-Ġkill er
-P ack
-enn y
-Ġdiv ine
-Ġnom ination
-be ing
-Ġeng ines
-Ġc ats
-Ġbuff er
-ĠPh ill
-Ġtra ff
-AG E
-Ġtong ue
-Ġrad iation
-ere r
-m em
-ĠExpl icit
-é¾ į
-Ġcou ples
-Ġphys ics
-ĠMc K
-Ġpolit ically
-aw ks
-ĠBl oom
-Ġwor ship
-e ger
-ut er
-ĠF O
-Ġmat hemat
-Ġsent enced
-Ġdis k
-ĠM arg
-Ġ/ *
-P I
-Ġoption al
-Ġbab ies
-Ġse eds
-ĠScott ish
-Ġth y
-] ]
-ĠHit ler
-P H
-ng th
-Ġrec overed
-ing e
-Ġpow der
-Ġl ips
-Ġdesign er
-Ġdis orders
-Ġcour age
-Ġch aos
-" },{"
-Ġcar rier
-b ably
-H igh
-ĠR T
-es ity
-l en
-Ġrout es
-u ating
-F il
-N OT
-w all
-s burgh
-Ġeng aging
-ĠJava Script
-ore r
-li hood
-Ġun ions
-ĠF ederation
-ĠTes la
-Ġcomple tion
-ĠT a
-Ġprivile ge
-ĠOr ange
-Ġne ur
-paren cy
-Ġb ones
-Ġtit led
-Ġprosecut ors
-ĠM E
-Ġengine er
-ĠUn iverse
-ĠH ig
-n ie
-o ard
-Ġheart s
-ĠG re
-uss ion
-Ġmin istry
-Ġpen et
-ĠN ut
-ĠO w
-ĠX P
-in stein
-Ġbul k
-S ystem
-ic ism
-ĠMarket able
-Ġpre val
-Ġpost er
-Ġatt ending
-ur able
-Ġlicens ed
-ĠG h
-et ry
-ĠTrad able
-Ġbl ast
-à ¤
-ĠTit an
-ell ed
-d ie
-H ave
-ĠFl ame
-Ġprof ound
-Ġparticip ating
-Ġan ime
-ĠE ss
-Ġspec ify
-Ġregard ed
-ĠSpe ll
-Ġs ons
-own ed
-Ġm erc
-Ġexper imental
-land o
-h s
-ĠDun geon
-in os
-Ġcomp ly
-ĠSystem s
-ar th
-Ġse ized
-l ocal
-ĠGirl s
-ud o
-on ed
-ĠF le
-Ġconstruct ed
-Ġhost ed
-Ġsc ared
-act ic
-ĠIs lands
-ĠM ORE
-Ġbl ess
-Ġblock ing
-Ġch ips
-Ġev ac
-P s
-Ġcorpor ation
-Ġo x
-Ġlight ing
-Ġneighb ors
-ĠU b
-ar o
-Ġbe ef
-ĠU ber
-F acebook
-ar med
-it ate
-ĠR ating
-ĠQu ick
-Ġoccup ied
-Ġaim s
-ĠAdd itionally
-ĠInt erest
-Ġdram atically
-Ġhe al
-Ġpain ting
-Ġengine ers
-M M
-ĠM ust
-Ġquant ity
-P aul
-Ġearn ings
-ĠPost s
-st ra
-ãĥ¼ ãĥ
-Ġst ance
-Ġdro pping
-sc ript
-Ġd ressed
-M ake
-Ġjust ify
-ĠL td
-Ġprompt ed
-Ġscr ut
-Ġspeed s
-ĠGi ants
-om er
-ĠEd itor
-Ġdescrib ing
-ĠL ie
-ment ed
-Ġnow here
-oc aly
-Ġinst ruction
-fort able
-Ġent ities
-Ġc m
-ĠN atural
-Ġinqu iry
-Ġpress ed
-iz ont
-for ced
-Ġra ises
-ĠNet flix
-ĠS ide
-Ġout er
-Ġamong st
-im s
-ows ki
-Ġclim b
-ne ver
-Ġcomb ine
-d ing
-Ġcomp r
-Ġsignific ance
-Ġremem bered
-ĠNev ada
-ĠT el
-ĠSc ar
-ĠWar riors
-ĠJ ane
-Ġcou p
-b as
-Ġtermin al
-, -
-O H
-Ġt ension
-Ġw ings
-ĠMy ster
-�� ��
-ĠUn like
-val id
-viron ments
-ĠAl i
-Ġn aked
-book s
-ĠM un
-ĠG ulf
-Ġd ensity
-Ġdim in
-Ġdesper ate
-Ġpres idency
-Ġ198 6
-h y
-IN D
-Ġun lock
-im ens
-Ġhand led
-ĠE b
-Ġdisapp eared
-Ġgen re
-Ġ198 8
-Ġdetermin ation
-St ream
-ik o
-ap ters
-Ġacknow ledge
-J an
-Ġcapital ism
-P at
-Ġ20 20
-Ġpain ful
-Ġcur ve
-Ġbom bs
-st orm
-ĠMet al
-en cer
-ĠF ig
-ĠA aron
-anc hes
-Ġins piration
-Ġexha ust
-t ains
-ash i
-Ġdesc ript
-Ġr itual
-ĠChel sea
-Ġpromot ion
-ĠH ung
-ĠW ard
-iv a
-ĠE T
-Ġto ss
-all ow
-ĠFranc is
-D ep
-Ġhapp iness
-ĠGl ass
-Ġbet a
-Ġstreng then
-N E
-o a
-Ġbutt ons
-ĠMur ray
-Ġkick ed
-Qu est
-ĠT alk
-ĠS everal
-ĠZ ero
-Ġdr one
-ul k
-Ġc am
-ĠM obile
-Ġprevent ing
-Ġret ro
-ĠA x
-Ġcru el
-Ġflo at
-. ),
-Ġfil ing
-ĠGr ant
-ĠB or
-Ġr ib
-Ġchampions hip
-ĠM erc
-Ġsty les
-Ġc ake
-Ġbuild s
-ĠS elf
-io x
-Ġep ic
-oy d
-B el
-ĠSt ew
-. (
-ah u
-ĠBe yond
-Ġout s
-Ġsol o
-ĠT ree
-Ġpres erve
-Ġt ub
-AR E
-ro c
-ĠIm pro
-ĠW right
-Ġbu nd
-Ġtr aged
-Ġoccas ional
-b ian
-Sec ond
-r ons
-Ġinter actions
-form ed
-s ing
-Ġown s
-Ġh ockey
-Gener al
-Ġlog ical
-Ġexp end
-Ġesc al
-ĠGr iff
-ĠC rown
-ĠRes erve
-Ġsto pping
-Ġexc use
-sec ond
-Ġoper ated
-Ġre aches
-ĠMal ays
-Ġpoll ution
-ĠBrook lyn
-Ġde lete
-Ġhas h
-Bl ock
-ah a
-âĢ ³
-Ġsh orter
-p iece
->
-Ġh orm
-ĠW at
-ĠBre ak
-Ġprohib ited
-Ġint ensity
-ĠAl an
-Ġli ability
-? !
-and ed
-Ġneigh bour
-ĠCol lection
-Ġf ires
-Ġrevolution ary
-f ly
-ĠOr leans
-Wh ite
-ĠW rit
-ĠD awn
-Ġsett le
-Ġexec ute
-B M
-Ġspokes woman
-Ġlif estyle
-Ġclick ing
-ĠK ill
-ĠLiber al
-ĠN azi
-Ġtra iler
-Ġmount ains
-Ġdam n
-z es
-p es
-Ġpress ing
-Ġb ail
-ĠOrgan ization
-Ġp ir
-Ġth irty
-Ġelect rical
-Ġ1 15
-ĠP oly
-ĠR ap
-ĠSt rike
-ĠC ann
-Ġdemand ed
-Ġback ing
-def ault
-spe ed
-ĠLeg isl
-Ġmother s
-ĠB ody
-Ġvar iation
-ced ented
-p owered
-le ading
-N ever
-Ġg rave
-ĠAnt i
-A W
-Ġinterview ed
-ĠG ab
-ĠF at
-Ġrook ie
-u u
-Ġdep os
-ix on
-Ġam pl
-ret ion
-ĠHe at
-Ġpeace ful
-S M
-ie ve
-Ġd iver
-ĠVict oria
-Ġm ic
-p df
-Ġst ating
-Ġl ung
-Ġcritic ized
-Ġvacc ine
-ĠLoad ing
-ur se
-T ake
-ĠFr an
-ĠS old
-ĠRob in
-Ġdetect ed
-ĠSc ript
-Ġadjust ed
-Ġsen ator
-Ġopp osing
-Er ror
-C ount
-Ġconflic ts
-Ġo w
-ĠAr gent
-Ġmatch ing
-h h
-ĠTre k
-st arter
-" ),
-ĠA F
-od er
-xx xx
-ĠAl t
-ac re
-ĠP ick
-ĠSol ar
-ĠD al
-O ct
-ĠB att
-Ġs rc
-Ġeng agement
-Ġexecut ives
-Ġliber ty
-j ava
-Ġtal ented
-igen ous
-Ġcon secut
-.. ...
-In fo
-Ġhor rible
-Ġsurprising ly
-f eed
-ic ating
-ĠL ED
-Ġfem ales
-St ation
-ell er
-ĠOak land
-Ġmechan ical
-i ology
-ĠV ar
-Ġrob ust
-ett ings
-ott a
-Ġthe oret
-Ġret ain
-k ward
-Ġd a
-Ġdeploy ed
-d el
-ĠAnd y
-Ġsubsc ribe
-we b
-Ġn a
-ĠMic hel
-Ġpart ially
-ĠCome y
-Ġc rown
-ĠM aj
-ĠBl u
-r ator
-D ay
-IN T
-Ġdocument ed
-ĠG DP
-g i
-che ll
-Ġbrut al
-ĠB ab
-st ration
-Ġthe ft
-Ġt ube
-@ @
-Ġqu ery
-ĠL incoln
-Ġpublish ing
-Ġw ore
-or ical
-Ġr ic
-Ġnot able
-Ġsubsequ ently
-ne x
-Ġobser ve
-ĠB oe
-Ġc odes
-m ain
-W H
-ĠS L
-Ġresident ial
-av an
-Ġm as
-are st
-ade on
-OU T
-Ġsoph istic
-ant e
-Ġc ens
-Ġ **
-Ġmort ality
-Ġyour s
-Ġoccas ions
-Ġrec alled
-ĠDri ver
-Ġv ocal
-Ġbath room
-Ġsh ops
-Ġcollabor ation
-ĠOb amacare
-ĠC ell
-Ch ar
-Su per
-C re
-Ġt ends
-Ġt orn
-Ġeconom ics
-a very
-ĠR aid
-ĠS em
-Ġshould ers
-Ġexpect ing
-Ġexam ination
-en ame
-ĠU I
-i ability
-ol as
-ĠAm b
-ĠD ra
-Ġmid field
-ĠI C
-Ġlay out
-Ġflo ating
-f i
-it ative
-Ġtremend ous
-Ġ Ð
-Ġab und
-W ork
-ĠLight ning
-Ġsimilar ly
-Ġconserv atives
-Ġpr ay
-B E
-iz arre
-Ġt empt
-Ġemphas is
-ĠMet ro
-Ġf ishing
-Ġmar ry
-ne g
-ĠStud y
-Ġrec k
-Ġdis pos
-on ing
-bs ite
-Ġsusp ic
-Ġmer ch
-ĠG ib
-ĠDes cription
-ĠD VD
-w he
-ĠY emen
-Ġen vironments
-oot ing
-ĠMod ern
-e u
-Ġreflect s
-Ġh oney
-Ġanaly st
-Ġg ut
-d ec
-A ction
-Ġhousehold s
-Ġst er
-Ġtem ple
-Ġreform s
-Ġfavour ite
-Ġdead line
-ĠL E
-Th ree
-ĠWith in
-A ug
-Ġnight s
-elt a
-Ġinv alid
-ĠEx change
-ĠDel hi
-w hen
-inc ome
-Ġ ðŁ
-Ġwire less
-sc ribe
-ist a
-Ġhost ile
-Ġall y
-Ġg ig
-Ġout lets
-ĠD or
-EM ENT
-Ġas h
-Ġab stract
-OR D
-ĠMot or
-Ġadv iser
-ist le
-Ġb ases
-Ġcourt esy
-Ġcross ing
-Ġcle ared
-Ġrefuge e
-cos ystem
-Ġthrow s
-f un
-bour ne
-d ays
-Ġdisag ree
-ĠN ative
-Ġreflect ed
-ĠF ast
-ĠY ellow
-ĠSing apore
-ĠR aven
-Ġembr ace
-ĠK u
-ĠC hen
-ĠEar ly
-Ġappoint ment
-ĠMin i
-it ement
-Ġpl acing
-Ġb icy
-S R
-Ġwh is
-S U
-Ġinvestig ated
-Ġphotograph s
-g ithub
-ĠBe at
-ĠR ing
-ig hed
-i ar
-Ġev olved
-eral d
-Ġd un
-Ġh ub
-I AL
-Ġencour aging
-ĠPr int
-ĠD ays
-Ġpro secution
-Ġp ants
-az y
-l ive
-Ġfoss il
-ĠJ u
-Ġro cks
-ud ge
-ĠR ace
-Ġg reet
-b ie
-Ġf illing
-ĠL en
-Ġdi abetes
-Ġfire arms
-um ing
-enez uel
-ĠB B
-Ġaccept ing
-AT H
-Ġres ort
-Ġh unt
-ri k
-uck er
-am ents
-Ġsust ained
-Ġcross ed
-Ġbreak fast
-Ġatt ributes
-lect ed
-at ile
-Ġv ibr
-ĠK al
-ars on
-op les
-Ġtou ched
-Ġdam ages
-Ġimp ressed
-ru p
-Ġan ch
-ĠAd ams
-H el
-ĠVict or
-Ġmount ed
-ĠC C
-Ġdelic ious
-sp an
-ell a
-Ġel abor
-am ples
-Ġdef ic
-Ġconstit u
-u ates
-ĠM ission
-ĠT her
-ĠMon ster
-b es
-Re uters
-ĠInd ones
-h ill
-mun ition
-Ġconfirm ation
-ĠCons ider
-ac ent
-Ġj et
-ĠEm ploy
-ĠGT X
-n an
-ĠSp ider
-Ġprocess or
-Ġpat ri
-ĠPent agon
-ĠRob inson
-Ġreal istic
-Ã ±
-Ġappear ing
-Ġp ipe
-om ed
-Ġf ru
-Ġaw ful
-Ġeval uation
-Ġintellig ent
-ĠC itiz
-Ġfund ra
-od ium
-Ġtwe ets
-Ġwor n
-pr ing
-Ġkid n
-Ġreb els
-ĠK am
-ĠNether lands
-ĠS W
-Ġacqu isition
-ĠM ale
-ãĥ ª
-omb ies
-Ġtrad em
-ĠStat us
-B re
-ĠTH IS
-Ġad verse
-ĠN EW
-s ign
-Ġorgan isation
-en c
-ĠHar per
-ap or
-ĠMem bers
-ĠPe ace
-ĠAir port
-ĠOther s
-Ġscr atch
-ĠP il
-Ġsens or
-Ġadop tion
-ĠHot el
-ĠDr ag
-Ġhonest ly
-Ġy ard
-ĠFor ces
-Ġpat ent
-Ġb ass
-Ġquiet ly
-Ġbreat hing
-Ġp ose
-i ors
-ĠJ ess
-st atic
-IT E
-O ffic
-Ġj ew
-w cs
-Ġ14 0
-Ġpre view
-ipp i
-Ġunf ortunately
-oke mon
-Ġh orn
-Ġre ass
-Ġpe er
-ock er
-Ġunt o
-ĠGr ay
-Ġclean ing
-Ġattract ed
-200 7
-P oint
-k ill
-ĠAg reement
-ur ches
-Ġhor r
-ĠMiss iss
-Ġworth y
-Ġfl owers
-t own
-d ll
-Ġre actions
-Ġde ce
-Ġindic ating
-M D
-Ġpre ference
-ĠM VP
-ess ional
-ĠT arget
-g ence
-ĠInd ians
-Ġm isc
-Ġfree ly
-Ġmus cles
-Ġline up
-Ġimpact s
-ous ing
-om i
-ac ular
-Ġcontro lling
-ag ine
-c ery
-he ll
-Ġrank ing
-ĠN ich
-ĠA ve
-12 8
-Ġhigh way
-Ġinc ons
-Ġb inding
-Ġstrugg les
-ĠPitt sburgh
-Ġgr ay
-r in
-Ġcom ics
-ĠS port
-Ġrel atives
-Ġfr ight
-Ġpro be
-ĠPort ug
-Ġv oc
-Ġt u
-ĠCor ps
-Ġposs ibilities
-Ġqual ify
-wcs store
-Ġl ibraries
-Ġm igrants
-Ġent ries
-Ġconsecut ive
-v als
-ĠChair man
-Ġh ill
-IM E
-ĠG ard
-Ġinequ ality
-f ox
-ĠS ave
-Ġc ort
-claim ed
-Ġtra its
-Ġp our
-Ġmiss iles
-Ġess ence
-Ġs ends
-Ġall iance
-Ġw ishes
-ĠChrist opher
-B ig
-N Y
-ĠJac ob
-s an
-ur red
-ĠS O
-ll y
-Ġadvoc ate
-ĠB ond
-Ġ" /
-Us ing
-Ġdistrict s
-ĠG ate
-ĠB ir
-r idge
-ĠN az
-ĠR s
-bo ards
-ĠG a
-ĠRe agan
-Ġinflu enced
-1 000
-ap y
-Ġchalleng ed
-Ġb arg
-Ġfac ulty
-ĠF if
-Ġacqu ire
-A c
-Ġin sect
-Ġinstr uments
-Ġle af
-th odox
-M essage
-Ġt ale
-Ġthere by
-Ġtra p
-Ġstrong est
-ĠMil itary
-is ible
-Ġ198 4
-ethe less
-Ġflex ible
-Ġkill s
-Ġfin ishing
-ĠS ize
-Ġredu ces
-Ġep id
-Ġorient ation
-f ull
-Ġtr ace
-Ġl aser
-Ġopp ose
-Ġed iting
-Ġmoment um
-ä º
-sh ow
-V I
-ĠL ad
-Ġ198 5
-Ġmurd ered
-9 00
-ut her
-Ġprob ability
-ĠP oll
-Ġrel uct
-ĠChe m
-ĠMont real
-Ġadequ ate
-ĠPol and
-ĠSher iff
-um ph
-Ġo k
-Ġ 000
-Ġ" [
-Ġoper ators
-ĠF er
-Ġmod es
-ĠE ve
-Ġdiscipl ine
-N ET
-H and
-Ġor al
-ĠW E
-em ail
-J P
-ĠPalestin ians
-Ġhe nce
-ĠL ess
-Ġover l
-d ig
-Ġintim id
-ĠCo al
-Ġr anging
-th a
-Ġdist ant
-Ġf ib
-ĠInd ex
-ĠW onder
-ĠP el
-hatt an
-ĠH ug
-Ã Ĺ
-ra it
-Ġwra pped
-ĠR PG
-Ġchemical s
-ĠM oney
-Ġfro zen
-Ġind irect
-ĠAgain st
-E nd
-Ġuncom fortable
-ĠGall ery
-ĠPost ed
-Ø §
-ond uct
-Ġconsequ ence
-Ġbit ter
-Ġ198 7
-p op
-Ġcount less
-ĠAl aska
-ff ff
-Ġdepart ure
-Ġref und
-ĠI an
-i ated
-Ġsee ks
-Ġmechan ics
-Ġjurisd iction
-lyn n
-Ġal ike
-ĠH unt
-ath on
-Ġres olved
-Ġc ache
-Ġdist inction
-d irect
-Ġenc ount
-ou b
-be at
-ĠCount ry
-se arch
-Ġcontin uous
-Ġmod est
-ĠR ail
-th ood
-1 30
-B UG
-Ġcrim inals
-Ġindic ation
-Ġencount ered
-l ast
-ĠW y
-Ġide ology
-ĠP DF
-sec urity
-] )
-ĠJim my
-ĠE N
-Ġh iring
-T em
-Ġp ig
-aun t
-ĠCry stal
-Ġpen alties
-Ġcap ability
-Ġp y
-Ġproduct ive
-Ġbal anced
-ĠGe Force
-cl ick
-olit an
-od s
-Ġafter wards
-Ġplay offs
-ĠG ill
-U ser
-Ġback s
-p ub
-t ag
-Ġabs urd
-p iring
-Ġc iting
-Ġtr illion
-Ġoblig ation
-Ġmax im
-ah oo
-c f
-um i
-ĠAl pha
-ĠN elson
-Ġpursu ant
-in itely
-Ġf ract
-ent ry
-ber y
-ĠTh or
-Add ed
-ĠD J
-ĠG ene
-Ġaw kward
-St ud
-Ġwal let
-ĠDiv ine
-ari os
-Ġrele asing
-Ġed ited
-Ġaccompl ished
-B est
-Ġed ges
-Ġplan es
-Ġfeed ing
-" },"
-Ġdiscl osure
-Ġgr ain
-air y
-o ons
-ern and
-V R
-Ġreason ably
-Ġdr um
-Ġpart ial
-Ġgraph ic
-Ġunpre cedented
-Ġadv ised
-M icro
-ĠAss ad
-point s
-sc ar
-ĠZ one
-tt es
-Ġ7 00
-v o
-ĠH amp
-Ġfix es
-Ġca ution
-Ġstr ings
-Ġpan els
-Ġle ak
-Ġpr icing
-row th
-ĠEr ror
-ĠS aints
-f ix
-Ġobserv ations
-ĠA bs
-Ġsuggest ion
-ĠUkrain ian
-Ġbar rier
-Ġpain ted
-B et
-im ir
-ĠS pect
-p ot
-orne ys
-Ġcomp ound
-Ġbe ars
-ĠR ush
-Ġlux ury
-S um
-Ġor bit
-ĠMar c
-Ġex empt
-ĠTra il
-ĠM O
-ĠH ans
-ĠWe apon
-oc used
-umin um
-ĠJer ry
-Ġb ust
-ĠA G
-ĠW iki
-Ġend less
-ĠV lad
-ĠB ah
-ĠR adeon
-ke ys
-ĠSur vey
-ĠV iol
-def ine
-le an
-Ġcomm od
-Ġreven ues
-Å į
-Ġfurn iture
-Ġcast ing
-Ġdiplom atic
-ĠPlay ers
-ĠK illed
-Ġmod ify
-Ġinnov ative
-ĠAb u
-n or
-Ġbond s
-Ġcoach ing
-M er
-Ġmod ules
-ĠPatri ots
-Ġenh anced
-Ġproceed ings
-Ġteam mates
-Ġ12 8
-ard o
-Ġcomprom ise
-ĠM uch
-Ġfle w
-ĠEd ge
-Ġunnecess ary
-Ġdoct rine
-re port
-ĠOr lando
-ĠProf ile
-Ġplay off
-friend ly
-Ġcompl ain
-ĠM C
-ĠO pt
-ĠG B
-Ġbeat en
-Ġg olf
-Ġpl acement
-B it
-Ġnews letter
-Ġ201 9
-vis or
-raw l
-ĠiP ad
-Ġact ed
-Ġju ice
-Ġdec ks
-P N
-su ccess
-ĠH alf
-Ġdele ted
-Ġsec rets
-Ġas ylum
-M art
-ĠAct iv
-ĠGu y
-ĠT s
-Ġd ys
-Ġassum ing
-Ġman a
-Ġsub ur
-Ġ12 5
-M edia
-AR Y
-r ide
-c p
-Ġdifficult ies
-Ġcollect ing
-Ġbank rupt
-n on
-Ġcomp osed
-Ġvol t
-Ġmilit ants
-Ġ> >>
-ĠM ormon
-t or
-Ġpartic les
-ĠB art
-ry ption
-Ġad min
-Ġsqu ee
-VID IA
-Ġcreat or
-iam eter
-ic ular
-N BC
-Ġgrab bed
-Ġn odd
-Ġr ated
-Ġrot ation
-Ġgr asp
-Ġexcess ive
-ĠE C
-ĠWh it
-Ġinvent ory
-ault s
-ĠF B
-Ġe cosystem
-Ġbill ions
-Ġvent ure
-n amed
-Ġdef ender
-out e
-Inst ead
-ir able
-W ar
-Ġassum ption
-Ġb ite
-Ġearth qu
-t ail
-sp ace
-Ġgif ts
-boy s
-Ġinev itable
-Ġstruct ural
-Ġbenef icial
-Ġcompe lling
-h ole
-erv ation
-Ġco at
-o j
-inc arn
-ĠY ears
-Ġdetermin ing
-Ġrhet oric
-Ġbound aries
-Ġwh ites
-A nt
-add y
-) -
-ra ham
-eter min
-Ġhar vest
-ĠCon c
-Ġlapt op
-ĠM atch
-Ġenjoy ing
-cc a
-oll ar
-Ġtri ps
-Ġadd iction
-ĠS ak
-Ġpow ered
-Ġc ous
-ĠRuss ians
-ie re
-Ġret rie
-qu ality
-Ġdiff er
-Ġking dom
-ĠL aur
-ĠCap itol
-Ġcon clusions
-ĠAl tern
-ĠN av
-Ġtrans parent
-B ER
-G roup
-ĠCom plete
-Ġinf er
-Ġint rig
-Ġins ane
-R O
-oph ob
-is en
-qu al
-Mich ael
-Ġm useum
-ĠP ope
-Ġres et
-r ative
-f ive
-Ġagg reg
-itte es
-osit ory
-Ġcar b
-ĠRec ord
-Ġdec ides
-ĠF ix
-Ġexcept ions
-ĠCommission er
-un s
-ĠEnvironment al
-Ġlegend ary
-ist ence
-Ġtun nel
-k m
-Ġins ult
-Ġt roll
-Ġsh ake
-Ġdet ention
-qu es
-ĠCh rome
-ĠF iles
-Ġsub t
-Ġprospect s
-Ġpro l
-re nder
-pro of
-Ġperform ances
-St r
-Ġh ref
-ern ame
-Ġachieve ment
-Ġf ut
-F ull
-ĠLe ban
-go ogle
-ãĥ Ī
-amp a
-May be
-Ġproject ed
-ĠE mb
-Ġcol leg
-Ġa wards
-Ġâ Ķ
-G old
-ĠBl ake
-ĠR aj
-if ting
-Ġp ending
-Ġinst inct
-Ġdevelop ments
-Con nect
-ĠM and
-ĠW ITH
-ĠPhilipp ines
-prof ile
-Ġalt ogether
-ĠB und
-ĠT D
-oo oo
-amp ed
-ip h
-Ġste am
-Ġold est
-Ġdet ection
-ul pt
-Ġ ç
-ĠWay ne
-200 6
-f a
-Ġcir cles
-ĠF u
-Ġdon ors
-appropri ate
-ĠDak ota
-j amin
-Ġmotiv ated
-Ġpurch ases
-ĠLouis iana
-ĠS pl
-Ġgl obe
-Ġ10 5
-z ip
-c all
-Ġdepart ments
-Ġsustain able
-10 5
-ĠO P
-if iers
-Ġprevent ed
-Ġinc omp
-ĠComm ander
-Ġdom inated
-ĠÂ »
-Ġinvest ed
-Ġcomplex ity
-Ġin cl
-Ġens uring
-Ġreal m
-yn c
-ĠInd ependent
-r ained
-ĠJ en
-ĠFl ight
-Ġat he
-Ġspec ulation
-ĠT E
-oc ate
-t ic
-Ġpl aint
-her ry
-Ġto y
-Ġ1 11
-Ġpl ates
-st atus
-ĠIs a
-Ġdev oted
-C op
-ĠE S
-25 5
-ur rency
-M ain
-Ġsl aves
-Ġpe pper
-Ġqu otes
-Ġce iling
-ĠF ish
-Ġtrans formation
-Ġfra ction
-Ġadvant ages
-Ġto ile
-Ġstun ning
-Ġmo ist
-bre aking
-s i
-ĠL ocation
-ĠMed ium
-Ġtext s
-Ġu gly
-Ġb io
-. âĢĶ
-ĠB ased
-Ġtr ains
-ĠW ing
-ĠAn cient
-ĠRec ords
-ĠH ope
-Spe cial
-ades h
-ob i
-[ /
-Ġtempor arily
-V er
-h u
-os er
-Ġover night
-Ġm amm
-ĠTre asury
-ĠV enezuel
-ĠMeg a
-Ġt ar
-Ġexpect s
-bl ack
-or ph
-\\ \\
-Ġaccept ance
-Ġrad ar
-s is
-Ġjun ior
-Ġfram es
-Ġobserv ation
-ac ies
-P ower
-ĠAdv anced
-M ag
-olog ically
-ĠMe chan
-Ġsent ences
-Ġanaly sts
-augh ters
-force ment
-Ġv ague
-Ġcl ause
-Ġdirect ors
-Ġeval uate
-Ġcabin et
-M att
-ĠClass ic
-A ng
-Ġcl er
-ĠB uck
-Ġresear cher
-Ġ16 0
-Ġpoor ly
-Ġexperien cing
-ĠP ed
-ĠMan hattan
-Ġfre ed
-Ġthem es
-ad vant
-Ġn in
-Ġpra ise
-10 4
-ĠLib ya
-b est
-Ġtrust ed
-Ġce ase
-Ġd ign
-D irect
-Ġbomb ing
-Ġm igration
-ĠSci ences
-Ġmunicip al
-ĠA verage
-Ġgl ory
-Ġreve aling
-Ġare na
-Ġuncertain ty
-Ġbattle field
-ia o
-G od
-Ġc inem
-ra pe
-el le
-ap ons
-Ġlist ing
-Ġwa ited
-Ġsp otted
-ke ley
-ĠAud io
-e or
-ard ing
-idd ing
-ig ma
-ĠN eg
-Ġl one
-Ġ ----
-ex e
-d eg
-Ġtrans f
-Ġwas h
-Ġsl avery
-Ġexpl oring
-ĠW W
-ats on
-Ġen cl
-l ies
-ĠC reek
-Ġwood en
-Man ager
-ĠBr and
-um my
-ĠAr thur
-Ġbureau cr
-Ġbl end
-ar ians
-F urther
-Ġsupposed ly
-Ġwind s
-Ġ19 79
-Ġgrav ity
-Ġanalys es
-ĠTra vel
-ĠV eter
-Ġd umb
-Ġaltern ate
-g al
-Ġconsum ed
-Ġeffect iveness
-.' '
-Ġpath s
-ond a
-L A
-ĠStr ong
-Ġen ables
-Ġesc aped
-Ġ" "
-Ġ1 12
-Ġ198 3
-Ġsm iled
-Ġtend ency
-F ire
-Ġp ars
-ĠR oc
-Ġl ake
-Ġf itness
-ĠA th
-ĠH orn
-Ġh ier
-Ġimp ose
-m other
-Ġp ension
-ic ut
-bor ne
-ic iary
-. _
-ĠS U
-Ġpol ar
-is y
-eng u
-itial ized
-AT A
-w rite
-Ġexerc ises
-ĠD iamond
-ot ypes
-Ġharm ful
-on z
-Ġprint ing
-st ory
-Ġexpert ise
-ĠG er
-Ġtraged y
-ĠF ly
-Ġd ivid
-amp ire
-st ock
-M em
-Ġre ign
-Ġun ve
-Ġam end
-ĠProp het
-Ġmut ual
-ĠF ac
-Ġrepl acing
-H ar
-ĠCirc uit
-Ġthro at
-ĠSh ot
-Ġbatter ies
-Ġto ll
-Ġaddress ing
-ĠMedic aid
-Ġp upp
-ĠN ar
-ol k
-Ġequ ity
-M R
-ĠHis pan
-ĠL arge
-m id
-D ev
-Ġexp ed
-Ġdem o
-ĠMarsh all
-erg us
-Ġf iber
-Ġdiv orce
-ĠCre ate
-Ġsl ower
-ĠPark er
-ĠStud ent
-ĠTr aining
-Ret urn
-ĠT ru
-Ġc ub
-ĠRe ached
-Ġpan ic
-Ġqu arters
-Ġre ct
-Ġtreat ing
-Ġr ats
-ĠChristian ity
-ol er
-Ġsac red
-Ġdecl are
-ul ative
-et ing
-Ġdeliver ing
-est one
-Ġt el
-ĠL arry
-Ġmet a
-ac cept
-art z
-ĠRog er
-hand ed
-Ġhead er
-Ġtra pped
-ĠCent ury
-Ġkn ocked
-ĠOx ford
-Ġsurviv ors
-b ot
-Ġdemon stration
-Ġd irt
-Ġass ists
-OM E
-ĠD raft
-ortun ate
-fol io
-pe red
-ust ers
-g t
-ĠL ock
-Ġjud icial
-ver ted
-Ġsec ured
-out ing
-ĠBook s
-Ġhost ing
-Ġlif ted
-l ength
-Ġj er
-Ġwhe els
-ĠR ange
-umbn ails
-Ġdiagn osis
-te ch
-ĠStew art
-ĠP ract
-Ġnation wide
-Ġde ar
-Ġoblig ations
-Ġgrow s
-Ġmand atory
-Ġsusp icious
-! '
-A pr
-G reat
-Ġmort gage
-Ġprosecut or
-Ġeditor ial
-ĠK r
-Ġprocess ed
-ung le
-Ġflex ibility
-Ear lier
-ĠC art
-ĠS ug
-Ġfoc uses
-Ġstart up
-Ġbre ach
-ĠT ob
-cy cle
-ãĢ Į
-ro se
-Ġb izarre
-ãĢ į
-Ġveget ables
-$ $
-Ġret reat
-osh i
-ĠSh op
-ĠG round
-ĠSt op
-ĠHawai i
-ĠA y
-Per haps
-ĠBe aut
-uff er
-enn a
-Ġproduct ivity
-F ixed
-cont rol
-Ġabs ent
-ĠCamp aign
-G reen
-Ġident ifying
-Ġreg ret
-Ġpromot ed
-ĠSe ven
-Ġer u
-ne ath
-aug hed
-ĠP in
-ĠL iving
-C ost
-om atic
-me ga
-ĠN ig
-oc y
-Ġin box
-Ġem pire
-Ġhor izont
-Ġbr anches
-Ġmet aph
-Act ive
-ed i
-ĠFil m
-ĠS omething
-Ġmod s
-inc ial
-ĠOrig inal
-G en
-Ġspir its
-Ġear ning
-H ist
-Ġr iders
-Ġsacr ific
-M T
-ĠV A
-ĠS alt
-Ġoccup ation
-ĠM i
-Ġdis g
-lic t
-Ġn it
-Ġn odes
-e em
-ĠP ier
-Ġhat red
-ps y
-ãĥ ī
-Ġthe ater
-Ġsophistic ated
-Ġdef ended
-Ġbes ides
-Ġthorough ly
-ĠMedic are
-Ġbl amed
-arent ly
-Ġcry ing
-F OR
-pri v
-Ġsing ing
-ĠI l
-Ġc ute
-o ided
-olit ical
-ĠNe uro
-å ¤
-Ġdon ation
-ĠEag les
-ĠG ive
-T om
-Ġsubstant ially
-ĠLic ense
-ĠJ a
-Ġg rey
-ĠAn imal
-ĠE R
-ĠU nd
-Ġke en
-Ġconclud e
-ĠMississ ippi
-Eng ine
-ĠStud ios
-P ress
-o vers
-ll ers
-Ġ3 50
-ĠR angers
-Ġr ou
-ert o
-E p
-iss a
-iv an
-Ġse al
-ĠReg ist
-dis play
-Ġwe aken
-u um
-ĠComm ons
-ĠS ay
-Ġcult ures
-Ġl aughed
-Ġsl ip
-Ġtreat ments
-iz able
-m art
-ĠR ice
-Ġbe ast
-Ġob esity
-ĠLa ure
-ig a
-Wh ich
-hold er
-Ġelder ly
-Ġp ays
-Ġcompl ained
-Ġc rop
-Ġpro c
-Ġexplos ive
-ĠF an
-ĠAr senal
-A uthor
-ef ul
-Ġme als
-Ġ( -
-id ays
-Ġimag ination
-Ġann ually
-Ġm s
-as ures
-H ead
-ik h
-m atic
-Ġboy friend
-ĠCom puter
-Ġb ump
-Ġsur ge
-ĠCra ig
-ĠKir k
-D el
-medi ate
-Ġscen arios
-ĠM ut
-ĠSt ream
-Ġcompet itors
-Ù Ħ
-ĠStan ford
-ĠRes ources
-az ed
-b age
-Ġorgan is
-ĠRe lease
-Ġsepar ately
-Ġha bits
-Ġmeasure ments
-ĠCl ose
-Ġaccomp any
-Ġg ly
-Ġt ang
-ĠR ou
-Ġplug in
-Ġcon vey
-ĠChall enge
-oot s
-j an
-Ġcur s
-ĠRel ations
-ke eper
-Ġapproach ing
-p ing
-Spe aking
-Ġarrang ement
-ĠV I
-are ttes
-Ġaffect ing
-Ġperm its
-b ecause
-Ġu seless
-ĠH us
-!! !!
-Ġdestro ying
-Un fortunately
-Ġfasc inating
-S em
-Ġelect oral
-Ġtrans parency
-ĠCh aos
-Ġvolunte er
-Ġstatist ical
-Ġactiv ated
-ro x
-We b
-H E
-ĠHamp shire
-is ive
-M ap
-Ġtr ash
-ĠLaw rence
-st ick
-C r
-Ġr ings
-EX T
-Ġoper ational
-op es
-D oes
-ĠEv ans
-Ġwitness ed
-P ort
-Ġlaunch ing
-ec onom
-w ear
-ĠPart icip
-um m
-cul es
-ĠR AM
-ĠT un
-Ġass ured
-Ġb inary
-Ġbet ray
-Ġexpl oration
-ĠF el
-Ġad mission
-it ated
-S y
-Ġav oided
-ĠSim ulator
-Ġcelebr ated
-ĠElect ric
-¥ ŀ
-Ġcl uster
-itzer land
-he alth
-L ine
-ĠN ash
-at on
-Ġsp are
-Ġenter prise
-ĠD IS
-clud es
-Ġfl ights
-Ġreg ards
-ĠÃ Ĺ
-h alf
-Ġtr ucks
-Ġcontact s
-Ġunc ons
-ĠCl imate
-Ġimm ense
-N EW
-oc c
-ect ive
-Ġemb od
-Ġpat rol
-Ġbes ide
-Ġv iable
-Ġcre ep
-Ġtrig gered
-ver ning
-Ġcompar able
-q l
-Ġg aining
-ass es
-Ġ( );
-ĠG rey
-ĠM LS
-s ized
-Ġpros per
-" ?
-Ġpoll ing
-Ġsh ar
-ĠR C
-Ġfire arm
-or ient
-Ġf ence
-Ġvari ations
-g iving
-ĠP i
-osp el
-Ġpled ge
-Ġc ure
-Ġsp y
-Ġviol ated
-Ġr ushed
-Ġstro ke
-ĠBl og
-sel s
-ĠE c
-,' '
-Ġp ale
-ĠColl ins
-ter ror
-ĠCanad ians
-Ġt une
-Ġlabor atory
-Ġn ons
-t arian
-Ġdis ability
-ĠG am
-Ġsing er
-al g
-ĠSen ior
-Ġtrad ed
-ĠWar rior
-Ġinf ring
-ĠFrank lin
-Ġstr ain
-ĠSwed ish
-Ġsevent h
-ĠB enn
-ĠT ell
-Ġsynd rome
-Ġwond ered
-id en
-++ ++
-ig o
-Ġpur ple
-Ġjournal ism
-Ġreb el
-Ġf u
-bl og
-Ġinv ite
-ren cies
-ĠCont act
-Is rael
-ĠCont ent
-Ġche er
-Ġbed room
-ĠEngine ering
-ĠQue ens
-Ġd well
-ĠPlay Station
-ĠD im
-ĠCol on
-l r
-Ġoper ates
-Ġmotiv ation
-US A
-ast ered
-C ore
-ĠTr uth
-ol o
-OS E
-ĠMem ory
-Ġpred ec
-Ġan arch
-Ġ19 20
-ĠY am
-Ã ¨
-b id
-Ġgr ateful
-Ġexc itement
-Ġtre asure
-Ġlong est
-ct ive
-Ġdes erves
-Ġreserv es
-Ġcop s
-ĠOtt awa
-ĠEgypt ian
-ank ed
-Ġart if
-Ġhypot hesis
-: /
-Ġpurch asing
-Ġlove ly
-H P
-Ġdiv ide
-Ġstrict ly
-Ġquestion ing
-Ġtaxp ayers
-ĠJ oy
-Ġroll s
-ĠHe avy
-Ġp orts
-Ġmag netic
-Ġinf lamm
-Ġbr ush
-t ics
-â ĪĴ
-Ġbott les
-pp y
-Ġp add
-ãĤ ¯
-m illion
-Ġdevast ating
-Ġcomp iled
-Ġmed ication
-Ġtw elve
-ĠPer ry
-Sp ace
-im b
-y our
-Ġle aked
-ĠT ar
-Ġun ity
-Ġinfect ed
-Ġtravel ed
-ID E
-ĠMc Donald
-t xt
-ĠPr inc
-Ġinter ven
-ĠTai wan
-ĠP ow
-Ġbe aring
-ĠTh read
-Ġz ones
-iz ards
-un ks
-Ch apter
-ll or
-ĠÂ ·
-Ġw ounds
-Ġdisc retion
-Ġsucceed ed
-ik ing
-Ġicon ic
-C all
-Ġscreen ing
-ĠM is
-ict s
-Ġmin isters
-Ġsepar ation
-Pl ayer
-Ġb ip
-Ġbel oved
-Ġcount ing
-ĠE ye
-ar ound
-ing ing
-Ġtable t
-Ġoff ence
-in ance
-h ave
-ĠInf o
-ĠNin ja
-Ġprotect ive
-ĠC ass
-M ac
-ĠQual ity
-N orth
-Ġ ic
-ĠCub a
-ĠChron icle
-ĠPro perty
-Ġfast est
-ot os
-ĠG erm
-OW N
-Ġbo om
-ĠStan ley
-ergus on
-Ġcle ver
-Ġent ers
-m ode
-ter ior
-ĠS ens
-Ġlin ear
-AR K
-Ġcomp aring
-Ġpure ly
-Ġsaf er
-ĠPot ter
-Ġc ups
-R T
-Ġgl uc
-Ġatt ributed
-Ġdu pl
-ĠP ap
-Ġprec ious
-Ġp a
-iction ary
-ĠT ig
-ĠTo o
-ol utions
-st an
-Ġrob ots
-Ġlob b
-Ġstat ute
-Ġprevent ion
-w estern
-16 0
-ĠAct ive
-ĠMar ia
-h al
-N one
-ell ar
-ĠK B
-ĠPart ners
-ĠSing le
-ĠFollow ing
-ang o
-ac ious
-Ġth ou
-Ġk g
-Ġinflu ential
-ĠFriend s
-S ur
-ain ted
-Ġfor ums
-Ġst arter
-Ġcitizens hip
-ĠE lection
-on ge
-ot ation
-os ph
-;; ;;
-ut ical
-p ur
-ere n
-Ġaccus ations
-bit ious
-ab bit
-ĠOr d
-Post ed
-ir k
-Ġsens itivity
-ic he
-ĠAm y
-ĠF ab
-Ġsum mit
-Ġped est
-Ġrub ber
-Ġagric ultural
-Ġcan cel
-A E
-Ġin aug
-Ġcont am
-Ġfirm ly
-i w
-st age
-ĠK an
-Ġt ier
-Ġinv ention
-Ġtransl ated
-ĠR ules
-B ox
-Tw itter
-ID S
-Ġp izza
-Ġdeb ug
-ĠD rop
-v s
-Ġh orses
-b ig
-Ġb oring
-Ġh ood
-ĠMcC ain
-at ched
-ĠBro s
-Ġsk ip
-Ġess ay
-st at
-ĠLeg ends
-Ġam munition
-au c
-Ġshoot er
-Ġun h
-Ġsuppl ied
-Ġgener ic
-ĠS K
-ib an
-yr ics
-Ġ25 5
-Ġclim bing
-Form er
-Ġfl ip
-Ġjump ing
-Ġfrust ration
-ĠTer ry
-Ġneighborhood s
-Ġmed ian
-be an
-Ġbr ains
-Follow ing
-Ġsh aped
-Ġdraw s
-Ġal tered
-J ack
-Ġrecip es
-Ġsk illed
-we alth
-ach i
-e lection
-Ġbehavi ors
-de als
-ĠU ntil
-F e
-Ġdecl aration
-mar ks
-ĠBet ween
-cel ona
-Ġres on
-Ġbub ble
-Am ong
-Ġim perial
-G S
-Ġfemin ist
-200 5
-ĠK yle
-Ġaccount ing
-ĠTe le
-ĠT yr
-Ġconnect ing
-Ġre hab
-ĠP red
-s im
-Ġmeant ime
-Ġphys ician
-M W
-ĠCamp bell
-ĠBr andon
-Ġcontribut ing
-ĠR ule
-ĠWe ight
-ĠN ap
-Ġinter active
-Ġv ag
-Ġhel met
-ĠCom b
-f our
-Ġsh ipped
-Ġcomple ting
-ĠP D
-PD ATE
-Ġspread ing
-Ġsc ary
-erv ing
-ĠG as
-Ġfr ank
-s chool
-Ġrom antic
-Ġstab il
-R ob
-Ġaccur ately
-Ġac ute
-ĠH ann
-Ġsymbol s
-Ġcivil ization
-ĠA W
-Ġlight ning
-Ġcons iders
-Ġven ue
-Ġ ×
-Ġo ven
-ĠS F
-h is
-Ġn u
-ĠLear n
-Ġpe oples
-Ġst d
-Ġsle e
-Ġs lic
-ĠStat istics
-Ġcor ners
-ĠB aker
-Ġ: )
-ment ation
-ol ver
-Ġlaugh ing
-ĠT odd
-ond e
-ĠH ills
-Ġn uts
-ĠW oman
-pl ane
-Ġl iver
-ĠIn side
-S orry
-Ġagre es
-Ġfund ament
-ĠF isher
-Ġa uction
-Ġthread s
-gl as
-ĠBas ic
-ĠN at
-Ġlack ing
-Ġceleb ration
-j u
-Ġs illy
-E uro
-Ġt att
-ight y
-cont rolled
-T est
-ĠSing h
-Ġr age
-Ġrh yth
-o ffic
-ĠPh antom
-Ġhead lines
-Ġrespond ing
-ĠMor ning
-Ġvit amin
-Ġboot s
-ĠS ite
-al in
-p i
-Ġvir al
-ĠU C
-D ER
-ĠSe x
-Ġst ocks
-c urrent
-Ġch urches
-ĠR are
-ĠMur phy
-Ġden ial
-ĠG aming
-Ġtou g
-Ġn ick
-Ġm akers
-ĠRon ald
-Ġgener ous
-ĠD oc
-ĠMor ris
-Ġtransform ed
-ĠN ormal
-Ġ10 4
-ĠKick starter
-ĠUp on
-On line
-ĠI RS
-Ġw rap
-Ġl oving
-Ġarri ves
-ĠD ue
-Ġhe ter
-ĠM ade
-Ġrent al
-Ġbelong s
-Ġatt orneys
-Ġcro ps
-Ġmat ched
-ul um
-ol ine
-10 9
-Ġdis par
-Ġbuy ers
-ĠCam bridge
-Ġeth ics
-rou ps
-Ġjust ified
-Ġmarg inal
-Ġrespect ed
-win ning
-Ġnodd ed
-ĠSer ge
-ĠForm er
-C raft
-######## ########
-ĠWar ner
-Ġd ash
-et e
-Ġent ert
-ĠE scape
-out heast
-Ġkn ees
-ĠB omb
-Ġr ug
-P ass
-Ġatt itudes
-go vernment
-ĠPri or
-Ġqual ities
-Ġnot ification
-ĠPh one
-l ie
-Ġanticip ated
-ĠCom bat
-ĠBar ry
-Ġ198 2
-Us ers
-on er
-Ġcomput ing
-ĠConnect icut
-Ġless er
-Ġpe ers
-ĠC u
-Ġtechn ically
-Ġsub mission
-ĠUn iversal
-Ġman ually
-our ge
-Ġrespond ents
-ĠB TC
-ĠH ost
-Ġf are
-ĠB ird
-Ġrece ipt
-al so
-Ġj ack
-Ġagric ulture
-Ġsk ull
-Ġ! =
-Ġpass ive
-ĠC I
-Ġsoc ieties
-Ġremind ed
-Ġinter ference
-B uy
-Ġâ ľ
-g on
-Ġscrut iny
-ĠW itch
-Ġconduct ing
-Ġ ãĥ
-Ġexch anges
-ĠMit chell
-Ġinhab it
-Ġtw ist
-B D
-Ġwhere ver
-group on
-Ġj okes
-ĠBen jamin
-ĠR andom
-fr ame
-ĠL ions
-Ġhighlight ed
-ĠArk ansas
-E nt
-Ġp ile
-Ġpre lim
-g s
-mind ed
-Ġfel ony
-ĠG A
-ĠL uck
-Ġpract ically
-ĠB os
-Ġact ress
-D am
-ĠB ou
-Ġvis a
-Ġembed ded
-Ġhy brid
-Ġear liest
-Ġsoon er
-s ocial
-ĠH A
-Ġste ep
-Ġdis advant
-Ġexplo it
-ĠE gg
-ĠUlt ra
-Ġnecess ity
-L ocal
-ie ge
-Ġd ated
-Ġmass es
-Ġsubsc ription
-pl ess
-Ġan onym
-Ġpresum ably
-Bl ue
-The ir
-asket ball
-ĠPhil ip
-Ġcom ed
-load ed
-r ane
-Ġref lection
-Ch ina
-Ġext ends
-Ġform ing
-Ġund ers
-200 1
-Ġgr at
-Ġconcent rations
-Ġins ulin
-Ġsec ular
-Ġwh ilst
-Ġwin ners
-Ad vertisements
-Ġdeliber ately
-ĠWork ing
-Ġs ink
-et ics
-d ale
-Ġmand ate
-Ġg ram
-Ġvac ation
-Ġwarn ings
-ri pp
-ĠTH AT
-Ġcomment ary
-Ġint u
-Ġa est
-Ġreason ing
-Ġbreak down
-ĠZ ombie
-Ġ-- >
-ĠPolit ical
-c ott
-Ġthr ust
-Ġtechn ological
-Ġdec iding
-Ġtraff icking
-L ong
-W elcome
-pr ising
-ĠCommun ications
-Ġend ors
-Ġsw ift
-Ġmetab ol
-co ins
-res a
-ĠHT TP
-Ġen roll
-ĠH appy
-us r
-int age
-Ġ[ "
-u ably
-ĠM aterial
-Ġrepe al
-Se pt
-k h
-ĠMod i
-Ġunder neath
-ĠI L
-sh ore
-Ġdiagn osed
-ace utical
-Ġsh ower
-au x
-ĠSw itch
-ĠStre ngth
-Ġj ihad
-n ational
-Ġtra uma
-uss y
-on i
-Ġcons olid
-Ġcal ories
-ĠF lynn
-ag ged
-16 8
-ĠP ink
-Ġfulf ill
-Ġch ains
-Ġnot ably
-ĠA V
-L ife
-ĠCh uck
-m us
-ĠUr ban
-ĠH end
-Ġdep osit
-ĠS ad
-Ġaff air
-OR K
-ie val
-ĠF DA
-Ġt rop
-ĠOver all
-Ġvirt ue
-Ġsatisf action
-au nd
-Ġl un
-ĠSw itzerland
-ĠOper ation
-pro cess
-Ġsh ook
-Ġcount ies
-le ased
-ĠCharl otte
-1 12
-Ġtrans cript
-Ġre dd
-p ush
-ĠHe y
-ĠAn alysis
-[ "
-Ġaltern atives
-ard less
-Ġele ph
-Ġpre jud
-ĠLe af
-H aving
-ĠH ub
-Ġexpress ions
-ĠVol ume
-Ġshock ing
-ĠRed s
-Ġread ily
-Ġplan ets
-ad ata
-Ġcollaps ed
-ĠMad rid
-Ġir rit
-i pper
-ĠEn c
-ĠW ire
-Ġbu zz
-ĠG P
-ash a
-Ġaccident ally
-ur u
-Ġfrust rated
-ĠS A
-Ġhung ry
-ĠH uff
-Ġlab els
-ant o
-ĠE P
-Ġbar riers
-) |
-ĠBer keley
-ĠJ ets
-Ġp airs
-ĠL an
-J ames
-ĠB ear
-Ġhum or
-ĠLiber ty
-Ġmagn itude
-Ġag ing
-ĠM ason
-Ġfriends hip
-umb ling
-Ġemer ge
-Ġnewsp apers
-Ġam bitious
-ĠRich ards
-atern al
-Ġ198 1
-Ġcook ies
-Ġsc ulpt
-Ġpur suit
-L ocation
-Ġscript s
-p c
-Ġarrang ements
-Ġd iameter
-Ġl oses
-am ation
-Ġl iqu
-ĠJ ake
-aret te
-Ġunderstand s
-ĠZ en
-v m
-Ġappro ve
-Ġw ip
-Ġult ra
-Ġint end
-ĠD I
-asc ular
-Ġst ays
-ĠK or
-ĠK l
-Ġinvest ing
-L a
-Ġbelie ving
-b ad
-m outh
-Ġtaxp ayer
-ãĥ ĥ
-ĠQue bec
-Ġl ap
-ĠSw iss
-d rop
-Ġdr ain
-ir i
-et c
-ft en
-ĠN ex
-Ġst raw
-Ġscream ing
-Ġcount ed
-Ġdam aging
-Ġamb assador
-cent ury
-Ġpro x
-Ġarrest s
-u v
-il ateral
-ĠCh arg
-Ġpresc ribed
-Ġindepend ently
-Ġf ierce
-ĠB aby
-Ġb rave
-Ġsu its
-= >
-Ġbas eline
-ĠR ate
-Ġis lands
-Ġ( (
-g reen
-ix els
-Ġname ly
-ĠVill age
-th an
-am y
-V ersion
-g mail
-ential s
-ĠS ud
-ĠMel bourne
-Ġarri ving
-Ġquant um
-e ff
-rop olitan
-T ri
-Ġfun eral
-ĠI R
-ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ
-ĠC ob
-it ably
-Ġt urb
-Ġcomb o
-Re view
-Ġdeploy ment
-u ity
-ĠB ott
-Ġinv isible
-Ġrender ing
-Ġunl ocked
-Ġa qu
-ĠVlad imir
-Ġp ad
-ĠBr ain
-ĠLeg acy
-dr agon
-ĠKurd ish
-Ġsound ed
-Ġdet ained
-ĠD M
-g ary
-Ġd aughters
-Ġdistur bing
-uk a
-ĠPar ad
-Ġt ast
-Ġunf ortunate
-Ġu l
-em in
-Ġattend ance
-tr l
-Ġpar ks
-ĠMem orial
-ĠAl ice
-oth y
-gu ard
-ĠD ise
-ĠSh an
-ĠFor um
-R ich
-Ġshif ted
-ue z
-Ġl ighter
-ĠMag n
-Ġc od
-S ch
-ham mad
-P ub
-3 50
-ĠP okemon
-Ġprot otype
-Ġun re
-B ase
-ĠStud ents
-ĠRep ly
-ĠCommun ist
-Ġg au
-ĠTy ler
-I Z
-Ġparticip ated
-Ġsup rem
-ĠDet ails
-Ġvessel s
-ro d
-Ġt ribe
-ke ep
-Ġassum ptions
-Ġp ound
-Ġcr ude
-ĠAv ailable
-Ġswim ming
-Ġin clusion
-Ġadv ances
-c ulation
-Ġconserv ation
-Ġover d
-ĠBuff alo
-Art icle
-ed ge
-Ġaw a
-ĠMad ison
-Ġsid ew
-Ġcat ast
-ĠK rist
-uc le
-ĠHigh way
-ĠTer ror
-Ġactiv ation
-Ġuncons cious
-ĠSat an
-ĠSus an
-ill ery
-Ġarr anged
-i op
-Ġrum ors
-ur ring
-th ink
-ĠKe ith
-ĠK ind
-Ġavoid ing
-by n
-n ut
-ĠSpe aker
-r us
-n ames
-Ġgu ilt
-ĠOlymp ics
-Ġsa il
-ĠM es
-lev ant
-ĠColumb us
-a ft
-C ity
-S outh
-ĠHar vey
-ĠP un
-S everal
-Ġment ally
-Ġimp ress
-m ount
-ĠUb untu
-âĢĶâĢĶâĢĶâĢĶ âĢĶâĢĶâĢĶâĢĶ
-ĠSuper man
-ĠMP s
-Ġintent ions
-ĠR acing
-Ġlike lihood
-Ġ2 40
-T otal
-Ġto ys
-ĠW atson
-Ġur ge
-L ear
-ĠP aper
-Ġoccur ring
-ĠB eng
-ĠC ert
-Ġst ones
-T im
-ĠTw in
-z b
-ĠD ynam
-Ġpolit ician
-k ens
-ĠEnter prise
-UT ERS
-Ġab ol
-Ġref resh
-Ġarbit rary
-pe ction
-Ġtrou bles
-Ġ} );
-t v
-Ġpil ots
-Ġdist ribute
-Ġaud it
-Ġp ause
-orig inal
-Ġr ivals
-Â £
-F ig
-T L
-ab il
-ry ing
-L in
-ion ed
-l on
-Ġf ancy
-Ġcr ashed
-Ġt ract
-Ġshe d
-Ġcons ume
-B ased
-down load
-in it
-Ġvolt age
-Int rodu
-Ġcondem ned
-ĠFin ance
-res pect
-Ġex cluded
-Ġestablish ing
-her ic
-Ġher itage
-Ġspect acular
-Ġun st
-ĠSnow den
-ĠL ane
-S an
-Ġprotect ions
-st ruction
-inc inn
-Ġmac ro
-C ustom
-ios ity
-Ġes p
-Ġfunction ing
-Ġm ush
-Ġp uzzle
-Ġeth ical
-M al
-Ġgo verning
-ĠF erguson
-Ġrest ored
-Ġst ressed
-ĠCoun ter
-ĠK as
-cl ip
-AN S
-Ġse iz
-U K
-by ss
-old own
-ap i
-Ġperman ently
-oun ters
-W est
-Th rough
-L ight
-at oes
-Ġne at
-Ġc ord
-ure r
-Ġsevere ly
-ĠA ven
-Ġinter rog
-Ġtri ple
-G iven
-N umber
-Ġar ise
-Ġs her
-pl ant
-Ġfl ower
-ĠC ou
-Ġat e
-Ġnew er
-b ul
-Ġmean while
-ĠL air
-Ġadjust ment
-ĠCop yright
-Ġd ivers
-i ological
-Ġgam ers
-o at
-Ġhistor ically
-Ġanal og
-Ġlong time
-Ġpres cription
-ĠM ist
-ĠHy per
-ĠM aine
-ĠDe ity
-Ġmulti pl
-ĠRe incarn
-ĠH yd
-ĠP ic
-S il
-r ants
-ĠC ris
-. ;
-( {
-epend ence
-Ġrec y
-ate ur
-Ġqu ad
-Ġgl ob
-Ġcon ced
-te am
-Ġcapital ist
-ĠL ot
-Ġroy al
-ĠCy ber
-Ġblack s
-met ic
-ri v
-ĠD anny
-Ġsp o
-ĠR O
-Ġanim ated
-rypt ed
-ĠDep uty
-Ġrend ered
-F E
-Ġstre ak
-Ġcloud s
-ĠDou g
-~~~~ ~~~~
-Ġdisc our
-ĠVe h
-Ġpsych ology
-ĠJ ourney
-Ġcry stal
-ĠFro st
-Ġsuspic ion
-Ġrel ate
-or us
-ĠC rypt
-ĠN VIDIA
-com ed
-ut ing
-incinn ati
-Ġvulner ability
-ost ic
-Ġisol ation
-Ġcool ing
-ĠCoal ition
-Ġ1 19
-F our
-ĠDe al
-Ġâ ī
-se mble
-ram ent
-ĠBar celona
-Ġ10 2
-Ġcoc aine
-ocaly pse
-F eb
-ogen ic
-Ġmut ation
-Ġcrypt oc
-ĠK el
-ĠG it
-a is
-Ġs isters
-AN K
-Ġactiv ate
-T er
-Ġd read
-yl on
-Ġprop ri
-A ust
-ĠDef ault
-Ġout door
-Ġshe er
-ce ive
-Ġg ently
-Ð ¾
-Pro gram
-Ġâ ĨĴ
-Ġve gan
-ĠCr us
-Ġrespons ibilities
-ĠH R
-OL D
-Ġprev ents
-Ġst iff
-ĠW ere
-Ġathlet ic
-ĠSc ore
-Ġ) :
-Ġcolumn s
-ĠL oc
-av ailable
-ĠF ram
-ĠS essions
-Ġcompan ion
-Ġpack s
-14 0
-ĠKn ights
-Ġf art
-Ġstream s
-Ġsh ore
-Ġapp eals
-ĠPer formance
-h aul
-ĠSt ra
-ĠN ag
-10 3
-ĠTrans portation
-B B
-E v
-z an
-P ublic
-Ġtw in
-uls ion
-M ult
-Ġelect ro
-Ġstat ue
-ation ally
-ĠN ort
-Ġins pection
-/ *
-ig ue
-Ġcomp assion
-ĠT ales
-ĠSte in
-ĠSc reen
-ĠB ug
-ĠL ion
-g irl
-Ġwithdraw al
-Ġobject ives
-Ġblood y
-Ġprelim inary
-Ġj acket
-Ġdim ensions
-ĠC ool
-ĠOcc up
-Ġw reck
-Ġdoub led
-ank ing
-Ġ19 75
-Ġglass es
-ĠW ang
-pro v
-P ath
-connect ed
-ĠMult i
-ĠNor way
-agon ist
-Ġfe ared
-Ġtouch ing
-Ġarg uably
-¯¯¯¯ ¯¯¯¯
-ĠNC AA
-che m
-Ġsp at
-ĠW WE
-ĠC el
-ig ger
-Ġattack er
-ĠJo in
-ob ject
-ett a
-Ġelim inated
-d et
-Ġdest ruct
-ĠLuc as
-ct uary
-18 0
-ĠBr ady
-ĠBl ues
-B ay
-au kee
-Ġtim eline
-Ġdeleg ates
-w ritten
-uff icient
-Ġsh apes
-Cop yright
-ou ble
-serv ice
-Ġp ione
-Ġcolleg es
-Ġrow s
-Ġsp ite
-Ġassess ed
-3 60
-Ġle ase
-Ġconfident ial
-ck er
-ĠMan ning
-ĠV oice
-Ġse aled
-Ġcalcul ate
-N O
-ĠAss istant
-Ġteen ager
-ul ent
-ather ine
-Ġm ock
-Ġd iamond
-Ġf est
-Ġsw itched
-Ġres ume
-ĠPu erto
-Ġl anes
-ir ation
-ĠSimilar ly
-Ġro d
-ĠS el
-ĠPal ace
-ĠLim ited
-e ous
-Ġvar iant
-Ġw ard
-Ġ) )
-Sh ow
-OO K
-A lex
-ĠN ep
-br is
-ĠWik ipedia
-Ġexcept ional
-Ġman ages
-ĠD raw
-Ag ain
-Ġco pper
-ut t
-Ġex ports
-Ġport folio
-Ġelev ated
-R ated
-ĠOther wise
-ĠT act
-ĠShe l
-ĠT X
-" âĢĶ
-Ġres ur
-ĠW a
-ven ant
-Ġmon etary
-pe ople
-E mail
-Ġfif ty
-ĠS weet
-ĠMalays ia
-Ġconf using
-ĠR io
-ud a
-uten ant
-" );
-Ġpra ised
-Ġvol umes
-t urn
-Ġm ature
-Ġnon profit
-Ġpassion ate
-ĠPriv ate
-Ġ10 3
-Ġdesc end
-ç ¥ŀ
-uff y
-head ed
-Whe ther
-ri en
-ze ch
-be it
-Ġch rom
-ĠMc M
-Ġd ancing
-Ġe leg
-ĠNot iced
-11 5
-Ġadvoc acy
-ENT S
-amb ling
-ĠMin or
-ĠF inn
-Ġprior ities
-Ġthere of
-ĠSt age
-ĠRog ers
-Ġsubst itute
-ĠJ ar
-ĠJeff erson
-Ġlight ly
-10 2
-ĠL isa
-u its
-ys ical
-Ġshif ts
-Ġd rones
-Ġwork place
-Ġres id
-ens ed
-ah n
-Ġpref erences
-ser ver
-Ġdeb ates
-d oc
-ĠGod s
-Ġhelicop ter
-Ġhon our
-Ġconsider ably
-ed ed
-ĠF emale
-ĠAn ne
-Ġre un
-ĠF ace
-ĠHall ow
-ĠBud get
-Ġcondem n
-Ġt ender
-Pro f
-ocr atic
-ĠTurn er
-ĠAg ric
-Ġ19 76
-Ġa pt
-d isc
-ĠF ighter
-ĠA ur
-Ġgar bage
-in put
-ĠK arl
-ĠOl iver
-ĠL anguage
-k n
-N on
-ĠCl ar
-Ġtrad itions
-Ġad vertisement
-ĠS or
-Ġarch ive
-Ġvill ages
-7 50
-Ġimplement ing
-w aukee
-Ġdiet ary
-Ġswitch ing
-Rep ublic
-Ġvel ocity
-Ġc it
-ĠA wards
-Ġfin ancing
-Ġlast ed
-) ]
-Ġrem inder
-P erson
-Ġprec ision
-Ġdesign ers
-ĠF ried
-ĠB order
-Ġtr agic
-Ġw ield
-Ġiniti atives
-ĠT ank
-w er
-Ġjo ins
-R o
-in ery
-Ġar row
-Ġgener ating
-found er
-Ġsear ches
-Ġrandom ly
-A ccess
-Ġb atch
-Ġp osed
-l at
-Ġpursu ing
-as a
-Ġtest ified
-form ing
-ĠSh ar
-w iki
-ĠE ither
-S ometimes
-Ġsen ators
-ĠJohn ny
-ĠTal iban
-ĠG PS
-":" /
-ãģ® å
-Ġanaly zed
-ĠRub io
-ĠMove ment
-op ard
-ii i
-St and
-f ight
-Ġign oring
-i ang
-ĠG N
-so ever
-ĠST AT
-Ġref using
-Ġswe at
-Ġb ay
-P ORT
-ir med
-ak y
-Ġdis pro
-Ġlabel ed
-Ġ10 8
-H ello
-Ġple asant
-ab a
-Ġtri umph
-Ġab oard
-Ġinc om
-ĠC row
-le tt
-Ġfol k
-Ġch ase
-` `
-ĠBr us
-Ġte ens
-c ue
-Ġter rain
-h yd
-il ight
-OR Y
-Su pport
-ew s
-ll i
-rain ts
-ĠC and
-Ġab used
-ach ment
-l arg
-B as
-ĠC ancer
-Ġ19 78
-Ġsupp orter
-ac cess
-ĠTer min
-ĠT ampa
-ĠAN Y
-Ġnew est
-ĠCrim inal
-ed u
-Ġ19 30
-Ġadm its
-Ġend e
-Ġfail ures
-ur ate
-ful ness
-cy cl
-ĠSub ject
-Ġinf inite
-th ree
-W A
-p it
-ĠInst all
-R ad
-ili ation
-G M
-Ġcontin ent
-Ġaccommod ate
-ĠCl ay
-Ġp up
-ĠF unction
-Ġham mer
-ĠAlbert a
-Ġrev ised
-Ġminor ities
-Ġmeasure ment
-Con nell
-Ġdis able
-ĠM ix
-In cre
-Ġfor k
-ĠR osen
-Ġimpl ies
-umb lr
-AN G
-Ġprote ins
-Ġagg ression
-Ġfacilit ate
-S N
-Ġilleg ally
-u er
-Ġacad em
-Ġp uzz
-ĠSh ift
-p ay
-oll o
-Ġaud iences
-B uild
-Ġno ble
-Ġsynt ax
-â ĺħ
-Ġbe am
-ĠB ed
-ĠA ld
-Ġorig ins
-v ideo
-Ġ19 77
-ĠAss ault
-Ġgar age
-Te am
-Ġver dict
-Ġd war
-ĠVirt ual
-e vent
-Ke ep
-Ġsent iment
-Ġwild life
-sh irt
-Ġb urg
-Ġrecommend ation
-rep resent
-Ġgall ery
-own ers
-Ġsch olar
-Ġconven ience
-ĠSw ift
-Ġconv inc
-C ap
-Ġwar fare
-ĠVis ual
-Ġconst itute
-Ġab ort
-ĠWe ather
-ĠLook ing
-ĠH em
-Ġmart ial
-Ġinc oming
-et ition
-Ġtoler ance
-ĠCre ated
-Ġfl ows
-ĠE lder
-Ġsoul s
-Ġf oul
-ĠP ain
-ĠC AN
-Ġ2 20
-b c
-he nd
-Ġgen ius
-R eal
-ĠW r
-omet er
-p ad
-Ġlim iting
-ĠS i
-ĠL ore
-ĠAd ventures
-Ġvar ied
-D isc
-f in
-ĠPerson al
-Ch ris
-Ġinv ented
-Ġd ive
-ĠR ise
-Ġo z
-ĠCom ics
-Ġexp ose
-ĠRe b
-let ters
-s ite
-im ated
-Ġh acking
-Ġeduc ated
-ĠNob ody
-Ġdep ri
-Ġincent ive
-ãĤ ·
-Ġovers ight
-Ġtrib es
-ĠBelg ium
-Ġlicens ing
-our t
-Produ ct
-ah l
-ĠG em
-Ġspecial ist
-Ġc ra
-ann ers
-ĠCor byn
-Ġ19 73
-RE AD
-Ġsum mar
-Ġover look
-ĠApp lication
-Ġin appropriate
-Ġdownload ed
-Q ue
-ĠB ears
-Ġth umb
-ĠChar acter
-ĠReincarn ated
-ĠS id
-Ġdemonstr ates
-s ky
-ĠBloom berg
-ĠAr ray
-ĠRes ults
-ĠFour th
-ĠED T
-ĠO scar
-c end
-Ġ10 6
-ĠN ULL
-ĠH ERE
-m atch
-ĠBr un
-Ġgluc ose
-ie g
-eg u
-Ġcert ified
-Ġrel ie
-Ġhuman itarian
-Ġpr ayers
-K ing
-Ġn an
-h ou
-10 8
-ul u
-Ġrenew able
-Ġdistingu ish
-Ġd ense
-ĠV ent
-ĠPack age
-ĠB oss
-Ġedit ors
-Ġm igr
-T ra
-ĠPet ers
-ĠAr ctic
-200 4
-ĠC ape
-Ġloc ally
-Ġlast ing
-Ġhand y
-. ).
-P an
-ĠR ES
-Ind ex
-Ġt ensions
-Ġformer ly
-Ġide ological
-Ġsens ors
-Ġdeal ers
-Ġdef ines
-S k
-Ġproceed s
-Ġpro xy
-az ines
-ĠB ash
-ĠP ad
-ĠC raft
-eal ous
-Ġshe ets
-omet ry
-J une
-cl ock
-T T
-ĠThe atre
-ĠB uzz
-Ġch apters
-Ġmill enn
-Ġd ough
-ĠCongress ional
-Ġimag ined
-av ior
-Ġclin ic
-Ġ19 45
-Ġhold er
-ro ot
-oles ter
-Ġrest art
-B N
-ĠHam as
-ĠJ ob
-Ġor b
-Ġr am
-Ġdiscl ose
-Ġtransl ate
-Ġimm igrant
-Ġannoy ing
-Ġtreat y
-an ium
-ĠTe a
-ĠLeg ion
-Ġcrowd s
-ĠB ec
-ĠA er
-oh yd
-B ro
-Look ing
-Ġl bs
-Ġagg ress
-Ġse am
-Ġinter cept
-ĠM I
-mer cial
-act iv
-ĠC it
-Ġdim ension
-Ġconsist ency
-Ġr ushing
-ĠDou glas
-Ġtr im
-Inst all
-ick er
-Ġsh y
-10 6
-Ġment ions
-pe lled
-ĠT ak
-c ost
-Ġclass room
-Ġfort une
-dri ven
-Ġun le
-ĠWhe el
-Ġinvest or
-ĠM asters
-k it
-Ġassoci ations
-ĠEv olution
-op ing
-us cript
-Ġprov incial
-ĠWal ter
-av i
-S O
-Ġun limited
-Eng lish
-ĠC ards
-ĠEb ola
-ne red
-Ġreven ge
-Ġout right
-um per
-Ġf itting
-ĠSol id
-Ġform ally
-Ġproblem atic
-Ġhaz ard
-Ġenc ryption
-Ġstraight forward
-ĠA K
-Ġp se
-ĠOr b
-ĠCh amber
-ĠM ak
-Cont ents
-Ġloyal ty
-Ġl yrics
-ĠSy m
-Ġwel comed
-Ġcook ed
-Ġmon op
-Ġn urse
-Ġmis leading
-Ġe ternal
-Ġshif ting
-Ġ+ =
-V is
-Ġinst itutional
-ill ary
-Ġp ant
-VER T
-ĠA CC
-ĠEn h
-Ġinc on
-ĠRE UTERS
-Ġdon ated
-âĢ¦âĢ¦ âĢ¦âĢ¦
-In tern
-Ġexhib it
-Ġt ire
-ĠR ic
-ĠCh ampion
-ĠMu hammad
-N ING
-ĠSoc cer
-Ġmob ility
-Ġvary ing
-ĠM ovie
-Ġl ord
-o ak
-F ield
-Ġve ctor
-us ions
-Ġsc rap
-Ġen abling
-m ake
-T or
-. *
-| |
-ĠWe bsite
-ĠN PC
-Ġsocial ist
-ĠBill y
-ĠAdd itional
-Ġc argo
-Ġfar ms
-ĠSo on
-ĠPri ze
-Ġmid night
-Ġ9 00
-se en
-ĠSp ot
-Ġshe ep
-Ġspons ored
-ĠH i
-ĠJ ump
-Ġ19 67
-Micro soft
-ĠAg ent
-Ġch arts
-d ir
-Ġadj acent
-Ġtr icks
-Ġman ga
-Ġex agger
-/ >
-foot ball
-ĠF CC
-G C
-ĠT ier
-and ra
-OU ND
-% ),
-Ġfru its
-V C
-ĠA A
-R ober
-Ġmid st
-â Ĺ
-ank a
-Ġlegisl ature
-ĠNe il
-Ġtour ists
-" "
-ĠWar ning
-ĠNever theless
-ĠOffic ial
-ĠWh atever
-Ġm old
-Ġdraft ed
-Ġsubst ances
-Ġbre ed
-Ġt ags
-ĠT ask
-Ġver b
-Ġmanufact ured
-com ments
-ĠPol ish
-Pro v
-Ġdetermin es
-Ob ama
-k ers
-Ġutter ly
-Ġse ct
-sc he
-ĠG ates
-ĠCh ap
-Ġal uminum
-Ġz ombie
-ĠT ouch
-ĠU P
-Ġsatisf y
-Ġpred omin
-asc ript
-Ġelabor ate
-Ġ19 68
-Ġmeas uring
-ĠV ari
-any ahu
-Ġs ir
-ul ates
-id ges
-ick ets
-ĠSp encer
-T M
-oub ted
-Ġpre y
-Ġinstall ing
-ĠC ab
-re ed
-re ated
-Su pp
-Ġwr ist
-ĠK erry
-10 7
-ĠK le
-ĠR achel
-Ġc otton
-ĠA RE
-ĠE le
-Cont rol
-Ġload s
-ĠD od
-an as
-b one
-Ġclass ical
-ĠReg ional
-ĠInt eg
-V M
-Ġdes ires
-Ġaut ism
-support ed
-ĠM essage
-Ġcomp act
-writ er
-Ġ10 9
-ĠHur ricane
-c ision
-Ġcy cles
-Ġdr ill
-Ġcolle ague
-Ġm aker
-G erman
-Ġmist aken
-S un
-ĠG ay
-Ġwhat soever
-Ġsell s
-ĠA irl
-l iv
-ĠO ption
-Ġsol ved
-Ġse ctors
-Ġhorizont al
-Ġequ ation
-ĠSk ill
-ĠB io
-g ement
-ĠSn ap
-ĠLeg al
-Ġtradem ark
-Ġmake up
-Ġassemb led
-Ġsa ves
-ĠHallow een
-ĠVer mont
-ĠFR OM
-Ġfar ming
-ĠP odcast
-accept able
-ĠHig her
-Ġas leep
-ull ivan
-Ġrefere n
-ĠLe v
-Ġbul lets
-ok o
-H C
-Ġst airs
-Ġmain tains
-ĠL ower
-ĠV i
-Ġmar ine
-Ġac res
-Ġcoordin ator
-ĠJ oh
-Ġcounterpart s
-ĠBrother s
-Ġind ict
-b ra
-Ġch unk
-Ġc ents
-H ome
-ĠMon th
-Ġaccording ly
-if les
-ĠGerm ans
-ĠSy n
-H ub
-Ġey eb
-âĶĢâĶĢ âĶĢâĶĢ
-Ġr anges
-ĠHoll and
-ĠRob ot
-f c
-M ike
-Ġpl asma
-Ġsw ap
-Ġath lete
-ĠR ams
-,' "
-Ġinfect ions
-Ġcor rid
-Ġv ib
-Ġpat ches
-Ġtradition ally
-Ġrevel ation
-Ġswe ep
-Ġgl ance
-Ġin ex
-200 3
-ĠR aw
-work ing
-os ures
-ĠD at
-ĠLyn ch
-Ġle verage
-ĠRe id
-Ġcorrel ation
-ian ces
-av ascript
-Ġrep ository
-ret ty
-Ġ19 72
-24 0
-Ġo un
-p ol
-ĠRe ed
-Ġtact ical
-is ite
-App le
-ĠQu inn
-Ġrap ed
-ill o
-Euro pe
-Ġalgorith ms
-ĠRod rig
-i u
-Ġill um
-Ġf ame
-Ġintrodu cing
-Ġdel ays
-ĠRaid ers
-Ġwh istle
-Ġnovel s
-ĠRe ally
-Ġder iv
-Ġpublic ations
-ĠNe ither
-ĠCom merce
-Ġa ston
-l anguage
-Not es
-ĠR oth
-ĠF ear
-Ġm ate
-Ġpar ade
-ĠQ B
-Ġman eu
-ĠC incinnati
-m itting
-Ġwa ist
-ĠR ew
-Ġdisc ont
-Ð °
-Ġst aring
-Ġal ias
-Ġsec urities
-Ġtoile t
-ĠJ edi
-Ġun law
-v ised
-//// ////
-] (
-ĠWe iss
-Ġpre st
-ĠComp an
-Ġmem o
-ĠGr ace
-J uly
-ĠEl ite
-cent er
-ĠSt ay
-Ġgal axy
-Ġto oth
-ĠS ettings
-Ġsubject ed
-ãĤ ¦
-Ġline back
-Ġretail ers
-ĠW ant
-Ġd angers
-A ir
-Ġvolunt ary
-ew ay
-Ġinterpret ed
-ot ine
-Ã §
-Ġp el
-Serv ice
-ĠEvent ually
-Ġcare ers
-Ġthreat en
-Ġmem or
-ĠBrad ley
-anc ies
-s n
-ĠUn known
-N ational
-Ġsh adows
-ail and
-ĠD ash
-Every one
-izz ard
-M arch
-= (
-Ġpull s
-Ġstr anger
-Ġback wards
-ĠBern ard
-imens ional
-Ġch ron
-Ġtheoret ical
-k top
-Ġw are
-ĠInvest ig
-ĠIn iti
-ĠOper ations
-o ven
-oc ide
-* /
-Ġfl ames
-ĠC ash
-sh it
-Ġc ab
-ĠAn aly
-ĠSe ah
-Ġdefin ing
-Ġorder ing
-Ġimm un
-Ġpers istent
-AC H
-Russ ian
-m ans
-Ġh ind
-Ġphot ography
-Â ©
-Ġh ug
-Ġ10 7
-ĠH ence
-i ots
-ude au
-Ġsubsid ies
-Ġroutine ly
-ĠDev ice
-it ic
-Ġdisg ust
-land er
-Ġ19 40
-Ġassign ment
-ĠB esides
-w ick
-ĠD ust
-us c
-struct ed
-11 1
-de velop
-Ġf ond
-Ġinter section
-Ġdign ity
-Ġcommission er
-With out
-re ach
-Ġcart oon
-Ġsc ales
-ãĥ Ń
-F IG
-Ġsurve ys
-ĠIndones ia
-Ġart work
-Ġun ch
-Ġcy cling
-un ct
-au er
-or ate
-ĠOb viously
-Ġcharacter ized
-fe ld
-Ġaff irm
-Ġinn ings
-Ġ é
-Ġal iens
-Ġcl oth
-et ooth
-ĠC ertain
-Â §
-Ġdig est
-k now
-ĠX L
-Ġpredict ions
-Ġd in
-W AR
-Ġafter math
-Ex ample
-ĠSu ccess
-ĠTh r
-IG N
-Ġmin er
-B us
-Ġcl arity
-heim er
-ĠO UT
-ĠS end
-ĠCirc le
-ĠD iet
-Ġpron ounced
-Ġcreat ors
-Ġearthqu ake
-atter y
-ge ons
-Ġo d
-Ġlay ing
-or p
-U lt
-pro ject
-Ġunder min
-Ġsequ el
-S am
-ĠDark ness
-Ġre ception
-b ull
-Y S
-ĠV ir
-Ġsequ ences
-ĠCo in
-Ġout fit
-ĠW ait
-1 19
-Ġdel ivers
-.... ..
-Ġbl own
-ĠE sc
-ĠM ath
-per m
-ĠU l
-Ġgl im
-Ġfac ial
-Ġgreen house
-Ġto kens
-/ -
-ĠAnn ual
-ĠON E
-Ġteen age
-ĠPhys ical
-ĠL ang
-ĠC elt
-Ġsu ed
-ivid ually
-Ġpat ience
-ch air
-reg ular
-Ġa ug
-in v
-ex cept
-ĠL il
-Ġn est
-f d
-s um
-ĠCh ase
-Russ ia
-ĠJenn ifer
-Ġoff season
-Over all
-F ore
-Ġr iot
-A ud
-form er
-Ġdefend ers
-ĠC T
-iot ic
-rib ly
-Ġautom ated
-Ġpen is
-Ġins ist
-Ġdi agram
-ĠS QL
-ĠG arc
-Ġw itch
-cl ient
-ier ra
-am bers
-Ġrec ount
-f ar
-V ery
-oster one
-Ġappreci ated
-ĠPer fect
-S ection
-Ġd oses
-oca ust
-Ġcost ly
-Ġg rams
-ĠSh i
-Ġwrest ling
-Ġ19 71
-Ġtro phy
-Ġn erve
-ĠK az
-ĠExper ience
-Ġpled ged
-Ġplay back
-Ġcreat ivity
-by e
-Ġattack ers
-Ġhold ers
-ĠCo ach
-ĠPh D
-Ġtransf ers
-Ġcol ored
-ĠH indu
-Ġd rown
-Ġlist ened
-ĠW A
-ias m
-P O
-Ġappeal ing
-Ġdiscl osed
-ĠCh icken
-ag ging
-Ġple aded
-Ġnav igation
-ĠReturn s
-Ġ[ [
-R OR
-E A
-Ġphotograp her
-ĠR ider
-ipp ers
-Ġsl ice
-Ġe rect
-Ġhe d
-iss ance
-ĠVik ings
-ur ious
-Ġapp et
-oubted ly
-Ch ild
-Ġauthent ic
-o os
-ĠM aking
-Ġannoun cing
-Ġb od
-Ġmet er
-ĠN ine
-ĠR ogue
-Ġwork force
-Ġrenew ed
-Ġorganis ations
-ac s
-P LE
-Sh ort
-Ġcomp ounds
-ĠVis it
-Ġen velop
-ear th
-Ġsupport ive
-gg le
-ĠBrus sels
-ĠGu ild
-Cre ate
-RE L
-Ġaver aged
-Ġ19 69
-ri ages
-Ġlength y
-Ġforg ot
-O kay
-ĠE rd
-Ġdeal er
-Ġrec ession
-D D
-Ġdesper ately
-Ġhun ger
-Ġst icks
-Ġm ph
-ĠF aith
-Ġintention ally
-Ġdem ol
-ue ller
-ĠS ale
-Ġde bris
-s pring
-Ġle ap
->> >>
-Ġcontain ers
-se lling
-rane an
-atter ing
-Ġcomment ed
-ĠC M
-on ut
-Ġwood s
-es pecially
-Ġorgan ize
-iv ic
-ĠWood s
-ang a
-s qu
-Ġm aj
-am on
-Ġax is
-Ġ19 74
-ĠDen mark
-Ġwar rior
-ĠP and
-Ġout lined
-ĠB O
-ins ula
-z illa
-eb ook
-Ġd are
-Ġsear ched
-Ġnav igate
-S n
-writ ing
-Ġun ited
-J apan
-ĠHe brew
-Ġfl ame
-Ġrel ies
-Ġcatch ing
-ĠSh o
-Ġimprison ment
-Ġp ockets
-Ġclos ure
-ĠF am
-t im
-ade qu
-Act ivity
-Ġrecru iting
-ĠW ATCH
-ĠArgent ina
-d est
-Ġapolog ize
-or o
-Ġlack s
-Ġtun ed
-ĠGriff in
-Ġinf amous
-Ġcelebr ity
-ss on
-Ġ ----------------------------------------------------------------
-ĠIs is
-ĠDis play
-Ġcred ibility
-Ġeconom ies
-Ġhead line
-ĠCow boys
-Ġind ef
-Ġl ately
-Ġincent ives
-but ton
-ĠM ob
-A ut
-Ġres igned
-ĠO m
-c amp
-Ġprof iles
-Ġsche mes
-olph ins
-ay ed
-Cl inton
-en h
-ĠY ahoo
-Ġab st
-Ġan k
-su its
-Ġw ished
-ĠMar co
-udd en
-Ġsp here
-ĠB ishop
-Ġincorpor ated
-ĠPl ant
-11 4
-Ġh ated
-p ic
-Ġdon ate
-Ġl ined
-Ġbe ans
-Ġsteal ing
-Ġcost ume
-Ġsher iff
-Ġfor ty
-Ġint act
-Ġadapt ed
-Ġtrave lling
-b art
-Ġnice ly
-Ġdri ed
-Ġsc al
-os ity
-NOT E
-ĠB h
-ĠBron cos
-ĠI gn
-Ġint imate
-Ġchem istry
-Ġopt imal
-D eb
-ĠGener ation
-Ġ] ,
-ich i
-ĠW ii
-ĠYOU R
-vent ions
-W rite
-Ġpop ul
-un ning
-ĠW or
-V ol
-Ġqu een
-head s
-K K
-Ġanaly ze
-op ic
-ear chers
-Ġd ot
-leg raph
-ast ically
-Ġupgr ades
-Ġca res
-Ġext ending
-Ġfree ze
-Ġin ability
-Ġorg ans
-Ġpret end
-Ġout let
-11 3
-ol an
-ĠM all
-ul ing
-t alk
-Ġexpress ing
-ĠAl ways
-ĠBe gin
-f iles
-Ġlic enses
-% %
-ĠM itt
-Ġfil ters
-ĠMil waukee
-G N
-Ġunf old
-M o
-Ġnut rition
-pp o
-B o
-Ġfound ing
-Ġunder mine
-Ġeas iest
-ĠC zech
-ĠM ack
-Ġsexual ity
-ĠN ixon
-W in
-ĠAr n
-ĠK in
-ãĤ £
-ic er
-Ġfort un
-Ġsurf aces
-agh d
-Ġcar riers
-ĠP ART
-ĠT ib
-Ġinter val
-Ġfrust rating
-ĠSh ip
-ĠAr med
-ff e
-Ġbo ats
-ĠAb raham
-in is
-Ġsu ited
-th read
-i ov
-ab ul
-ĠVenezuel a
-Ġto m
-su per
-Ġcast le
-alth ough
-iox ide
-ec hes
-Ġevolution ary
-Ġnegoti ate
-Ġconfront ed
-Rem ember
-Ġ17 0
-S uch
-Ġ9 11
-m ult
-ĠA byss
-ur ry
-ke es
-spe c
-ĠBarb ara
-Ġbelong ing
-Ġvill ain
-ist ani
-Ġaccount able
-Ġport ions
-ĠDe cl
-U r
-ĠK ate
-g re
-Ġmag azines
-UC K
-Ġregul ate
-om on
-ĠAl most
-Ġover view
-Ġsc ram
-Ġl oot
-ĠF itz
-Ġcharacter istic
-ĠSn ake
-s ay
-ĠR ico
-Ġtra it
-ĠJo ined
-au cus
-Ġadapt ation
-ĠAirl ines
-Ġarch ae
-ĠI de
-Ġb ikes
-Ġliter ary
-Ġinflu ences
-ĠUs ed
-C reat
-Ġple a
-ĠDef ence
-ĠAss ass
-Ġp ond
-UL T
-) "
-Ġeval uated
-Ġob taining
-Ġdem ographic
-Ġvig il
-ale y
-Ġsp ouse
-ĠSeah awks
-resp ons
-ĠB elt
-um atic
-Ġr ises
-run ner
-ĠMichel le
-Ġpot ent
-r ace
-ĠP AC
-F ind
-olester ol
-IS S
-ĠIntrodu ced
-ress es
-ign ment
-O s
-ĠT u
-ĠDe x
-ic ides
-Ġspark ed
-ĠLaur a
-ĠBry ant
-Ġsm iling
-ĠNex us
-Ġdefend ants
-ĠCat al
-Ġdis hes
-sh aped
-Ġpro long
-m t
-( $
-ãĢ Ĥ
-Ġcalcul ations
-ĠS ame
-Ġp iv
-H H
-Ġcance lled
-Ġgr in
-Ġterrit ories
-ist ically
-C ome
-ĠP arent
-Pro ject
-Ġneg lig
-ĠPriv acy
-Ġam mo
-LE CT
-olute ly
-ĠEp ic
-Ġmis under
-w al
-Apr il
-m os
-path y
-ĠC arson
-Ġalbum s
-ĠE asy
-Ġpist ol
-< <
-Ġ\ (
-t arget
-hel p
-Ġinter pre
-cons cious
-ĠH ousing
-ĠJ oint
-12 7
-Ġbe ers
-s cience
-ĠFire fox
-effect ive
-ĠC abin
-ĠO kay
-ĠApp lic
-Ġspace craft
-ĠS R
-ve t
-ĠStr ange
-S B
-Ġcor ps
-iber al
-e fficient
-Ġpreval ence
-Ġeconom ists
-11 8
-Th read
-ord able
-OD E
-ĠC ant
-=- =-
-if iable
-ĠA round
-Ġpo le
-Ġwilling ness
-CL A
-ĠK id
-Ġcomple ment
-Ġsc attered
-Ġin mates
-Ġble eding
-e very
-Ġque ue
-ĠTr ain
-Ġh ij
-Ġme lee
-ple ted
-Ġdig it
-Ġg em
-offic ial
-Ġlif ting
-Ð µ
-Re qu
-it utes
-Ġpack aging
-ĠWork ers
-h ran
-ĠLeban on
-ol esc
-Ġpun ished
-ĠJ uan
-Ġj am
-ĠD ocument
-Ġm apping
-ic ates
-Ġinev itably
-Ġvan illa
-ĠT on
-Ġwat ches
-Ġle agues
-Ġiniti ated
-deg ree
-port ion
-Ġrec alls
-Ġru in
-Ġm elt
-I AN
-Ġhe m
-Ex p
-Ġb aking
-ĠCol omb
-at ible
-Ġrad ius
-pl ug
-ĠI F
-et ically
-Ġf ict
-H ER
-ĠT ap
-atin um
-Ġin k
-Ġco h
-ĠW izard
-b oth
-te x
-Ġsp ends
-ĠCurrent ly
-ĠP it
-Ġneur ons
-ig nt
-Ġr all
-Ġbus es
-b uilding
-Ġadjust ments
-Ġc ried
-ibl ical
-att ed
-ĠZ ion
-ĠM atter
-Ġmed itation
-ĠD ennis
-Ġour s
-ĠT ab
-Ġrank ings
-ort al
-Ġad vers
-Ġsur render
-ĠG ob
-ci um
-om as
-im eter
-Ġmulti player
-Ġhero in
-Ġoptim istic
-Ġindic ator
-ĠBr ig
-Ġgro cery
-Ġapplic ant
-ĠRock et
-v id
-Ex ception
-p ent
-Ġorgan izing
-Ġenc ounters
-ĠT OD
-Ġjew el
-S ave
-ĠChrist ie
-Ġhe ating
-Ġl azy
-ĠC P
-Ġcous in
-Con fig
-Ġreg ener
-Ġne arest
-Ġachie ving
-EN S
-th row
-ĠRich mond
-ant le
-200 2
-Ġan ten
-b ird
-13 3
-Ġn arc
-r aint
-un ny
-ĠHispan ic
-ourn aments
-Ġprop he
-ĠTh ailand
-ĠT i
-Ġinject ion
-Ġinher it
-rav is
-Ġmed i
-Ġwho ever
-ĠDE BUG
-G P
-ĠH ud
-C ard
-p rom
-Ġp or
-Ġover head
-L aw
-Ġviol ate
-Ġhe ated
-Ġdescript ions
-Ġachieve ments
-ĠBe er
-ĠQu ant
-W as
-Ġe ighth
-ĠI v
-Ġspecial ized
-U PDATE
-ĠD elta
-P op
-J ul
-ĠAs k
-oph y
-Ġnews letters
-ĠT ool
-Ġg ard
-ĠConf eder
-ĠGM T
-ĠAb bott
-Ġimm unity
-ĠV M
-Is lam
-Ġimpl icit
-w d
-Ġ19 44
-rav ity
-omet ric
-Ġsurv iving
-ur ai
-ĠPr ison
-Ġr ust
-ĠSk etch
-Ġbe es
-ĠThe ory
-Ġmer it
-T ex
-ch at
-Ġm im
-Ġpast e
-ĠK och
-Ġignor ance
-ĠSh oot
-Ġbas ement
-Un ited
-ĠAd vis
-he ight
-Ġf oster
-Ġdet ain
-in formation
-Ġne ural
-' ;
-Ġprov es
-all ery
-Ġinv itation
-um bers
-Ġc attle
-Ġbicy cle
-z i
-Ġconsult ant
-Ġap ology
-ĠT iger
-Ġ12 3
-99 9
-Ġind ividually
-r t
-ig ion
-ĠBrazil ian
-Ġdist urb
-Ġentreprene urs
-Ġfore sts
-cer pt
-pl ates
-p her
-clip se
-Ġtw itter
-Ġac ids
-ograph ical
-h um
-ĠB ald
-if ully
-Ġcomp iler
-ĠD A
-Ġdon or
-as i
-Ġtrib al
-l ash
-ĠCon fig
-Ġapplic ants
-Ġsal aries
-13 5
-Put in
-ĠF ocus
-ir s
-Ġmisc onduct
-ĠH az
-Ġeat en
-M obile
-Mus lim
-ĠMar cus
-v iol
-Ġfavor able
-Ġst ub
-ad in
-ĠH ob
-Ġfaith ful
-Ġelectron ics
-Ġvac uum
-w ait
-back ed
-econom ic
-d ist
-Ġten ure
-Ġsince re
-ĠT ogether
-ĠW ave
-Ġprog ression
-Ġden ying
-Ġdist ress
-br aska
-th ird
-Ġmix ing
-Ġcolon ial
-Ġpriv ately
-Ġun rest
-atern ity
-Ġprem ises
-ant i
-greg ation
-Ġlic ence
-ĠH ind
-ĠSam uel
-Ġconvinc ing
-ĠA ce
-ĠR ust
-ĠNet anyahu
-Ġhand les
-ĠP atch
-orient ed
-ah o
-ĠG onz
-Ġhack ers
-claim er
-Ġcustom s
-ĠGr an
-f ighters
-Ġl uc
-Ġman uscript
-aren thood
-Ġdev il
-Ġwar riors
-Ġoff enders
-Will iam
-Ġhol idays
-Ġnight mare
-Ġle ver
-iff erent
-St at
-Ġexhib ition
-put ed
-ĠP ure
-Ġal pha
-Ġenthus iasm
-ĠRepresent atives
-E AR
-ĠT yp
-Ġwhe at
-ĠAl f
-Ġcor rection
-Ġev angel
-AT T
-M iss
-Ġs oup
-Ġimpl ied
-par am
-Ġsex y
-ĠL ux
-Ġrep ublic
-p atch
-ab lish
-Ġic ons
-Ġfather s
-ĠG ET
-ĠCar ib
-Ġregul ated
-ĠCo hen
-ĠBob by
-Ġn er
-Ġb ent
-vent ory
-ĠAl ong
-ĠE ST
-ĠWall ace
-Ġmurd ers
-r ise
-ke ll
-ĠCommon wealth
-Ġn asty
-et a
-ĠM IT
-Ġadminist ered
-Ġgenuine ly
-Ed itor
-n ick
-Ġhyd ro
-**************** ****************
-ĠB le
-Ġfin es
-Ġg orge
-aus ible
-r h
-Ġapp le
-ment ioned
-Ġro pe
-ot yp
-H R
-Ġdisappoint ing
-Ġc age
-n ik
-Ġdoub ts
-ĠF REE
-print s
-ĠM UST
-Ġvend ors
-ĠIn qu
-Ġliber als
-Ġcontract or
-Ġup side
-child ren
-Ġtrick y
-Ġregul ators
-charg ed
-l iter
-Ġ ***
-Ġreb ell
-l ang
-Ġloc als
-Ġphys icians
-Ġhe y
-ar se
-t m
-ĠLe x
-Ġbehavior al
-success ful
-F X
-Ġbr ick
-ov ic
-Ġcon form
-Ġreview ing
-Ġins ights
-Ġbi ology
-ĠRem ove
-ĠExt ra
-Ġcomm itting
-indu ced
-ignt y
-ig m
-Ġat omic
-Comm on
-ĠE M
-ĠP ere
-ĠIt ems
-e h
-Ġpres erved
-ĠH ood
-Ġprison er
-Ġbankrupt cy
-Ġg ren
-us hes
-Ġexplo itation
-Ġsign atures
-Ġfin an
-] ,"
-ĠM R
-Ġme g
-rem lin
-Ġmusic ians
-Ġselect ing
-Ġexam ining
-IN K
-l ated
-H i
-Ġart ic
-Ġp ets
-Ġimp air
-ĠM AN
-Ġtable ts
-in clude
-R ange
-Ġca ut
-Ġlog s
-Ġmount ing
-Ġun aware
-Ġdynam ics
-ĠPalest ine
-ĠQu arter
-ĠPur ple
-Ġm a
-ĠIm port
-Ġcollect ions
-ci ation
-Ġsuccess or
-Ġcl one
-Ġaim ing
-Ġposs essed
-Ġstick ing
-Ġsh aking
-Ġloc ate
-ĠH ockey
-T urn
-17 0
-Ġfif teen
-ĠHar rison
-Ġcontinu ously
-ĠT C
-ĠVal ent
-ĠRes cue
-Ġby pass
-am ount
-Ġm ast
-Ġprotect s
-Ġart istic
-Ġsomet ime
-Ġsh oe
-Ġshout ed
-ific ant
-et itive
-ĠReg ister
-ĠJ in
-Ġconcent rated
-ling ton
-on ies
-Ġgener ator
-yr im
-ĠAr men
-Ġclear ing
-id o
-ĠT W
-al ph
-Ġlad ies
-H ard
-Ġdial og
-Ġinput s
-æ ľ
-Ġpos es
-Ġsl ots
-ĠPrem ium
-Ġle aks
-Ġboss es
-Ġ11 3
-c ourse
-A cc
-ĠNew ton
-ĠAust ria
-ĠM age
-Ġte aches
-ab ad
-Ġwe ars
-Ġc yl
-Ġcur se
-ĠS ales
-ĠW ings
-Ġp sy
-Ġg aps
-ĠIce land
-ĠP interest
-Ġland lord
-Ġdefin itions
-ĠK er
-Ġsufficient ly
-ĠP ence
-ĠArch itect
-Ġsur pass
-Ġ11 4
-Ġsuper hero
-ĠDise ase
-Ġpri ests
-ĠC ulture
-Ġdefin itive
-Ġsecret ly
-ĠD ance
-inst all
-ch ief
-ĠJess ica
-W ould
-Up dated
-Ġlock er
-ĠK ay
-Ġmem orial
-è ¦
-f at
-Ġdis gu
-Ġflav ors
-ĠBase ball
-ĠRes istance
-Ġk icks
-Ġen v
-Ġteen agers
-D ark
-ĠC AR
-Ġh alt
-ĠL G
-ĠGab riel
-Ġfe ver
-Ġs atur
-Ġm all
-Ġaffili ate
-ĠS leep
-ĠSpe cific
-ĠV el
-Ġj ar
-ĠSac red
-ĠEd wards
-ĠA CL
-Ġret ained
-ĠG iant
-Ġlim itation
-in ces
-Ġref usal
-ĠT ale
-ĠBut ler
-Ġacc idents
-ĠC SS
-Ġimport ed
-ĠCop y
-Î ±
-ER T
-z el
-Ġdiv isions
-h ots
-ĠAl b
-ĠD S
-Load er
-W ashington
-at isf
-ĠCreat ive
-\ .
-ĠAut om
-red ict
-Ġrecept or
-ĠCarl os
-Met hod
-ok a
-Ġmal icious
-Ġste pping
-, [
-ĠD ad
-Ġatt raction
-ĠEffect s
-ĠPir ate
-ĠC er
-ĠIndust ry
-ĠR ud
-Ġchar ter
-Ġd ining
-Ġins ists
-Ġconfig ure
-Ġ( #
-ĠSim ple
-ĠSc roll
-UT C
-17 5
-ĠK on
-Ġmarket place
-Ġ ãĤ
-Ġref res
-Ġg ates
-er red
-ĠP od
-Ġbeh ave
-Fr ank
-n ode
-Ġendors ed
-he tt
-as ive
-ĠHom eland
-Ġr ides
-ĠLe ave
-er ness
-Ġflood ing
-A FP
-Ġris en
-Ġcontin ually
-Ġun anim
-ĠCont ract
-ĠP as
-Ġgu ided
-ĠCh ile
-b d
-Ġsu cc
-pt ic
-Ġcomm ittees
-ĠL uther
-ĠAny one
-Ġs ab
-12 4
-Ġp ixel
-ĠB ak
-ĠT ag
-ĠBenn ett
-En ter
-sm all
-ĠPresident ial
-Ġp ul
-Ġcontr ace
-arch ive
-Ġcoast al
-ĠK ids
-19 2
-âĢ ²
-ick y
-ING TON
-Ġw olf
-ĠSt alin
-T ur
-id get
-am as
-ĠUn less
-Ġspons or
-Ġmor ph
-ĠCho ose
-Ġrun ner
-Ġun bel
-Ġm ud
-ĠMan a
-Ġdub bed
-Ġg odd
-ure rs
-wind ow
-Ġrel ied
-Ġcelebr ating
-os c
-Ġ13 5
-Ġlobb ying
-Ġincom plete
-Ġrestrict ion
-Ġinc ap
-it us
-Ġexpect ation
-ĠAp ollo
-Ġint ens
-Ġsyn c
-G H
-Ġmanip ulation
-B Y
-Ġspe ar
-Ġbre asts
-Ġvol can
-il ia
-M aterial
-Ġform ats
-ĠB ast
-Ġparliament ary
-Ġsn ake
-Ġserv ants
-ĠTr udeau
-ĠGr im
-ĠArab ic
-ĠSC P
-ĠBoy s
-st ation
-Ġprospect ive
-ord e
-in itialized
-Ġb ored
-AB LE
-Ġaccess ed
-Ġtax i
-ĠShe ll
-aid en
-urs ed
-in ates
-ĠIns urance
-ĠPet e
-Sept ember
-6 50
-Ġad ventures
-ĠCo ver
-Ġt ribute
-Ġsk etch
-Ġem power
-Ġ Ø
-ĠGl enn
-ĠD aw
-= \"
-ĠPolit ics
-Ġgu ides
-Ġd ioxide
-ĠG ore
-ĠBr ight
-ĠS ierra
-Ġval ued
-c ond
-Ġpo inter
-Se lect
-Ġrisk y
-Ġabsor b
-im ages
-Ġref uses
-Ġbon uses
-__ _
-Ġh ilar
-ĠF eatures
-2 20
-ĠCollect or
-F oot
-Ġ19 64
-cul us
-Ġd awn
-Ġwork out
-ĠL O
-Ġphilosoph ical
-ĠSand y
-ĠYou th
-Ġl iable
-A f
-bl ue
-Ġovert urn
-less ness
-ĠTrib une
-ĠIn g
-Ġfact ories
-Ġcat ches
-Ġpr one
-Ġmat rix
-Ġlog in
-Ġin acc
-Ġex ert
-s ys
-Ġneed le
-ĠQ ur
-Ġnot ified
-ould er
-t x
-Ġremind s
-Ġpublisher s
-Ġn ort
-Ġg it
-Ġfl ies
-ĠEm ily
-Ġflow ing
-ĠAl ien
-ĠStr ateg
-Ġhard est
-Ġmod ification
-AP I
-ĠM Y
-Ġcr ashes
-st airs
-n umber
-Ġur ging
-ch annel
-ĠFal con
-Ġinhabit ants
-Ġterr ifying
-Ġutil ize
-Ġban ner
-Ġcig arettes
-Ġsens es
-ĠHol mes
-Ġpract ition
-ĠPhill ips
-ott o
-Ġcomp ile
-Mod el
-ĠK o
-Ġ[ ]
-Americ ans
-ĠTer ms
-Ġmed ications
-ĠAn a
-Ġfundament ally
-ĠNot ice
-Ġwe aker
-Ġ 0000
-Ġgar lic
-Ġout break
-Ġeconom ist
-ĠB irth
-Ġobst acles
-ar cer
-ĠOr thodox
-Ġplace bo
-ĠC rew
-asp berry
-ĠAng els
-Ġdis charge
-Ġdestruct ive
-11 7
-ĠR ising
-Ġd airy
-l ate
-Ġcoll ision
-ĠTig ers
-ean or
-ocument ed
-ĠIn valid
-Ġd ont
-ĠL iter
-ĠV a
-Ġhyd rogen
-Ġvari ants
-ĠBrown s
-Ġ19 65
-Ġind igenous
-Ġtrad es
-Ġremain der
-Ġswe pt
-ĠImp act
-Ġred ist
-Ġun int
-grad uate
-ãĥ ķ
-ĠW ILL
-ãģ® ç
-ĠCrit ical
-Ġf isher
-Ġv icious
-Ġrevers ed
-Y ear
-ĠS ox
-Ġshoot ings
-Ġfil ming
-Ġtouchdown s
-ai res
-m el
-Ġgrand father
-Ġaffect ion
-ing le
-Ġover ly
-Add itional
-Ġsup reme
-ĠGr ad
-Ġsport ing
-Ġmer cy
-ĠBrook s
-ount y
-Ġperform s
-Ġtight ly
-Ġdem ons
-Ġkill ings
-Ġfact ion
-ĠNov a
-aut s
-Ġund oubtedly
-ar in
-Ġunder way
-ra k
-Ġl iv
-ĠReg ion
-Ġbrief ing
-s ers
-cl oud
-ĠM ik
-us p
-Ġpred iction
-az or
-Ġport able
-ĠG and
-Ġpresent ing
-Ġ10 80
-Â »
-ush i
-ĠSp ark
-there um
-Ġjust ification
-ĠN y
-Ġcontract ors
-ming ham
-ĠSt yle
-å ħ
-ĠChron icles
-ĠPict ure
-Ġprov ing
-Ġw ives
-set t
-Ġmole cules
-ĠFair y
-Ġconsist ing
-Ġp ier
-al one
-in ition
-Ġn ucle
-j son
-Ġg otta
-Ġmob il
-Ġver bal
-ar ium
-Ġmon ument
-uck ed
-Ġ25 6
-T ech
-mine craft
-ĠTr ack
-Ġt ile
-Ġcompat ibility
-as is
-Ġs add
-Ġinstruct ed
-ĠM ueller
-Ġle thal
-Ġhorm one
-Ġor che
-el se
-Ġske let
-Ġentert aining
-Ġminim ize
-ag ain
-Ġunder go
-Ġconst raints
-Ġcig arette
-ĠIslam ist
-Ġtravel s
-ĠPant hers
-l ings
-C are
-Ġlaw suits
-ur as
-Ġcry st
-Ġlow ered
-Ġaer ial
-Ġcomb inations
-Ġha un
-Ġch a
-Ġv ine
-Ġquant ities
-Ġlink ing
-b ank
-Ġso y
-B ill
-ĠAngel a
-Ġrecip ient
-ĠProt est
-Ġs ocket
-Ġsolid arity
-Ġâ Ĩ
-m ill
-Ġvar ies
-ĠPak istani
-Dr agon
-Ġun e
-Ġhor izon
-³³³³ ³³³³
-Ġprov inces
-Ġfrank ly
-Ġenact ed
-not es
-[ '
-Ġ19 2
-ocr acy
-Ġendorse ment
-Ġover time
-Tr ue
-L ab
-lic ted
-ĠD NC
-Ġbe ats
-ĠJam ie
-15 2
-ĠIN T
-Cont act
-Ġaccount ed
-h ash
-ĠPack ers
-p ires
-Ġles bian
-Ġamend ments
-Ġhop eful
-ĠFin land
-Ġspot light
-Ġconfig ured
-Ġtrou bled
-Ġg aze
-ĠCal gary
-Ġrel iability
-Ġins urg
-sw er
-b uy
-ĠSk in
-Ġp ixels
-Ġhand gun
-Ġpar as
-Ġcateg or
-ĠE L
-ĠRe x
-Ind eed
-Ġkind a
-Ġconj unction
-ĠBry an
-ĠMan ufact
-y ang
-Pl us
-S QL
-ish ment
-Ġdom inate
-Ġn ail
-Ġo ath
-Ġeru pt
-ĠF ine
-it bart
-ĠCh ip
-ĠAb d
-ĠN am
-Ġbuy er
-Ġdiss ent
-Le aks
-Cont in
-Ġr ider
-ĠSome one
-Ġill usion
-c in
-ĠBoe ing
-Ġin adequ
-ov ation
-i ants
-Ġreb uild
-4 50
-ĠDest iny
-S W
-ĠT ill
-H it
-ia z
-ĠBang l
-acher s
-ĠRe form
-Ġse gments
-Ġsystem atic
-d c
-ĠConserv atives
-Ġport al
-h or
-ĠDragon bound
-Ġdrag ged
-om o
-Ġthe e
-ad vert
-ĠRep orts
-ĠE t
-Ġbarrel s
-Aug ust
-Ġcompar isons
-Ġhe x
-Ġan throp
-" [
-bor ough
-ab i
-Ġpict ured
-play ing
-ĠAdd ress
-ĠMir ror
-Sm ith
-Ġt ires
-ĠN PR
-AA AA
-Ġclass ification
-ĠTh an
-ĠH arm
-ĠR A
-Ġreject ion
-min ation
-Ġr anged
-ĠF alls
-D I
-H ost
-ãĤ ´
-ĠEx ample
-list ed
-th irds
-Ġsaf egu
-br and
-Ġprob able
-Can ada
-IT ION
-ĠQ aeda
-Ġch ick
-Ġimport s
-h it
-l oc
-W W
-Ġble w
-Ġany time
-Ġwh oles
-ik ed
-Ġcal culation
-cre ate
-ĠO ri
-Ġupgr aded
-Ġapp ar
-ut ory
-ĠM ol
-B rit
-ĠJ ong
-IN AL
-ĠStart ing
-Ġd ice
-urt le
-Ġre lying
-cl osure
-Ġprof itable
-Ġsl aughter
-ĠMan ual
-c aster
-Ġ" $
-Ġfe ather
-ĠSim ply
-ie ves
-Ġdeter ior
-ĠPC I
-Ġst amp
-Ġfl aws
-Ġsh ade
-ham mer
-Ġpass port
-Ġcont ing
-am el
-Ġobser vers
-Ġneg lect
-ĠR B
-ĠBrother hood
-Ġskept ical
-f amily
-us k
-Ġemotion ally
-â Ļ
-ĠBet a
-ason able
-id ity
-ĠM ul
-Ġkick ing
-ĠC arm
-oll ah
-VERT IS
-ĠAt hen
-Ġlad der
-ĠBul let
-å £
-00 01
-ĠWild life
-ĠM ask
-ĠN an
-R ev
-Ġun acceptable
-leg al
-Ġcrowd ed
-ag i
-ĠC ox
-j e
-Ġmor ality
-Ġfu els
-Ġc ables
-Ġman kind
-ĠCarib bean
-Ġanch or
-Ġby te
-ĠO ften
-ĠO z
-Ġcraft ed
-Ġhistor ian
-ĠW u
-Ġtow ers
-ĠCitiz ens
-Ġhel m
-Ġcred entials
-Ġsing ular
-ĠJes se
-Ġtack les
-Ġcont empt
-Ġa fore
-ĠSh adows
-Ġn il
-Ġur gent
-app le
-bl ood
-Ġv on
-Ġoff line
-Ġbreat he
-Ġj umps
-Ġirre levant
-ox ic
-om al
-import ant
-J im
-Ġgl oves
-arm ing
-dep th
-Ġtal ents
-ook ie
-ĠS B
-Ġpal m
-uff s
-est a
-IG H
-Ġcan on
-ĠVer izon
-ĠP le
-Ġcou pled
-vel t
-Ġfundra ising
-ĠGet ting
-ĠD LC
-Ġmathemat ical
-ĠH S
-ĠCard inals
-te lling
-Ġspons ors
-Ġ Ï
-ĠBull s
-op tion
-Ġprop ose
-Ġmem orable
-Ġembr aced
-Ġdecl ining
-He alth
-ed a
-Ġ} ;
-Ġsp am
-m ile
-Ġpit cher
-ĠE ight
-Ġcar ing
-ut ic
-ro le
-Ġair line
-ernand ez
-ĠAth let
-Ġcert ification
-ux e
-rig er
-Ġem pir
-Ġsens ation
-Ġdis m
-Ġb olt
-Ġev olve
-H ouse
-Ġconsult ation
-ĠD uty
-Ġtou ches
-ĠN athan
-Ġf aint
-h ad
-" (
-ĠCons umer
-ĠExt reme
-Ġ12 7
-ĠHer m
-ĠSac rament
-iz oph
-Ġanx ious
-ul ously
-Ġsoc ially
-ĠU TC
-Ġsol ving
-ĠLet ter
-Hist ory
-ed uc
-Pr ice
-) );
-Ġrel oad
-am ic
-Ġp ork
-Ġdisc ourse
-Ġt ournaments
-ai ro
-ĠK ur
-ĠCost a
-Ġviol ating
-Ġinterf ere
-Ġrecre ational
-uff le
-Ġspe eches
-Ġneed ing
-Ġremem bers
-Ġcred ited
-n ia
-f ocused
-amer a
-Ġb ru
-um bs
-ĠCub an
-Ġpreced ing
-Ġnons ense
-ac ial
-Ġsmart phones
-ĠSt ories
-S ports
-ĠEmer gency
-oun cing
-ef ined
-Ġb er
-Ġconsult ing
-Ġm asters
-he astern
-." [
-ĠRun ning
-Ġsus cept
-ĠF eng
-Americ a
-pr ises
-st itial
-ĠWeek ly
-ĠGreat er
-mod ules
-if ter
-G raphics
-ul er
-Ġwho lly
-Ġsupp ress
-Ġconce aled
-Ġhapp ily
-Ġaccept s
-ĠEn joy
-Ġr ivers
-ĠEx cept
-2 25
-ĠN HS
-ĠMc Connell
-Ġp ussy
-fer red
-ut able
-Ġatt ain
-Ġ> =
-Ġdepos its
-roph ic
-Ġnot orious
-ĠSh aw
-il itation
-Ġepid emic
-all ic
-Ġsmall est
-ov ich
-Ġaccess ories
-per ties
-Ġsur plus
-ĠMe ch
-Ġamb ig
-ĠImm igration
-Ġch im
-ev al
-Ġpract icing
-ĠMyster y
-Ġdom ains
-ĠSil icon
-app s
-Ġkilomet ers
-e a
-ĠSm ash
-Ġwarrant y
-Ġn ost
-s il
-re v
-J on
-ĠDub lin
-Ġtast es
-Ġb out
-g reat
-er ror
-Ġsw itches
-ĠB apt
-D O
-ok i
-Ġsour ced
-pro du
-Ġattach ment
-ĠIss ue
-ĠQuest ion
-Jo in
-Ġf itted
-Ġunlaw ful
-^ ^
-ere k
-Ġauthent ication
-Ġst ole
-Ġaccount ability
-l abel
-S earch
-Ġal beit
-atic an
-fund ed
-ĠAdd ing
-ĠI Q
-Ġsub mar
-l it
-a que
-ĠLear ning
-Ġint eger
-M aster
-ĠCh rom
-Ġprem ier
-O p
-ĠLi u
-Ġbl essed
-ĠGl obe
-ĠResp onse
-Ġlegit im
-ĠMer kel
-Ġdispos al
-Â ´
-Ġgau ge
-pe at
-Ġindu ced
-Ġquestion able
-arth y
-ĠV it
-ĠF eed
-U ntil
-U t
-worth y
-R Y
-ĠH erald
-ĠHam mer
-Ġmed al
-ĠR ivers
-ĠH ack
-Ġclar ify
-Ġtrack ed
-Ġautonom ous
-Ġten ant
-ĠQ atar
-er ie
-Ġgr im
-ĠMon itor
-Ġresist ant
-ĠSpe c
-ĠWell s
-N AS
-14 8
-Ġmin ers
-iot ics
-Ġmiss es
-11 6
-g ian
-g it
-ĠE yes
-p res
-Ġgrad uated
-Ġang el
-Ġsyn chron
-Ġefficient ly
-Ġtrans mitted
-H arry
-Ġglob ally
-EN CE
-ĠMont ana
-r aged
-ĠPre vention
-Ġp iss
-ĠL l
-Ġshe lf
-ĠB JP
-ĠTest ament
-ĠL ate
-ik er
-ĠH app
-ĠJul ian
-h all
-Ġsp ont
-Ġshut down
-Ġincons istent
-Ġsubscrib ers
-Ġske leton
-ĠNe braska
-Ġins pire
-ĠV oid
-F eed
-Ġang les
-ĠSpr ings
-Ġbench mark
-Ġvacc ines
-izoph ren
-se xual
-uff ed
-Ġsh ine
-ĠK ath
-Ġgest ure
-ine a
-Ġr ip
-Ġopp ression
-Ġcons cience
-b t
-ĠL um
-Ġinc idence
-ĠF a
-w r
-Ġmin eral
-ĠSp urs
-alk y
-Ġth under
-Ġop io
-Be ing
-ĠPal m
-Ġwas ted
-Ġl b
-i aries
-ĠIniti ative
-Ġcur ric
-Ġmark er
-ĠMc L
-Ġext ensions
-ĠP v
-ĠAr ms
-Ġoffer ings
-Ġdef enses
-Ġvend or
-Ġcontrad ict
-ĠCol in
-Ġredd it
-Ġper ipher
-12 2
-Ġs ins
-E dit
-IC T
-So ft
-ĠSh ah
-Ġadministr ator
-ĠT rip
-Ġporn ography
-Ġtu ition
-in ence
-ĠPro gress
-Ġcat alog
-Ġsu ite
-Ġh ike
-Ġreprodu ctive
-eng ine
-Ġd rought
-ĠNo ah
-Ġ2 30
-Ġd ude
-Ġrelax ed
-Ġpart ition
-Ġparticip ant
-Ġtel esc
-Ġfe as
-ĠF F
-own er
-Ġswe eping
-Ġl enses
-Ġmatch up
-ĠRe pl
-ourn als
-Ġcred ible
-Ġgrand mother
-Ġther mal
-Ġsubscrib ing
-Ġident ities
-col m
-U CT
-Ġreluct ant
-us ers
-ĠC ort
-Ġassist ed
-OS S
-ATION S
-IS H
-Ġpharm aceutical
-ic able
-ad ian
-ĠSon ic
-ĠF ury
-ĠM ong
-A H
-ĠPsych ology
-Ġph osph
-Ġtreat s
-Ń Ķ
-Ġstead ily
-ĠHell o
-Ġrel ates
-Ġcl ue
-Ex pl
-a uth
-Ġrev ision
-Ġe ld
-os ion
-Ġbr on
-14 4
-ri kes
-Ġmin es
-Ġblank et
-ĠF ail
-el ed
-ĠIm agine
-ĠPl anned
-a ic
-Re quest
-M ad
-ĠHor se
-ĠEag le
-Ġcap ac
-15 7
-Ġl ing
-ĠN ice
-ĠP arenthood
-min ster
-og s
-ens itive
-Not hing
-Ġcar n
-F in
-ĠP E
-Ġr ifles
-ĠL P
-S and
-Ġgui Active
-Ġtour ist
-C NN
-Ġunve iled
-Ġpredec essor
-} {
-u ber
-Ġoff shore
-Ġopt ical
-ĠR ot
-ĠPear l
-et on
-Ġst ared
-Ġfart her
-at ility
-cont in
-ĠG y
-ĠF oster
-ĠC oc
-ri ents
-Ġdesign ing
-ĠEconom y
-ON G
-W omen
-ĠN ancy
-er ver
-Ġmas cul
-Ġcasual ties
-Ġ2 25
-ĠS ullivan
-ĠCh oice
-Ġa ster
-w s
-Ġhot els
-Ġconsider ations
-Ġcou ch
-ĠSt rip
-ĠG n
-Ġmanip ulate
-l ied
-Ġsynt hetic
-Ġassault ed
-Ġoff enses
-ĠDra ke
-Ġim pe
-Oct ober
-ĠHer itage
-h l
-ĠBl air
-Un like
-Ġg rief
-Ġ4 50
-Ġopt ed
-Ġresign ation
-il o
-Ġver se
-ĠT omb
-Ġu pt
-Ġa ired
-ĠH ook
-ĠML B
-Ġassum es
-out ed
-ĠV ers
-Ġinfer ior
-Ġbund le
-ĠD NS
-ograp her
-Ġmult ip
-ĠSoul s
-Ġillust rated
-Ġtact ic
-Ġdress ing
-Ġdu o
-Con f
-Ġrel ent
-Ġc ant
-Ġscar ce
-Ġcand y
-ĠC F
-Ġaffili ated
-Ġspr int
-yl an
-ĠGarc ia
-Ġj unk
-Pr int
-ex ec
-C rit
-Ġport rait
-ir ies
-ĠOF F
-Ġdisp utes
-W R
-L ove
-ãģ Ħ
-ĠRe yn
-Ġh ipp
-op ath
-Ġflo ors
-ĠFe el
-Ġwor ries
-Ġsett lements
-ĠP os
-Ġmos que
-Ġfin als
-Ġcr ushed
-ĠPro bably
-ĠB ot
-ĠM ans
-ĠPer iod
-Ġsovere ignty
-Ġsell er
-Ġap ost
-Ġam ateur
-Ġd orm
-Ġconsum ing
-Ġarm our
-ĠRo ose
-Ġint ensive
-Ġelim inating
-ĠSun ni
-ĠAle ppo
-j in
-Ġadv ise
-p al
-ĠH alo
-Ġdes cent
-Ġsimpl er
-Ġbo oth
-ST R
-L ater
-ĠC ave
-== =
-Ġm ol
-Ġf ist
-Ġshot gun
-su pp
-Ġrob bery
-E ffect
-Ġobsc ure
-ĠProf essional
-Ġemb assy
-Ġmilit ant
-Ġinc arcer
-Ġgener ates
-Ġlaun ches
-Ġadministr ators
-Ġsh aft
-Ġcirc ular
-Ġfresh man
-ĠW es
-ĠJo el
-ĠD rew
-ĠDun can
-ĠApp arently
-s ight
-ĠIntern al
-ĠInd ividual
-ĠF E
-Ġb ore
-ĠM t
-Ġbroad ly
-ĠO ptions
-ount ain
-ip es
-ĠV ideos
-20 4
-Ġh ills
-Ġsim ulation
-Ġdisappoint ment
-it an
-ĠLabor atory
-Ġup ward
-Ġbound ary
-Ġdark er
-h art
-Ġdomin ance
-C ong
-ĠOr acle
-ĠL ords
-Ġscholars hip
-ĠVin cent
-ed e
-ĠR ah
-Ġencour ages
-ro v
-Ġqu o
-Ġprem ise
-ĠCris is
-ĠHol ocaust
-Ġrhyth m
-Ġmet ric
-cl ub
-Ġtransport ed
-Ġn od
-ĠP ist
-Ġancest ors
-ĠFred er
-th umbnails
-ĠC E
-ON D
-Ph il
-ven ge
-ĠProduct s
-cast le
-Ġqual ifying
-ĠK aren
-VERTIS EMENT
-Ġmight y
-Ġexplan ations
-Ġfix ing
-D i
-Ġdecl aring
-Ġanonym ity
-Ġju ven
-ĠN ord
-ĠDo om
-ĠAct ually
-O k
-ph is
-ĠDes ert
-Ġ11 6
-I K
-ĠF M
-Ġinc omes
-V EL
-ok ers
-Ġpe cul
-Ġlight weight
-g ue
-Ġacc ent
-Ġincre ment
-ĠCh an
-Ġcompl aining
-ĠB aghd
-Ġmidfield er
-Ġover haul
-Pro cess
-ĠH ollow
-ĠTit ans
-Sm all
-man uel
-ĠUn ity
-ĠEv ents
-S ty
-Ġdispro portion
-n esty
-en es
-ĠC od
-Ġdemonstr ations
-ĠCrim son
-ĠO H
-Ġen rolled
-Ġc el
-ĠBre tt
-Ġa ide
-Ġhe els
-Ġbroad band
-Ġmark ing
-Ġw izard
-ĠN J
-ĠChief s
-Ġingred ient
-Ġd ug
-ĠSh ut
-urch ase
-end or
-Ġfar mer
-ĠGold man
-12 9
-15 5
-Or der
-Ġl ion
-i ably
-Ġst ain
-ar ray
-ilit ary
-ĠFA Q
-Ġexpl oded
-ĠMcC arthy
-ĠT weet
-ĠG reens
-ek ing
-l n
-ens en
-Ġmotor cycle
-Ġpartic le
-Ġch olesterol
-B ron
-Ġst air
-Ġox id
-Ġdes irable
-ib les
-Ġthe or
-for cing
-Ġpromot ional
-ov o
-b oot
-ĠBon us
-raw ling
-Ġshort age
-ĠP sy
-Ġrecru ited
-Ġinf ants
-Ġtest osterone
-Ġded uct
-Ġdistinct ive
-Ġfirm ware
-bu ilt
-14 5
-Ġexpl ored
-Ġfact ions
-Ġv ide
-Ġtatt oo
-Ġfinan cially
-Ġfat igue
-Ġproceed ing
-const itutional
-Ġmis er
-Ġch airs
-gg ing
-ipp le
-Ġd ent
-Ġdis reg
-ç Ķ
-st ant
-ll o
-b ps
-aken ing
-Ġab normal
-ĠE RA
-å£ «
-ĠH BO
-ĠM AR
-Ġcon cess
-Ġserv ant
-Ġas pir
-l av
-ĠPan el
-am o
-Ġprec ip
-Ġrecord ings
-Ġproceed ed
-Ġcol ony
-ĠT ang
-ab lo
-Ġstri pped
-Le ft
-to o
-Ġpot atoes
-Ġfin est
-% ).
-Ġc rap
-ĠZ ach
-ab ases
-ĠG oth
-Ġbillion aire
-w olf
-Ġsan ction
-S K
-Ġlog ged
-P o
-ey ed
-un al
-Ġcr icket
-Ġarm ies
-Ġunc overed
-Cl oud
-ó n
-Ġreb ounds
-Ġm es
-O per
-P ac
-Ġnation ally
-Ġinsert ed
-p ict
-Ġgovern ance
-Ð ¸
-Ġprivile ges
-G ET
-Ġfavor ites
-im ity
-Ġlo ver
-the m
-em pl
-Ġgorge ous
-An n
-Ġsl ipped
-Ġve to
-B ob
-Ġsl im
-u cc
-ĠF ame
-udden ly
-Ġden ies
-ĠM aur
-Ġdist ances
-Ġw anna
-t ar
-ĠS ER
-Ġâ Ī
-Ġle mon
-at hetic
-Ġlit eral
-Ġdistingu ished
-Ġansw ering
-G I
-Ġrelig ions
-ĠPhil os
-ĠL ay
-Ġcomp os
-ire ments
-ĠK os
-ine z
-roll ing
-Ġyoung est
-and ise
-ĠB orn
-Ġalt ar
-am ina
-ĠB oot
-v oc
-Ġdig ging
-Ġpress ures
-Ġl en
-26 4
-Ġassass ination
-ĠBir mingham
-ĠMy th
-Ġsovere ign
-ĠArt ist
-ĠPhot ograph
-Ġdep icted
-Ġdisp ens
-orth y
-Ġamb ul
-int eg
-ĠC ele
-ĠTib et
-Ġhier archy
-Ġc u
-Ġpre season
-ĠPet erson
-Ġcol ours
-Ġworry ing
-Ġback ers
-ĠPal mer
-ĠÎ ¼
-Ġcontribut or
-Ġhear ings
-Ġur ine
-Ġ Ù
-ourge ois
-Sim ilar
-ĠZ immer
-s omething
-ĠUS C
-Ġstrength s
-ĠF I
-Ġlog ging
-As ked
-ĠTh ai
-in qu
-ĠW alt
-Ġcrew s
-it ism
-3 01
-Ġshar ply
-um ed
-Ġred irect
-r ators
-In f
-ĠWe apons
-Ġte asp
-19 99
-L ive
-ĠEs pecially
-ĠS ter
-ĠVeter ans
-Ġint ro
-other apy
-Ġmal ware
-Ġbre eding
-Ġmole cular
-ĠR oute
-ĠCom ment
-oc hem
-Ġa in
-Se ason
-Ġlineback er
-Ä «
-ĠEconom ics
-es ar
-ĠL ives
-ĠEm ma
-Ġk in
-ĠTer rit
-Ġpl anted
-ot on
-ĠBut ter
-ĠSp ons
-P ER
-Ġdun geon
-Ġsymb olic
-Ġfil med
-Ġdi ets
-Ġconclud es
-Ġcertain ty
-ĠForm at
-Ġstr angers
-form at
-ĠPh ase
-Ġcop ied
-Ġmet res
-ld a
-ĠUs ers
-Ġdeliber ate
-Ġwas hed
-ĠL ance
-im ation
-Ġimpro per
-ĠGen esis
-ick r
-ĠK ush
-Ġreal ise
-Ġembarrass ing
-alk ing
-b ucks
-Ġver ified
-Ġout line
-year s
-ĠIn come
-20 2
-Ġz ombies
-F inal
-ĠMill enn
-Ġmod ifications
-ĠV ision
-ĠM oses
-ver b
-iter ranean
-ĠJ et
-Ġnav al
-ĠA gg
-Ġur l
-Ġvict ories
-Ġnon etheless
-Ġinj ust
-ĠF act
-ç ļ
-Ġins ufficient
-re view
-face book
-Ġnegoti ating
-Ġguarant ees
-im en
-uten berg
-Ġg ambling
-Ġcon gr
-Load ing
-Ġnever theless
-Ġpres idents
-ĠIndust rial
-Ġ11 8
-Ġp oured
-ĠT ory
-Ġ17 5
-Ġ: =
-Sc ott
-ange red
-T ok
-Ġorgan izers
-M at
-ĠG rowth
-Ġad ul
-Ġens ures
-Ġ11 7
-é¾į å
-Ġmass acre
-Ġgr ades
-be fore
-AD VERTISEMENT
-ĠSl ow
-ĠM MA
-âĢĶ "
-ĠV atican
-Q aeda
-Ġo we
-66 66
-ĠS orry
-ĠGr ass
-Ġbackground s
-Ġexha usted
-Ġcl an
-Ġcomprom ised
-ĠE lf
-ĠIsa ac
-ens on
-In vest
-IF A
-Ġinterrupt ed
-ãĥī ãĥ©
-Ġtw isted
-ĠDrag ons
-M ode
-ĠK remlin
-Ġfert il
-he res
-ph an
-ĠN ode
-f ed
-ĠOr c
-Ġunw illing
-C ent
-Ġprior it
-Ġgrad uates
-Ġsubject ive
-Ġiss uing
-ĠL t
-Ġview er
-Ġw oke
-Th us
-bro ok
-Ġdep ressed
-Ġbr acket
-ĠG or
-ĠFight ing
-Ġstri ker
-Rep ort
-ĠPortug al
-Ġne o
-w ed
-19 9
-Ġflee ing
-sh adow
-ident ified
-US E
-Ste am
-Ġstret ched
-Ġrevel ations
-art ed
-ĠD w
-Ġalign ment
-est on
-ĠJ ared
-S ep
-Ġblog s
-up date
-g om
-r isk
-Ġcl ash
-ĠH our
-Ġrun time
-Ġunw anted
-Ġsc am
-Ġr ack
-Ġen light
-on est
-ĠF err
-Ġconv ictions
-Ġp iano
-Ġcirc ulation
-ĠW elcome
-Ġback lash
-ĠW ade
-Ġrece ivers
-ot ive
-J eff
-Ġnetwork ing
-ĠPre p
-ĠExpl orer
-Ġlect ure
-Ġupload ed
-ĠMe at
-B LE
-ĠNaz is
-ĠSy nd
-st ud
-ro ots
-ri ans
-Ġportray ed
-Ġ ??
-ĠBudd ha
-s un
-Rober t
-ĠCom plex
-Ġover see
-Ġste alth
-T itle
-ĠJ obs
-ĠK um
-Ġappreci ation
-ĠM OD
-Ġbas ics
-Ġcl ips
-Ġnurs ing
-Ġpropos ition
-Ġreal ised
-ĠNY C
-Ġall ocated
-ri um
-ar an
-ĠPro duction
-ĠV ote
-Ġsm ugg
-Ġhun ter
-az er
-ĠCh anges
-Ġfl uct
-y on
-Ar ray
-Ġk its
-W ater
-Ġuncom mon
-Ġrest ing
-ell s
-w ould
-Ġpurs ued
-Ġassert ion
-omet own
-ĠMos ul
-ĠPl atform
-io let
-Ġshare holders
-Ġtra ils
-P ay
-ĠEn forcement
-ty pes
-ĠAn onymous
-Ġsatisf ying
-il ogy
-Ġ( '
-w ave
-c ity
-Ste ve
-Ġconfront ation
-ĠE ld
-C apt
-ah an
-ht m
-ĠC trl
-ON S
-2 30
-if a
-hold ing
-Ġdelic ate
-Ġj aw
-ĠGo ing
-or um
-S al
-Ġd ull
-ĠB eth
-Ġpr isons
-Ġe go
-ĠEl sa
-avor ite
-ĠG ang
-ĠN uclear
-Ġsp ider
-ats u
-Ġsam pling
-Ġabsor bed
-ĠPh arm
-iet h
-Ġbuck et
-ĠRec omm
-O F
-ĠF actory
-AN CE
-Ġb acter
-H as
-ĠObs erv
-12 1
-Ġprem iere
-De velop
-Ġcur rencies
-C ast
-Ġaccompany ing
-ĠNash ville
-Ġfat ty
-ĠBre nd
-Ġloc ks
-Ġcent ered
-ĠU T
-augh s
-or ie
-ĠAff ordable
-v ance
-D L
-em et
-Ġthr one
-ĠBlu etooth
-Ġn aming
-if ts
-AD E
-Ġcorrect ed
-Ġprompt ly
-ĠST R
-Ġgen ome
-Ġcop e
-Ġval ley
-Ġround ed
-ĠK end
-al ion
-p ers
-Ġtour ism
-Ġst ark
-v l
-Ġblow ing
-ĠSche dule
-st d
-Ġunh appy
-Ġlit igation
-ced es
-Ġand roid
-Ġinteg ral
-ere rs
-ud ed
-t ax
-Ġre iter
-ĠMot ors
-oci ated
-Ġwond ers
-ĠAp ost
-uck ing
-ĠRoose velt
-f ram
-Ġyield s
-Ġconstit utes
-aw k
-Int erest
-Ġinter im
-Ġbreak through
-ĠC her
-Ġpro sec
-ĠD j
-ĠM T
-Res p
-ĠP T
-Ġs perm
-ed it
-B T
-Lin ux
-count ry
-le ague
-Ġd ick
-Ġo ct
-Ġinsert ing
-Ġsc ra
-ĠBrew ing
-Ġ19 66
-Ġrun ners
-Ġpl un
-id y
-ĠD ian
-Ġdys function
-Ġex clusion
-Ġdis gr
-Ġincorpor ate
-Ġrecon c
-Ġnom inated
-ĠAr cher
-d raw
-achel or
-Ġwrit ings
-Ġshall ow
-Ġh ast
-ĠB MW
-ĠR S
-Ġth igh
-Ġ19 63
-Ġl amb
-Ġfav ored
-ag le
-Ġcool er
-ĠH ours
-ĠG U
-ĠOrig in
-Ġglim pse
----------------- ----
-L im
-Ġche ek
-Ġj ealous
-- '
-Ġhar ness
-ĠPo ison
-Ġdis abilities
-ne apolis
-Ġout look
-Ġnot ify
-ĠIndian apolis
-Ġab rupt
-ns ic
-Ġenc rypted
-Ġfor fe
-reat h
-Ġr abb
-Ġfound ations
-Ġcompl iment
-ĠInter view
-ĠS we
-Ġad olesc
-Ġmon itors
-ĠSacrament o
-Ġtime ly
-Ġcontem pl
-Ġposition ed
-Ġpost ers
-ph ies
-iov ascular
-v oid
-ĠFif th
-Ġinvestig ative
-OU N
-Ġinteg rate
-ĠIN C
-ish a
-ibl ings
-ĠRe quest
-ĠRodrig uez
-Ġsl ides
-ĠD X
-Ġfemin ism
-Ġdat as
-Ġb end
-ir us
-ĠNig eria
-F ox
-Ch ange
-Ġair plane
-ĠLad en
-Ġpublic ity
-ixt y
-Ġcommit ments
-Ġaggreg ate
-Ġdisplay ing
-ĠAr row
-Ġ12 2
-Ġrespect s
-and roid
-s ix
-ĠSh a
-Ġrest oration
-) \
-W S
-oy s
-Ġillust rate
-with out
-12 6
-ĠâĶ Ĥ
-Ġpick up
-n els
-Ġ ....
-f ood
-ĠF en
-) ?
-Ġphenomen a
-Ġcompan ions
-ĠW rite
-Ġsp ill
-Ġbr idges
-ĠUp dated
-ĠF o
-Ġinsect s
-ASH INGTON
-Ġsc are
-il tr
-ĠZh ang
-Ġsever ity
-Ġind ul
-14 9
-ĠCo ffee
-Ġnorm s
-Ġp ulse
-ĠF T
-Ġhorr ific
-ĠDest roy
-ĠJ SON
-Ġo live
-Ġdiscuss es
-R est
-E lect
-ĠW inn
-ĠSurv iv
-ĠH ait
-S ure
-op ed
-Ġro oted
-ĠS ke
-ĠBron ze
-Ġl ol
-Def ault
-Ġcommod ity
-red ited
-Ġliber tarian
-Ġforb idden
-Ġgr an
-à ¨
-Ġl ag
-en z
-dri ve
-Ġmathemat ics
-Ġw ires
-Ġcrit ically
-Ġcarb ohyd
-ĠChance llor
-ĠEd die
-Ġban ning
-ĠF ri
-Ġcompl ications
-et ric
-ĠBangl adesh
-Ġband width
-St op
-ĠOrig inally
-Ġhalf way
-yn asty
-sh ine
-Ġt ales
-rit ies
-av ier
-Ġspin ning
-ĠWH O
-Ġneighbour hood
-b ach
-Ġcommer ce
-ĠS le
-B U
-Ġentreprene ur
-Ġpecul iar
-ĠCom ments
-f re
-3 20
-IC S
-Ġimag ery
-ĠCan on
-ĠElect ronic
-sh ort
-( (
-D ig
-Ġcomm em
-u ced
-Ġincl ined
-ĠSum mon
-Ġcl iff
-ĠMed iterranean
-Ġpo etry
-Ġprosper ity
-ĠRe ce
-Ġp ills
-m ember
-Ġfin ale
-un c
-ĠG ig
-ä ½
-Ġl od
-Ġback ward
-- +
-ĠFor ward
-Ġth ri
-s ure
-Ġso ap
-ĠF X
-R ES
-ĠSe xual
-oul os
-Ġfool ish
-Ġright eous
-Ġco ff
-terror ism
-ust ain
-ot er
-Ġab uses
-ne xt
-Ġab usive
-Ġthere after
-Ġprohib ition
-ĠS UP
-Ġd ip
-Ġr ipped
-Ġinher ited
-Ġb ats
-st ru
-G T
-Ġflaw ed
-ph abet
-Ġf og
-do ors
-Ġim aging
-Ġdig its
-ĠHung ary
-Ġar rog
-Ġteach ings
-Ġprotocol s
-ĠB anks
-à ¸
-p ound
-ĠC urt
-." )
-. /
-Ġex emption
-end ix
-ĠM ull
-Ġimpro ves
-ĠG amer
-d imensional
-I con
-ĠMarg aret
-St atus
-d ates
-Ġint ends
-Ġdep ict
-Ġpark ed
-J oe
-ĠMar ines
-chn ology
-! ).
-Ġjud ged
-Ġwe ights
-R ay
-Ġapart ments
-he ster
-Ġrein force
-Ġoff ender
-occ up
-Ġs ore
-e pt
-ĠPH P
-ĠB row
-Ġauthor ization
-ĠR isk
-ĠDel aware
-ĠQ U
-Ġnot ifications
-Ġsun light
-Ġex clude
-d at
-Ġm esh
-ĠSud an
-Ġbelong ed
-Ġsub way
-Ġno on
-ĠInter ior
-ol ics
-ĠL akers
-Ġc oding
-Dis claimer
-Cal if
-O ld
-Ġdis l
-???? ?
-Ġconfir ms
-Ġrecruit ment
-Ġhom icide
-Cons ider
-ĠJeff rey
-ft y
-} ;
-Ġobject ion
-do ing
-ĠLe o
-W ant
-Ġgl ow
-ĠClar ke
-ĠNorm an
-Ġver ification
-Ġpack et
-ĠForm ula
-Ġpl ag
-es ville
-Ġshout ing
-Ġo v
-ĠR EC
-ĠB ub
-Ġn inth
-Ġener g
-Ġvalid ity
-Ġup s
-j ack
-Ġneighbor ing
-ĠN ec
-ew orks
-ĠH ab
-are z
-Ġsp ine
-Ġevent ual
-ĠLe aders
-ĠC arn
-Ġprob ation
-Ġrom ance
-ms g
-ĠMechan ical
-ER Y
-R ock
-Ġpart isan
-N ode
-ass ets
-min ent
-Ġforeign ers
-Ġtest ify
-ĠUs ually
-l ords
-ĠG ren
-ĠPow ell
-BI L
-Ġs r
-Ġadd ict
-Ġshell s
-Ġs igh
-ĠY ale
-tern ity
-Ġ7 50
-E U
-ĠR ifle
-Ġpat ron
-em a
-ĠB annon
-an ity
-Ġtrop ical
-ĠV II
-c ross
-Every thing
-ĠIS O
-Ġhum ble
-ass ing
-ĠF IG
-Ġupd ating
-ys on
-Ġcal cium
-Ġcompet ent
-Ġste ering
-Pro t
-ĠS Y
-ĠFin als
-ĠR ug
-15 9
-13 7
-ĠG olf
-Ġ12 6
-Ġaccommod ation
-ĠHug hes
-Ġaest hetic
-art isan
-ĠTw ilight
-Ġpr ince
-ĠAgric ulture
-ĠDis co
-Ġpreced ent
-Ġtyp ing
-author ized
-O ption
-ĠA ub
-l ishes
-ach t
-m ag
-P eter
-ĠU FO
-mont on
-ĠL ith
-Ġa rom
-Ġsec uring
-Ġconf ined
-priv ate
-Ġsw ords
-Ġmark ers
-Ġmetab olic
-se lect
-ĠCur se
-ĠO t
-g ressive
-Ġinc umb
-ĠS aga
-Ġpr iced
-Ġclear ance
-Cont ent
-Ġdr illing
-Ġnot ices
-Ġb ourgeois
-Ġv est
-Ġcook ie
-ĠGuard ians
-ry s
-in yl
-Ġ12 4
-Ġpl ausible
-on gh
-ĠOd in
-Ġconcept ion
-ĠY uk
-ĠBaghd ad
-ĠFl ag
-Aust ral
-ĠI BM
-Ġintern ationally
-ĠWiki Leaks
-I ED
-Ġc yn
-Ġcho oses
-ĠP ill
-Ġcomb ining
-Ġrad i
-ĠMoh ammed
-def ense
-atch ing
-Sub ject
-ic iency
-Fr ame
-Ġ{ "
-Ġche ss
-Ġtim er
-19 0
-Ġt in
-Ġord inance
-emet ery
-Ġacc using
-Ġnotice able
-Ġcent res
-Ġl id
-ĠM ills
-img ur
-Ġz oom
-erg ic
-Ġcomp ression
-pr im
-f ind
-Ġsur g
-Ġp and
-ĠK ee
-ĠCh ad
-cell ence
-oy le
-Ġsocial ism
-ĠT ravis
-ĠM Hz
-Ġgu ild
-ALL Y
-ĠSub scribe
-ĠRel ated
-Ġoccur rence
-itch ing
-Ġfict ional
-Ġcr ush
-ĠE A
-c od
-m ix
-ĠTri ple
-Ġretrie ve
-Ġstimul us
-Ġpsych iat
-ĠDo or
-Ġhomosexual ity
-Ġelement ary
-Ġcell ular
-id ian
-ĠL aun
-Ġintrig uing
-Ġfo am
-ĠB ass
-id i
-its u
-Ġass ure
-Ġcongr at
-Ġbusiness man
-ĠBo ost
-cl ose
-Ġl ied
-Ġsc iences
-ĠO mega
-ĠG raphics
-Ġ< =
-sp oken
-Ġconnect ivity
-S aturday
-ĠAven gers
-Ġto ggle
-Ġank le
-Ġnational ist
-mod el
-ĠP ool
-ophob ia
-V ar
-ĠM ons
-ator ies
-Ġaggress ively
-C lear
-For ge
-act ers
-Ġhed ge
-Ġpip es
-Ġbl unt
-Ġs q
-Ġremote ly
-W ed
-as ers
-Ġref riger
-Ġt iles
-Ġresc ued
-Ġcompr ised
-ins ky
-Ġman if
-avan augh
-Ġprol ifer
-Ġal igned
-x ml
-Ġtri v
-Ġcoord ination
-ĠP ER
-ĠQu ote
-13 4
-b f
-ĠS aw
-Ġtermin ation
-Ġ19 0
-Ġadd itions
-Ġtri o
-Ġproject ions
-Ġpositive ly
-Ġin clusive
-Ġmem br
-19 90
-old er
-Ġpract iced
-ink le
-Ar ch
-Ġstar ters
-ari us
-Ġinter mediate
-ĠBen ef
-ĠK iller
-Ġinter ventions
-ĠK il
-ĠF lying
-In v
-Ġprem ature
-Ġpsych iatric
-Ġind ie
-Ġcoll ar
-ĠRain bow
-af i
-Ġdis ruption
-ĠFO X
-cast ing
-Ġmis dem
-c ro
-Ġw ipe
-ard on
-Ġb ast
-ĠTom my
-ĠRepresent ative
-Ġbell y
-ĠP O
-ĠBre itbart
-13 2
-Ġmess aging
-Sh ould
-Ref erences
-ĠG RE
-ist ical
-L P
-ĠC av
-ĠC razy
-Ġintu itive
-ke eping
-ĠM oss
-Ġdiscont in
-ĠMod ule
-Ġun related
-ĠPract ice
-ĠTrans port
-Ġstatist ically
-orn s
-Ġs ized
-p u
-Ġca f
-ĠWorld s
-ĠRod gers
-ĠL un
-ĠCom ic
-l iving
-Ġc ared
-Ġclim bed
-) {
-Ġconsist ed
-Ġmed ieval
-fol k
-Ġh acked
-Ġd ire
-ĠHerm ione
-Ġt ended
-ce ans
-D aniel
-w ent
-Ġlegisl ators
-Ġred es
-g ames
-Ġg n
-am iliar
-Ġ+ +
-gg y
-th reat
-Ġmag net
-Ġper ceive
-Ġz ip
-Ġindict ment
-Ġcrit ique
-g ard
-ĠSaf e
-ĠC ream
-Ġad vent
-ob a
-Ġv owed
-ous ands
-Ġsk i
-Ġabort ions
-u art
-Ġstun ned
-Ġadv ancing
-Ġlack ed
-Ġ\ "
-Ġsch izophren
-Ġeleg ant
-Ġconf erences
-Ġcance led
-ĠHud son
-ĠHop efully
-Ġtr ump
-Ġfrequ encies
-Ġmet eor
-ĠJun ior
-ĠFle et
-ĠMal colm
-ĠT ools
-Ġ ........
-Ġh obby
-ĠEurope ans
-Ġ15 00
-ĠInt o
-Ġs way
-ĠApp ro
-ĠCom pl
-Comm unity
-Ġt ide
-ĠSum mit
-ä »
-Ġinter vals
-ĠE ther
-Ġhabit at
-ĠSteven s
-lish ing
-ĠDom ain
-Ġtrig gers
-Ġch asing
-Ġchar m
-ĠFl ower
-it ored
-Ġbless ing
-Ġtext ures
-F ive
-Ġliqu or
-R P
-F IN
-Ġ19 62
-C AR
-Un known
-Ġres il
-ĠL ily
-Ġabund ance
-Ġpredict able
-r ar
-Ġbull shit
-le en
-che t
-M or
-M uch
-ä ¹
-Ġemphas ized
-Ġcr ust
-Ġprim itive
-Ġenjoy able
-ĠPict ures
-Ġteam mate
-pl er
-ĠT ol
-ĠK ane
-Ġsummon ed
-th y
-ram a
-ĠH onda
-Ġreal izing
-Ġquick er
-Ġconcent rate
-cle ar
-Ġ2 10
-ĠErd ogan
-ar is
-Ġrespond s
-ĠB I
-Ġelig ibility
-Ġpus hes
-ĠId aho
-Ġagg rav
-Ġru ins
-ur ations
-Ġb ans
-Ġan at
-sh are
-Ġgr ind
-h in
-um en
-Ġut ilities
-ĠYan kees
-Ġdat abases
-ĠD D
-Ġdispl aced
-Ġdepend encies
-Ġstim ulation
-h un
-h ouses
-ĠP retty
-ĠRaven s
-ĠTOD AY
-Ġassoci ates
-Ġthe rape
-cl ed
-Ġde er
-Ġrep airs
-rent ice
-Ġrecept ors
-Ġrem ed
-ĠC e
-Ġmar riages
-Ġball ots
-ĠSold ier
-Ġhilar ious
-op l
-13 8
-Ġinherent ly
-Ġignor ant
-Ġb ounce
-ĠE aster
-REL ATED
-ĠCur rency
-E V
-ãĥ ŀ
-ĠLe ad
-Ġdece ased
-B rien
-ĠMus k
-J S
-Ġmer ge
-heart ed
-c reat
-m itt
-m und
-ĠâĢ ĭ
-ĠB ag
-Ġproject ion
-Ġj ava
-ĠStand ards
-ĠLeon ard
-Ġcoc onut
-ĠPop ulation
-Ġtra ject
-Ġimp ly
-Ġcur iosity
-ĠD B
-ĠF resh
-ĠP or
-Ġheav ier
-ne ys
-gom ery
-Ġdes erved
-Ġphr ases
-ĠG C
-Ġye ast
-d esc
-De ath
-Ġreb oot
-Ġmet adata
-IC AL
-Ġrep ay
-ĠInd ependence
-Ġsubur ban
-ical s
-Ġat op
-Ġall ocation
-gener ation
-ĠG ram
-Ġmoist ure
-Ġp ine
-ĠLiber als
-Ġa ides
-Ġund erest
-ĠBer ry
-Ġcere mon
-3 70
-ast rous
-ĠPir ates
-Ġt ense
-ĠIndust ries
-ĠApp eals
-ĠN ear
-Ġè£ı ç
-Ġlo vers
-ĠC AP
-ĠC raw
-Ġg iants
-Ġeffic acy
-E lement
-ĠBeh avior
-ĠToy ota
-Ġint est
-P riv
-A I
-Ġmaneu ver
-Ġperfect ion
-Ġb ang
-p aper
-r ill
-Ge orge
-b order
-in ters
-ĠS eth
-Ġcl ues
-ĠLe vi
-ĠRe venue
-14 7
-Ġv apor
-Ġfortun ate
-Ġthreat ens
-Ġve t
-Ġdepend ency
-ers ed
-art icle
-ĠBl izzard
-Ġch lor
-Ġmin us
-ĠB ills
-Ġcryptoc urrency
-Ġmetabol ism
-ter ing
-Ġp estic
-step s
-ĠTre asure
-ract ed
-ĠConst ant
-Ġtem p
-13 9
-ĠDet ective
-ur ally
-Ġrecover ing
-Ġcort ex
-Ġ14 4
-cl osed
-Ġprejud ice
-aun ted
-Ġstorm s
-ĠN OW
-Ġmach inery
-Add ress
-Ġcompe lled
-27 0
-Ġdesp air
-b ane
-Ġveget able
-Ġbed s
-Lear n
-Ġcolor ful
-Ġsp ike
-Ġmarg ins
-Ġsymp athy
-Ġworks hop
-ĠC BC
-S at
-Ġburn s
-ĠG ender
-Ġ12 9
-ĠC able
-Ġdeb ts
-ĠThe resa
-Ġreflect ing
-Ġa irst
-Ġr im
-ram id
-Ġweakness es
-W rit
-ogg le
-t i
-ĠCh arge
-Ġwe ighed
-Ġ( .
-Ġl aughter
-Ġrou ter
-ĠDemocr acy
-D ear
-Ġhas ht
-Ġd y
-Ġhint s
-run ning
-Ġfin ishes
-ar us
-M ass
-res ult
-asc us
-Ġv intage
-Ġcon qu
-Ġwild ly
-ac ist
-Ġl ingu
-Ġprot agonist
-st rom
-te enth
-ĠSol o
-m ac
-f illed
-Ġre nown
-it ives
-Ġmot ive
-ĠAnt ar
-ĠM ann
-ĠAd just
-Ġrock ets
-Ġtrou bling
-e i
-Ġorgan isms
-ass is
-Christ ian
-Ġ14 5
-ĠH ass
-Ġsw all
-Ġw ax
-ĠSurv ival
-V S
-ĠM urd
-v d
-stand ard
-Ġdrag ons
-Ġacceler ation
-r ational
-f inal
-Ġp aired
-ĠE thereum
-Ġinterf aces
-Ġres ent
-Ġartif acts
-Å «
-are l
-Ġcompet itor
-ĠNich olas
-ĠSur face
-c pp
-ĠT ot
-Ġeconom ically
-Ġorgan ised
-Ġen forced
-in ho
-Ġvar ieties
-Ġab dom
-ĠBa iley
-id av
-ĠSal v
-p aid
-Ġalt itude
-ess ert
-ĠG utenberg
-are a
-op oulos
-Ġprofess ors
-igg s
-ĠF ate
-he y
-Ġ3 000
-D ist
-Ġtw ins
-c ill
-ĠM aps
-Ġtra ps
-Ġwe ed
-ĠK iss
-Ġy oga
-Ġrecip ients
-ĠWest minster
-Ġpool s
-ĠWal mart
-18 8
-ĠSchool s
-att ack
-ĠAR M
-par agraph
-W arning
-j l
-Ġself ish
-anche z
-ĠHe ights
-F re
-ĠS oph
-Ġ --------------------------------
-t ml
-33 3
-Ġraid s
-Ġsatell ites
-KE Y
-Ġlast s
-Ñ Ĥ
-In s
-ĠD ame
-Ġunp redict
-// /
-gh ai
-Ġart illery
-Ġcru ise
-Ġg el
-ĠCabin et
-Ġbl ows
-ĠE sp
-Ġprox imity
-ot he
-ĠSk ills
-ĠU pper
-ob o
-ĠN DP
-Ġenjoy s
-Ġrepe ating
-ĠConst ruction
-ĠQuest ions
-H illary
-Ġu int
-Ġprocess ors
-ĠGib son
-ĠMult iple
-q a
-ĠB om
-ĠM iles
-vent ional
-Ġhur ts
-s kin
-ĠA IDS
-Ġadvis ers
-ĠR oot
-Ġmethod ology
-ĠD ale
-Ġdet on
-ĠKnow ledge
-sequ ently
-Ġ12 1
-Ġconnect s
-C y
-ĠD anger
-Ġcontribut ors
-ĠB ent
-Ġbr ass
-ĠGun s
-int o
-ĠFort une
-Ġbro ker
-bal ance
-Ġlength s
-Ġv ic
-Ġaver aging
-Ġappropri ately
-ĠCamer a
-Ġsand wich
-ĠCD C
-Ġcoord inate
-Ġnav ig
-Ġgood ness
-l aim
-Ġbra ke
-Ġextrem ist
-ĠW ake
-ĠM end
-ĠT iny
-ĠC OL
-ĠR F
-ĠD ual
-ĠW ine
-C ase
-Ġref ined
-Ġl amp
-L ead
-Ġb apt
-ĠCar b
-ĠS add
-ĠMin neapolis
-PD F
-Ear ly
-ĠH idden
-I ts
-ĠT IME
-Ġp ap
-Ġcommission ed
-ĠF ew
-ĠCol ts
-ĠB ren
-Ġbot hered
-Ġlike wise
-Ex per
-ĠSch w
-c ry
-n n
-ĠM itch
-im on
-M G
-b m
-UM P
-r ays
-Ġregist ry
-Ġ2 70
-ach ine
-re lla
-ant ing
-00 000
-Ġru ined
-sp ot
-Ġt a
-Ġmaxim ize
-Ġincon ven
-D ead
-H uman
-En abled
-ĠMar ie
-Ġch ill
-ĠParad ise
-Ġstar ring
-ĠLat ino
-ĠProt ocol
-ĠE VER
-Ġsuppl iers
-m essage
-ĠBro ck
-Ġser um
-âĸĪâĸĪ âĸĪâĸĪ
-Ġen comp
-Ġamb ition
-ues e
-Ġar rows
-And rew
-Ġanten na
-Ġ19 61
-ĠB ark
-Ġb ool
-ãĤ ª
-ĠSt orage
-Ġrail way
-Ġtoug her
-ĠC ad
-Ġwas hing
-P y
-' ]
-em bed
-ĠMem phis
-ack le
-Ġfam ously
-ĠF ortunately
-ov ies
-Ġmind set
-Ġsne ak
-ĠD h
-RA W
-ĠSim pson
-Ġliv est
-Ġland mark
-Ġc ement
-L ow
-Ġthr illed
-ĠCour se
-in el
-Ġch uck
-id ate
-gl obal
-Ġwh it
-Ġ �
-ad ays
-s ki
-ĠS V
-Ġvir uses
-30 6
-ĠResp ons
-Ġthe aters
-ĠBr anch
-ĠGene va
-ĠM K
-Ġunbel iev
-Ġcommun ist
-Orig inal
-ĠRe ceived
-ĠTrans fer
-ĠAr g
-In put
-ĠStr ategy
-Ġpal ace
-the ning
-D ri
-Ġsent encing
-umbn ail
-Ġp ins
-re cy
-Ġs iblings
-Get ting
-ĠB U
-ĠNorth west
-Ġprolong ed
-ĠSak ura
-C omb
-ĠB our
-Ġinadequ ate
-ĠK ash
-Ġus ername
-ĠImpro ve
-Ġbatt ling
-ĠM AC
-Ġcurric ulum
-Ġs oda
-ĠC annon
-Ġsens ible
-sp ons
-De cember
-Ġw icked
-ĠP engu
-Ġdict ators
-ĠHe arts
-og yn
-Ġsimilar ities
-ĠSt ats
-Ġh ollow
-it ations
-": [
-Ġh over
-ĠList en
-s ch
-S und
-Ġc ad
-ĠPar ks
-Ġl ur
-Ġhy pe
-ĠL em
-N AME
-is ure
-Fr iday
-Ġshoot s
-Ġclos es
-Ġd b
-ĠR idge
-ĠDiff erent
-Ġrepl ies
-ĠBroad way
-op ers
-Ġint oler
-ĠZe us
-akes pe
-Ġpropri etary
-Ġrequest ing
-Ġcontro llers
-ĠM IN
-im edia
-be cca
-Ġexp ans
-Ġoil s
-B ot
-ĠCh and
-Ġpr inter
-Ġto pped
-ĠP OL
-ĠEar lier
-S ocial
-av in
-Ġdecre ases
-ĠSe b
-Ġspecific ations
-ĠBl ast
-ĠK urt
-Ġfre el
-B rown
-Ġdil ig
-ro e
-ĠPro blem
-ĠQu ad
-Ġdecent ral
-ĠV ector
-an ut
-Ġplug ins
-ĠGreg ory
-Ġfuck ed
-el ines
-ĠAmb assador
-t ake
-Ġcle ans
-ong yang
-An onymous
-st ro
-" }
-al ine
-ĠO dd
-ĠE ug
-2 16
-Ġbo il
-ĠP owers
-Ġnurs es
-Ob viously
-ĠTechn ical
-Ġexceed ed
-OR S
-Ġextrem ists
-Ġtr aces
-ex pl
-Ġcom r
-ĠS ach
-) /
-Ġm asks
-Ġsc i
-B on
-Ġreg ression
-we gian
-Ġadvis or
-it ures
-ĠV o
-ex ample
-ĠInst ruct
-Ġs iege
-Ġredu ctions
-pt r
-Ġstat utory
-Ġrem oves
-Ġp uck
-red its
-Ġbe e
-Ġsal ad
-Ġpromot ions
-ĠJosh ua
-with standing
-ET H
-ĠCh a
-im us
-Ġexpend iture
-aun ting
-Ġdelight ed
-Ġ15 5
-be h
-Ġcar pet
-ĠSp art
-Ġj ungle
-l ists
-Ġbull ying
-ĠNob el
-ĠGl en
-Ġreferen ced
-Ġintrodu ces
-se in
-Ġcho pped
-gl ass
-ĠW rest
-Ġneutral ity
-Ġâ Ļ
-Ġinvestig ator
-Ġshel ves
-Ġun constitutional
-Ġreprodu ction
-Ġmer chant
-m ia
-Ġmet rics
-Ġexplos ives
-ĠSon ia
-Ġbod ily
-Ġthick ness
-Ġpredomin antly
-ĠAb ility
-Ġmon itored
-IC H
-Ġ] .
-ĠMart inez
-Ġvis ibility
-Ġqu eries
-Ġgen ocide
-ĠWar fare
-Qu ery
-Ġstud ios
-Ġemb ry
-Ġcorrid or
-Ġclean ed
-com plete
-ĠM H
-Ġenroll ment
-ING S
-Ġimpact ed
-Ġdis astrous
-ĠY un
-ĠCl aire
-ĠBas ically
-y t
-uster ity
-Ġindirect ly
-w ik
-Ġd od
-ĠCar r
-Ġam p
-Ġprohib it
-ĠIn itial
-ĠR d
-ij i
-Ġeduc ate
-c orn
-i ott
-ĠBeaut y
-Ġdetect ive
-ĠCon n
-s ince
-Ġst agger
-Ġob ese
-Ġb ree
-olog ic
-is se
-walk er
-Ġbl ades
-Ġlaw ful
-fun c
-ĠBeh ind
-Ġappet ite
-Ġ( *
-Ġt ennis
-Ġoff spring
-Ġj ets
-Ġstruct ured
-Ġafore mentioned
-N ov
-Ġsc aling
-f ill
-Ġst ew
-Ġcur b
-ĠStep han
-ed In
-S F
-ob ic
-é ŃĶ
-ou g
-ĠM M
-Ġgen etically
-ope z
-13 6
-Ġu mb
-anc ers
-Ġcoh ort
-Ġmerch andise
-Ġimp osing
-ĠLegisl ature
-ĠArch ive
-iv ia
-ĠN aval
-Ġoff ences
-Ġmir acle
-Ġsn apped
-Ġf oes
-Ġextensive ly
-ĠR af
-Ġc ater
-ed ience
-K it
-ĠB in
-Ġrecomm ends
-ĠC ities
-Ġrig id
-ĠRE AD
-ĠNob le
-ĠT ian
-Ġcertific ates
-ant is
-o iler
-ĠBudd hist
-d id
-Ġsurvey ed
-Ġdown ward
-Ġprint s
-ĠMot ion
-ron ics
-ĠS ans
-oss ibly
-u ctions
-Ġcolon ies
-ĠDan ish
-un it
-Ġsp oil
-Ġadvis ory
-ber ries
-Pl an
-Ġspecific ation
-op hers
-ĠRes ource
-Ġsh irts
-prising ly
-commun ications
-Ġtriv ial
-Ġmention ing
-ise xual
-Ġsupp lements
-Ġsuper vision
-B P
-v or
-Ġw it
-Ġco oldown
-Ġplaint iff
-ĠReview s
-ĠS ri
-ĠM int
-ĠSug ar
-Ġafter ward
-ĠPri est
-ĠInvest ment
-og ene
-ĠT aking
-Ġstretch ing
-Ġinflamm ation
-ĠTe hran
-Ġl ining
-Ġfree zing
-ĠEnt ity
-Ġins piring
-spe cial
-pr ice
-Ġsu e
-ĠP orter
-oun ge
-ET A
-ĠD erek
-ĠLu is
-u o
-ym ph
-Ġex terior
-ih il
-ĠAsh ley
-in ator
-Ġnut rients
-ĠTh rones
-Ġfin ances
-ĠIn spect
-Ġspe cially
-ĠRequ ired
-ĠP TS
-ĠViol ence
-oint ed
-sh ots
-Ġex cerpt
-co on
-IN S
-ĠG ri
-Ġrecogn ised
-We ek
-You ng
-Ġv om
-is le
-ĠCur ry
-ĠBudd h
-Ġnot ebook
-Ġd urable
-/ ?
-ĠG ad
-ĠP upp
-Ġforg ive
-p ark
-Ġpersonal ities
-an alysis
-cl amation
-Ġelev ator
-Ġware house
-ĠR ole
-un n
-Ġillust ration
-ĠSc an
-Ġatmosp heric
-Im port
-AN C
-rict ed
-f u
-01 0
-Ġar che
-Ġreward ed
-akespe are
-Ġintern ally
-ĠR BI
-alk er
-Ġeleph ant
-ow itz
-ĠP izza
-Ġbip artisan
-é s
-Ġslow ed
-ĠSt ark
-Ġover ride
-OU S
-Ġ3 20
-undred s
-ĠDe ck
-ĠC ensus
-be e
-14 6
-ot or
-Ġ ip
-Ġu b
-oc ations
-ĠBut ton
-r ice
-Ġc ripp
-ff f
-Ġorig inated
-Ġoverwhel med
-app a
-Ġfore most
-âĢ ij
-ĠL EG
-re lease
-eat ured
-at ches
-Ġre ps
-Ġl ending
-ĠRe ference
-ĠCl ient
-16 5
-vent h
-Com plete
-ĠPat rol
-Ġsw orn
-c am
-Ġshut tle
-ĠR alph
-Ġh ometown
-- ,
-on al
-ĠB P
-å ı
-Ġpersu ade
-ĠAlex and
-Ġcomb ines
-Ġv ivid
-ĠL ag
-Ġenc oding
-Ġsal vation
-w en
-ĠRec overy
-i ya
-Un iversity
-ĠB iden
-Ġbud gets
-ĠTex ans
-f its
-Ġhon ored
-Ġp ython
-T D
-## #
-cl one
-Ġbl ink
-ĠL iquid
-Ġunemploy ed
-Ġcl ashes
-ĠCoun sel
-Ġdirect ing
-Ġpun ct
-ĠFal cons
-Ġsh ark
-ĠDam ascus
-Ġje ans
-Ġemb ark
-Ġse ize
-Ġup wards
-2 80
-ĠE z
-ĠAny thing
-Ġex otic
-l ower
-ĠCreat or
-ĠU m
-Ġsubur bs
-ber ger
-ĠW end
-Ġm int
-ĠX X
-ĠD ro
-Ġsuff ers
-Ġher b
-t ree
-Ġfrag ile
-Ġflood ed
-ĠAl cohol
-ole an
-ny der
-ĠK O
-F ram
-Ġ13 6
-Ġow ed
-ĠMe lee
-ĠH ash
-Ġwh isk
-Ġsu do
-r r
-Qu ick
-app ro
-Ġi i
-ĠEx amples
-he e
-Ġpromot es
-per ature
-k ar
-ĠHon or
-Ġs odium
-ĠL if
-ros so
-intend ent
-Ġcorrespond ent
-F ound
-sec ret
-Ġident ifies
-ag ne
-Ġl ou
-ĠP P
-Ġcoinc idence
-m ove
-Ġmilit ia
-Ġinf iltr
-ĠPrim ary
-Ġpitch ing
-ĠI b
-ĠGO OD
-ãĤ ¸
-ĠW izards
-ir al
-ĠVen us
-R R
-ĠâĢ ķ
-ĠCase y
-Ġsad ly
-Ġadm ire
-Ġembarrass ed
-c b
-M el
-Ġtub es
-Ġbeaut ifully
-ĠQueens land
-Bel ow
-re z
-qu et
-ple asant
-ĠÂ «
-C amp
-Ġdec isive
-19 98
-ĠL amb
-ut ton
-h n
-ĠJ agu
-au nder
-ĠC ord
-Ġcl erk
-Ġca ffe
-Ġwip ed
-Ġre im
-ĠMount ains
-Ġimprison ed
-Ġdevelop s
-ĠP ra
-Ġmodel ing
-Any one
-ance l
-ĠS it
-Ġshield s
-Ġl awn
-Ġcard iovascular
-Ġdemonstr ating
-Ġpar se
-ĠIsrael is
-Ġeuro s
-14 3
-Ġgl orious
-ins ki
-ec d
-Ġcondition ing
-Ġhel pless
-Ġmicro sc
-ĠHar bor
-Ġst akes
-Ġ2 60
-Ġun equ
-ĠFl oyd
-Ġd amp
-Ġappar atus
-ĠLaw s
-Ġcoun ters
-Ġindu ce
-at able
-ĠAh med
-Ġsl am
-N ovember
-Ġpers ist
-Ġim minent
-á n
-Ġsh red
-Ġph ases
-ĠEd monton
-ĠArm strong
-ĠMe et
-ĠK itty
-Ñ Ģ
-c irc
-ĠAd ult
-Ġa rose
-ĠX en
-D an
-g ow
-Ġsuper f
-ĠAd mir
-Ġend ure
-Ġkey word
-yr us
-Ġy arn
-Ġpath way
-ĠHop kins
-mid t
-Ġcens orship
-d ependent
-Ġinstruct or
-S ources
-Ġto e
-Ġball oon
-N ob
-Ġsw ear
-ĠCast ro
-Ġgl oss
-ĠK avanaugh
-Ġremark ably
-Ph otos
-ĠN om
-ĠS outheast
-y ers
-Ġvalid ation
-Ġcann on
-ĠVict ory
-ĠPier re
-Ġcaut ious
-Aud io
-Ġf etch
-ĠG ift
-ĠH yp
-Ġrem edy
-Z E
-Ġsc ent
-Ġbe ard
-ĠR ut
-- "
-Ġpat ents
-H y
-Ġun just
-Ġpot ato
-Ġforth coming
-Ġche f
-ĠR ift
-aff e
-ĠR OM
-ĠL aunch
-Ġp ads
-ĠNe o
-Ġon set
-Ġsquee ze
-s afe
-Ġpref ix
-ĠT M
-ĠN early
-ĠClin ical
-ĠM ental
-ot iation
-ĠUn ic
-ant ry
-ĠC ir
-Ġep it
-Ã ¦
-Ġextract ed
-verse ly
-ri ad
-Ġstr ains
-Ġto ps
-Ġpo em
-ĠRand y
-ĠMap le
-TH ER
-up iter
-ĠSS D
-ļ é
-Ġun con
-per ing
-Ġsle pt
-in ers
-Ġunder water
-ĠEv idence
-g one
-20 5
-Ġhistor ians
-Ġsynt hesis
-Ġf rog
-b asketball
-Ġvibr ant
-Ġsub ord
-Ġ3 65
-ĠD ial
-Ġcooper ate
-HA HA
-Ġgreet ed
-15 8
-Ġj azz
-Ġinto x
-ĠWalk ing
-Ġsuper visor
-ĠF usion
-ĠMer cedes
-s end
-H am
-s d
-n l
-Ġtour s
-ĠF IFA
-Ġcul p
-g d
-30 4
-Ġple as
-Ġillust rates
-ĠColomb ia
-Ġhighlight ing
-ĠSum mary
-Ġexp osing
-ĠD ru
-Ġir ony
-r itional
-ĠCar roll
-ĠEll is
-P ict
-ĠR apt
-Ġad apter
-Ġun m
-Ġcor pse
-Ġceleb rities
-D en
-at um
-ĠAp ocalypse
-ĠW ag
-lin ing
-Ġhorm ones
-R ub
-ĠX i
-ĠV aults
-20 8
-alky rie
-inos aur
-Ġfeed s
-v ity
-Ġdefe ating
-W ait
-Ġemphas ize
-ĠSteel ers
-yr inth
-le ys
-ĠWhe never
-Current ly
-ĠCl ock
-Ġcollect ively
-any on
-ĠJ P
-Ġment ality
-Ġdownload s
-Ġsurround ings
-ĠBarn es
-Ġflags hip
-Ġindic ators
-Ġgra pp
-Jan uary
-ĠElement al
-ĠAthen a
-ib al
-Ġs ights
-Ġcap ita
-ĠTreat y
-Ġvo iced
-ĠG az
-let te
-Ġy a
-Ġexp ired
-Leg end
-H ot
-n ature
-Ġunst able
-Ġ2 80
-Ã º
-Com ment
-AL E
-Ġquest s
-Ġhand ler
-n is
-Ġvers atile
-Ġconce al
-enge ance
-ĠInter active
-Ġobs essed
-ĠDog s
-Ġcr acked
-S ound
-s v
-ĠD ylan
-ro ads
-f x
-ĠCath olics
-ĠH ag
-Ġsl ammed
-Ġgl owing
-s ale
-Ġtiss ues
-ĠCh i
-ne e
-Ġc her
-s ic
-ur rection
-Ġb acon
-ul atory
-) ."
-Ġir regular
-FOR M
-ass ed
-Ġintention al
-Ġcompens ate
-ĠSpe aking
-ĠS ets
-15 3
-Ġconvent ions
-b ands
-em ade
-Ġe cc
-ĠWin ston
-ĠAssass in
-ĠBelg ian
-Ġdepend ence
-Ġnic he
-Ġb ark
-ĠJ azz
-Ġdisadvant age
-Ġgas oline
-Ġ16 5
-çļ Ħ
-ess a
-mod ule
-ang ular
-O Y
-ĠTreat ment
-it as
-ol ation
-ĠArn old
-Ġfe ud
-ĠN est
-Ġthe atre
-ew ater
-Ġmin ors
-olic y
-ĠH aven
-div ision
-Ġtr unk
-F ar
-ĠP ull
-Ġcapt uring
-Ġ18 00
-ĠTe en
-Ġex empl
-Ġclin ics
-ĠB urg
-Ġsubst it
-Ġpay load
-ĠL av
-ĠT roy
-ĠW itness
-Ġfrag ments
-Ġpass words
-Ġg ospel
-ĠG in
-Ġten ants
-ol ith
-S ix
-Pre vious
-ĠAg es
-ĠDar win
-Ġbl at
-Ġem pathy
-sm ith
-b ag
-ĠE cho
-ĠC amb
-ĠM add
-ĠB oo
-Ġred e
-ĠBurn ing
-Ġsmooth ly
-ĠAd rian
-ĠV ampire
-ĠMon sters
-ste am
-Sty le
-M a
-re a
-ĠD war
-aly st
-urs or
-Ġelim ination
-Ġcrypt o
-ch t
-ĠE ternal
-âĢ¦ ]
-ĠS orce
-I ll
-N ER
-Ġu h
-Con clusion
-w age
-Ġresp ir
-Ġrem inis
-het ical
-Ġg y
-Ġutil ized
-ic idal
-Ġ19 00
-Ġhun ters
-ĠSw an
-ĠRe act
-Ġvis itor
-ĠThanks giving
-30 8
-Post s
-Ġh ips
-19 97
-om ers
-Ġkn ocking
-ĠVeh icle
-Ġt il
-Ġ13 8
-Ġm i
-ĠInvest igation
-ĠKen ya
-Ġcas ino
-Ġmot ives
-Ġreg ain
-re x
-Ġweek ends
-Ġstab bed
-bor o
-Ġexplo ited
-ĠHA VE
-ĠTe levision
-c ock
-Ġprepar ations
-Ġende av
-ĠRem ote
-ĠM aker
-ĠPro du
-ĠEv an
-Ġinform ational
-ĠLouis ville
-15 4
-ĠDream s
-Ġpl ots
-ĠRun ner
-Ġhur ting
-Ġacad emy
-ĠMont gomery
-n m
-ĠL anc
-ĠAl z
-2 10
-el ong
-Ġretail er
-Ġar ising
-Ġrebell ion
-Ġbl onde
-play ed
-Ġinstrument al
-C ross
-Ġret ention
-Ġtherape utic
-Ġse as
-Ġinfant ry
-ĠCl int
-Ġprompt ing
-Ġbit ch
-Ġst ems
-ĠK ra
-Ġthe sis
-ĠB og
-ru ed
-Ġk ings
-Ġcl ay
-ific ent
-ĠY ES
-ĠTh ing
-ĠCub s
-vey ard
-els h
-in arily
-ĠE y
-ĠRoll ing
-Ġev olving
-Ind ia
-Ġrecogn izes
-Ġgrad uation
-is ers
-Ġfert ility
-ĠMil an
-Comm and
-Ġbox ing
-Ġ19 43
-Ġgl uten
-ĠEm ir
-Ġid ol
-Ġcon ceived
-ĠCre ation
-Mer it
-udd y
-uss ions
-ĠLie utenant
-iet al
-Ġunch anged
-ĠSc ale
-ĠCrime a
-ball s
-ator ial
-Ġdepth s
-Ġempir ical
-Ġtrans m
-Ġuns afe
-miss ible
-com fort
-15 6
-Ġmechan ic
-00 2
-l ins
-Ġsm oked
-P os
-Ġslow ing
-Ġl av
-Tex as
-Ġche ating
-ĠMet ropolitan
-eth yl
-Ġdiscover ing
-as se
-Ġpen cil
-ĠPy ongyang
-Ġclos et
-ĠShe et
-ĠEnt ry
-ou stic
-Ġmy st
-er ate
-ari at
-Ġminer als
-Ġmusic ian
-ĠP ul
-ĠM az
-24 9
-Ġper missions
-Ġ iv
-en ary
-ick ers
-ĠB ing
-he a
-en able
-Ġgri ev
-Ġassert ed
-ĠColon el
-Ġaff idav
-w o
-Ġse ated
-ĠR ide
-Ġpaint ings
-ĠP ix
-Ġ13 7
-ish i
-umb ai
-g otten
-ĠEar l
-Ġin ning
-Ġc ensus
-Ġtrave lled
-ĠCons ult
-18 5
-b ind
-Ġsimpl icity
-Ġoverlook ed
-ĠHelp ful
-Ġmon key
-Ġoverwhelming ly
-Bl ood
-ĠFl int
-ĠJ ama
-ĠPres ent
-ĠR age
-ĠT A
-pt ive
-Ġturn out
-w ald
-ĠD olphins
-ĠV PN
-Ġon ion
-Ġcraft ing
-m ma
-ĠMerc ury
-Ġarr ange
-Ġalert s
-ĠO T
-zb ollah
-Ġg ases
-ĠRichards on
-s al
-l ar
-Ġfro st
-Ġlower ing
-Ġacc laim
-Ġstart ups
-ĠG ain
-ess ment
-Ġguard ian
-äº º
-ĠP ie
-ĠL inks
-Ġmer its
-Ġaw ake
-Ġparent al
-Ġexceed s
-Ġid le
-ĠPil ot
-Ġe Bay
-ĠAc cept
-ipe g
-C am
-ĠK ot
-Ġtrad ers
-olit ics
-unk er
-ĠP ale
-os i
-an mar
-Ġ19 47
-ĠF ell
-est ial
-it ating
-G F
-ĠS r
-if ted
-Ġconnect or
-ĠB one
-ill es
-2 60
-h ma
-Ġoverl ap
-ĠGit Hub
-Ġclean er
-ĠBapt ist
-ĠW AS
-Ġlung s
-Ñ ģ
-ĠB UT
-Ġc ite
-Ġpit ched
-reat ment
-Ġtro phies
-ĠN u
-38 6
-ĠPr ide
-Ġattend ees
-[ ]
-17 9
-Ġspat ial
-Ġpri zes
-ĠRel igion
-Ġshow case
-ĠC ategory
-vid ia
-T arget
-Pro perty
-? ,
-Ġf usion
-p ie
-ĠU CLA
-Ġsound track
-Ġprin cess
-ĠC aval
-sh ould
-Ġlim bs
-Back ground
-Ġlone ly
-Ġc ores
-ĠT ail
-she et
-Ġ13 2
-R a
-ãĤ «
-ĠB olt
-Ġbook ed
-Ġadmin ister
-Ġequ als
-w y
-Ġobserv ing
-ĠBar on
-ĠAd obe
-Ġv irgin
-ĠSocial ist
-M ove
-gh azi
-ĠLind a
-2 12
-Ġbre wing
-Ġmerch ants
-bur se
-Ġdiv or
-Ġmet als
-ĠN er
-Ġsum s
-ĠEn emy
-Ġen vision
-Ġgrant ing
-ĠH oney
-ĠSk yrim
-Ġsoc io
-gr aded
-Ġselect ive
-W ASHINGTON
-Ġ19 48
-ĠSir ius
-ĠG ross
-act ivity
-ĠI van
-Ġfur ious
-BS D
-ĠPre vious
-Ġrespons ive
-Ġchar itable
-Ġle aning
-ĠP ew
-Ġviol ates
-\\\\ \\\\
-ĠCom ing
-w ire
-Ġpo et
-Ġres olutions
-comm and
-ĠPortug uese
-Ġnick name
-Ġde af
-Feb ruary
-Ġrecogn ise
-Ġentire ty
-Ġseason al
-pl aced
-ĠTe legraph
-Ġmicro phone
-our ing
-Ġgr ains
-Ġgovern ed
-Ġpost p
-ĠW aters
-in ement
-Ġund ocumented
-ĠCom cast
-Ġf ox
-Ġassault s
-re on
-man y
-ĠJen kins
-ĠAny way
-Ġassess ments
-Ġdown s
-ĠM ouse
-Ġsuper b
-k t
-ĠD ow
-Ġtax ation
-4 01
-Ġsm iles
-Ġundert aken
-Ġex h
-Ġenthusi astic
-Ġtw ent
-Ġgovernment al
-Ġautonom y
-ĠTechn ologies
-ĠCh ain
-Ġpreval ent
-f b
-Ġnic otine
-og ram
-j ob
-Ġawa iting
-ĠMen u
-Ġdep uties
-k ov
-ish ops
-But ton
-ĠShan ghai
-Ġdies el
-ĠD uck
-R yan
-ĠPC s
-N F
-j ury
-ent e
-Ġinacc urate
-edd y
-Wh atever
-Ġshow c
-ĠN ad
-od us
-et r
-Ġplaint iffs
-ĠW OR
-ĠAss ange
-Ġpriv at
-Ġpremium s
-Ġt am
-UR L
-Ġel ites
-ĠR anger
-otten ham
-ĠH off
-ĠAt hens
-Ġdefin ite
-Ġs ighed
-Ġeven ly
-2 11
-ĠAm ber
-ak ia
-Ġmail ing
-Ġcr ashing
-ĠConfeder ate
-ru gged
-W al
-ĠDep ths
-Ġjuven ile
-Ġreact or
-Introdu ction
-ĠDel uxe
-19 95
-ĠS anchez
-ĠM ead
-iv able
-: -
-ĠPlan ning
-ĠT rap
-qu in
-ĠProt ect
-ve red
-In formation
-Ġkid ney
-inn amon
-l as
-Ġpolic ing
-Ġtoler ate
-ĠQ i
-Ġbi ased
-F ort
-ĠK i
-s ave
-Ġprivile ged
-Ġbe asts
-ĠGl as
-ĠC inem
-Ġcome back
-Sund ay
-Ġext inction
-h ops
-Ġtrans mit
-Ġdoub les
-ĠFl at
-16 7
-Ġdis puted
-Ġinjust ice
-f oo
-V ict
-role um
-ĠJul ie
-Con text
-ĠR arity
-iss ue
-Comp onent
-Ġcounsel ing
-an ne
-d ark
-Ġobject ions
-u ilt
-Ġg ast
-Ġpl ac
-Ġun used
-ãĥ ĩ
-ĠT rial
-ĠJ as
-hed ral
-ob b
-Ġtempor al
-ĠPR O
-ĠN W
-ĠAnn iversary
-L arge
-Ġther m
-Ġd avid
-Ġsystem ic
-ĠSh ir
-m ut
-ĠNe pt
-add ress
-Ġscan ning
-Ġunderstand able
-Ġcan vas
-C at
-ĠZ oo
-Ġang els
-L O
-ĠStat ement
-ĠS ig
-ov able
-ĠA way
-sh aring
-ocr ats
-st ated
-Ġweigh ing
-N or
-w ild
-B ey
-Ġaston ishing
-ĠReyn olds
-Ġop ener
-Ġtrain er
-Ġsurg ical
-p n
-Ġadjust ing
-whe el
-Ġf rown
-erv ative
-Ġsusp end
-With in
-te in
-Ġobst acle
-Ġliber ties
-ym es
-Ġur anium
-ans om
-an ol
-ub a
-ĠL oss
-Ġa rous
-ĠHend erson
-W ow
-s pl
-c ur
-ĠÂ Ń
-Ġtheir s
-Dam age
-Ġdownload ing
-Ġdisc ern
-ĠSt o
-ĠFl a
-Ġh ath
-ĠA j
-Ġun pleasant
-Europe an
-exp ensive
-Ġscreens hot
-ĠU V
-Ġall ied
-ĠPers ian
-Ġmonop oly
-Ġat om
-ĠReds kins
-"> <
-Ġcan cell
-Ġcinem a
-13 1
-f air
-ĠAlf red
-Ġd uck
-arg s
-22 3
-ĠIS I
-Ġsign aling
-in ar
-Ġlaugh s
-Ġfor wards
-Ġreck less
-Ġlisten ers
-at ivity
-Ġvast ly
-n ant
-L ess
-ĠHun ting
-ĠScient ific
-IT ED
-Ġkn ight
-ĠH TC
-us a
-t mp
-Ġr ude
-ĠLegend ary
-Ġar ises
-B ad
-ĠCl aim
-pe g
-Ġreal ities
-Th ink
-ĠÂ °
-Ġro de
-Ġstri ve
-Ġan ecd
-Ġshort s
-Ġhypot hes
-Ġcoord inated
-ĠGand hi
-ĠF PS
-R ED
-Ġsuscept ible
-Ġshr ink
-ĠCh art
-Hel p
-Ġ ion
-de ep
-rib es
-ĠK ai
-ĠCustom er
-Sum mary
-Ġc ough
-w ife
-Ġl end
-Ġposition ing
-Ġlot tery
-ĠC anyon
-Ġf ade
-Ġbron ze
-ĠKenn y
-Ġbo asts
-ĠEnh anced
-rec ord
-Ġemer gence
-Ġa kin
-ĠB ert
-it ous
-âĸ ij
-Ġst ip
-Ġexch anged
-om ore
-als h
-Ġreserv oir
-Ġstand point
-W M
-Ġiniti ate
-Ġdec ay
-Ġbrew ery
-Ġter ribly
-Ġmort al
-lev ard
-Ġrev is
-N I
-el o
-Ġconf ess
-ĠMS NBC
-Ġsub missions
-Cont roller
-Ġ20 2
-ĠR uth
-} );
-ĠAz ure
-Ġ ."
-20 6
-ĠMarket ing
-Ġl aund
-ien cies
-Ġrenown ed
-ĠT rou
-ĠN GO
-ble ms
-Ġterr ified
-Ġwar ns
-Ġper t
-Ġuns ure
-4 80
-ale z
-ult z
-ĠOut side
-Ġst yl
-ĠUnder ground
-Ġp anc
-Ġd ictionary
-Ġf oe
-rim inal
-ĠNor wegian
-Ġj ailed
-Ġm aternal
-é e
-ĠLu cy
-c op
-Ch o
-Ġuns igned
-ĠZe lda
-ĠIns ider
-ĠContin ued
-Ġ13 3
-ĠNar uto
-ĠMajor ity
-16 9
-ĠW o
-ãĤ ĵ
-Ġpast or
-Ġinform al
-Ð ½
-an throp
-jo in
-ãģ Ĺ
-it ational
-N P
-ĠWrit ing
-f n
-ĠB ever
-19 5
-Ġy elling
-Ġdr astically
-Ġe ject
-Ġne ut
-Ġth rive
-ĠFre qu
-ou x
-Ġpossess es
-ĠSen ators
-ĠD ES
-ĠSh akespeare
-ĠFran co
-ĠL B
-uch i
-Ġinc arn
-Ġfound ers
-F unction
-Ġbright ness
-ĠB T
-Ġwh ale
-ĠThe ater
-m ass
-ĠD oll
-S omething
-Ġecho ed
-ĠHe x
-c rit
-af ia
-Ġgodd ess
-Ġele ven
-ĠPre view
-ĠAur ora
-Ġ4 01
-uls ive
-ĠLog an
-in burgh
-ĠCent ers
-ĠON LY
-ĠA id
-Ġparad ox
-Ġh urd
-ĠL C
-D ue
-c ourt
-Ġoff ended
-Ġeval uating
-ĠMatthew s
-Ġto mb
-Ġpay roll
-Ġextra ction
-ĠH ands
-if i
-Ġsuper natural
-ĠCOM M
-] =
-dog s
-Ġ5 12
-ĠMe eting
-Rich ard
-ĠMax imum
-Ġide als
-Th ings
-m and
-ĠReg ardless
-Ġhum ili
-b uffer
-L ittle
-ĠD ani
-ĠN ak
-Ġliber ation
-ĠA be
-ĠO L
-Ġstuff ed
-ac a
-ind a
-raph ic
-Ġmos qu
-Ġcampaign ing
-Ġoccup y
-S qu
-r ina
-ĠW el
-ĠV S
-Ġphys ic
-Ġp uls
-r int
-oad ed
-ET F
-ĠArch ives
-Ġven ues
-h ner
-ĠTur bo
-Ġl ust
-Ġappeal ed
-que z
-il ib
-ĠTim othy
-Ġo mn
-d ro
-Ġobs ession
-ĠSav age
-19 96
-Gl obal
-J es
-2 14
-Ġsl iding
-Ġdisapp ro
-ĠMag ical
-Ġvolunt arily
-g b
-ane y
-Ġprop het
-ĠRe in
-ĠJul ia
-ĠW orth
-aur us
-Ġb ounds
-ie u
-)) )
-Ġcro re
-ĠCitiz en
-S ky
-Ġcolumn ist
-Ġseek ers
-ond o
-IS A
-ĠL ength
-Ġnost alg
-Ġnew com
-Ġdet rim
-ent ric
-3 75
-ĠG E
-Ġaut op
-Ġacadem ics
-App Data
-ĠS hen
-Ġid iot
-ĠTrans it
-Ġteasp oon
-W il
-K O
-ĠCom edy
-> ,
-Ġpop ulated
-W D
-Ġp igs
-ĠO culus
-Ġsymp athetic
-Ġmar athon
-19 8
-Ġseiz ure
-s ided
-Ġd op
-irt ual
-L and
-ĠFl oor
-osa urs
-... ]
-Ġl os
-Ġsubsid iary
-E Y
-ĠPart s
-ĠSt ef
-ĠJud iciary
-Ġ13 4
-Ġmir rors
-Ġk et
-t imes
-Ġneuro log
-Ġc av
-ĠGu est
-Ġtum or
-sc ill
-ĠLl oyd
-E st
-Ġcle arer
-Ġstere otypes
-Ġd ur
-not hing
-Red dit
-Ġnegoti ated
----------------- --------
-23 5
-Ġfl own
-ĠSe oul
-ĠRes ident
-ĠS CH
-Ġdisappear ance
-ĠV ince
-g rown
-Ġgrab s
-r il
-ĠInf inite
-ĠTw enty
-Ġpedest rian
-Ġjer sey
-ĠF ur
-ĠInf inity
-ĠEll iott
-Ġment or
-Ġmor ally
-Ġob ey
-sec ure
-iff e
-Ġantib iotics
-ang led
-ĠFre eman
-ĠIntrodu ction
-J un
-Ġm arsh
-ic ans
-ĠEV ENTS
-och ond
-W all
-icult y
-Ġmisdem eanor
-Ġl y
-Th omas
-ĠRes olution
-Ġanim ations
-ĠD ry
-Ġinter course
-ĠNew castle
-ĠH og
-ĠEqu ipment
-17 7
-Ġterrit orial
-Ġarch ives
-20 3
-Fil ter
-ĠMun ich
-Ġcommand ed
-ĠW and
-Ġpit ches
-ĠCro at
-Ġrat ios
-ĠM its
-Ġaccum ulated
-ĠSpecific ally
-Ġgentle man
-acer b
-Ġp enn
-Ġa ka
-ĠF uk
-Ġinterven e
-ĠRef uge
-ĠAlz heimer
-Ġsuccess ion
-oh an
-d oes
-L ord
-Ġsepar at
-Ġcorrespond ence
-Ġsh iny
-P rior
-Ġs ulf
-Ġmiser able
-Ġded ication
-( ).
-Ġspecial ists
-Ġdefect s
-ĠC ult
-ĠX ia
-Ġje opard
-ĠO re
-Ab ility
-Ġle ar
-Ġamb itions
-ĠB MI
-ĠArab s
-Ġ19 42
-Ġpres ervation
-ific ate
-Ġash amed
-l oss
-ĠRest aur
-Ġrese mble
-Ġen rich
-ĠK N
-ĠCl an
-fl oat
-Ġplay able
-IT T
-Ġharm ony
-arr ison
-ĠWe instein
-w ere
-Ġpoison ing
-ĠCom put
-ĠWord Press
-m ajor
-ĠVal ve
-F an
-ĠTh row
-ĠRom ans
-ĠDep ression
-ad os
-Ġtort ured
-Ġbal ancing
-bott om
-Ġacqu iring
-ĠMon te
-ard i
-Ġa ura
-Ġ# #
-ĠStand ing
-ĠAtl as
-C F
-Ġintr ins
-ĠBen ghazi
-Ġcamp ing
-Ġt apped
-bl ade
-st rous
-ĠR abb
-ĠW ritten
-t ip
-ĠNe igh
-ster dam
-ĠAll ow
-ĠHe aling
-ĠR hod
-n um
-Ġcaffe ine
-ĠPer cent
-Ġbo o
-Ġapp les
-30 5
-Ġwel coming
-Ġappl aud
-Ġa usterity
-Â ±
-ĠRe ality
-ef e
-å ®
-Ġsu cks
-Ġtab s
-ĠPay Pal
-Ġback pack
-Ġgif ted
-abul ary
-ĠSc out
-ir teen
-Ġch in
-Ġo mitted
-Ġnegative ly
-Ġaccess ing
-ĠE arn
-Ġambul ance
-Ġhead phones
-Ġ20 5
-ĠRef resh
-p resident
-ĠKit chen
-ĠEnt ered
-ĠS nyder
-00 5
-om ical
-Ġborrow ed
-ĠN em
-Ġav iation
-Ġst all
-rim ination
-Ġuniform s
-it ime
-ĠSim mons
-ener gy
-ab lished
-y y
-qual ified
-Ġrall ies
-ĠSt uart
-fl ight
-Ġgang s
-r ag
-Ġv ault
-lu x
-ĠCom par
-Ġdesign ation
-20 9
-ĠJ os
-d ollar
-z ero
-Ġwell s
-30 3
-Ġconstitu ents
-Ġhe ck
-Ġc ows
-Ġcommand ers
-Ġdifferent ial
-ĠC atherine
-29 9
-Ġval ve
-Ġbr ace
-Ġperspect ives
-c ert
-f act
-icular ly
-ĠMc N
-pl anes
-Ġint ric
-Ġpe as
-ov an
-Ġtoss ed
-ret ch
-ĠL opez
-Ġunf amiliar
-de ath
-ĠA part
-ĠCh ang
-Ġrelie ved
-rop he
-Ġair ports
-Ġfre ak
-ut il
-M ill
-ĠCh in
-ĠOw en
-m ale
-ĠBro ken
-ĠWind s
-ro b
-r ising
-Ġfire fighters
-Ġauthor itarian
-Ġ14 8
-Bit coin
-ex ternal
-Ġbrow sers
-iche ver
-or ian
-Ġun b
-Ġpo ke
-ĠZ ot
-M id
-ĠPop ular
-Ġco vert
-Ġcont ributes
-Ġ6 50
-Ġcont ention
-G ate
-Ġcons oles
-Ġchrom os
-ĠI X
-Ġvis ually
-ĠE isen
-Ġjewel ry
-Ġdeleg ation
-Ġacceler ate
-ĠR iley
-Ġsl ope
-Ġind oor
-it ially
-Ġhuge ly
-Ġtun nels
-Ġfin ed
-Ġdirect ive
-Ġfore head
-ustom ed
-Ġsk ate
-Mus ic
-g as
-Ġrecogn izing
-am bo
-Ġover weight
-ĠGr ade
-Ù Ĭ
-Ġsound ing
-Ġlock ing
-ĠR EM
-St ore
-Ġexc av
-ĠLike wise
-ĠL ights
-Ġel bow
-ĠSupp ly
-w ic
-Ġhands ome
-19 94
-C oll
-Ġadequ ately
-ĠAssoci ate
-Ġstri ps
-Ġcrack down
-Ġmar vel
-ĠK un
-Ġpass ages
-@@ @@
-ĠT all
-Ġthought ful
-names e
-Ġprost itution
-bus iness
-Ġball istic
-person al
-c ig
-iz ational
-R ound
-ĠÂłĠÂł ĠÂłĠÂł
-ĠCole man
-Ġadm itting
-ĠPl ug
-Ġbit coins
-ĠSu z
-Ġfair ness
-Ġsupp lier
-Ġcatast rophic
-ĠHel en
-o qu
-M arc
-ĠArt icles
-g ie
-Ġend angered
-Ġdest iny
-ĠVol t
-ol ia
-ax is
-Ġche at
-Ġun ified
-IC O
-qu ote
-30 2
-ĠS ed
-Ġsupp ression
-Ġanaly zing
-Ġsqu at
-Ġfig uring
-Ġcoordin ates
-Ġch unks
-Ġ19 46
-Ġsub p
-Ġw iki
-ĠFor bes
-ĠJ upiter
-ĠE rik
-im er
-ĠCom mercial
-\ )
-Ġlegitim acy
-Ġd ental
-ĠMe an
-Ġdefic its
-5 50
-Orig inally
-ĠHor ror
-Ġcontam ination
-ll ah
-Ġconf isc
-ĠCl are
-T B
-ĠF ailed
-an ed
-Ġrul er
-ĠCont roller
-Ġfemin ists
-F ix
-g ay
-20 7
-Ġr abbit
-Th ird
-ownt own
-Ġgl ue
-Ġvol atile
-Ġsh ining
-Ġf oll
-Ġimp aired
-Ġsup ers
-æ Ī
-Ġcl utch
-ļé ĨĴ
-Ġpro let
-Ġ( !
-Ġy elled
-ĠK iev
-ĠEr n
-ĠSh ock
-K B
-Ġsit uated
-qu ery
-ĠN as
-Ġan nex
-char acter
-ĠHol iday
-Ġautom ation
-ĠJ ill
-ĠRem astered
-Ġl inem
-Ġwild erness
-ĠHor izon
-ĠGu inea
-A Z
-Ġmain land
-Ġsec recy
-LE ASE
-Ġp unk
-ĠProv ince
-( ),
-Spe ed
-Ġhand ing
-ĠSeb ast
-S ir
-r ase
-Ġj ournals
-Ġcon gest
-ĠT ut
-ir rel
-Ġschizophren ia
-Ġmis ogyn
-health y
-I ron
-Ġreact ed
-- $
-25 2
-Ġpl ural
-Ġpl um
-Ġbarg ain
-Ġground ed
-f inder
-Ġdis se
-ĠL az
-O OD
-Ġat roc
-F actory
-Ġmin ions
-Ġo ri
-ĠB rave
-ĠP RE
-ĠMy anmar
-ĠH od
-Ġexped ition
-Ġexpl ode
-ĠCo ord
-Ġext r
-ĠB rief
-ĠAD HD
-Ġhard core
-feed ing
-Ġd ile
-ĠF ruit
-Ġvacc ination
-ĠM ao
-osp here
-Ġcont ests
-- |
-Ġf ren
-isp here
-R om
-ĠSh arp
-ĠTre nd
-Ġdis connect
-âĢ¢ âĢ¢
-Ġper secution
-Ear th
-Ġhealth ier
-38 4
-Ġc ob
-ĠTr inity
-OW S
-AN N
-Ġspecial ty
-Ġg ru
-Ġcooper ative
-wh y
-Start ing
-ĠIss ues
-st re
-ens or
-Ġ18 5
-Ad v
-! ?
-ĠRe vel
-em ia
-ĠH ulk
-Ġcelebr ations
-ĠS ou
-ra ud
-ĠKle in
-Ġun real
-con text
-Ġpartners hips
-Ġadop ting
-t ical
-Ġspl ash
-ĠHe zbollah
-c ategory
-cycl op
-xt on
-ĠD ot
-urd y
-t z
-Ġenvelop e
-ĠN L
-â ķ
-Ġwhere in
-Spe c
-18 4
-Ġte lev
-al iation
-Ġmyth s
-å °
-Ġrig orous
-Ġcommun icating
-Ġobser ver
-Ġre he
-ĠW ash
-Ġapolog ized
-ĠT in
-Ġexpend itures
-work ers
-d ocument
-Ġhes itate
-ĠLen in
-Ġunpredict able
-Ġrenew al
-cl er
-ok ia
-ĠCON T
-Ġpost season
-Tok ens
-Ġex acerb
-Ġbet ting
-Ġ14 7
-Ġelev ation
-W ood
-ĠSol omon
-19 4
-00 4
-out put
-Ġredu nd
-ĠM umbai
-Ġp H
-Ġreprodu ce
-ĠD uration
-MA X
-Ġb og
-C BS
-ĠBal ance
-ĠS gt
-ĠRec ent
-Ġc d
-Ġpo pped
-Ġincomp et
-pro p
-ay an
-g uy
-Pac ific
-Ġty r
-Ġ{ {
-ĠMy stic
-ĠD ana
-Ġmast urb
-Ġge ometry
-Ã ¢
-ĠCor rect
-Ġtraject ory
-Ġdistract ed
-Ġf oo
-ĠW elsh
-L uc
-m ith
-Ġrug by
-Ġrespir atory
-Ġtri angle
-Ġ2 15
-Ġunder graduate
-ĠSuper ior
-ch anging
-_ -
-Ġright ly
-Ġrefere e
-Ġluc rative
-Ġun authorized
-Ġresemb les
-ĠGN U
-ĠDer by
-Ġpath ways
-ĠL ed
-Ġend urance
-Ġst int
-Ġcollect or
-F ast
-Ġd ots
-Ġnational s
-ĠSec urities
-Ġwh ip
-Par am
-Ġlearn s
-M agic
-Ġdetail ing
-m oon
-Ġbroadcast ing
-Ġb aked
-26 5
-hol m
-ĠS ah
-ĠHus sein
-ĠCourt esy
-17 4
-Ġ14 6
-Ġge ographic
-pe ace
-Ġjud ging
-ĠS tern
-B ur
-Ġstory line
-G un
-ĠSt ick
-24 5
-30 7
-ãĤ´ ãĥ³
-ĠAdminist rator
-Ġbur nt
-Ġp ave
-ch oes
-Ex ec
-Ġcamp uses
-Res ult
-Ġmut ations
-ĠCh arter
-Ġcapt ures
-Ġcomp ares
-Ġbad ge
-S cient
-Ġer ad
-ier y
-o i
-ett es
-ĠE state
-Ġst rap
-Ġproud ly
-Ġf ried
-Ġwithd rawn
-ĠV oy
-ph ony
-It ems
-ĠP ierce
-b ard
-Ġann otation
-ant on
-ill on
-Im pro
-... )
-Ġhapp ier
----- --
-ad just
-Ġstaff ers
-Ġactiv ism
-Ġper f
-Ġal right
-N eed
-Ġcomm ence
-Ġopio id
-ĠAm anda
-E s
-ĠP ars
-ĠK aw
-W orks
-24 8
-Ġind o
-t c
-end ant
-ĠM oto
-Ġlegal ization
-OT E
-Ġtask ed
-Ġt sp
-ĠACT IONS
-16 6
-Ġrefres hing
-ĠN R
-ĠPere z
-Ġinfring ement
-S Y
-List en
-in ning
-k u
-Ġrot ate
-pro gram
-ar ah
-Des ign
-Ġ( £
-Ġst oring
-Ġwar rants
-Ġjud gement
-ĠB rist
-us ually
-ph oto
-ĠR an
-ĠP ine
-Ġoutrage ous
-ĠValent ine
-lu ence
-ĠEvery body
-Al tern
-Ġrele vance
-Ġtermin ated
-Ġd essert
-Ġfulf illed
-Ġprosecut ed
-ĠW ords
-Ġm igrant
-Ġcultiv ation
-ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ
-idel ity
-ĠV ern
-ĠLog in
-Ġmetaph or
-ĠT ip
-Ġrecru its
-ĠP ig
-rib ing
-Ġenthusi asts
-ex per
-Ġfright ening
-ĠH air
-ans on
-str ate
-Ġh i
-He ight
-Ġown ing
-n one
-Ġdis like
-Ġkn ives
-pher d
-Ġloud ly
-ĠAP Is
-Dis play
-ĠL ac
-ĠUS S
-ab l
-ver ages
-J ew
-Ġ17 2
-ĠHist orical
-at oon
-ĠPhys ics
-in tern
-Ġwarm th
-Ġto pp
-D M
-Ġgun man
-Ġem peror
-od i
-ãĥ £
-in atory
-ĠR ib
-Ġ13 1
-ĠSat urn
-ĠSh ining
-Ġw aking
-Qu otes
-Ġcomed ian
-en berg
-Â ½
-Ġbelie vers
-Ġpaper work
-c ustom
-Ġle v
-Ġl ament
-Ġpour ing
-22 2
-p olitical
-ĠSupp lement
-m aid
-Ġcruel ty
-Ġt read
-ys ics
-A w
-rit es
-Ġmod ifier
-ĠP osition
-Ad am
-l b
-ub s
-Ġimper fect
-Ġcl usters
-ĠEngine er
-ĠC herry
-Ġinaug uration
-ĠS au
-Ġembod iment
-ĠUn cle
-Ġover r
-Ġexplos ions
-c ule
-ĠPrinc eton
-ĠAndre a
-Ġincorrect ly
-Ġearn est
-Ġpil gr
-ĠS print
-Ġslee ve
-Ġhe ars
-ĠAm azing
-Ġbrow sing
-ag in
-Ġhom eland
-Ġha w
-Ġd iving
-ist ered
-17 8
-Ġbarg aining
-ĠArc ade
-Ġdeleg ate
-ters on
-................................ ................................
-ĠJackson ville
-27 5
-Ġst agn
-Ġad am
-ĠSher man
-C B
-Ġsub urb
-ĠFood s
-Ġconver ting
-ĠAr ist
-Ġch ambers
-l ove
-Ġam ino
-ĠG an
-Ġmad ness
-m c
-ĠUS E
-def ined
-Ġul tr
-ind ust
-Ġw olves
-l ance
-Add itionally
-Ġcr acks
-as ia
-ĠRe ason
-ĠP ump
-Ġaccident al
-ĠL aser
-ĠR id
-Ġinitial ized
-ell i
-Ġun named
-Ġn oun
-ĠPass ed
-Ġhost age
-ĠEth iop
-sh irts
-Ġun rel
-ĠEmb assy
-Ġ19 41
-Ġat oms
-Ġpur ported
-16 4
-ĠF i
-Ġgall ons
-ĠMon ica
-Ġp g
-en ment
-Ġsort ed
-ĠG ospel
-Ġhe ights
-Ġtr aced
-Ġunder going
-She ll
-Ġs acks
-Ġproport ions
-Ġhall uc
-F ont
-ac et
-Ġwar mer
-ĠIN TER
-Ġgrab bing
-Pl ug
-Ġreal ization
-ĠBur ke
-Ġen chant
-AT ER
-ĠSe ed
-Ġabund ant
-F M
-Ġc ivic
-V s
-is i
-Ġv ow
-Ġre per
-ĠPartners hip
-Ġpenet ration
-Ġax e
-Ġsh attered
-ĠZ ombies
-Ġv inyl
-ĠAl ert
-e on
-Ġoblig ed
-ĠIll ust
-ĠPl aza
-ĠFront ier
-Ġdavid jl
-ĠSer ial
-ĠH av
-ĠNut rition
-B i
-Ġâĸ Ī
-ĠJ ays
-lin ux
-Ġhur ry
-Ġv oy
-Ġhop eless
-ĠSte alth
-Ġ ãģ
-ess ors
-tt le
-b org
-ĠSaf ari
-f ell
-Ġw ary
-d ue
-ĠAb ove
-H a
-E LL
-Ġnot or
-ĠW on
-T oo
-Ġoccup ations
-Ġposs essions
-Ġinv iting
-Ġpred ators
-Ġacceler ated
-Ġ15 7
-uter te
-ĠC ube
-e ast
-acc ount
-G ive
-Ġtrans plant
-red ients
-id able
-Ġscreens hots
-ĠG und
-ĠF S
-Ġtravel ers
-Ġsens ory
-ĠF iat
-ĠRock ets
-İ ĭ
-_ {
-F riend
-Ġchar ming
-AL S
-Ġenjoy ment
-m ph
-Ġ5 000
-ĠRE G
-Ù Ĩ
-b ia
-Ġcomp ilation
-ro st
-ĠV P
-ĠSch ne
-201 9
-Ġcop ying
-M ORE
-ĠFl ore
-f alls
-2 15
-t otal
-Ġdis ciples
-d ouble
-Ġexceed ing
-Ġsm ashed
-Ġconcept ual
-ĠRom ania
-ĠB rent
-ĠI CE
-ĠT ou
-Ġg rap
-Ġn ails
-18 9
-ãĥ ĺ
-Ġproc ure
-e ur
-Ġconfir ming
-ĠC ec
-aw i
-ĠEd en
-Ġn g
-Ġengine ered
-at ics
-Ġhook ed
-Ġdisgust ing
-ĠMur der
-ãĤ ¿
-L ibrary
-Ġ16 8
-Al most
-hem atic
-Men u
-ĠNot re
-ĠJ ur
-Ġkidn apped
-Ġhack er
-ĠJ ade
-Ġcreep y
-Ġdraw ings
-ĠSpons or
-Ġcycl ists
-ĠGob lin
-Ġoptim ized
-Ġst aged
-ĠMc D
-bet ween
-A ge
-en o
-S ex
-ĠW ide
-n ings
-av is
-Ġincap able
-ĠK ob
-Ġreward ing
-ĠL one
-oles cent
-Ġcontract ed
-Ġstick y
-J ose
-B all
-f est
-ĠIn put
-ĠRec ently
-Ġto mat
-squ are
-App lication
-Ġnit rogen
-Ġdupl icate
-ĠRec on
-ĠD ear
-L ondon
-Ġint ra
-Ġd ock
-Ġout reach
-ĠM illion
-Ġmamm als
-am pton
-V AL
-Ġsn aps
-Ġd os
-ĠWh ole
-ĠRead y
-T ry
-ĠWinn ipeg
-ear ance
-Ġinc urred
-ren ched
-ĠNS W
-il ot
-rain e
-Ġc ube
-g ot
-Ġrun way
-etermin ed
-ĠHaw ks
-Ġsurviv or
-ĠW ish
-ĠD in
-ĠDE F
-ĠV ault
-18 7
-Ġmush rooms
-Ġcris p
-be y
-ĠDisco very
-Ġdevelopment al
-Ġparad igm
-Ġcha otic
-ĠT su
-Ġ3 33
-b ons
-Ġbacter ial
-Ġcomm its
-Ġcos mic
-Ġme ga
-oc ative
-ĠP aint
-ophob ic
-Ġv ain
-Ġcar ved
-ĠTh ief
-ĠG ul
-ows hip
-Ġc ites
-ĠEd inburgh
-Ġdimin ished
-Ġacknowled ges
-ĠK ills
-Ġmic row
-ĠHer a
-Ġsen iors
-Ġwhere by
-H op
-at ron
-Ġun available
-ĠN ate
-Ġ4 80
-Ġsl ated
-ĠRe becca
-ĠB attery
-Ġgram mar
-Ġhead set
-Ġcurs or
-Ġex cluding
-any e
-aunder ing
-eb in
-Ġfeas ible
-ĠPub lishing
-ĠLab s
-ĠCl iff
-ĠFerr ari
-Ġp ac
-vis ible
-mark ed
-pe ll
-Ġpol ite
-Ġstagger ing
-ĠGal actic
-Ġsuper st
-Ġpar an
-ĠOffic ers
-ãĢ ģ
-Ġspecific s
-ul us
-23 9
-ĠP aste
-AM P
-ĠPan ama
-ĠDe lete
-angu ard
-rest rial
-Ġhero ic
-ĠD y
-ا ÙĦ
-Ġincumb ent
-Ġcr unch
-t ro
-Ġsc oop
-Ġblog ger
-Ġsell ers
-ure n
-Ġmedic ines
-ĠC aps
-ĠAnim ation
-ox y
-Ġout ward
-Ġinqu iries
-22 9
-Ġpsych ologist
-ĠS ask
-ev il
-Ġcontam inated
-ãĤ ¨
-he rence
-Ġbrand ed
-ĠAbd ul
-z h
-Ġparagraph s
-Ġmin s
-Ġcor related
-er b
-Ġimp art
-Ġmil estone
-ĠSol utions
-ot le
-Ġunder cover
-Ġmar ched
-ĠCharg ers
-f ax
-ĠSec rets
-Ġr uth
-we ather
-Ġfemin ine
-Ġsh am
-Ġprest igious
-igg ins
-Ġs ung
-hist ory
-ett le
-gg ie
-Ġout dated
-ol and
-Ġper ceptions
-ĠS ession
-ĠDod gers
-u j
-ĠE ND
-D oc
-Ġdefic iency
-Gr and
-ĠJ oker
-Ġretro spect
-Ġdiagn ostic
-Ġharm less
-Ġro gue
-ĠA val
-E qu
-Ġtrans c
-ĠRoberts on
-ĠDep ending
-ĠBurn s
-iv o
-Ġhost ility
-F eatures
-ĵ ĺ
-Ġdis comfort
-ĠL CD
-spec ified
-ĠEx pect
-3 40
-Ġimper ative
-ĠReg ular
-Ch inese
-Ġstate wide
-Ġsy mm
-Ġlo ops
-Ġaut umn
-N ick
-Ġsh aping
-Ġqu ot
-Ġc herry
-ĠCross ref
-è¦ ļéĨĴ
-Stand ard
-he ed
-ĠD ell
-ĠViet namese
-Ġo st
-ĠV alkyrie
-O A
-Ass ad
-Ġreb ound
-ĠTra ffic
-pl aces
-æ ĺ
-ĠB uc
-17 2
-Ġshel ters
-Ġins isting
-ĠCertain ly
-ĠKenn eth
-ĠT CP
-Ġpen al
-ĠRe play
-he ard
-Ġdial ect
-iz a
-ĠF Y
-it cher
-ĠD L
-Ġspir al
-Ġquarterback s
-Ġh ull
-Ġgo ogle
-Ġto dd
-ĠSter ling
-ĠPl ate
-Ġsp ying
-mb ol
-ĠReal m
-ĠPro ced
-ĠCr ash
-Ġtermin ate
-Ġprotest ing
-C enter
-gu ided
-Ġun cover
-Ġboy cott
-Ġreal izes
-s ound
-Ġpret ending
-ĠV as
-19 80
-Ġfram ed
-Ġ13 9
-Ġdesc ended
-Ġrehab ilitation
-Ġborrow ing
-ĠB uch
-Ġbl ur
-R on
-ĠFro zen
-en za
-Ch ief
-ĠP oor
-Ġtransl ates
-M IN
-Ġ2 12
-J ECT
-Ġerupt ed
-Ġsuccess es
-S EC
-Ġpl ague
-Ġg ems
-d oms
-Ġstret ches
-ĠSp y
-Ġstory telling
-C redit
-ĠP ush
-Ġtra ction
-Ġin effective
-ĠL una
-Ġt apes
-Ġanaly tics
-erc ise
-Ġprogram mes
-ĠCar bon
-Ġbeh old
-he avy
-ĠConserv ation
-ĠF IR
-Ġs ack
-ter min
-ric ks
-Ġhous ed
-Ġunus ually
-I ce
-Ġexecut ing
-ĠMor oc
-ed ay
-Ġed itions
-Ġsm arter
-ĠB A
-Ġout law
-Ġvan ished
-ib a
-AL SE
-ĠSil va
-23 8
-C ould
-Ġphilos opher
-Ġevac uated
-Sec ret
-14 2
-Ġvis as
-ãĤ ¬
-ĠM alt
-ĠClear ly
-ĠN iger
-ĠC airo
-ĠF ist
-3 80
-ĠX ML
-aut o
-it ant
-Ġrein forced
-Rec ord
-ĠSurviv or
-G Hz
-Ġscrew s
-parent s
-Ġo ceans
-ma res
-Ġbra kes
-vas ive
-Ġhell o
-ĠS IM
-rim p
-Ġo re
-ĠArm our
-24 7
-Ġterr ific
-Ġt ones
-14 1
-ĠMin utes
-Ep isode
-Ġcur ves
-Ġinflamm atory
-Ġbat ting
-ĠBeaut iful
-L ay
-Ġunp op
-v able
-Ġr iots
-ĠTact ics
-b augh
-ĠC ock
-Ġorg asm
-ĠS as
-Ġconstruct or
-et z
-G ov
-Ġant agon
-Ġthe at
-Ġde eds
-ha o
-c uts
-ĠMc Cl
-Ġu m
-ĠScient ists
-Ġgrass roots
-ys sey
-"] =>
-Ġsurf aced
-Ġsh ades
-Ġneighb ours
-Ġad vertis
-oy a
-Ġmer ged
-Up on
-Ġg ad
-Ġanticip ate
-Any way
-Ġsl ogan
-Ġdis respect
-I ran
-ĠT B
-act ed
-Ġsubp oen
-medi ately
-OO OO
-Ġwa iver
-Ġvulner abilities
-ott esville
-ĠHuff ington
-J osh
-ĠD H
-M onday
-ĠEll en
-K now
-x on
-it ems
-22 8
-Ġf ills
-ĠN ike
-Ġcum ulative
-and als
-I r
-Ġ ì
-Ġfr iction
-ig ator
-Ġsc ans
-ĠVi enna
-ld om
-Ġperform ers
-P rim
-Ġb idding
-M ur
-Ġlean ed
-ĠPri x
-al ks
-Ġ[ âĢ¦]
-ĠTw itch
-ĠDevelop er
-ĠG ir
-Ġcall back
-Ab stract
-Ġacc ustomed
-Ġfreed oms
-ĠP G
-ur acy
-Ġl ump
-is man
-,, ,,
-19 92
-ĠR ED
-Ġwor m
-M atch
-ĠPl atinum
-I J
-ĠOwn er
-Tri via
-com pl
-Ġnew born
-Ġfant as
-O wn
-Ġ19 59
-Ġsymp ath
-Ġub iqu
-Ġoutput s
-Ġal lev
-Ġpr ag
-K evin
-Ġfav ors
-Ġbur ial
-Ġn urt
-so lete
-c ache
-Ġ15 6
-Ġunl ocks
-te chn
-M aking
-Ġcon quer
-ad ic
-æ ĸ
-Ġel f
-Ġelect orate
-ĠKurd s
-ĠSt ack
-ĠSam urai
-Ġâ ĺħ
-Ġ{ }
-ĠS aid
-ĠFall out
-Ġkind ness
-ĠCustom s
-ĠBou levard
-Ġhelicop ters
-ot ics
-ĠVe get
-com ment
-Ġcritic ised
-Ġpol ished
-ĠRem ix
-ĠC ultural
-Ġrec ons
-Ġdo i
-at em
-Sc reen
-Ġbar red
-Com ments
-ĠGener ally
-Ġsl ap
-7 20
-V ari
-p ine
-Ġem pt
-Ġh ats
-ĠPlay ing
-l ab
-a verage
-form s
-ĠC otton
-Ġcan s
-ĠD ON
-ĠSom alia
-C rypt
-ĠIncre ases
-E ver
-mod ern
-Ġsur geon
-3 000
-Ġrandom ized
-================================ ================================
-B ern
-im pl
-ĠC OR
-Ġpro claim
-th ouse
-Ġto es
-Ġam ple
-Ġpres erving
-Ġdis bel
-gr and
-B esides
-Ġsil k
-ĠPat tern
-h m
-Ġenter prises
-Ġaffidav it
-ĠAdvis ory
-Ġadvert ised
-ĠRel igious
-se ctions
-psy ch
-ĠField s
-aw ays
-Ġhasht ag
-ĠNight mare
-Ġv ampire
-Ġfore nsic
-rosso ver
-n ar
-Ġn avy
-Ġvac ant
-ĠD uel
-Ġhall way
-Ġface book
-ident ally
-ĠN RA
-Ġm att
-Ġhur ricane
-ĠKir by
-ĠP uzzle
-Ġsk irt
-ou st
-du llah
-Ġanal ogy
-in ion
-Ġtomat oes
-ĠN V
-ĠPe ak
-ĠMe yer
-Ġappoint ments
-Ġm asc
-Ġal ley
-re hend
-Ġchar ities
-Ġund o
-Ġdest inations
-ĠTest ing
-">
-Ġdest ined
-Ġimp lements
-ĠHar old
-RE CT
-Ġoptim ization
-Ġkilomet res
-Ġc md
-Ġimpair ment
-Ġun successful
-Ġswift ly
-ĠGlas gow
-art en
-ĠSh ares
-ĠAn swer
-ĠAl bum
-Ġnut ritional
-ãĥ ĸ
-ĠF ut
-Ġbl oc
-ĠN FC
-Ġwholes ale
-ĠC W
-Ġneg lected
-Ġlaun cher
-Ġannounce ments
-OU LD
-com b
-Ġrot ating
-Ġrest s
-ĠT icket
-ched el
-L ou
-ĠV ic
-Ġ" '
-Ġtem plates
-Ġrepl aces
-Ar c
-:: ::
-ĠGil bert
-Ġillness es
-Ġsched ules
-Ġheter osexual
-L INE
-Ġhere in
-Ġco erc
-Ġdecre asing
-Ġde portation
-s udo
-ĠInd igenous
-Ġweigh s
-Al ong
-' );
-ĠBeng als
-70 7
-Ġjoint s
-ver ts
-Ġ14 9
-na ire
-Ġsimpl est
-Ġl ore
-10 80
-f iction
-ĠDat abase
-Ġreserv ation
-Ġs ou
-Ġsan ctuary
-aud io
-ap le
-Ġveget arian
-Ġanticip ation
-m icro
-Ġend uring
-Ġdepart ed
-Ġsidew alk
-Ġprohib its
-ĠF ont
-Ġcomp ute
-ĠS ect
-Ġ15 8
-B attle
-Ġbom ber
-Ġdist raction
-Ġend ured
-Ġpractition ers
-Ġdistur bed
-Ġdr ank
-ord ered
-Ġsurpr ises
-se at
-Sec urity
-ĠW isdom
-og o
-Ġsub paragraph
-ĠPen insula
-ĠOrig ins
-ire n
-ĠP av
-igg le
-Ġgrat itude
-ĠG ravity
-over ty
-im an
-ct r
-ĠCa esar
-c ould
-g em
-Ġsk ies
-Ġch amp
-Ġagree ing
-F amily
-D iv
-17 6
-Ġmess y
-um ption
-F ederal
-ern o
-ĠCh at
-Bey ond
-Ġdev ote
-ĠW alsh
-Ġdump ed
-Ġaccum ulation
-st ad
-hib ition
-Ġsm okers
-Ġinspect or
-F rench
-iss an
-ĠV ita
-Ġresearch ing
-R AM
-ĠCelt ics
-Ġcl oak
-ĠTer ra
-M ary
-so ld
-ĠD OM
-mod s
-Int el
-Ġmult itude
-ĠImpro ved
-Ġrel iance
-Ġartif act
-Ġalarm ing
-P rom
-h on
-T ION
-med ium
-Ġref lex
-ĠEx cel
-Ġweaken ed
-16 3
-2 24
-Ġcost umes
-Ġunique ly
-Ġs orrow
-Ġm ansion
-w p
-Ġsal v
-ĠGro ve
-bs p
-ĠSn iper
-ĠSh ipping
-ĠP OW
-Ġund is
-Ġbrand ing
-G irl
-ĠAh mad
-ĠL akes
-ĠCore y
-Ġinherit ance
-ener y
-Ġpack ing
-ĠP rest
-D est
-F W
-Ġregul ator
-l ocked
-Ġcont ested
-ĠMel issa
-ĠD uc
-Ġunpop ular
-Ġst acked
-Ġ19 17
-Ġyear ly
-Ġst are
-Ġassess ing
-Ã ¸
-Ġbe verages
-Ġcompet itions
-Ġstreng thening
-al ong
-ĠL ud
-Ġmel ted
-stan bul
-Ġb ounty
-EN C
-ĠL ands
-Ġdecl ares
-Ġcustom ize
-Ġcomp osite
-ãĥ ¬
-C M
-ograph ics
-ĠTem p
-Ġcont ender
-Ġins ign
-ĠL AN
-Ġdis asters
-ins pired
-Ġjud gments
-ustain able
-urs ion
-Ġvar iance
-ĠUlt imately
-Ġ --------
-u ador
-ĠR X
-Ġmel ting
-ĠExt ended
-ĠT we
-M ajor
-ĠB il
-Ġsy rup
-qu ick
-ĠHold er
-Ġinnoc ence
-U LE
-ĠM ight
-99 99
-Ġf al
-Ġcontinu ity
-Ġ19 53
-ĠB S
-st ill
-L at
-ĠAb use
-Ġun supported
-xxxx xxxx
-Ġinst itute
-Ġfrag ment
-ĠP ep
-W estern
-ĠC ause
-ĠFr ag
-ĠAr s
-à ¥
-ast ics
-Ġb ishop
-Ġcross es
-Ġ15 4
-ĠUp grade
-Ġmit igate
-ĠRay mond
-Mod s
-Ġtom ato
-Ġst umbled
-Ġdiff ers
-In itial
-ĠR aspberry
-Ġign ores
-Ġt ant
-Ã ł
-Ġrel ay
-Ġb isexual
-Ġconf ession
-Ġd ement
-in as
-ĠHe ather
-pl atform
-dri ving
-bour g
-ĠM ush
-Ġhy ster
-Det ails
-Ġdr ift
-ĠW ald
-ĠLuck ily
-or f
-Ġexp ire
-ĠP unch
-zy me
-g old
-Ġunp aid
-ĠT rent
-Ġun armed
-Ġill icit
-ĠT ottenham
-Ġsm ash
-Intern ational
-ink er
-Ġst ing
-ĠSadd am
-ĠAR T
-Ġtruth s
-b irth
-Ġso ber
-ĠN it
-Ġ ib
-Ġus able
-Ġst acks
-ĠSy lv
-Ġnort heast
-Ġdom ination
-ĠM our
-EN SE
-ĠMe asure
-Ġprogram mer
-Ġ< -
-18 2
-ĠCond ition
-Ġback yard
-ir ling
-ĠJ eb
-ĠCre ed
-ĠH ang
-ĠCOM P
-F ER
-ĠIs h
-Ġdetect ives
------------- ---
-ĠMess enger
-Ġlo oph
-Ġgate way
-15 1
-ĠMaterial s
-ĠD T
-Ġdo omed
-od o
-Ġslic es
-Ġemail ed
-ĠPer l
-Ġren ov
-UT H
-ody nam
-ĠSouth west
-get ic
-ĠT PP
-Ġoptim ism
-ĠT ow
-ul ators
-prot ected
-y les
-Â «
-Ġex ile
-en v
-P rop
-ĠZimmer man
-Ù İ
-C a
-om aly
-ãĥ Ĩ
-Ġrail road
-L ee
-23 2
-Ġrepl icate
-Ġcomfort ably
-act ly
-Ġr av
-Ġtelesc ope
-Ġhonest y
-ĠPe pper
-ĠBr ing
-Ġric hest
-Ġout doors
-Ġh alls
-Ġcont end
-IS E
-Ġsub mitting
-Ġna ive
-ar ations
-Ġ14 3
-Ġpo ised
-respons ible
-Ġsoc ks
-ĠSk ull
-Quest ion
-Ġdiscover ies
-Jo ined
-ĠEn emies
-ĠWire less
-ĠRe venge
-Ġpuzz les
-Ġce ased
-29 0
-cript ions
-ĠCon sole
-Ġbo iling
-Ġdisc rep
-Ġded uction
-Ġar senal
-XX XX
-ĠAm sterdam
-rox imately
-ĠSh ane
-Ġpos ing
-ĠACL U
-ĠCompan ies
-Ġthe ology
-ĠU g
-qu arter
-ĠH ank
-Co in
-ĠL v
-Ġalleg ation
-ĠAv oid
-Ġindef initely
-Ġcommod ities
-Ġbr ig
-ĠMan it
-Ġt enth
-met hod
-ĠKn icks
-ĠâĢ İ
-Ġinv oked
-D ial
-AR A
-Ġc aucus
-22 7
-ĠJ ab
-Ġoun ces
-b ay
-Ġbud dy
-f an
-23 4
-ĠH il
-ad h
-ĠT Y
-ĠIN D
-Ġ19 39
-Ġiter ation
-ĠGonz alez
-ĠV ert
-ĠI O
-em b
-re ra
-en ch
-ĠRequ irements
-ĠW ins
-Ġlivest ock
-h ours
-" âĢ¦
-b ral
-M arg
-ĠD one
-Ġwas ting
-ing ed
-g roups
-Ġw ishing
-ĠT umblr
-Ġt apping
-Ġnational ism
-ĠB yr
-Ġsqu ares
-ĠAct ions
-ãĥ ¥
-In side
-deb ug
-Ġapp end
-Ġstub born
-ĠC ind
-T ell
-Ġt earing
-ĠRe y
-or c
-ĠDay ton
-ĠN H
-ĠMad ness
-Ch arl
-ĠMor rison
-fil ter
-Ġacc use
-Ġ. /
-Ġtor rent
-Ġdecl ines
-g allery
-M ine
-Ġneg otiation
-ĠBash ar
-op ia
-19 93
-em ort
-ĠNo vel
-ĠF ang
-ers ive
-ĠInst ant
-Ġroll er
-A round
-ĠElect ions
-G ames
-Ġin expensive
-Ġwor s
-Ġv ul
-ĠH ole
-Ġunbeliev able
-Ġn ause
-Ġent r
-bo at
-ĠST E
-Ġbus h
-ĠHass an
-Ġw o
-Ġpa used
-ĠM ig
-l ived
-Ġsc out
-Ġl ith
-Pub lished
-du ino
-c ool
-Ġcirc ulating
-id as
-ĠP am
-viol ent
-ĠCraw ford
-udd le
-ĠLet ters
-Gu ard
-mor ph
-Ġwand ering
-Ġsoph omore
-Ġque er
-ĠBl ind
-r ue
-ĠMar riage
-D om
-Ġpadd ing
-Ġfold ers
-Ġmeaning less
-Ġcandid acy
-af ort
-Ġwhistle bl
-ĠIdent ified
-Ġcig ar
-Ġh id
-ĠDub ai
-Ġpost ure
-Ġh iking
-ĠTermin al
-Legend ary
-ĠT P
-ĠAT K
-ĠStar bucks
-ĠR iot
-19 91
-ĠBott om
-e ffic
-ĠEug ene
-ĠWy oming
-ĠRock y
-Ġsal mon
-Ġmet ro
-Ġb ilateral
-Ġcelebr ates
-L ength
-b illion
-B at
-Ġre leg
-Ġpse udo
-D T
-ĠRh ode
-P arent
-ple tion
-Ġatt ribut
-Ġtun ing
-ĠNOT E
-ĠRe bel
-ic us
-F und
-Ġcock tail
-Ġ5 01
-Ġsp oon
-Ġbrut ality
-Ġun ite
-Ġmicro bi
-ĠRe ich
-pos itive
-Ġam azed
-ĠN T
-D esc
-ECT ION
-Ġfalse ly
-ĠHigh lander
-ĠC rist
-ĠVictor ian
-Ġdistribut ions
-the ir
-ĠE instein
-Ġp od
-Ġepid em
-Ġhe ap
-ĠR anch
-Ġan them
-Ġre app
-ĠAub urn
-Ġconc urrent
-ĠThrough out
-ĠP OST
-â ĺ
-Ġhom emade
-k ick
-B eg
-Ġch assis
-c ounter
-Ġmer ger
-Ġl aps
-2 17
-un ion
-ĠTr igger
-Ġdeb ated
-Ġsil ently
-Ġrest raint
-B al
-0000 000
-Ġform idable
-ĠFil ip
-Ġsacrific es
-F ood
-Ġdwar f
-ĠSe qu
-in ian
-More over
-Ġtang ible
-ops is
-ĠMine craft
-ĠRegist ration
-o an
-Ġrepresent ations
-Ġth irst
-Ġcor p
-ire ment
-M ade
-l oe
-> "
-c ats
-* .
-Ġgest ures
-gener al
-Le ague
-Ġpack ets
-ĠInspect or
-ĠBer g
-Ġfraud ulent
-Ġcritic ize
-F un
-Ġbl aming
-nd ra
-Ġsl ash
-ĠE ston
-Ġpropos ing
-Ġwh ales
-Ġtherap ist
-Ġsub set
-Ġle isure
-EL D
-ĠC VE
-ĠAct ivity
-Ġcul min
-sh op
-ĠD AY
-is cher
-ĠAdmir al
-ĠAtt acks
-Ġ19 58
-Ġmem oir
-Ġfold ed
-Ġsex ist
-Ġ15 3
-ĠL I
-Ġread ings
-Ġembarrass ment
-ĠEmploy ment
-w art
-ch in
-Ġcontin uation
-l ia
-Rec ently
-Ġd uel
-Ġevac uation
-ĠKash mir
-Ġdis position
-ĠR ig
-Ġbol ts
-Ġins urers
-4 67
-M ex
-Ġret aliation
-Ġmis ery
-Ġunre asonable
-r aining
-I mm
-ĠP U
-em er
-Ġgen ital
-ãĤ ³
-ĠC andy
-Ġon ions
-ĠP att
-lin er
-Ġconced ed
-Ġf a
-Ġfor c
-ĠH ernandez
-ĠGe off
-deb ian
-ĠTe ams
-Ġc ries
-Ġhome owners
-23 7
-A BC
-Ġst itch
-Ġstat istic
-Ġhead ers
-ĠBi ology
-Ġmot ors
-ĠG EN
-ĠL ip
-Ġh ates
-Ġhe el
-S elf
-i pl
-ED IT
-ort ing
-Ġann ot
-ĠSpe ech
-old emort
-ĠJ avascript
-ĠLe Bron
-Ġfoot print
-Ġf n
-Ġseiz ures
-n as
-h ide
-Ġ19 54
-ĠBe e
-ĠDecl aration
-ĠKat ie
-Ġreserv ations
-N R
-f emale
-Ġsatur ated
-Ġb iblical
-Ġtroll s
-Dev ice
-ph otos
-Ġdr ums
-ãĥīãĥ© ãĤ´ãĥ³
-N ight
-f ighter
-ĠH ak
-ri ber
-Ġc ush
-Ġdiscipl inary
-ba um
-ĠG H
-ĠSch midt
-ilib rium
-Ġs ixty
-ĠKush ner
-ro ts
-Ġp und
-ĠR ac
-Ġspr ings
-Ġcon ve
-Bus iness
-F all
-Ġqual ifications
-Ġvers es
-Ġnarc iss
-ĠK oh
-ĠW ow
-ĠCharl ottesville
-ed o
-Ġinterrog ation
-ĠW ool
-36 5
-B rian
-Ġâľ ĵ
-Ġalleg es
-ond s
-id ation
-ĠJack ie
-y u
-Ġl akes
-Ġworth while
-Ġcryst als
-ĠJud a
-Ġcomp rehend
-Ġfl ush
-Ġabsor ption
-ĠO C
-Ġfright ened
-ĠCh ocolate
-Mart in
-Ġbu ys
-Ġbu cks
-Ġapp ell
-ĠChampions hips
-Ġlist ener
-ĠDef ensive
-Ġc z
-ud s
-ĠM ate
-Ġre play
-Ġdecor ated
-Ġs unk
-ĠV IP
-ĠAn k
-Ġ19 5
-aa aa
-Nob ody
-ĠMil k
-ĠG ur
-ĠM k
-ĠS ara
-Ġse ating
-ĠW id
-Tr ack
-Ġemploy s
-Ġgig antic
-AP P
-ãĤ §
-in ventory
-Ġtow el
-at che
-l asting
-ĠT L
-Ġlat ency
-Ġkn e
-B er
-me aning
-Ġup held
-Ġplay ground
-Ġm ant
-S ide
-Ġstere o
-Ġnorth west
-Ġexception ally
-Ġr ays
-Ġrec urring
-D rive
-Ġup right
-Ġab duct
-ĠMar athon
-Ġgood bye
-Ġal phabet
-h p
-Ġcourt room
-ring ton
-ot hing
-T ag
-Ġdiplom ats
-Ġbar bar
-ĠAqu a
-18 3
-33 33
-Ġmat urity
-Ġinst ability
-ĠAp ache
-Ġ= ==
-Ġfast ing
-ĠGr id
-Mod Loader
-Ġ15 2
-A bs
-ĠOper ating
-ett i
-Ġacqu aint
-Don nell
-ĠK em
-ĠFor ge
-Ġarm ored
-M il
-Ġphilos ophers
-in vest
-Pl ayers
-â Ī
-Ġmy riad
-Ġcomr ades
-R ot
-Ġremember ing
-Ġcorrespond s
-Ġprogram mers
-ĠLyn n
-Ġo lig
-Ġco herent
-yn chron
-ĠChem ical
-Ġj ugg
-p air
-post s
-E ye
-ĠIn ner
-Ġsem ester
-ott est
-ĠEmir ates
-ric anes
-or ously
-m its
-ĠW is
-Ġd odge
-l ocation
-Ġf aded
-Am azon
-ĠPro ceed
-ĠIN FO
-j ournal
-ĠTru ck
-T en
-Ġ2 17
-Ġstat utes
-m obile
-ĠT ypes
-Rec omm
-b uster
-pe x
-Ġleg ends
-Ġhead ache
-f aced
-ĠWi Fi
-if ty
-ĠH ER
-Ġcirc uits
-ER ROR
-22 6
-ol in
-Ġcyl inder
-osp ace
-ik ers
-P rem
-Qu ant
-Ġconflic ting
-Ġslight est
-Ġfor ged
-ion age
-Step hen
-ĠK ub
-ĠOpp ortun
-ĠHe al
-Ġbl o
-Ġrul ers
-Ġh uh
-Ġsubmar ine
-f y
-ass er
-Ġallow ance
-ĠKas ich
-ĠT as
-ĠAustral ians
-Forge ModLoader
-ĠâĨ ij
-ĠMat rix
-am ins
-Ġ12 00
-ĠAc qu
-23 6
-D ocument
-ĠBre aking
-19 3
-ĠSub st
-ĠRoll er
-ĠPro perties
-ĠN I
-t ier
-Ġcr ushing
-Ġadvoc ating
-Further more
-keep ers
-Ġsex ism
-x d
-Ġcall er
-ĠS ense
-chie ve
-ĠT F
-Ġfuel ed
-Ġreminis cent
-Ġobs ess
-ur st
-Ġup hold
-ĠF ans
-het ics
-Ġâ Ĺ
-ĠB ath
-Ġbe verage
-Ġo scill
-25 4
-Ġpol es
-Ġgrad ual
-Ġex ting
-ĠS uff
-ĠS uddenly
-Ġlik ing
-Ġ19 49
-un ciation
-am ination
-ĠO mar
-ĠL V
-ĠCon sequently
-Ġsynt hes
-ĠG IF
-Ġp ains
-Ġinteract ing
-u ously
-inc re
-Ġrum or
-ĠScient ology
-19 7
-ĠZ ig
-Ġspe lling
-ĠA SS
-Ġexting u
-ms on
-Ġg h
-Ġremark ed
-ĠStrateg ic
-ĠM ON
-å ¥
-g ae
-ĠWH AT
-E ric
-ĠCamp us
-Ġmeth ane
-Ġimag in
-J UST
-ĠAl m
-X T
-i q
-ĠR SS
-Ġwrong doing
-att a
-Ġbig ot
-Ġdemonstr ators
-ĠCal vin
-ĠV illa
-Ġmembr ane
-ĠAw esome
-Ġbenef ic
-26 8
-Ġmagn ificent
-ĠL ots
-G reg
-ĠBor is
-Ġdetain ees
-ĠH erman
-Ġwhis pered
-Ġa we
-Prof essor
-fund ing
-Ġphys iological
-ĠDest ruction
-Ġlim b
-Ġmanip ulated
-Ġbub bles
-Ġpse ud
-Ġhyd ra
-ĠBrist ol
-Ġst ellar
-ĠExp ansion
-ĠK ell
-ĠInterest ingly
-Ġm ans
-Ġdrag ging
-Ġec ological
-ĠF it
-Ġg ent
-Ġbenef ited
-ĠHait i
-Ġpoly g
-ãĥ İ
-Ġ20 30
-Ġpro w
-Ġrecon struction
-Ġwas t
-Ġpsych ic
-ĠGree ks
-Hand ler
-16 2
-ĠP ulse
-Ġsol icit
-Ġsy s
-Ġinflu x
-ĠG entle
-per cent
-Ġprolifer ation
-Ġtax able
-Ġdisreg ard
-Ġesc aping
-Ġg inger
-Ġwith stand
-Ġdevast ated
-ĠD ew
-ser ies
-Ġinject ed
-ela ide
-Ġturn over
-he at
-Ļ Ĥ
-H appy
-ĠSil ent
-ãĤ Ń
-iv ism
-Ġir rational
-AM A
-Ġre ef
-r ub
-Ġ16 2
-Ġbank ers
-ĠEth ics
-v v
-Ġcritic isms
-K n
-18 6
-M ovie
-ĠT ories
-Ġno od
-Ġdist ortion
-F alse
-od ore
-Ġt asty
-Res earch
-ĠU ID
-- )
-Ġdivor ced
-ĠM U
-ĠHay es
-ĠIs n
-ian i
-ĠH Q
-Ġ" #
-ign ant
-Ġtra umatic
-ĠL ing
-H un
-Ġsab ot
-on line
-r andom
-Ġren amed
-ra red
-K A
-d ead
-é t
-ĠAss istance
-Ġse af
-++++ ++++
-Ġse ldom
-ĠWeb b
-Ġbo olean
-u let
-Ġref rain
-ĠDI Y
-ru le
-Ġshut ting
-Ġutil izing
-load ing
-ĠPar am
-co al
-oot er
-Ġattract ing
-ĠD ol
-Ġher s
-ag netic
-ĠRe ach
-im o
-Ġdisc arded
-ĠP ip
-01 5
-ü r
-Ġm ug
-Im agine
-C OL
-Ġcurs ed
-ĠSh ows
-ĠCurt is
-ĠSach s
-spe aking
-ĠV ista
-ĠFram ework
-ong o
-Ġsub reddit
-Ġcr us
-ĠO val
-R ow
-g rowing
-Ġinstall ment
-Ġgl ac
-ĠAdv ance
-EC K
-ĠLGBT Q
-LE Y
-Ġac et
-Ġsuccess ive
-ĠNic ole
-Ġ19 57
-Qu ote
-Ġcircumst ance
-ack ets
-Ġ14 2
-ort ium
-Ġguess ed
-ĠFr ame
-Ġperpet rators
-ĠAv iation
-ĠBen ch
-Ġhand c
-A p
-Ġ19 56
-25 9
-r and
-Net Message
-d in
-urt les
-h ig
-ĠV III
-ff iti
-ĠSw ords
-b ial
-Ġkidn apping
-dev ice
-Ġb arn
-ĠEl i
-auc as
-S end
-Con structed
-ĠÂ ½
-Ġneed les
-Ġad vertisements
-Ġv ou
-Ġexhib ited
-ĠFort ress
-As k
-B erry
-TY PE
-Ġcan cers
-ump ing
-ĠTerrit ory
-Ġpr ud
-Ġn as
-Ġathe ist
-Ġbal ances
-ãģ Ł
-ĠSh awn
-& &
-Ġland sc
-ĠR GB
-Ġpet ty
-Ġex cellence
-Ġtransl ations
-Ġpar cel
-ĠChe v
-E ast
-ĠOut put
-im i
-Ġamb ient
-ĠTh reat
-Ġvill ains
-Ġ5 50
-IC A
-Ġtall er
-Ġle aking
-c up
-Ġpol ish
-Ġinfect ious
-ĠK C
-Ġ@ @
-back ground
-Ġbureaucr acy
-ĠS ai
-un less
-it ious
-ĠSky pe
-At l
-ID ENT
-00 8
-Ġhyp ocr
-Ġpit chers
-Ġguess ing
-ĠF INAL
-Bet ween
-Ġvill agers
-Ġ25 2
-f ashion
-ĠTun is
-Be h
-ĠEx c
-ĠM ID
-28 8
-ĠHas kell
-19 6
-ĠN OR
-Ġspec s
-Ġinv ari
-Ġgl ut
-ĠC ars
-Ġimp ulse
-Ġhon ors
-g el
-Ġjurisd ictions
-ĠBund le
-ul as
-Calif ornia
-ĠIncre ase
-Ġp ear
-Ġsing les
-Ġc ues
-Ġunder went
-ĠW S
-Ġexagger ated
-Ġdub ious
-Ġfl ashing
-L OG
-) ].
-J ournal
-t g
-V an
-ĠI stanbul
-ĠIn sp
-ĠFrank en
-D raw
-Ġsad ness
-Ġiron ic
-ĠF ry
-x c
-Ġ16 4
-is ch
-W ay
-ĠProtest ant
-h orn
-Ġun aff
-ĠV iv
-ill as
-ĠProduct ions
-ĠH ogan
-Ġper imeter
-ĠS isters
-Ġspont aneous
-Ġdown side
-Ġdescend ants
-Ġor n
-w orm
-Japan ese
-Ġ19 55
-Ġ15 1
-ĠDo ing
-els en
-umb les
-Ġrad ically
-ĠDr um
-ĠB ach
-Ġli abilities
-ĠO B
-ĠElement ary
-Ġmem e
-yn es
-Ġfinger print
-ĠGr ab
-Ġundert ake
-Mem bers
-ĠRead er
-ĠSim s
-g od
-Ġhypot hetical
-s cient
-ĠA J
-Ġchar ism
-Ġad missions
-ĠMiss ile
-tr ade
-Ġexerc ising
-ĠBack ground
-W ritten
-Ġvoc als
-whe ther
-Ġv i
-ĠW inner
-Ġl itter
-ĠSh ooting
-ST EM
-ãĤ ¡
-ĠA FL
-Ġvari ability
-Ġe ats
-ĠD PS
-b row
-Ġeleph ants
-Ġstr at
-Ġ Å
-Ġsett lers
-Matt hew
-Ġin advert
-H I
-ĠIM F
-ĠGo al
-Ġnerv es
-John son
-ey e
-ablish ment
-Th ursday
-BIL ITY
-H ad
-am oto
-het amine
-ep s
-Ġmit ochond
-Ġcomp ressed
-ĠTre vor
-ĠAnim als
-T ool
-L ock
-Ġtwe ak
-Ġpin ch
-Ġcancell ation
-P ot
-Ġfoc al
-ĠAst ron
-17 3
-ĠA SC
-ĠO THER
-umn i
-Ġdem ise
-d l
-Ù ħ
-Sem itism
-Ġcr acking
-Ġcollabor ative
-Ġexpl ores
-s ql
-Ġher bs
-Ġconfig urations
-m is
-ĠRes ult
-ace y
-ĠSm oke
-Ġsan ct
-el ia
-Ġdeg ener
-Ġdeep est
-Ġscream ed
-Ġn ap
-Soft ware
-ĠST AR
-E F
-ĠX in
-spons ored
-mans hip
-23 3
-Ġprim aries
-Ġfilter ing
-Ġas semble
-m il
-ĠMy ers
-b ows
-Ġpun ched
-M ic
-Ġinnov ations
-Ġfun c
-and o
-Ġfr acking
-ĠV ul
-о Ð
-osh op
-ĠIm mun
-Ġsett ling
-Ġadolesc ents
-Ġreb uilding
-Ġtransform ing
-Ġpar ole
-Ġhar bor
-Ġbook ing
-ot ional
-onge vity
-ĠY o
-b ug
-Ġemer ges
-ĠMethod s
-ĠCh u
-P res
-ĠDun geons
-Ġtra iling
-ĠR um
-ĠH ugh
-å¤ ©
-ĠE ra
-ĠBatt les
-Res ults
-ĠTr ading
-Ġvers a
-c ss
-ax ies
-he et
-Ġgre ed
-19 89
-Ġgard ens
-Ġconting ent
-P ark
-ĠLeaf s
-h ook
-ro be
-Ġdiplom acy
-ĠF uel
-ĠInv asion
-Ġupgr ading
-M ale
-Ġe lic
-Ġrelent less
-ĠCo venant
-ap esh
-ĠT rop
-T y
-pro duction
-art y
-Ġpun ches
-ak o
-cyclop edia
-ĠR abbit
-ĠHD MI
-Ġ14 1
-Ġf oil
-Item Image
-ĠF G
-Ġimplement ations
-ĠP om
-ixt ures
-Ġaw ait
-Ġ3 30
-am us
-Ġumb rella
-Ġfore see
-se par
-Ġcircum cision
-Ġperipher al
-S ay
-ĠExper t
-In c
-Ġwithd rew
-ĠAnd ers
-f ried
-Ġradio active
-ĠOp ening
-Ġboard ing
-ĠN D
-Ġover throw
-Act iv
-W P
-ĠAct s
-× Ļ
-Ġmot ions
-v ic
-ĠM ighty
-ĠDef ender
-a er
-Ġthank ful
-ĠK illing
-ĠBr is
-mo il
-Ġpredict ing
-26 6
-ch oice
-Ġkill ers
-Ġinc ub
-ĠChe st
-ather ing
-Ġpro claimed
-fl ower
-oss om
-umbled ore
-ĠCy cling
-ĠOccup y
-AG ES
-P en
-ĠY ug
-Ġpack aged
-Ġheight ened
-c ot
-st ack
-C ond
-Ġst amps
-m age
-Ġpersu aded
-Ġens l
-ĠCard inal
-Ġsol itary
-Ġpossess ing
-ĠC ork
-Ġev id
-ĠT ay
-Ġbl ues
-Ġextrem ism
-Ġlun ar
-Ġcl own
-Te chn
-Ġfest ivals
-ĠPv P
-ĠL ar
-Ġconsequ ently
-p resent
-Ġsom eday
-ç İĭ
-ĠMet eor
-Ġtour ing
-c ulture
-Ġbe aches
-S hip
-c ause
-ĠFl ood
-ãĥ ¯
-Ġpur ity
-th ose
-Ġem ission
-b olt
-Ġch ord
-ĠScript ure
-L u
-Ġ$ {
-cre ated
-Other s
-25 8
-Ġelement al
-Ġannoy ed
-ĠA E
-d an
-ĠS ag
-Res earchers
-Ġfair y
-âĢĵ âĢĵ
-======== ====
-Sm art
-GG GG
-Ġskelet ons
-Ġpup ils
-link ed
-Ġur gency
-en abled
-ĠF uck
-Ġcoun cill
-r ab
-U AL
-T I
-Ġlif es
-Ġconf essed
-B ug
-Ġharm on
-ĠCON FIG
-ĠNe utral
-D ouble
-Ġst aple
-ĠSH A
-Brit ish
-ĠSN P
-AT OR
-oc o
-Ġswing ing
-ge x
-ole on
-pl ain
-ĠMiss ing
-ĠTro phy
-v ari
-ran ch
-Ġ3 01
-4 40
-00000000 00000000
-Ġrest oring
-Ġha ul
-uc ing
-ner g
-Ġfut ures
-Ġstrateg ist
-quest ion
-Ġlater al
-ĠB ard
-Ġs or
-ĠRhod es
-ĠD owntown
-????? -
-ĠL it
-ĠB ened
-Ġco il
-st reet
-ĠPort al
-FI LE
-ĠG ru
-* ,
-23 1
-ne um
-Ġsuck ed
-Ġr apper
-Ġtend encies
-ĠLaure n
-cell aneous
-26 7
-Ġbrow se
-Ġover c
-head er
-o ise
-Ġbe et
-ĠG le
-St ay
-Ġm um
-Ġtyp ed
-Ġdiscount s
-T alk
-ĠO g
-ex isting
-ĠS ell
-u ph
-C I
-ĠAust rian
-ĠW arm
-Ġdismiss al
-Ġaver ages
-c amera
-Ġalleg iance
-L AN
-=" #
-Ġcomment ators
-ĠSet ting
-ĠMid west
-Ġpharm ac
-ĠEX P
-Ġstain less
-Ch icago
-Ġt an
-24 4
-Ġcountry side
-ĠV ac
-29 5
-Ġpin ned
-Ġcr ises
-Ġstandard ized
-T ask
-ĠJ ail
-ĠD ocker
-col ored
-f orth
-" },
-Ġpat rons
-Ġsp ice
-Ġm ourn
-ĠM ood
-Ġlaund ry
-Ġequ ip
-ĠM ole
-y ll
-ĠTH C
-n ation
-ĠSher lock
-Ġiss u
-ĠK re
-ĠAmeric as
-ĠA AA
-Ġsystem atically
-Ġcont ra
-ĠS ally
-Ġrational e
-Ġcar riage
-Ġpe aks
-Ġcontrad iction
-ens ation
-ĠFail ure
-Ġpro ps
-Ġnames pace
-Ġc ove
-field s
-ãĤ ĭ
-Ġw ool
-ĠC atch
-Ġpresum ed
-ĠD iana
-r agon
-ig i
-Ġh amm
-Ġst unt
-ĠG UI
-ĠObserv atory
-ĠSh ore
-Ġsmell s
-ann ah
-Ġcock pit
-ĠD uterte
-8 50
-Ġopp ressed
-bre aker
-ĠCont ribut
-ĠPer u
-ĠMons anto
-ĠAtt empt
-Ġcommand ing
-Ġfr idge
-ĠR in
-ĠChe ss
-ual ity
-Ġo l
-Republic an
-ĠGl ory
-ĠW IN
-.... ...
-ag ent
-read ing
-Ġin h
-J ones
-Ġcl icks
-al an
-Ġ[ ];
-ĠMaj esty
-ĠC ed
-op us
-ate l
-Ã ª
-AR C
-ĠEc uador
-ãĥ ł
-ĠK uro
-Ġritual s
-Ġcapt ive
-Ġoun ce
-Ġdisag reement
-Ġsl og
-f uel
-P et
-M ail
-Ġexerc ised
-Ġsol ic
-Ġrain fall
-Ġdev otion
-ĠAss essment
-Ġrob otic
-opt ions
-ĠR P
-ĠFam ilies
-ĠFl ames
-Ġassign ments
-00 7
-aked own
-Ġvoc abulary
-Re illy
-Ġc aval
-g ars
-Ġsupp ressed
-ĠS ET
-ĠJohn s
-Ġwar p
-bro ken
-Ġstat ues
-Ġadvoc ated
-Ġ2 75
-Ġper il
-om orph
-ĠF emin
-per fect
-Ġh atch
-L ib
-5 12
-Ġlif elong
-3 13
-Ġche eks
-Ġnum bered
-ĠM ug
-B ody
-ra vel
-We ight
-ĠJ ak
-ĠHe ath
-Ġkiss ing
-ĠJ UST
-Ġw aving
-u pload
-Ġins ider
-ĠPro gressive
-ĠFil ter
-tt a
-ĠBe am
-Ġviol ently
-ip ation
-Ġskept icism
-Ġ19 18
-ĠAnn ie
-ĠS I
-Ġgen etics
-Ġon board
-at l
-ĠFried man
-ĠB ri
-cept ive
-Ġpir ate
-ĠRep orter
-27 8
-Ġmyth ology
-Ġe clipse
-Ġsk ins
-Ġgly ph
-ing ham
-F iles
-C our
-w omen
-Ġreg imes
-Ġphotograp hed
-K at
-ĠMA X
-Offic ials
-Ġunexpected ly
-Ġimpress ions
-F ront
-;;;; ;;;;
-Ġsuprem acy
-Ġs ang
-Ġaggrav ated
-Ġabrupt ly
-ĠS ector
-Ġexc uses
-Ġcost ing
-ide press
-St ack
-ĠR NA
-ob il
-Ġghost s
-ld on
-at ibility
-Top ics
-Ġreim burse
-ĠH M
-ĠDe g
-Ġth ief
-y et
-ogen esis
-le aning
-ĠK ol
-ĠB asketball
-Ġf i
-ĠSee ing
-Ġrecy cling
-Ġ[ -
-Cong ress
-Ġlect ures
-P sy
-Ġne p
-Ġm aid
-Ġori ented
-A X
-Ġrespect ful
-re ne
-fl ush
-ĠUn loaded
-re quest
-gr id
-ĠAltern atively
-ĠHug o
-Ġdec ree
-ĠBuddh ism
-and um
-And roid
-ĠCong o
-ĠJoy ce
-Ġacknowled ging
-hes ive
-ĠTom orrow
-ĠH iro
-th ren
-ĠM aced
-Ġho ax
-ĠIncre ased
-ĠPr adesh
-W ild
-____ __
-16 1
-Ġa unt
-Ġdistribut ing
-ĠT ucker
-ĠSS L
-ĠW olves
-B uilding
-ou lt
-ĠLu o
-ĠY as
-ĠSp ir
-ĠSh ape
-ĠCamb od
-ĠIP v
-Ġm l
-Ġext rad
-39 0
-ĠPenn y
-d ream
-Ġstation ed
-opt ional
-ew orthy
-.
-Ġundert aking
-Ġchick ens
-Ġstimul i
-ĠEl se
-ig ators
-ĠBegin ning
-ct ory
-Ġprep ares
-Ġdel ta
-Ġvic inity
-t ool
-Ġworks hops
-M Hz
-Ġaccus ation
-Ġhist ories
-rop olis
-ĠChurch ill
-Ġne on
-Ġb aff
-d ies
-may be
-Ġè£ı è¦ļéĨĴ
-Ġsympt om
-EC H
-ĠMan uel
-Ġban ana
-ĠH B
-Ġ ****
-ĠKore ans
-c oll
-F B
-Ġpr aying
-ĠCann ot
-ĠM ile
-Ġembr acing
-ĠSil k
-39 3
-ot ers
-F D
-Ġday light
-al ias
-ĠBrig ade
-ĠHann ah
-Ġcler gy
-Ġs outheast
-Ġalcohol ic
-Ġpropos es
-liv ion
-Ġcalcul ating
-Ġstim ulate
-Ġspl itting
-e ight
-ĠInd y
-pl ays
-ĠP ik
-Ġdom est
-Ġforg iveness
-ĠR ings
-pat ient
-kins on
-M ont
-ig ible
-; "
-Ġperiod ically
-amm ad
-ĠBr itt
-p ard
-Ġarbit ration
-ĠSchne ider
-ĠCorpor ate
-ĠMay a
-Ġsn akes
-a um
-Ġbl asted
-Ġmyster ies
-Ġrev ive
-oc amp
-ĠD odge
-ĠOper a
-27 9
-Ġor phan
-Ġspec ifies
-ĠM ets
-D uration
-H en
-Ġfire works
-Ġprosec ute
-ĠTill erson
-d p
-us age
-l iness
-ĠDeb ian
-Ġ2 24
-ris es
-ĠIn fect
-at ra
-ĠR R
-ĠL or
-d iff
-ĠCharl eston
-Ġac oustic
-Ġam use
-3 30
-Ġc er
-ĠT ac
-Ġ[ +
-Ġcard iac
-ĠRestaur ant
-er gy
-Ġf uzz
-Ġbit es
-Ġhazard ous
-Ġbr ighter
-r ans
-ĠStephan ie
-ext ra
-RE T
-ĠChrist ine
-ĠS ue
-stat ement
-Ġbol ster
-Ġant it
-Rad io
-B IT
-ãĤ °
-Ġvis ions
-ĠCon cept
-Ġin line
-ĠPhilos ophy
-is ans
-ĠIr ving
-Ã £
-t aking
-Ġincons ist
-ĠKum ar
-Ġl ig
-ĠSch umer
-ĠReg ulations
-ĠH z
-th ro
-ĠV oldemort
-ĠM ED
-ĠFreder ick
-P ad
-22 1
-Ġalleg ing
-ĠCommun ication
-Ġ16 7
-Ġforecast s
-Ġsp iders
-Or gan
-ĠParticip ants
-ĠO ps
-des ign
-Cl ose
-Ġfact o
-Ġbom bers
-res istant
-ateg ories
-S chool
-Ġhom ework
-Ġcor ro
-T uesday
-ĠBrend an
-ĠM X
-ĠT S
-ĠSt ri
-Ġstake holders
-ĠMillenn ium
-Ġtransfer ring
-J ud
-Ġt ac
-Ġ16 00
-ĠSD K
-r b
-Ġinterpret ations
-ĠS G
-Ġup stairs
-ĠHar vest
-Ġvag ina
-Ġing est
-x f
-ĠOr ion
-ĠJoe y
-Ġsand wic
-Ġimm ortal
-Ġfl ipped
-ort ex
-threat ening
-Ġsn iper
-Ġconver ts
-Ġinstall ations
-ĠBul gar
-ors che
-m ails
-Ġl ure
-Ġnarrow ly
-Ġgren ade
-ĠG ing
-Ġunder wear
------------- --
-Ġch ased
-ĠV AL
-Ġparent ing
-ĠH amb
-ĠBl az
-Ġanarch ist
-ĠMed ian
-ĠProgram s
-Î ½
-Ġob j
-ĠN okia
-orm an
-an qu
-at ism
-op a
-Ġfulf illing
-Ġpupp y
-Ġent it
-ĠSebast ian
-Ġshoot ers
-Ġric her
-è ¡
-Ġtempt ed
-ĠAT T
-ĠC V
-Ġto re
-Res ource
-ĠDevil s
-40 8
-in ational
-Ġass urance
-ĠDar ren
-Ġwh ichever
-pos ure
-Ġf ury
-St ock
-Ġunivers ally
-resp onse
-Ġo ak
-Ġwork load
-ĠCor ner
-ee le
-" ...
-Ġdepri ved
-k owski
-Ġcast s
-Ġaffili ation
-ĠA ch
-ĠAs ked
-at he
-Ġl act
-ĠTh u
-r m
-Ġair lines
-Ġnot ions
-Form at
-ĠF AA
-ãĥ Ĭ
-dri ver
-Ġtrans cend
-S ettings
-ĠPro secut
-Ġsp inal
-Ġdefault s
-F K
-Ġpref ers
-rend ered
-th us
-fil m
-Ġt iger
-ĠSp icer
-rec ogn
-ĠRug by
-Net work
-Ġp ity
-Ġcomp artment
-c asters
-ĠMon roe
-Ġ7 20
-Ġcorrect ions
-Ġdop amine
-ĠA Z
-C ut
-Ġro omm
-Ġspec ulate
-H ash
-Ġrestrict ive
-11 11
-red ible
-on el
-Ġramp ant
-re ported
-ĠSu ite
-ĠMin imum
-al ys
-az ard
-lo op
-Ġl ent
-sh a
-Ġv andal
-men u
-ĠBoe hner
-Ġnarr atives
-Ġauthent icity
-26 9
-an ic
-d uty
-28 5
-Ġthank ed
-Ġbetray ed
-l ift
-Ġsouth west
-ĠDex ter
-ĠB od
-Ġkey words
-A verage
-D IS
-Ġethnic ity
-! ),
-ĠNational s
-á ¹
-ĠT ah
-iox id
-Ġwid get
-Ġpast a
-Ġbill ing
-Ġtr ilogy
-ĠL ines
-Ġsn iff
-Ġnep hew
-L ate
-Ġprinc ip
-ĠLo op
-ĠMarx ist
-Ġdiss olved
-Ġcontext s
-ĠAm ount
-ĠSp ike
-Ġtot als
-Ġorgan izer
-Ġup rising
-s hips
-Y Y
-ĠNort heast
-m oney
-grad ation
-Ġgoal keeper
-ĠH ear
-Ġste ak
-ĠBuzz Feed
-Ġsole mn
-ĠSc and
-Ġpo pping
-Ġad here
-ĠAl leg
-by te
-ĠW olver
-Ġun in
-Ġrec ol
-it ud
-Ġmim ic
-ib us
-Ġpredict s
-ĠKee per
-i ating
-Ġde ception
-Ġlear nt
-Ġdi ary
-Ġcond itional
-Ġre lic
-Ġinv oke
-ien ced
-å Ī
-ĠP ont
-Ġcell phone
-Ġspeed ing
-Ġtack ling
-Ġn ude
-op ened
-ĠMan afort
-Ġ19 52
-Ġmaj ors
-ĠSil ence
-Ġlog istics
-Ġweight ed
-ĠPsych iat
-": ["
-Ġsick ness
-Ġdivid ends
-z on
-Re lease
-ĠKe ys
-ĠI ch
-Ġen z
-ĠF ernand
-ĠÎ ±
-Ġmean ings
-Ġp enny
-Ġst ern
-Ġl ar
-ĠPub lished
-Ġback drop
-K im
-ĠSy nt
-Ġdeb uted
-w m
-ĠIs le
-Ġregul ating
-ott i
-ĠSch olars
-ices ter
-ĠChe f
-Ġpop s
-ĠLaun cher
-ĠVar ious
-Ġcomment ing
-os lav
-enz ie
-Ġrival ry
-â Ĥ¬
-Re ally
-Ġor c
-Ġbe an
-ĠJud y
-Not ice
-ĠB ike
-? ]
-Ġrent ed
-st en
-Ġfore front
-ĠBald win
-Ġyield ed
-t ails
-Pr ime
-ĠS ources
-ic ator
-Se an
-Ġmarch ing
-Out put
-ĠJ ungle
-Ġres ide
-zz le
-ĠAndrew s
-Ġtor que
-Bas ic
-Act ually
-st rap
-p enter
-Ġexam s
-ĠY a
-Ġ15 9
-ĠDec ision
-Ġr ansom
-ete enth
-ens ing
-2 13
-Ġsun set
-40 4
-ĠRap id
-ĠHe in
-ĠAb original
-Ġorgan ism
-ĠS ever
-Ġcl a
-aj i
-Sim ple
-ĠFl avor
-ĠE val
-pr us
-Ġch orus
-D AY
-Ġden ounced
-Ġbi ography
-ĠTurn bull
-Rec ent
-N ormal
-lect ions
-W ord
-Ġf erry
-ĠWag ner
-h om
-Un it
-Ġsuper market
-ĠS ith
-Ġnomine es
-Ġdictators hip
-idd ler
-Ġannoun ces
-ĠThe m
-ĠNept une
-Ġde ity
-ĠY i
-Ġmon arch
-AR R
-Ġinv aded
-ĠH ok
-unt ary
-C ertain
-eg a
-Ġk idding
-ĠReg ulation
-Ġtr ay
-Ġphotograp hers
-ĠArc ane
-Ġdis charged
-Ġevangel ical
-Ġinter change
-Ġfilm maker
-ĠEnd less
-Ġ29 0
-ĠSalv ador
-AS Y
-ĠSign al
-Ġwr ath
-â ľ
-l ot
-' /
-Ġproject ile
-Ġemploy ing
-ĠInter face
-19 1
-atell ite
-ĠR ath
-pack age
-Ġindic ations
-J ason
-Ġarg s
-ĠG Hz
-Ġt ilt
-n ants
-w on
-ãĤ µ
-red d
-res cent
-ĠCal endar
-Ġmod ular
-Ġassist ing
-Ġred eem
-ĠBe an
-Ġwor sh
-Ġdecentral ized
-) ...
-37 7
-Ġarr ays
-Ġaccomplish ments
-Î ¿
-d ot
-Ġmut ually
-Ġob struct
-Ġmis represent
-ore st
-ion ic
-ru ce
-% ;
-Ġknow ingly
-port ing
-in ently
-A ri
-ĠSch ultz
-D a
-ĠC ere
-Ġob solete
-ħ ĭ
-g ive
-Ġb ait
-Ġen larg
-Ne ill
-Ġ19 33
-Ġrecons ider
-ĠSerge ant
-ĠDian e
-ĠC ogn
-ĠI con
-P osition
-Ġf ost
-Ġstir ring
-se ven
-ĠSpace X
-ugg ets
-Ġmed d
-G al
-ĠS ister
-B oy
-Ġtrigger ing
-T aking
-Ġscream s
-Ġca usal
-Ġaw aken
-Ar m
-29 7
-Ġdisp atched
-ĠF ALSE
-Ġorgan izational
-ĠT ong
-Ġdile mma
-d emon
-S pl
-Ġhook s
-ud ing
-Ġvalid ate
-Ġpot ion
-Ġcl aw
-Ġburg l
-Ġqu ir
-AC A
-ĠBren nan
-Ġdur ability
-Ġbomb ings
-ĠWind ow
-Ġculp rit
-3 25
-There fore
-umb ered
-per formance
-w arts
-Ġen forcing
-ĠBl ow
-Ġre print
-if ax
-al pha
-Ġsin ister
-Ġbur ger
-fight ing
-Sc ore
-ĠSt ones
-i em
-40 5
-che my
-Ġvine gar
-n om
-Ġprev ailing
-ĠLat est
-Â ¶
-Ġb a
-ĠWrit er
-Ġ17 7
-ĠCon way
-Ġcollect s
-Ġquant itative
-Ġhor rors
-og ens
-ĠSl ov
-Ġl ays
-h aw
-ĠSl ash
-Ġnight club
-ĠDav ies
-Ġbr ide
-ĠScar let
-y mm
-ĠApplic ations
-vel ength
-Ġrev ival
-Ġsoft ly
-Ġz oo
-ita ire
-C ur
-Ġelect rom
-Ġplant ing
-OT O
-ĠE lements
-Ġsw allow
-por ter
-Ġlapt ops
-Ġpe anut
-Ġlobby ists
-Î ²
-Pan el
-ĠJo an
-im il
-t nc
-Ġresist ed
-Ġout we
-Ġret aining
-at ri
-Ġpo orer
-ĠSyri ans
-ĠHam mond
-Ġwe ld
-ud er
-top ic
-ĠT T
-ric ia
-Ġth ieves
-L ic
-ĠG ust
-ĠW ays
-are th
-24 3
-Ġbroad caster
-sh ield
-ass ium
-ub le
-Ġairst rikes
-on so
-Ġped al
-Ġcollect ors
-ĠV ander
-ĠMes a
-Ġdict ator
-Ġd ir
-ent on
-c art
-sc ore
-ad der
-C ry
-Ġs sh
-gg er
-Ġdrunk en
-ĠG S
-ĠSe at
-Ġcorner back
-Ġsk ipped
-ĠRes earchers
-ĠAud i
-Ref erence
-Ġhaun ted
-Ã «
-ĠClin ic
-c z
-Ġp s
-ĠPal adin
-ĠRec ipe
-Ġst igma
-opp y
-Ġmon keys
-ĠHaw k
-S ad
-" />
-ĠWorks hop
-ĠRet ail
-ĠAv atar
-6 25
-N a
-ĠV C
-ĠSec ure
-M Y
-19 88
-oss ip
-Ġpro state
-Ġund en
-Ġg amer
-ĠCont ents
-ĠWar hammer
-ĠSent inel
-3 10
-Ġse gregation
-ĠF lex
-ĠM AY
-Ġdr ills
-ĠDrug s
-Islam ic
-Ġsp ur
-Ġca fe
-Ġimag inary
-Ġgu iding
-Ġsw ings
-ĠThe me
-ob y
-Ġn ud
-Ġbe gging
-Ġstr ongh
-Ġreject ing
-Ġpedest rians
-ĠPro spect
-R are
-s le
-Ġconcess ions
-ĠConst itutional
-Ġbe ams
-Ġfib ers
-p oon
-Ġinstinct s
-pro perty
-ĠB IG
-Sand ers
-im ates
-Ġco ating
-Ġcorps es
-ĠTR UE
-check ed
-Ġ16 6
-A sh
-ĠJ S
-ĠF iction
-Ġcommun al
-Ġener getic
-oooo oooo
-Ġnow adays
-IL D
-ib o
-ĠSU V
-R en
-Ġdwell ing
-Sil ver
-Ġt ally
-ĠM oving
-Ġcow ard
-Ġgener als
-Ġhorn s
-Ġcirc ulated
-Ġrob bed
-ĠUn limited
-Ġharass ed
-Ġinhib it
-Ġcomp oser
-ĠSpot ify
-Ġspread s
-3 64
-Ġsu icidal
-Ġno ises
-ĠSt ur
-Ġs aga
-ĠK ag
-is o
-Ġtheoret ically
-M oney
-Ġsimilar ity
-Ġslic ed
-ut ils
-ing es
-" -
-Ġan th
-Ġimp ed
-Mod ule
-Through out
-Ġmen us
-comm ittee
-and i
-ob j
-in av
-f ired
-ĠAb dullah
-Ġund ead
-Ġfont s
-H old
-EN G
-Ġsustain ability
-Ġfl ick
-Ġr azor
-ĠF est
-ĠChar acters
-Ġword ing
-Ġpopul ist
-Ġcritic izing
-Ġm use
-v ine
-Ġcard board
-Ġkind ly
-Ġfr inge
-ĠThe ft
-icult ural
-Ġgovern ors
-Ġ ����
-Ġ16 3
-Ġtime out
-ĠA uth
-Child ren
-A U
-Ġred emption
-ĠAl ger
-Ġ19 14
-Ġw aved
-Ġastron auts
-og rams
-Ġsw amp
-ĠFinn ish
-Ġcand le
-Ġton nes
-ut m
-Ġr ay
-Ġsp un
-Ġfear ful
-art icles
-Ġca us
-or ically
-ĠRequ ires
-ĠG ol
-Ġpop e
-Ġinaug ural
-Ġg le
-AD A
-ĠIS IL
-ĠOff ensive
-Ġwatch dog
-Ġbal con
-ent ity
-ĠH oo
-Ġgall on
-AC C
-Ġdoub ling
-Ġimpl ication
-ĠS ight
-Ġdoct r
----- ---
-Ġ\ \
-Ġm alt
-R oll
-Ġâī ¥
-Ġrec ap
-add ing
-u ces
-ĠB end
-fig ure
-Ġtur key
-Ġsoc ietal
-ĠT ickets
-Ġcommer cially
-Ġsp icy
-Ġ2 16
-ĠR amp
-Ġsuperior ity
-Ã ¯
-ĠTr acker
-C arl
-ĠC oy
-ĠPatri ot
-Ġconsult ed
-Ġlist ings
-Ġsle w
-reens hot
-ĠG one
-Ġ[ ...]
-30 9
-Ġh ottest
-Ø ±
-Ġrock y
-ĠD iaz
-Ġmass age
-Ġpar aly
-Ġp ony
-A z
-Ġcart ridge
-ĠN Z
-Ġsn ack
-ĠLam ar
-ple ment
-ĠLes lie
-Ġm ater
-Ġsn ipp
-24 6
-Ġjoint ly
-ĠBris bane
-ĠiP od
-Ġpump ing
-Ġgo at
-ĠSh aron
-eal ing
-Ġcor on
-Ġan omal
-rah im
-ĠConnect ion
-Ġsculpt ure
-Ġsched uling
-ĠD addy
-at hing
-Ġeyeb rows
-Ġcur ved
-Ġsent iments
-Ġdraft ing
-D rop
-( [
-Ġnom inal
-ĠLeaders hip
-ĠG row
-Ġ17 6
-Ġconstruct ive
-iv ation
-Ġcorrupt ed
-ger ald
-ĠC ros
-ĠChe ster
-ĠL ap
-ãģ ª
-OT H
-D ATA
-Ġal mond
-pro bably
-I mp
-Ġfe ast
-ĠWar craft
-F lor
-Ġcheck point
-Ġtrans cription
-Ġ20 4
-Ġtwe aks
-Ġrel ieve
-S cience
-Ġperform er
-Z one
-Ġtur moil
-ig ated
-hib it
-ĠC afe
-the med
-Ġflu or
-ben ch
-Ġde com
-ĠU nt
-ĠBar rett
-ĠF acts
-Ġt asting
-ĠPTS D
-ĠSe al
-ĠJuda ism
-ĠDynam ic
-ĠC ors
-V e
-ĠM ing
-ĠTrans form
-v on
-ĠDef enders
-ĠTact ical
-ĠV on
-ĠUn ivers
-Ġdist orted
-ĠB reath
-?' "
-Ġag on
-ĠDead ly
-Ġl an
-ĠCy cle
-orn ed
-Ġrel iably
-Ġgl or
-ĠMon key
-ãĥ ¡
-Ġad ren
-Ġmicrow ave
-ĠAl ban
-irc raft
-dig it
-sm art
-ĠD read
-¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯
-{ {
-ĠRoc hester
-Ġsimpl ified
-Ġinf licted
-Ġtake over
-Ġyour selves
-ad itional
-Ġmus cular
-K S
-Ġing en
-T ax
-ĠFe ature
-27 7
-Ġcru c
-Ġcr ate
-Ġun identified
-Ġacclaim ed
-ĠM anga
-ĠFr ances
-ĠNep al
-ĠG erald
-ĠKu wait
-Ġsl ain
-ĠHe b
-ĠG oku
-ãģ® æ
-28 6
-M rs
-ĠC ody
-ĠSan ctuary
-01 6
-Ġdism ant
-Ġdatas et
-ĠH ond
-b uck
-ĠPat terson
-Ġpal ette
-ĠG D
-ic ol
-ĠL odge
-Ġplanet ary
-ak in
-ĠRegist ered
-ab we
-ĠPeters burg
-Ġha iled
-ĠP iece
-S che
-ĠDO J
-Ġen umer
-18 1
-ĠObs erver
-ĠB old
-f ounded
-com merce
-Ġexplo its
-ĠF inding
-UR N
-ĠS ne
-ĠAc id
-ay ette
-ĠVal ues
-Ġdr astic
-Ġarchitect ural
-Ġ" .
-× ķ
-ump ed
-Ġwra pping
-Ġwid ow
-ĠSl ayer
-l ace
-on ce
-German y
-av oid
-Ġtem ples
-P AR
-Ã ´
-ĠLuc ifer
-ĠFl ickr
-l ov
-for ces
-Ġsc outing
-Ġlou der
-tes y
-Ġbefore hand
-Ä ĵ
-ĠNe on
-ĠW ol
-ĠTyp ically
-ĠPolit ico
--+ -+
-Ġbuild er
-Ġder ive
-K ill
-Ġp oker
-Ġambig uous
-Ġlif ts
-Ġcy t
-Ġrib s
-ood le
-ĠS ounds
-h air
-ĠSynd rome
-t f
-Ġproport ional
-u id
-Ġper taining
-ĠKind le
-ĠNeg ro
-Ġreiter ated
-ĠTon ight
-oth s
-ĠCorn ell
-Ġo wing
-Ġ20 8
-elf are
-oc ating
-ĠB irds
-Sub scribe
-Ġess ays
-Ġburd ens
-Ġillust rations
-ar ious
-ER AL
-ĠCal cul
-Ġx en
-ĠLink edIn
-ĠJ ung
-Ġredes ign
-Con nor
-29 6
-Ġrevers al
-ĠAd elaide
-ĠL L
-Ġs inking
-Ġg um
-US H
-c apt
-ĠGr imm
-Ġfoot steps
-ĠCB D
-isp ers
-Ġpro se
-Wed nesday
-ĠM ovies
-ed in
-Ġoverturn ed
-Ġcontent ious
-US B
-~~~~~~~~ ~~~~~~~~
-ĠCo pper
-Ġpoint less
-N V
-val ues
-olph in
-d ain
-Ġdepos ited
-ĠG W
-Ġpreced ed
-ĠCl a
-ĠGo lem
-ĠN im
-ĠÎ ²
-ĠEngine ers
-m iddle
-Ġfl att
-oper ative
-Ġcouncil s
-imb abwe
-el in
-Ġstress ful
-ĠL D
-Ġres h
-l ake
-Ġwheel chair
-ĠAltern ative
-Ġoptim ize
-oper ation
-Ġpe ek
-Ġones elf
-ig il
-Ġtrans itions
-op athy
-bl ank
-Ġ16 9
-17 1
-________________________________ ________________________________
-Ġl aundering
-En c
-ĠD EC
-Ġwork outs
-Ġsp ikes
-Ġdin osaurs
-Ġdiscrim inatory
-P ool
-R ather
-38 5
-R NA
-tes ters
-et o
-ĠIdent ity
-Ġve in
-ĠBur ton
-Ġarc ade
-4 20
-Ult imately
-ĠSad ly
-Ã °
-p ill
-Ġcub ic
-ĠSpect rum
-the se
-st ates
-Ġun official
-h awks
-ĠEVER Y
-Ġrain bow
-Ġincarcer ation
-and ing
-Ġsy ll
-ĠEver ton
-Ġ17 9
-ĠSer bia
-Ġ18 9
-m eter
-ĠMic key
-Ġant iqu
-Ġfact ual
-ne ck
-ĠN are
-n orm
-m ust
-Ġhigh ways
-Ġgl am
-Ġdivid ing
-ĠSquad ron
-ĠMar tha
-Ġbirth s
-C over
-//////// ////////
-ĠW ong
-Ph ot
-ĠA LS
-ri o
-ĠNon etheless
-ĠL emon
-Ġ20 6
-ĠE E
-Ġderiv ative
-ĠWW II
-v ote
-Ġthere in
-Ġsepar ating
-44 6
-sy nc
-ĠStre ets
-Ġr att
-Ġmunicip ality
-ĠShort ly
-Ġmon k
-) ,"
-Ġscr ub
-Ġoper atives
-Ne ither
-Pl ace
-ĠLim it
-F emale
-ĠAct or
-Char acter
-Ġconstit uted
-35 7
-Ġprotest ed
-ĠSt raw
-ĠHe ight
-ild a
-ĠTy ph
-Ġflood s
-Ġcos metic
-W AY
-pert ure
-up on
-t ons
-ess ing
-ĠP ocket
-Ġro oft
-ĠC aucas
-Ġant idepress
-Ġincomp atible
-EC D
-Ġoper a
-ĠCont est
-Ġgener ators
-l ime
-Def ense
-19 87
-for um
-Ġsav age
-ĠHung arian
-n z
-Ġmet allic
-Ġex pelled
-Ġres idency
-Ġdress es
-66 6
-ĠC lement
-f ires
-C ategory
-Ġge ek
-al is
-Ġc emetery
-educ ated
-Ġc rawl
-ĠUn able
-ĠT yson
-ak is
-Ġp ardon
-ĠW ra
-Ġstrengthen ed
-ĠF ors
-33 5
-ĠH C
-ĠM ond
-Ġvisual s
-ĠBeat les
-ett lement
-Ġ ï
-g ro
-Ġb ash
-Ġpo orest
-Ġex cel
-Ġaspir ations
-ĠM unicip
-ens ible
-Ġceremon ies
-Ġintimid ation
-ĠCON TR
-be ck
-ĠK ap
-as u
-Ġtradem arks
-ĠS ew
-ĠComp etition
-net work
-ĠAr ri
-ĠT et
-Ro aming
-W C
-D at
-Ġso b
-Ġpair ing
-Ġoverd ose
-SA Y
-ab er
-Ġrev olt
-ĠF ah
-act ing
-e q
-est ation
-F ight
-ĠMar ks
-27 3
-Ġ17 8
-R aw
-ãģ ĭ
-34 9
-bl ocks
-Ġver ge
-est ine
-ĠPod esta
-Ġinv asive
-Ġprofound ly
-ĠA o
-e ach
-Ġl est
-inter pret
-Ġshr inking
-Ġerr one
-Ġche es
-ly s
-ĠI vy
-ĠDirect ory
-Ġhint ed
-V ICE
-Ġcontact ing
-ĠG ent
-he i
-Ġlabel ing
-Ġmerc ury
-ĠL ite
-Ġexp ires
-Ġdest abil
-rit is
-c u
-Ġfeather s
-Ġste er
-Ġprogram med
-ĠV ader
-Go ing
-ĠE lim
-Ġy o
-ĠMic he
-Ġ20 3
-Ġslee ves
-Ġb ully
-ĠHum ans
-36 8
-Ġcomp ress
-ĠBan ner
-AR S
-Ġa while
-Ġcal ib
-Ġspons orship
-ĠDiff iculty
-ĠP apers
-Ġident ifier
-} .
-Ġy og
-ĠSh ia
-Ġclean up
-Ġvib e
-int rodu
-im ming
-Austral ia
-Ġout lines
-ĠY outube
-tr ain
-ĠM akes
-Ġde ported
-Ġcent r
-ĠD ug
-ĠB oulder
-ĠBuff y
-Ġinj unction
-ĠHar ley
-ĠG roups
-ĠD umbledore
-ĠCl ara
-Ġ" -
-Ġsacrific ed
-ep h
-Sh adow
-ib ling
-Ġfreel ance
-Ġevident ly
-ph al
-Ġret ains
-M ir
-Ġfin ite
-d ar
-ĠC ous
-Ġrep aired
-Ġperiod ic
-Ġchampions hips
-Ġaster oid
-bl ind
-Ġexpress ly
-ĠAst ros
-Ġsc aled
-Ġge ographical
-ĠRap ids
-En joy
-Ġel astic
-ĠMoh amed
-Mark et
-be gin
-Ġdisco vers
-Ġtele communications
-Ġscan ner
-Ġen large
-Ġsh arks
-Ġpsy chedel
-ĠRou ge
-Ġsnap shot
-is ine
-X P
-Ġpestic ides
-ĠL SD
-ĠDist ribution
-re ally
-Ġde gradation
-Ġdisgu ise
-Ġbi om
-ĠEX T
-Ġequ ations
-Ġhaz ards
-ĠComp ared
-) *
-Ġvirt ues
-Ġeld ers
-Ġenh ancing
-ĠAc ross
-er os
-ang ling
-Ġcomb ust
-ucc i
-Ġconc ussion
-Ġcontrace ption
-ĠK ang
-Ġexpress es
-Ġa ux
-ĠP ione
-Ġexhib its
-Deb ug
-OT AL
-ĠAl ready
-ĠWheel er
-Ġexp ands
-? :
-Ġreconc iliation
-Ġpir ates
-Ġpur se
-Ġdiscour age
-Ġspect acle
-R ank
-Ġwra ps
-ĠTh ought
-Ġimp ending
-O pp
-ĠAng lo
-ĠE UR
-Ġscrew ed
-ret ched
-Ġencour agement
-mod els
-Ġconf use
-mm m
-ĠVit amin
-âĸij âĸij
-C ru
-Ġkn ights
-Ġdisc ard
-Ġb ishops
-ĠW ear
-ĠGar rett
-k an
-ãĥ Ł
-Ġmascul ine
-cap ital
-ĠA us
-Ġfat ally
-th anks
-ĠA U
-ĠG ut
-12 00
-Ġ 00000000
-Ġsur rog
-ĠBI OS
-ra its
-ĠWat ts
-Ġresur rection
-ĠElect oral
-ĠT ips
-4 000
-Ġnut rient
-Ġdepict ing
-Ġspr ink
-Ġm uff
-ĠL IM
-ĠS ample
-ps c
-ib i
-gener ated
-Ġspec imens
-Ġdiss atisf
-Ġtail ored
-Ġhold ings
-ĠMonth ly
-ĠE at
-po ons
-Ġne c
-ĠC age
-ĠLot us
-ĠLan tern
-Ġfront ier
-Ġp ensions
-Ġj oked
-ĠHard y
-=-=- =-=-
-r ade
-U ID
-Ġr ails
-Ġem it
-Ġsl ate
-Ġsm ug
-Ġsp it
-ĠCall s
-ĠJac obs
-f eat
-ĠU E
-Ġrest ruct
-Ġregener ation
-Ġenerg ies
-ĠCon nor
-OH N
-ĠChe ese
-Ġg er
-Ġresur rect
-man agement
-N W
-Ġpres ently
-ĠBru ins
-M ember
-ĠM ang
-id an
-Ġboost ing
-w yn
-+ .
-requ isite
-ĠNY PD
-ĠMe gan
-ĠCond itions
-Ġp ics
-nes ium
-ĠR ash
-Ġ17 4
-ĠD ucks
-Ġemb ro
-z u
-on ian
-rel igious
-Ġc raz
-ĠAC A
-ĠZ ucker
-EM A
-ĠPro s
-We apon
-ĠKn ox
-ĠAr duino
-Ġst ove
-Ġheaven s
-ĠP urchase
-Ġher d
-Ġfundra iser
-Dig ital
-5 000
-Ġprop onents
-/ âĢĭ
-Ġj elly
-ĠVis a
-Ġmon ks
-Ġadvance ment
-ĠW er
-Ġ18 7
-e us
-ert ility
-Ġfet al
-Ġ19 36
-L o
-Ġout fits
-Ġstair case
-b omb
-Ġcustom ized
-cl air
-T ree
-Ġm apped
-ĠConsider ing
-ĠTor res
-Ġmeth yl
-Ġapprox imate
-Ġdo om
-ĠHans en
-Ġc rossover
-Ġstand alone
-ä ¼
-Ġinv ites
-Ġgra veyard
-Ġh p
-Donald Trump
-Ġesc ort
-G ar
-Ġpredec essors
-Ġh ay
-Ġen zyme
-ĠStra ight
-vis ors
-I ng
-ane ously
-ĠApp lied
-Ġf ec
-ĠDur ant
-Ġout spoken
-or b
-Ġz eal
-Ġdisgr ace
-' ).
-ĠChe ng
-28 9
-ĠRen a
-ĠSu icide
-29 4
-Ġout raged
-ĠNew man
-ĠN vidia
-ĠA ber
-ĠB ers
-Ġrecre ation
-Wind ow
-ĠD P
-x e
-Ġped oph
-Ġfall out
-ambo o
-Ġpresent ations
-ĠApp s
-Ġh tml
-3 45
-ĠX XX
-Ġrub bing
-ĠLe ather
-Ġhum idity
-se ys
-est ablished
-ĠUn its
-64 6
-Ġrespect able
-A uto
-Ġthri ving
-ĠInn ovation
-ang s
-Ext ra
-reg ulation
-29 8
-p ick
-Ex amples
-ĠC J
-Att ack
-Ġdr acon
-L T
-Ġstick er
-re rs
-Ġsun ny
-I ss
-reg ulated
-d im
-ĠAb stract
-Ġhus bands
-Off ice
-om ination
-it ars
-AN GE
-asc al
-ĠK ris
-ĠInf antry
-Ġm alf
-ĠA the
-ĠR ally
-bal anced
-................ ........
-OU P
-Ġmole cule
-met ics
-ĠSpl it
-ĠInstruct ions
-ĠN ights
-c ards
-Ġt ug
-Ġcon e
-å Ń
-Ġt x
-ĠDisc ussion
-Ġcatast rophe
-pp e
-g io
-Ġcommun ism
-Ġhal ted
-ĠGu ant
-cle an
-ĠSc hed
-ĠK anye
-Ġw ander
-ĠSer iously
-Ġ18 8
-enn ial
-f ollow
-product ive
-ĠFl ow
-ĠS ail
-Ġc raw
-Ġsim ulations
-or u
-ang les
-ĠN olan
-Ġmen stru
-4 70
-Ġ20 7
-aj a
-Ġcas ually
-board ing
-Ġ2 22
-ov y
-ĠN umbers
-um at
-O E
-28 7
-ĠCle mson
-Ġcert s
-Ġsl id
-ĠT ribe
-Ġto ast
-Ġfort unes
-Ġf als
-ĠComm ittees
-Ġg p
-Ġf iery
-ĠN ets
-ĠAn ime
-Pack age
-ĠComp are
-l aughter
-in fect
-Ġatroc ities
-Ġjust ices
-Ġins ults
-ĠVern on
-Ġsh aken
-Ġperson a
-est amp
-36 7
-br ain
-Ġexperiment ing
-K en
-ĠElect ronics
-Ġ16 1
-dom ain
-Ġgraph ical
-b ishop
-Ġwho pping
-ĠEv angel
-Ġadvertis ers
-ĠSpe ar
-Ġb ids
-Ġdestro ys
-ut z
-Ġunders c
-ĠAD D
-Ġan ts
-ĠC um
-ipp les
-ĠF ill
-Ġgl anced
-Ġind icted
-ĠE ff
-Ġmis con
-ĠDes ktop
-Ġab ide
-ãĥ Ģ
-ĠI o
-ĠC oul
-Ġcaps ule
-ĠCh rys
-M ON
-Ġund es
-ĠI RA
-Ġc itation
-Ġdict ate
-ĠNet works
-ĠConf lict
-ĠSt uff
-x a
-is ec
-ĠChem istry
-Ġquarter ly
-William s
-an an
-O pt
-ĠAlexand ria
-out heastern
-ĠSpring field
-ĠBlack s
-Ġge ography
-24 2
-Ġut most
-ĠEx xon
-ab outs
-E VA
-ĠEn able
-ĠBar r
-Ġdisag reed
-ĠCy prus
-Ġdement ia
-Ġlab s
-Ġubiqu itous
-ĠLO VE
-Ġconsolid ated
-s r
-Ġcream y
-ĠTim ber
-Reg ardless
-ĠCert ificate
-Ġ" ...
-ogen ous
-Capt ain
-Ġinsult ing
-ĠSor os
-ĠInst r
-ĠBulgar ia
-bet ter
-Ġsuck ing
-ĠDavid son
-at z
-Ġcoll ateral
-g if
-Ġplag ued
-ĠC ancel
-ĠGard ner
-R B
-Ġsix teen
-Rem ove
-ur istic
-c ook
-R od
-Ġcompr ising
-f le
-) âĢĶ
-ĠVik ing
-g rowth
-agon al
-Ġsr f
-af ety
-m ot
-N early
-st own
-ĠF actor
-Ġautom obile
-Ġproced ural
-m ask
-amp ires
-Ġdisapp ears
-j ab
-3 15
-Ġ19 51
-ne eded
-Ġd aring
-le ader
-Ġp odium
-Ġun healthy
-Ġm und
-Ġpy ramid
-oc re
-Ġkiss ed
-Ġdream ed
-ĠFant astic
-ĠG ly
-å Ĭ
-Ġgreat ness
-Ġsp ices
-Ġmet ropolitan
-Ġcomp uls
-i ets
-101 6
-ĠSh am
-ĠP yr
-fl ies
-ĠMid night
-Ġswall owed
-Ġgen res
-ĠL ucky
-ĠRew ards
-Ġdisp atch
-ĠI PA
-ĠApp ly
-Ġa ven
-al ities
-3 12
-th ings
-Ġ( ).
-Ġm ates
-ĠS z
-ĠC OP
-ol ate
-O FF
-Ġre charge
-c aps
-ĠYork er
-ic one
-Ġgal axies
-ile aks
-D ave
-ĠP uzz
-ĠCelt ic
-ĠA FC
-27 6
-ĠS ons
-Ġaffirm ative
-H or
-Ġtutorial s
-ĠC ITY
-ĠR osa
-ĠExt ension
-Ser ies
-Ġf ats
-Ġr ab
-l is
-Ġun ic
-Ġe ve
-ĠSp in
-Ġadul thood
-ty p
-Ġsect arian
-Ġcheck out
-ĠCy cl
-S ingle
-Ġmart yr
-Ġch illing
-88 8
-ou fl
-Ġ] ;
-Ġcongest ion
-m k
-ĠWhere as
-Ġ19 38
-ur rencies
-er ion
-Ġbo ast
-ĠPat ients
-Ġch ap
-ĠB D
-real DonaldTrump
-Ġexam ines
-h ov
-Ġstart ling
-ĠBab ylon
-w id
-om ew
-br ance
-ĠOd yssey
-w ig
-Ġtor ch
-ĠV ox
-ĠMo z
-ĠT roll
-ĠAn s
-Similar ly
-ĠF ul
-00 6
-Un less
-ĠAl one
-st ead
-ĠPub lisher
-r ights
-t u
-ĠDoes n
-Ġprofession ally
-Ġcl o
-ic z
-Ġste als
-Ġ á
-19 86
-Ġst urdy
-ĠJoh ann
-Ġmed als
-Ġfil ings
-ĠFr aser
-d one
-Ġmult inational
-Ġf eder
-Ġworth less
-Ġp est
-Yes terday
-ank ind
-Ġg ays
-Ġb orne
-ĠP OS
-Pict ure
-Ġpercent ages
-25 1
-r ame
-Ġpot ions
-AM D
-ĠLeban ese
-Ġr ang
-ĠL SU
-ong s
-Ġpen insula
-ĠCl ause
-AL K
-oh a
-ĠMac Book
-Ġunanim ous
-Ġl enders
-Ġhang s
-Ġfranch ises
-ore rs
-ĠUp dates
-Ġisol ate
-and ro
-S oon
-Ġdisrupt ive
-ĠSur ve
-Ġst itches
-ĠSc orp
-ĠDomin ion
-Ġsupp lying
-Ar g
-Ġtur ret
-ĠL uk
-Ġbr ackets
-* )
-ĠRevolution ary
-ĠHon est
-Ġnot icing
-ĠSh annon
-Ġafford ed
-Ġth a
-ĠJan et
-! --
-ĠNare ndra
-ĠPl ot
-H ol
-se ver
-e enth
-Ġobst ruction
-Ġ10 24
-st aff
-j as
-or get
-sc enes
-l aughs
-ĠF argo
-cr ime
-Ġorche str
-Ġde let
-ili ary
-rie ved
-Ġmilit ar
-ĠGreen e
-âĹ ı
-ãģ ¦
-ĠGu ards
-Ġunle ashed
-ĠWe ber
-Ġadjust able
-Ġcal iber
-Ġmotiv ations
-ĠÃ ł
-m Ah
-ĠL anka
-hand le
-Ġp ent
-ĠR av
-ĠAng ular
-ĠK au
-umb ing
-Ġphil anthrop
-Ġde hyd
-Ġtox icity
-e er
-ĠY ORK
-w itz
-å ¼
-ĠI E
-commun ity
-ĠA H
-Ġret ali
-Ġmass ively
-ĠDani els
-ĠD EL
-Ġcar cin
-Ur l
-Ġrout ing
-ĠNPC s
-ĠR AF
-ry ce
-Ġwa ived
-ĠGu atem
-Every body
-Ġco venant
-Ġ17 3
-Ġrelax ing
-Ġqu art
-al most
-Ġguard ed
-ĠSold iers
-ĠPL AY
-Ġout going
-L AND
-Ġre write
-ĠM OV
-ĠIm per
-ĠS olution
-Ġphenomen al
-Ġl ongevity
-Ġimp at
-ĠN issan
-ir ie
-Ġod or
-ĠZ ar
-ok s
-Ġmilit ias
-ĠSP EC
-Ġtoler ated
-ars er
-ĠBrad ford
-+ ,
-Ġsur real
-s f
-Can adian
-Ġresemb lance
-Ġcarbohyd rate
-VI EW
-Ġaccess ory
-me al
-larg est
-ieg el
-Some one
-Ġtoug hest
-os o
-Ġfun nel
-Ġcondemn ation
-lu ent
-Ġw ired
-ĠSun set
-Jes us
-ĠP ST
-ĠP ages
-ĠTy coon
-ĠP F
-Ġselect ions
-Ġ à¤
-part isan
-Ġhigh s
-ĠR une
-Ġcraft s
-le ad
-ĠParent s
-Ġre claim
-ek er
-ĠAll ied
-ae per
-Ġlo oming
-Ġbenefic iaries
-ĠH ull
-Stud ents
-Jew ish
-d j
-Ġp act
-tem plate
-ĠOffic ials
-ĠBay lor
-Ġhe mp
-Ġyouth s
-ĠLevel s
-ĠX iao
-ĠC hes
-Ġende avor
-ĠRem oved
-Ġhipp ocamp
-H ell
-ãĤ Ĭ
-80 5
-Ġd inosaur
-ĠWr ath
-ĠIndones ian
-Ġcalcul ator
-ĠD ictionary
-Ġ4 20
-ĠM AG
-( _
-! ,
-t arians
-Ġrestrict ing
-rac use
-Ġweek day
-OU NT
-Ġsh rugged
-leg round
-Ġb ald
-ĠDo ctors
-Ġt outed
-ĠMax well
-Ġ2 14
-Ġdiplom at
-Ġrep ression
-Ġconstitu ency
-v ice
-r anked
-ĠNap oleon
-g ang
-ĠFore ver
-t un
-Ġbul b
-ĠPD T
-ĠC isco
-V EN
-Ġres umed
-Ste ven
-ĠManit oba
-Ġfab ulous
-ĠAg ents
-19 84
-Ġam using
-ĠMyster ies
-Ġor thodox
-fl oor
-Ġquestion naire
-Ġpenet rate
-Ġfilm makers
-ĠUn c
-Ġst amped
-Ġth irteen
-Ġout field
-Ġforward ed
-Ġapp ra
-Ġa ided
-t ry
-Ġunf ocused
-ĠL iz
-ĠWend y
-ĠSc ene
-Ch arg
-Ġreject s
-Ġleft ist
-ĠProv idence
-ĠBr id
-reg n
-Ġprophe cy
-ĠL IVE
-4 99
-Ġfor ge
-ĠF ML
-Ġintrins ic
-ĠF rog
-Ġw ont
-ĠH olt
-Ġfam ed
-CL US
-aeper nick
-ĠH ate
-ĠC ay
-Ġregister ing
-ort ality
-rop y
-ocaly ptic
-a an
-n av
-Ġfasc ist
-IF IED
-Ġimpl icated
-ĠRes ort
-ĠChand ler
-ĠBr ick
-P in
-ys c
-Us age
-ĠHel m
-us ra
-âĺħ âĺħ
-ĠAb bas
-Ġunanim ously
-Ġke eper
-Ġadd icted
-?? ?
-Ġhelm ets
-Ġant ioxid
-aps ed
-80 8
-gi ene
-Ġwa its
-Ġmin ion
-ra ved
-ĠP orsche
-Ġdream ing
-Ġ17 1
-ĠC ain
-Ġun for
-ass o
-ĠConfig uration
-k un
-hard t
-Ġn ested
-ĠL DS
-L ES
-Ġt ying
-en os
-Ġc ue
-ĠMar qu
-sk irts
-Ġclick ed
-Ġexp iration
-ĠAccording ly
-ĠW C
-Ġbless ings
-Ġaddict ive
-ĠN arr
-y x
-ĠJagu ars
-Ġrent s
-ĠS iber
-Ġt ipped
-ous se
-ĠFitz gerald
-Ġhier arch
-out ine
-Ġwa velength
-> .
-ch id
-ĠProcess ing
-/ +
-r anking
-E asy
-ĠConst ruct
-Ġt et
-ins ured
-H UD
-Ġqu oting
-Ġcommun icated
-in x
-Ġin mate
-Ġerect ed
-ĠAbs olutely
-ĠSure ly
-Ġun im
-ĠThr one
-he id
-Ġcl aws
-Ġsuper star
-ĠL enn
-ĠWh is
-U k
-ab ol
-Ġsk et
-ĠN iet
-Ġper ks
-Ġaff inity
-Ġopen ings
-phas is
-Ġdiscrim inate
-T ip
-v c
-Ġgr inding
-ĠJenn y
-Ġast hma
-hol es
-ĠHom er
-Ġreg isters
-ĠGl ad
-Ġcre ations
-Ġlith ium
-Ġappl ause
-unt il
-Just ice
-ĠTur ks
-Ġsc andals
-Ġb ake
-t ank
-M ech
-ĠMe ans
-ĠM aid
-Republic ans
-is al
-wind ows
-ĠSant os
-Ġveget ation
-33 8
-t ri
-Ġfl ux
-ins ert
-Ġclar ified
-Ġmort g
-ĠCh im
-ĠT ort
-Ġdiscl aim
-met al
-ĠAs ide
-Ġindu ction
-Ġinf l
-Ġathe ists
-amp h
-Ġe ther
-ĠV ital
-ĠBu ilt
-M ind
-Ġweapon ry
-S ET
-Ġ18 6
-ad min
-g am
-cont ract
-af a
-Ġderiv atives
-Ġsn acks
-Ġch urn
-E conom
-Ġca pped
-ĠUnder standing
-ĠH ers
-ĠI z
-Ġd uct
-I ENT
-augh ty
-Ġâľ Ķ
-ĠN P
-Ġsa iling
-In itialized
-Ġt ed
-Ġreact ors
-ĠL omb
-Ġcho ke
-ĠW orm
-Ġadm iration
-Ġsw ung
-ens ibly
-Ġr ash
-ĠGo als
-ĠImport ant
-Sh ot
-ĠR as
-Ġtrain ers
-ĠB un
-Work ing
-Ġhar med
-ĠPand ora
-ĠL TE
-Ġmush room
-ĠCH AR
-ĠF ee
-ĠM oy
-B orn
-ol iberal
-ĠMart ial
-Ġgentle men
-Ġling ering
-Offic ial
-Ġgra ffiti
-ĠN ames
-D er
-Ġqu int
-ist rate
-aze era
-ĠNOT ICE
-ĠFlore nce
-Ġpay able
-Ġdep icts
-ĠSpe cies
-He art
-âĶĢâĶĢâĶĢâĶĢ âĶĢâĶĢâĶĢâĶĢ
-Ġencl osed
-Incre ases
-D aily
-ĠL is
-Ġenact ment
-ĠB acon
-ĠSt eele
-dem and
-Ġ18 3
-Ġmouth s
-Ġstr anded
-Ġenhance ment
-01 1
-ĠWh ats
-Ġhe aled
-en y
-ĠR ab
-Ġ3 40
-ĠLab yrinth
-ro ach
-ĠY osh
-ĠCl ippers
-Ġconcert s
-Intern et
-35 5
-Ġstick ers
-Ġter med
-ĠAx e
-Ġgrand parents
-Fr ance
-ĠCl im
-ĠU h
-ul ic
-Ġthr ill
-cent ric
-ĠOver view
-ĠCond uct
-Ġsubstant ive
-Ġ18 2
-m ur
-Ġstr ay
-ĠCo ff
-Ġrep etitive
-ĠFor gotten
-Ġqual ification
-ew itness
-ĠZ imbabwe
-Ġsim ulated
-ĠJ D
-25 3
-ĠW are
-Ġun sc
-T imes
-Ġsum mons
-Ġdis connected
-Ġ18 4
-ci us
-ĠGu jar
-od ka
-Ġer ase
-ĠTob acco
-elect ed
-Ġun cont
-ĠShe pard
-ĠL amp
-Ġalert ed
-Ġoper ative
-arn a
-u int
-Ġneglig ence
-ac ements
-Ġsup ra
-Ġprev ail
-ĠSh ark
-Ġbel ts
-ãģ «
-Ġt ighter
-Engine ers
-Ġin active
-Ġexp onent
-ĠWill ie
-a ples
-Ġhe ir
-ĠH its
-ian n
-ĠS ays
-Ġcurrent s
-ĠBeng al
-Ġar ist
-B uffer
-Ġbree ze
-ĠWes ley
-Col a
-Ġpron oun
-Ġde ed
-ĠK ling
-Ġof t
-Ġinf lict
-Ġpun ishing
-Ġn m
-ik u
-OD UCT
-01 4
-Ġsubsid y
-ĠDE A
-ĠHer bert
-ĠJ al
-B ank
-Ġdef erred
-Ġship ment
-B ott
-Ġal le
-b earing
-HT ML
-Off line
-Ġ2 13
-Ġscroll ing
-Ġsc anned
-ĠLib yan
-ĠT OP
-ch rom
-d t
-col umn
-Psy NetMessage
-Z ero
-Ġtor so
-0 50
-âķ IJ
-Ġimp erson
-ĠSchw artz
-ud ic
-Ġpiss ed
-ĠS app
-25 7
-ĠIS Ps
-og l
-Ġsuper vised
-Ġad olescent
-Ġatt ained
-ĠDel ivery
-ĠB unny
-Ġ19 37
-Ġmini ature
-Ġo s
-Ġ3 70
-60 8
-ĠMour inho
-Ġinn ate
-Ġtem po
-ĠN M
-ĠFall en
-00 9
-Ġprov ocative
-Stream er
-ĠBened ict
-ĠBol she
-Ġt urtle
-ĠPC B
-ĠEqu al
-Direct or
-ĠR end
-Ġflu ids
-Author ities
-Ġcous ins
-requ ency
-ĠNeigh bor
-s ets
-sh ared
-Char les
-pass word
-Ġg ears
-Ġ2 11
-ĠHard ware
-ri ka
-Ġup stream
-H om
-Ġdisproportion ately
-iv ities
-Ġund efined
-Ġelect rons
-Ġcommem or
-Event ually
-Ġ> <
-Ġir responsible
-2 18
-ĠRe leased
-ĠO VER
-ĠI GN
-ĠB read
-st ellar
-ĠS age
-tt ed
-dam age
-ed ition
-ĠPre c
-Ġl ime
-Ġconf inement
-Ġcal orie
-we apon
-Ġdiff ering
-ĠS ina
-m ys
-am d
-Ġintric ate
-k k
-ĠP AT
-ã o
-st ones
-lin ks
-Ġr anch
-Sem itic
-Ġdifferent iate
-ĠS inger
-occup ied
-Ġfort ress
-c md
-Ġinter ception
-ĠAnk ara
-Ġre pt
-ĠSol itaire
-Ġrem ake
-p red
-Ġd ared
-aut ions
-ĠB ACK
-Run ning
-Ġdebug ging
-Ġgraph s
-3 99
-ĠNig el
-Ġb un
-Ġpill ow
-Ġprog ressed
-fashion ed
-Ġob edience
-ER N
-Ġrehe ars
-C ell
-t l
-S her
-Ġher ald
-ĠPay ment
-ĠC ory
-ĠDe pt
-Ġrep ent
-ĠWe ak
-uck land
-Ġple asing
-Ġshort ages
-Ġjur ors
-ĠK ab
-q qa
-Ant i
-Ġw ow
-ĠRC MP
-Ġt sun
-ĠS ic
-Ġcomp rises
-Ġsp ies
-Ġprec inct
-n u
-Ġur ges
-Ġtim ed
-Ġstrip es
-ĠB oots
-Ġy en
-Adv anced
-Ġdisc rete
-ĠArch angel
-employ ment
-D iff
-Ġmon uments
-Ġ20 9
-work er
-Ġ19 6
-ĠI g
-utter stock
-T PS
-J ac
-Ġhomeless ness
-Ġcomment ator
-Ġrac ially
-f ing
-se ed
-E le
-ell ation
-Ġeth anol
-Ġpar ish
-ĠD ong
-ĠAw akening
-Ġdev iation
-ĠB earing
-ĠTsu k
-Ġrec ess
-Ġl ymph
-ĠCann abis
-å ľ
-ĠNEW S
-Ġd ra
-ĠStef an
-ĠWr ong
-ĠS AM
-Ġloose ly
-Ġinterpre ter
-ĠPl ain
-Go vernment
-Ġbigot ry
-Ġgren ades
-ave z
-pict ured
-Ġmand ated
-ĠMon k
-ĠPed ro
-Ġl ava
-27 4
-Ġcyn ical
-ĠScroll s
-l ocks
-M p
-Ġcon gregation
-orn ings
-ph il
-ĠI bid
-Ġf erv
-Ġdisapp earing
-Ġarrog ant
-sy n
-ĠMa ver
-ĠSu it
-24 1
-Ġab bre
-ack ers
-P a
-ĠY el
-Whe never
-Ġ23 5
-ĠV ine
-ĠAn at
-Ġext inct
-LE T
-Ġexecut able
-V ERS
-ox ide
-D NA
-ĠP rel
-Ġresent ment
-Ġcompr ise
-ĠAv iv
-Ġinter ceptions
-Ġprol ific
-IN A
-ĠEr in
-though t
-2 19
-ĠPsychiat ry
-un ky
-chem ist
-H o
-ĠMcC oy
-Ġbr icks
-L os
-ri ly
-ĠUS SR
-Ġr ud
-Ġl aud
-ĠW ise
-ĠEmer ald
-Ġrev ived
-Ġdam ned
-ĠRep air
-id em
-ct ica
-Ġpatri arch
-ĠN urs
-me g
-Ġcheap est
-re ements
-empt y
-ĠCele br
-Ġdepri vation
-ch anted
-ĠTh umbnails
-E nergy
-ĠEth an
-ĠQ ing
-Ġopp oses
-W IND
-v ik
-ĠM au
-ĠS UB
-66 7
-G RE
-ĠVol unte
-nt on
-C ook
-å IJ
-es que
-Ġplum met
-Ġsu ing
-Ġpron ounce
-Ġresist ing
-ĠF ishing
-ĠTri als
-Ġy ell
-Ġ3 10
-Ġin duct
-Ġpersonal ized
-oft en
-R eb
-EM BER
-Ġview point
-Ġexist ential
-() )
-rem ove
-MENT S
-l asses
-Ġev apor
-Ġa isle
-met a
-Ġreflect ive
-Ġentit lement
-Ġdev ised
-mus ic
-asc ade
-Ġwind ing
-off set
-Ġaccess ibility
-ke red
-Bet ter
-ĠJohn ston
-th inking
-S now
-ĠCroat ia
-ĠAt omic
-27 1
-34 8
-Ġtext book
-ĠSix th
-Ġ اÙĦ
-Ġsl ider
-ĠBur ger
-b ol
-S ync
-Ġgrand children
-Ġc erv
-+ )
-Ġe ternity
-Ġtweet ing
-Ġspec ulative
-Ġpiv otal
-ĠW P
-ĠT ER
-ynam ic
-Ġu pl
-ĠC ats
-per haps
-Ġclass mates
-Ġblat ant
-' -
-Ġl akh
-ant ine
-ĠB org
-i om
-/ (
-ĠAthlet ic
-Ġs ar
-OT A
-ĠHoff man
-Never theless
-Ġad orable
-Ġspawn ed
-Ass ociated
-ĠDom estic
-Ġimpl ant
-ĠLux em
-ĠK ens
-Ġp umps
-ĠS AT
-Att ributes
-50 9
-av our
-Ġcentral ized
-ĠT N
-Ġfresh ly
-ĠA chieve
-Ġouts iders
-her ty
-ĠRe e
-ĠT owers
-ĠD art
-ak able
-Ġm p
-ĠHeaven ly
-Ġr ipe
-ĠCarol ine
-ry an
-Ġclass ics
-Ġret iring
-Ġ2 28
-Ġa h
-Ġdeal ings
-Ġpunch ing
-ĠChap man
-O ptions
-max well
-vol ume
-Ġst al
-Ġex ported
-ĠQu ite
-Ġnumer ical
-B urn
-F act
-ĠKey stone
-Ġtrend ing
-Ġalter ing
-ĠAfric ans
-47 8
-ĠM N
-ĠKn ock
-Ġtempt ation
-Ġprest ige
-Over view
-ĠTrad itional
-ĠBah rain
-Priv ate
-ĠH OU
-Ġbar r
-ĠT at
-C ube
-US D
-ĠGrand e
-ĠG at
-ĠFl o
-Ġres ides
-Ġind ec
-vol ent
-Ġperpet ual
-ub es
-Ġworld view
-ĠQuant um
-Ġfil tered
-Ġen su
-orget own
-ERS ON
-ĠM ild
-37 9
-OT T
-Ã ¥
-Ġvit amins
-Ġrib bon
-Ġsincere ly
-ĠH in
-Ġeight een
-Ġcontradict ory
-Ġgl aring
-Ġexpect ancy
-Ġcons pir
-Ġmon strous
-Ġ3 80
-re ci
-Ġhand ic
-Ġpump ed
-Ġindic ative
-Ġr app
-Ġav ail
-ĠLEG O
-ĠMar ijuana
-19 85
-ert on
-Ġtwent ieth
-################ ################
-ĠSw amp
-Ġval uation
-Ġaffili ates
-adjust ed
-ĠFac ility
-26 2
-Ġenz ymes
-itud inal
-Ġimp rint
-S ite
-Ġinstall er
-ĠT RA
-m ology
-lin ear
-ĠCollect ive
-ig ating
-ĠT oken
-Ġspec ulated
-K N
-ĠC ly
-or ity
-Ġdef er
-Ġinspect ors
-appro ved
-R M
-ĠSun s
-Ġinform ing
-ĠSy racuse
-ib li
-7 65
-Ġgl ove
-Ġauthor ize
-âĢ¦âĢ¦âĢ¦âĢ¦ âĢ¦âĢ¦âĢ¦âĢ¦
-ĠCru ise
-Ġcontract ing
-she ll
-IF E
-ĠJew el
-p ract
-ĠPhot oshop
-ĠKnow ing
-h arm
-Ġattract ions
-ad an
-et us
-01 8
-w agen
-Al t
-Ġmultip ly
-Ġequ ilibrium
-: {
-ĠF ighters
-ĠEd gar
-Ġfour teen
-Go vern
-Ġmis use
-Ġab using
-Ġancest ry
-ram er
-64 4
-Ġwor ms
-Ġthick er
-ĠComb ine
-Ġpeas ants
-Ġv ind
-Ġcon quest
-Ġm ocked
-Ġc innamon
-ĠC ald
-ĠGall up
-Ġavoid ance
-Ġincarn ation
-ĠStr at
-Ġt asted
-ent a
-ĠN eal
-p ared
-Ġtermin ology
-ject ion
-Scient ists
-ĠIN S
-ĠDe e
-Ġdirect ories
-R oad
-ĠSh ap
-br ight
-ĠDirect ors
-ĠCol umn
-Ġb ob
-Ġprefer ably
-Ġgl itch
-f urt
-Ġe g
-id is
-C BC
-Ġsur rendered
-Ġtest ament
-33 6
-ug gest
-ĠN il
-an other
-Ġpat hetic
-ĠDon na
-Ġ2 18
-ĠA very
-Ġwhis key
-Ġf ixture
-ĠCon quest
-Ġbet s
-O cc
-ĠLe icester
-] ."
-Ġ) );
-Ġfl ashes
-45 6
-Ġmask ed
-ge bra
-Ġcomput ed
-che l
-aud er
-Ġdefe ats
-ĠLiber ation
-ĠOs ama
-ĠV ive
-Ch anges
-Ch annel
-Ġtar iffs
-Ġm age
-ĠS ax
-Ġinadvert ently
-ĠC RE
-ĠRe aper
-ink y
-gr ading
-Ġstere otyp
-Ġcur l
-ĠF ANT
-Ġfram eworks
-M om
-ĠAn ch
-Ġflav our
-car bon
-Ġperm itting
-let cher
-ĠMo zilla
-ĠPark ing
-ĠCh amp
-Sc roll
-Ġmurd erer
-Ġrest ed
-Ġow es
-ĠP oss
-AD D
-IF F
-res olution
-ĠMin ing
-Ġcompar ative
-D im
-Ġneighbour ing
-ĠA ST
-ĠT oxic
-Ġbi ases
-Ġgun fire
-ur ous
-ĠMom ent
-19 83
-Ġper vasive
-tt p
-ĠNorm ally
-r ir
-S arah
-ĠAlb any
-Ġun sett
-ĠS MS
-ip ers
-l ayer
-ĠWh ites
-up le
-Ġtur bo
-ĠLe eds
-Ġthat s
-ĠMin er
-M ER
-ĠRe ign
-Ġper me
-ĠBl itz
-Ġ19 34
-Ġintimid ating
-t ube
-Ġecc entric
-ab olic
-box es
-ĠAssoci ates
-v otes
-Ġsim ulate
-um bo
-aster y
-Ġship ments
-FF FF
-an th
-Ġseason ed
-Ġexperiment ation
-âĸ ł
-law s
-Me et
-idd les
-ant ics
-R ating
-IS IS
-h ift
-Ġfront s
-b uf
-01 7
-Ġun att
-ĠD il
-le ases
-ĠGard ens
-77 7
-t ouch
-ve ll
-45 8
-Ġ= ====
-s aving
-Ġer osion
-ĠQu in
-Ġearn s
-Ġaccomplish ment
-ĠWe i
-Ġ< [
-____ _
-Ġir rig
-ĠT eddy
-Ġconqu ered
-ĠArm ored
-Ġassert s
-Ġmanip ulating
-r é
-Ġtranscript s
-G allery
-Ġplot ting
-Ne il
-Ġbetray al
-load er
-ĠS ul
-Ġdispl acement
-Ġroy alty
-ĠW I
-he it
-ĠDev ices
-alle l
-Ġmunicipal ities
-Ġcan al
-St ars
-ĠU AE
-Ġ" âĢ¦
-ĠC U
-ab ove
-Ġreson ance
-ĠguiActive Un
-add ed
-ĠBra ves
-ĠI bn
-Ġhere by
-ĠB RE
-Ġshare holder
-ĠH ir
-ĠJ i
-Ġstrange ly
-Ġadm ired
-Ġpl ight
-Ġb achelor
-ĠP ole
-cipl inary
-T ony
-ĠArmen ian
-Ġun man
-ĠZion ist
-St age
-isco ver
-Ġautom otive
-Ġs idelines
-Ġsl ick
-ĠRena issance
-ĠF UN
-Im ages
-ĠH aj
-Ġp ing
-Ġshort cut
-ĠBl vd
-ĠLook s
-Ġbur sts
-Ġcl amp
-Ġm ish
-Ġsort ing
-Ġpatri ot
-Ġcorrect ness
-ĠScand inav
-ĠCaval iers
-p ython
-az ar
-Ġ3 75
-ĠJa une
-40 9
-Ġdetrim ental
-Ġstab bing
-Ġpoison ed
-Ġf ountain
-oc ent
-or st
-ĠMar i
-Ġr ains
-ĠO vers
-ĠInst itution
-ud get
-AM Y
-t ale
-ĠK R
-ĠPr ices
-Ġhead aches
-Ġlands l
-ĠA ura
-Bon us
-ĠZ hao
-ĠH ip
-Ġhop s
-ĠKurd istan
-Ġexplo iting
-ry n
-Ġhypocr isy
-op ening
-Ġgun shot
-Ġw ed
-inter stitial
-Inter stitial
-Ġam en
-Bre aking
-Ġmarket ed
-W ire
-ĠC rowd
-Contin ue
-ĠK nown
-ĠEffect ive
-ore an
-iz ons
-Jose ph
-Ġescal ation
-us ername
-Ġcur tain
-AT ES
-ĠP AR
-ĠM iy
-Ġcounter fe
-l ene
-Ġcont enders
-d aily
-ĠAs c
-ĠPhill ip
-most ly
-Ġfil ename
-he ne
-Ġresemb ling
-Ġst aging
-ĠCh loe
-Ġw iring
-H on
-ĠRen ew
-ott age
-ĠHy brid
-m uch
-Ġstro kes
-Ġpolicy makers
-AP TER
-ĠArk ham
-pl ot
-Ġassist ants
-Ġde port
-ĠSe ga
-Ġinflu enza
-ĠC ursed
-ĠK obe
-Ġskin ny
-Prov ider
-ĠR ip
-Ġincrement al
-product s
-B F
-Ġd ome
-ĠC redits
-Ġlos ers
-int s
-ĠBet ty
-ĠTal ent
-ĠD AM
-L v
-E ss
-Ġd ens
-tem p
-J udge
-od ic
-Ġ' (
-UR ES
-ets k
-V O
-Ġretrie ved
-Ġarchitect s
-Ù ĩ
-Ġeth ic
-ĠSecond ary
-st ocks
-ad ia
-Ġ3 25
-ĠOp inion
-Ġsimultane ous
-Ġd izz
-ul p
-Ġsmugg ling
-ipp ery
-R andom
-f acing
-ĠD as
-Ġstock p
-Ġdiscl osures
-po inter
-Ġcor al
-ĠSe lection
-ĠP ike
-ival ent
-Ġruth less
-ĠR im
-Ġensu ing
-ĠExper iment
-Ġcongress man
-Ġbelie ver
-Ġun specified
-ĠM ord
-Ġknowledge able
-ĠV ERY
-T X
-Ġstra ps
-Ġtur f
-apesh ifter
-Ġmar ital
-Ġfl ock
-ãģ Ĩ
-26 3
-AM ES
-ĠOpp osition
-Ġtre asures
-ĠG OD
-Ġmodel ed
-ĠWOR LD
-Ġ( [
-ĠUs age
-H F
-Ġ$ (
-uss ed
-Ġpione er
-E ight
-par se
-b read
-rit z
-ĠMir anda
-ĠK ant
-++ )
-ore n
-Ġprov oked
-Ġbre eds
-ĠIn cludes
-ĠPast ebin
-ĠFl ip
-J ava
-Ġbr ink
-Ġrum ored
-Ġun seen
-Ġgar nered
-ĠDef in
-al ted
-Ġtatt oos
-Ġhes itation
-is itions
-ĠWe aver
-ĠReport ing
-Ġtherap ies
-Ġconsult ants
-Ġresid ual
-ĠMal i
-ĠRom a
-i ago
-ĠRes idents
-ub i
-Ġremed ies
-Ġadapt ive
-ĠAl ive
-ĠBar cl
-Ġwal lets
-c rypt
-etermin ation
-ĠPel osi
-Ġsl ipping
-oton in
-Ġall iances
-pat rick
-ir is
-Ġor th
-ĠPer kins
-ĠDe V
-ĠG ets
-Ġdry ing
-ge e
-fore st
-ĠFor get
-ore m
-33 9
-Ġvague ly
-ĠD ion
-ĠP orn
-ĠH OW
-Ġp neum
-Ġrub ble
-ĠT aste
-enc ia
-ĠG el
-Ġd st
-Ġ24 5
-ĠMoroc co
-inf lamm
-ĠTw ins
-Ġb ots
-d aughter
-ĠB alk
-Ġbre thren
-Ġlog os
-Ġgo bl
-f ps
-Ġsub division
-Ġp awn
-Ġsquee zed
-Ġmor ale
-ĠD W
-' "
-Ġkn ot
-ook y
-Ġdiv isive
-Ġboost ed
-ch y
-ãĥ IJ
-if act
-Ġnewcom ers
-ĠWrest ling
-Ġsc outs
-w olves
-R at
-Ġnin eteenth
-ĠOs borne
-St ats
-Ġem powered
-Ġpsych opath
-ĠO EM
-ugg age
-ĠP K
-ĠMoh ammad
-P ak
-Ġanarch ists
-ĠExt ract
-est hes
-ĠStock holm
-l oo
-ĠG raph
-Ġdeploy ing
-ĠStr anger
-ĠM old
-Ġstaff er
-Ġdiscount ed
-uck le
-ple ase
-ĠLand ing
-ÃŃ a
-Ġ19 3
-Ġan te
-Ġrep etition
-Ġ+ /-
-Ġpar ody
-Ġlive ly
-AA A
-ĠHor us
-Ġp its
-ind ers
-L OC
-ĠVen ice
-40 6
-ĠDis cover
-â Ĩ
-ellect ual
-Ġp ens
-Ġey el
-ig uous
-Im pl
-Ġj oking
-Ġinv al
-ĠBel fast
-Ġcredit ors
-ĠSky walker
-ov sky
-Ġcease fire
-Ġse als
-is oft
-) ).
-ĠFel ix
-IT S
-Ġt resp
-ĠBlock chain
-ew are
-ĠSch war
-en ne
-mount ed
-ĠBe acon
-les h
-Ġimmense ly
-Ġche ering
-Em ploy
-sc ene
-ish ly
-atche wan
-ĠNic olas
-Ġdr ained
-ĠEx it
-ĠAz erb
-j un
-Ġflo ated
-u ania
-De ep
-Ġsuper v
-Ġmyst ical
-ĠD ollar
-ĠApost le
-ĠR EL
-ĠProv ided
-ĠB ucks
-ãĥ ´
-cut ting
-Ġenhance ments
-ĠPengu ins
-ĠIsa iah
-Ġj erk
-ĠW yn
-Ġst alled
-Ġcryptoc urrencies
-ĠR oland
-sing le
-Ġl umin
-ĠF ellow
-ĠCap acity
-ĠKaz akh
-W N
-Ġfin anced
-38 9
-Ġt id
-Ġcoll usion
-ĠMy r
-î Ģ
-Sen ator
-Ġped iatric
-Ġneat ly
-Ġsandwic hes
-ĠArchitect ure
-Ġt ucked
-Ġbalcon y
-Ġearthqu akes
-qu ire
-F uture
-Ġhe fty
-é Ĺ
-Ġspecial izes
-Ġstress es
-Ġs ender
-Ġmisunder standing
-Ġep ile
-Ġprov oke
-ĠCol ors
-Ġdis may
-uk o
-[ _
-58 6
-ne utral
-Ġdon ating
-ĠRand all
-Mult i
-Ġconvenient ly
-ĠS ung
-ĠC oca
-Ġt ents
-ĠAc celer
-Ġpart nered
-27 2
-ir ming
-ĠB AS
-s ometimes
-Ġobject ed
-ub ric
-p osed
-LC S
-gr ass
-Ġattribut able
-V IS
-Israel i
-Ġrepe ats
-ĠR M
-v ag
-ut a
-in ous
-Ġin ert
-ĠMig uel
-æ Ń
-ĠHawai ian
-B oard
-Ġart ific
-ĠAzerb ai
-as io
-ĠR ent
-A IN
-Ġappl iances
-Ġnational ity
-Ġass hole
-ĠN eb
-Ġnot ch
-h ani
-ĠBr ide
-Av ailability
-Ġintercept ed
-Ġcontin ental
-Ġsw elling
-ĠPers pect
-b ies
-. <
-ith metic
-ĠL ara
-Ġtempt ing
-add r
-Ġoversee ing
-cl ad
-ĠD V
-ĠGing rich
-Ġm un
-ĠApp ropri
-Ġalter ations
-ĠPat reon
-Ġha voc
-Ġdiscipl ines
-Ġnotor iously
-aku ya
-ier i
-? ).
-ĠW ent
-Ġsil icon
-Ġtre mb
-Cont ainer
-K nown
-Ġmort ar
-est e
-ick a
-Ar thur
-ĠPre viously
-ĠMart y
-Ġsp arse
-g ins
-Ġin ward
-ĠParticip ant
-C opy
-ĠM isc
-Ġantib iotic
-ĠRet ro
-Ġel usive
-Ġass ail
-ĠBatt alion
-ĠB ought
-Ġdimin ish
-ĠEuro pa
-s ession
-ĠDanger ous
-ies el
-Ġdisbel ief
-Ġbl asts
-ext reme
-ĠBoy d
-ĠProject s
-ĠGu ys
-Ġunder gone
-Ġgr ill
-ĠDw ight
-Ġ19 7
-US ER
-Ġfiles ystem
-Ġcl ocks
-T aylor
-Ġwra pper
-Ġfold ing
-ous and
-ĠPhilipp ine
-ATION AL
-ĠPer th
-Ġas hes
-Ġaccum ulate
-ĠGate way
-Sh op
-orks hire
-H an
-ĠBar rel
-ĠLe h
-ĠX V
-Ġwh im
-Ġrep o
-ĠC G
-ĠM am
-Ġincorpor ating
-Ġbail out
-Ġlingu istic
-Ġdis integ
-C LE
-Ġcinem atic
-ĠF iber
-S yn
-il ion
-ĠCom pos
-c hens
-Ġne oc
-Ġbo iled
-F INE
-on o
-un cle
-ik en
-ĠB M
-Î ¹
-Ġreceipt s
-Ġdisp osed
-ĠTh irty
-ĠR ough
-ĠA BS
-Ġnot withstanding
-oll en
-# $
-Ġunrel iable
-Ġbl oom
-Ġmedi ocre
-Ġtr am
-ĠTas man
-Ġsh akes
-Ġmanifest o
-ĠM W
-Ġsatisf actory
-Ġsh ores
-Ġcomput ation
-Ġassert ions
-orm ons
-ar ag
-ab it
-Dem ocrats
-ĠL oot
-ĠVol ks
-ha ired
-Ġgrav itational
-S ing
-ĠM iz
-Ġthro ttle
-Ġtyr anny
-ĠView s
-Ġrob ber
-ĠMinor ity
-Ġsh rine
-sc ope
-pur pose
-Ġnucle us
-our cing
-ĠUS DA
-ĠD HS
-w ra
-ĠBow ie
-Sc ale
-ĠB EL
-x i
-I ter
-Ġ( ),
-w right
-Ġsail ors
-ous ed
-NAS A
-ĠPro of
-ĠMin eral
-t oken
-ĠF D
-R ew
-Ġe ll
-6 30
-Ġchance llor
-ĠG os
-Ġamount ed
-ĠRec re
-ome z
-ĠOpt im
-ĠOl ive
-Ġtrack er
-ow ler
-ĠUn ique
-R oot
-Ġmar itime
-ĠQur an
-ĠAd apt
-Ġecosystem s
-ĠRe peat
-ĠS oy
-ĠI MP
-Ġgrad uating
-and em
-P ur
-ĠRes et
-ĠTr ick
-ĠPh illy
-ĠT ue
-ĠMalays ian
-Ġclim ax
-Ġb ury
-Ġcons pic
-ĠSouth ampton
-ĠFl owers
-Ġesc orted
-ĠEduc ational
-ĠI RC
-Ġbrut ally
-e ating
-Ġpill ar
-ĠS ang
-ĠJ ude
-ar ling
-ĠAm nesty
-Ġrem inding
-ĠAdminist rative
-hes da
-Ġfl ashed
-ĠP BS
-per ate
-fe ature
-Ġsw ipe
-Ġgra ves
-oult ry
-26 1
-bre aks
-ĠGu er
-Ġsh rimp
-ĠV oting
-qu ist
-Ġanaly tical
-Ġtables poons
-ĠS OU
-Ġresear ched
-Ġdisrupt ed
-Ġj our
-Ġrepl ica
-Ġcart oons
-b ians
-} )
-c opy
-G ot
-ou ched
-P UT
-Ġsw arm
-not ations
-s aid
-Ġreb uilt
-Ġcollabor ate
-Ġr aging
-Ġn ar
-Ġdem ographics
-ĠD DR
-Ġdist rust
-oss ier
-ĠK ro
-Ġpump kin
-Ġreg rets
-Ġfatal ities
-ĠL ens
-ĠO le
-p d
-Ġpupp et
-ĠOut look
-ĠSt am
-O l
-F air
-U U
-Ġre written
-Ä ±
-Ġfasc inated
-Ġve ctors
-Ġtrib unal
-u ay
-ĠM ats
-ĠCo ins
-[ [
-Ġ18 1
-Ġrend ers
-ĠK aepernick
-Ġesp ionage
-Ġsum m
-Ġd itch
-Acc ount
-Ġspread sheet
-Ġmut ant
-p ast
-40 7
-Ġd ye
-Ġinit iation
-Ġ4 000
-Ġpunish able
-Ġth inner
-ĠKh al
-Ġinter medi
-D un
-ĠGoth am
-Ġeager ly
-Ġvag inal
-p owers
-V W
-ĠWATCH ED
-Ġpred ator
-ams ung
-Ġdispar ity
-Ġ[ *
-Ġam ph
-Ġout skirts
-ĠSpir its
-Ġskelet al
-Ð »
-ĠR ear
-Ġissu ance
-ĠLog ic
-re leased
-Z Z
-ĠB ound
-Ent ry
-Ġex its
-is ol
-ĠFound er
-Ġw re
-ĠGreen land
-ĠM MO
-t aker
-IN C
-ãģ ¾
-Ġhour ly
-hen ko
-Ġfantas ies
-Ġdis ob
-Ġdemol ition
-ãĥ ĭ
-Ġen listed
-rat ulations
-Ġmis guided
-Ġens ured
-Ġdiscour aged
-m ort
-Ġfl ank
-Ġc ess
-Ġreact s
-ĠS ere
-s ensitive
-ĠSer pent
-ass ad
-Ġ24 7
-Ġcalm ly
-b usters
-Ġble ed
-ĠSt ro
-Ġamuse ment
-ĠAntar ctica
-Ġs cept
-ĠG aw
-a q
-ason ic
-Ġsp rawling
-n ative
-atur ated
-ĠBattle field
-IV ERS
-E B
-ĠG ems
-ĠNorth western
-ĠFil ms
-ĠAut omatic
-Ġappre hend
-ãģ ¨
-Ġgui Name
-Ġback end
-Ġevid enced
-ge ant
-01 2
-ĠS iege
-Ġexternal To
-Ġunfocused Range
-ĠguiActiveUn focused
-Ġgui Icon
-ĠexternalTo EVA
-ĠexternalToEVA Only
-F ri
-ch ard
-en aries
-Ġchief s
-Ġc f
-ĠH UD
-Ġcorro bor
-Ġd B
-ĠT aken
-ĠPat ricia
-ra il
-ĠCh arm
-ĠLiber tarian
-rie ve
-Person al
-ĠO UR
-ger ies
-Ġdump ing
-Ġneurolog ical
-it imate
-ĠClint ons
-raft ed
-ĠM olly
-Ġtermin als
-reg ister
-Ġfl are
-Ġenc oded
-Ġautop sy
-p el
-m achine
-Ġexempt ions
-ĠRoy als
-d istance
-Ġdraft s
-Ġl ame
-ĠC unning
-Ġsp ouses
-ĠMark ets
-ĠCar rier
-Ġimp lying
-ĠY ak
-s id
-Ġl oser
-Ġvigil ant
-Ġimpe achment
-Ġaug mented
-ĠEmploy ees
-Ġunint ended
-tern ally
-ĠW att
-Ġrecogn izable
-ess im
-æ Ŀ
-Ġco ated
-r ha
-Ġlie utenant
-ĠLegisl ation
-pub lished
-44 4
-01 3
-Ġide ally
-ĠPass word
-Ġsimpl ify
-ĠMet a
-ĠM RI
-Ġple ading
-organ ized
-hand ler
-Ġun ravel
-cor rect
-Ġ icy
-Ġparan oid
-Ġpass er
-Ġinspect ions
-of er
-ĠHealth care
-28 3
-ĠBr ut
-iol a
-for ge
-ĠMed ieval
-MS N
-ie vers
-ĠProgram ming
-å ī
-Ġ2 23
-m u
-ĠC LE
-ug a
-Ġsho ppers
-Ġinform ative
-ĠPl ans
-Ġsupplement ation
-ĠT ests
-ty ard
-ocy tes
-ĠVeg a
-ĠGujar at
-erman ent
-Ex cept
-ĠL OT
-all a
-ĠC umm
-ĠO sw
-Ġven om
-ĠDeb t
-ĠD OWN
-Ġreun ion
-Ġm uc
-ĠRel ief
-Ġge op
-ĠðŁ ĺ
-al ogue
-An th
-ech o
-Ġcor ros
-Ġrepl ication
-ĠBl azing
-ĠD aughter
-Ġinf lic
-ĠLind sey
-Ù Ī
-28 4
-Ex it
-Ġgl oom
-TA IN
-Ġundermin ing
-Ġadv ising
-h idden
-Ġover flow
-Ġg or
-urd ue
-Ġe choes
-enh agen
-Ġimp uls
-d rug
-c ash
-Ġas ync
-Ġmir ac
-at ts
-p unk
-Ġpiv ot
-ĠLegisl ative
-Ġblog gers
-ĠCl aw
-s burg
-d yl
-ĠRecomm end
-Ġver te
-Ġprohib iting
-ĠPant her
-Jon athan
-Ġo min
-Ġhate ful
-28 1
-ĠOr che
-ĠMurd och
-down s
-Ġas ymm
-G ER
-Al ways
-Ġinform s
-ĠW M
-ĠP ony
-ĠApp endix
-ĠAr lington
-J am
-Ġmedic inal
-ĠS lam
-IT IES
-Ġre aff
-ĠR i
-F G
-S pring
-b ool
-Ġthigh s
-Ġmark ings
-ĠRa qqa
-ĠL ak
-p oll
-ts ky
-ĠMort y
-ĠDef inition
-Ġdeb unk
-end ered
-ĠLe one
-a vers
-Ġmortg ages
-App arently
-N ic
-ha us
-ĠTh ousands
-au ld
-Ġm ash
-sh oot
-Ġdi arr
-Ġconscious ly
-H ero
-e as
-ĠN aturally
-ĠDestroy er
-Ġdash board
-serv ices
-R og
-Ġmillenn ials
-Ġinv ade
-- (
-Ġcomm issions
-ĠA uckland
-Ġbroadcast s
-Ġfront al
-Ġcr ank
-ĠHist oric
-Ġrum ours
-CT V
-Ġster il
-Ġboost er
-rock et
-ãĤ ¼
-ut sche
-ĠP I
-Ġ2 33
-ĠProdu cer
-ĠAnaly tics
-Ġinval uable
-Ġunint ention
-ĠC Y
-Ġscrut in
-Ġg igg
-Ġeng ulf
-Ġprolet ariat
-Ġh acks
-ĠH ew
-ar ak
-ĠSl ime
-ield ing
-ag her
-ĠEll iot
-Ġtele com
-Ġ2 19
-ult an
-ĠAr bor
-ĠSc outs
-B an
-Ġlifes pan
-Ġbl asp
-38 8
-Ġjud iciary
-ĠContin ental
-ask ing
-Mc C
-L ED
-Ġbag gage
-ĠSorce rer
-Ġrem nants
-ĠGriff ith
-ets u
-ĠSub aru
-ĠPerson ality
-des igned
-ush ima
-agn ar
-Ġrec oil
-Ġpass ions
-\ ":
-Ġte e
-Ġabol ition
-ĠCreat ing
-j ac
-Ġ19 4
-01 9
-Ġpill ars
-ric hed
-/ "
-t k
-Ġlive lihood
-Ġro asted
-ah on
-ĠH utch
-ass ert
-Ġdivid end
-Ġkn it
-Ġd aunting
-Ġdisturb ance
-Ġsh ale
-Ġcultiv ated
-Ġrefriger ator
-L B
-ĠN ET
-Ġcommercial s
-Ġthink ers
-45 5
-Ġch op
-B road
-Ġsuspic ions
-Ġtag ged
-l ifting
-Ġsty lish
-ĠShield s
-Short ly
-Ġt ails
-A uth
-ST E
-ĠG AME
-Ġse ism
-ĠK is
-olog ne
-Ġcow ork
-Ġforc ibly
-Ġthy roid
-ĠP B
-AN E
-mar ried
-h orse
-Ġpoly mer
-ĠCh al
-od or
-DE BUG
-ĠCon text
-Ġbl iss
-Ġpin point
-ĠMat hemat
-leg ram
-ĠWeek end
-Ġlab elled
-Ġb art
-it les
-Ġest rogen
-âĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶ âĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶ
-" '
-Ġvis ibly
-Ġouts ider
-aid a
-Are a
-Ġdisse min
-Ġdish onest
-ĠCl osed
-ĠBullet in
-ĠRam sey
-sw ord
-ĠX I
-our ced
-S ame
-34 6
-ĠRe pe
-ĠK ou
-c ake
-em is
-C ache
-ĠMe aning
-ĠEn light
-onom y
-Ġmanifest ation
-sw orth
-J ay
-Ġch ore
-ö r
-D ream
-Ġsanction ed
-Ġcult urally
-ĠA ra
-N av
-Ġthe ological
-Ġstr ut
-ĠV O
-ĠHand book
-Ġconstruct ing
-ĠÂ ¶
-ĠBenef its
-ĠPsych ological
-s ac
-å ¸
-p olicy
-ĠMat ters
-ĠReport ed
-ĠBy te
-Ġvit ro
-ĠM aiden
-Ġl am
-ĠJenn ings
-Ġgar ment
-ĠRut gers
-ĠStaff ord
-ĠWell ington
-Ġinter mitt
-Ġn pm
-Ġord eal
-Ġplug ged
-o oming
-in ished
-fram ework
-Ġtim ber
-Ġc ass
-Ġ8 50
-il ess
-ĠRed ux
-7 68
-St re
-Ġsurpass ed
-w hel
-Ġparalle ls
-Ġve il
-ĠG I
-ĠR EST
-Ġread iness
-s ort
-Ġmod ifying
-ĠSl ate
-ru ff
-Ġmar ble
-Ġinf rared
-Ġaud itor
-ĠFANT ASY
-ĠP overty
-ĠS PD
-Ġ" (
-K y
-RA Y
-Ġexecut ions
-ĠBever ly
-ĠMarx ism
-ĠBur st
-ĠK ali
-est ones
-Clear ly
-E ll
-ãģ §
-ĠProceed ings
-T oken
-IF IC
-ñ a
-Cent ral
-ĠH aley
-ĠD rama
-Ġform ations
-OR N
-Book s
-Ġdom inating
-ĠFly ers
-ĠCompan ion
-Ġdiscipl ined
-ĠYug oslav
-ĠSpell s
-Ġv engeance
-Ġland lords
-L en
-ĠO gre
-ano ia
-Ġpier cing
-Ġcon greg
-Ġscore r
-ob ia
-Ġnic kel
-ĠLear ns
-Ġre jo
-Ġmaster piece
-Fl ash
-Ġinhab ited
-ĠOpen GL
-ĠD ud
-ĠI CO
-Ġar ter
-Ġpl ur
-Ġmaster y
-Ġlong standing
-st ed
-Ġw ines
-Ġtelev ised
-ĠSh rine
-ĠBay ern
-Ġâ ĵĺ
-Ġencl osure
-j ohn
-Ġprophe ts
-ĠRes urrection
-ĠOrd ers
-Ġun even
-r als
-Ġd wind
-ĠL ah
-ĠSl oven
-37 8
-Ġins istence
-aff le
-ĠCl one
-Ġhard ship
-ĠCongress man
-Ġple ad
-Ġreview ers
-Ġc ured
-Ġ19 35
-as ley
-f ake
-ĠTh inking
-yd ia
-P ART
-ĠD ota
-o it
-Ġwh ipped
-Ġb ouncing
-ĠHispan ics
-com ings
-Ġcann abin
-ĠCh ambers
-ĠZ ack
-Option al
-Ġco ats
-Ġprow ess
-ĠNort on
-Ġplain ly
-Ġfre ight
-Ġinhib ition
-Ġcl am
-Ġ30 3
-ke f
-ale igh
-L uke
-Ġpsych o
-ator ium
-M ED
-Ġtreat ies
-Ġind isc
-Ġd c
-OP S
-Ġresil ient
-ĠInter state
-Ġsl ack
-Ġmund ane
-Ġestab lishes
-35 9
-Ġstr ained
-Ġn ond
-S us
-Ġcast e
-ar ate
-ie ving
-Ġunfair ly
-Ġpars er
-on ial
-urs ive
-V ia
-ĠOtt o
-ĠAuthor ities
-stro ke
-K R
-ĠMer cy
-Ġfurn ished
-Ġout set
-Ġmet ic
-19 82
-olith ic
-ĠT ent
-og ical
-ĠA ircraft
-Ġh ides
-ĠBec ame
-Ġeduc ators
-re aching
-Ġvol atility
-Ġtodd ler
-ĠNAS CAR
-ĠTw elve
-ĠHigh lights
-Ġgra pe
-Ġspl its
-Ġpe asant
-Ġre neg
-ĠMS I
-Tem p
-st ars
-Ġtre k
-ĠHy de
-b inding
-Ġreal ism
-Ġox ide
-ĠH os
-Ġmount s
-Ġbit ing
-Ġcollaps ing
-Ġpost al
-Ġmuse ums
-Ġdet ached
-Ġrespect ing
-Ġmonop ol
-Ġwork flow
-ĠC ake
-Tem plate
-ĠOrgan isation
-Ġpers istence
-36 9
-C oming
-B rad
-Ġredund ant
-ĠG TA
-Ġb ending
-Ġrev oked
-Ġoff ending
-Ġfram ing
-Ġprint f
-Comm un
-mem bers
-Out side
-Ġconst rued
-Ġc oded
-F ORE
-Ġch ast
-Ch at
-Ind ian
-ĠY ard
-? !"
-ĠP orts
-ĠX avier
-ĠR ET
-' ."
-ĠBo at
-iv ated
-ich t
-umer able
-D s
-ĠDun n
-Ġcoff in
-Ġsecure ly
-ĠRapt ors
-ĠB es
-Install ation
-Ġin ception
-ĠHealth y
-end ants
-Ġpsych ologists
-ĠShe ikh
-c ultural
-ĠBlack Berry
-sh ift
-F red
-oc he
-Ġc akes
-ĠS EO
-ĠG ian
-ĠAs ians
-og ging
-e lement
-Ġpund its
-ĠV augh
-ĠG avin
-Ġh itter
-Ġdrown ed
-Ġch alk
-ĠZ ika
-Ġmeas les
-80 2
-âĢ¦ ..
-ĠAW S
-] "
-Ġdist ort
-ĠM ast
-Ġantib odies
-ĠM ash
-Mem ory
-ĠUg anda
-ĠPro b
-Ġvom iting
-ĠTurn s
-Ġoccup ying
-Ġev asion
-ĠTher apy
-Ġprom o
-Ġelect r
-Ġblue print
-ĠD re
-pr iced
-ĠDep ot
-Ġallev iate
-ĠSom ali
-m arg
-n ine
-Ġnostalg ia
-ĠShe pherd
-Ġcaval ry
-Ġtor ped
-ĠBlood y
-x b
-Ġs ank
-Ġgo alt
-report print
-embed reportprint
-clone embedreportprint
-ĠIn itially
-ĠF ischer
-Ġnot eworthy
-c ern
-Ġin efficient
-raw download
-rawdownload cloneembedreportprint
-c ation
-ĠD ynasty
-l ag
-D ES
-Ġdistinct ly
-ĠEston ia
-Ġopen ness
-Ġg ossip
-ru ck
-W idth
-ĠIb rahim
-Ġpet roleum
-Ġav atar
-ĠH ed
-ath a
-ĠHog warts
-Ġc aves
-67 8
-Ġsafegu ard
-ĠM og
-iss on
-ĠDur ham
-sl aught
-ĠGrad uate
-Ġsub conscious
-ĠEx cellent
-ĠD um
----- -
-Ġp iles
-ĠW ORK
-ĠG arn
-ĠF ol
-ĠAT M
-Ġavoid s
-ĠT ul
-Ġble ak
-EL Y
-iv ist
-light ly
-P ers
-ĠD ob
-ĠL S
-Ġins anity
-Î µ
-atal ie
-En large
-Ġtw ists
-Ġfault y
-Ġpir acy
-Ġimp over
-Ġrug ged
-ĠF ashion
-Ġs ands
-' ?
-sw ick
-Ġn atives
-Ġhe n
-ĠNo ise
-ãĥ Ĺ
-Ġg reens
-Ġfree zer
-Ġd ynasty
-ĠFather s
-ĠNew ark
-Ġarchae ological
-Ġo t
-ob ar
-Ġblock ade
-Ġall erg
-L V
-Ġdeb it
-ĠR FC
-ĠMil ton
-ĠPress ure
-Ġwill ingly
-Ġdisproportion ate
-Ġopp ressive
-Ġdiamond s
-Ġbelong ings
-19 70
-Ġbell s
-Ġimperial ism
-Ġ2 27
-Ġexpl oding
-ĠE clipse
-Ġ19 19
-Ġr ant
-Ġnom inations
-34 7
-Ġpeace fully
-ric a
-ĠF UCK
-Ġvib ration
-mal ink
-Ġro pes
-ĠIv anka
-ĠBrew ery
-ĠBook er
-ĠOw ens
-go ers
-Serv ices
-ĠSn ape
-Ġ19 1
-39 5
-Ġ2 99
-just ice
-Ġb ri
-Ġdisc s
-Ġprom inently
-Ġvul gar
-Ġsk ipping
-l ves
-Ġtsun ami
-37 4
-ĠU rug
-ĠE id
-rec ated
-p hen
-Ġfault s
-ĠStart ed
-9 50
-Ġp i
-Ġdetect or
-Ġbast ard
-Ġvalid ated
-Space Engineers
-OUR CE
-Ġ( ~
-Ġuns ur
-Ġaff irmed
-Ġfasc ism
-Ġres olving
-ĠCh avez
-ĠC yn
-Ġdet ract
-L ost
-Ġrig ged
-Ġhom age
-ĠBrun o
-55 5
-ec a
-Ġpress es
-Ġhum our
-Ġsp acing
-Ġ' /
-olk ien
-C oun
-OP ER
-T re
-S on
-ĠCambod ia
-ier re
-m ong
-o zy
-Ġliquid ity
-ĠSov iets
-ĠFernand o
-Ġ2 29
-Ġsl ug
-ĠCatal an
-elect ric
-Ġsc enery
-ĠH earth
-Ġconst rained
-Ġgoal ie
-ĠGu idelines
-ĠAm mo
-ĠPear son
-Ġtax ed
-Ġfet us
-Resp onse
-ĠAlex is
-th ia
-G uy
-Ġrecon struct
-Ġextrem es
-Ġconclud ing
-ĠP eg
-ook s
-Ġded uctions
-R ose
-Ġground breaking
-ĠT arg
-ãĥ ģ
-ĠRe ve
-res ource
-Ġmo ons
-Ġelectrom agnetic
-Ġamid st
-ĠVik tor
-N ESS
-B ACK
-Ġcomm ute
-ĠAna heim
-Ġfluct uations
-6 40
-Ġnood les
-ĠCop enhagen
-ĠT ide
-ĠGri zz
-ĠS EE
-Ġpip elines
-Ġsc ars
-end o
-ag us
-ĠE TF
-/ #
-ĠBec ome
-44 8
-Ġvis c
-ĠRecomm ended
-Ġj umper
-Ġcogn ition
-Ġassass in
-Ġwitness ing
-ĠSet up
-Ġl ac
-v im
-IS M
-p ages
-SS L
-35 8
-Ġad ject
-indust rial
-l ore
-cher y
-Ġgl itter
-Ġc alf
-Flor ida
-Ġspoil ers
-Ġsucceed s
-Ġch anting
-Ġslog ans
-ĠTr acy
-Vis it
-rol ogy
-Ġm ornings
-Ġline age
-Ġs ip
-Ġintense ly
-Ġflour ish
-ĠSle eping
-ĠF em
-or por
-ĠK lan
-ĠDar th
-h ack
-ĠNi elsen
-Ġtum ors
-Ġprocure ment
-ĠY orkshire
-Ġra ided
-K Y
-An na
-Ġ// [
-ĠDis order
-ĠMust ang
-ĠW en
-ĠTry ing
-s q
-Ġdeliver ies
-Ġshut ter
-Ġcere bral
-Ġbip olar
-ĠC N
-l ass
-j et
-Ġdeb ating
-> :
-Ġe agle
-gr ades
-ĠD ixon
-UG C
-M AS
-ĠDr aco
-ĠMach ines
-aff er
-Ġem an
-Â ²
-pr on
-ĠG ym
-Ġcompar atively
-ĠTrib unal
-PR O
-Ġle x
-Ġfert ile
-Ġdep ressing
-Ġsuperf icial
-ess ential
-ĠHun ters
-g p
-Ġprom inence
-L iber
-ĠAn cest
-ote chnology
-Ġm ocking
-ĠTra ff
-ĸ ļ
-Med ium
-I raq
-Ġpsychiat rist
-Quant ity
-ĠL ect
-Ġno isy
-5 20
-G Y
-Ġsl apped
-ĠM TV
-Ġpar a
-p ull
-Mult iple
-as her
-Ġn our
-ĠSe g
-Spe ll
-v ous
-ord ial
-Sen ior
-ĠGold berg
-ĠPl asma
-ne ed
-Ġmess enger
-ere t
-Ġteam ed
-Ġliter acy
-ĠLe ah
-ĠD oyle
-Ġem itted
-U X
-Ġev ade
-Ġm aze
-Ġwrong ly
-ĠL ars
-Ġstere otype
-Ġpled ges
-Ġarom a
-ĠM ET
-Ġac re
-ĠO D
-Ġf f
-Ġbrew eries
-ĠH ilton
-und le
-ĠK ak
-ĠThank fully
-ĠCan ucks
-in ctions
-ĠApp ears
-Ġco er
-Ġundermin ed
-ro vers
-And re
-Ġbl aze
-um ers
-Ġfam ine
-amp hetamine
-ulk an
-Am ount
-Ġdesper ation
-wik ipedia
-develop ment
-ĠCor inth
-uss ia
-Jack son
-L I
-N ative
-R s
-Oh io
-ĠKath leen
-F ortunately
-Ġattend ant
-ĠPre ferred
-ĠDid n
-ĠV s
-M is
-Ġrespond ent
-Ġb oun
-st able
-Ġp aved
-Ġunex pl
-ĠChe ney
-L M
-ĠC ull
-bl own
-Ġconfront ing
-oc ese
-serv ing
-W i
-ĠLith uania
-ann i
-Ġst alk
-h d
-Ġv ener
-AP H
-ynchron ous
-UR R
-um ably
-hist oric
-H alf
-H ay
-Ġresil ience
-spe ction
-Ġabandon ing
-O bs
-ĠDeb bie
-Ġgrad ient
-ĠPl aint
-ĠCan al
-AR CH
-Ġexpans ive
-Ġfun g
-Ġb ounced
-U nd
-Ġprec autions
-Ġclar ification
-Ġd agger
-Ġgri ps
-ĠÂ µ
-ĠRiver a
-ĠUnd ead
-is ites
-ĠFIR ST
-ñ o
-aud i
-Ġhost ages
-Ġcompl iant
-Ġal umni
-Se ven
-Ġcyber security
-e ither
-Col lect
-Ġinvari ably
-ĠS oci
-Ġlaw maker
-Ġa le
-ĠPerson ally
-N azi
-Ġcustom ization
-ĠPro c
-ĠSask atchewan
-eat uring
-Ġsp ared
-Ġdiscontin ued
-Ġcomput ational
-ĠMotor ola
-Ġsuprem acist
-government al
-Ġparad ise
-ĠDown ing
-ĠNik on
-Ġcat alyst
-ber ra
-Tor onto
-8 75
-bet a
-ĠMac ron
-Ġunreal istic
-ve ctor
-ĠVeh icles
-it iveness
-ĠR V
-ĠCol bert
-s in
-o ji
-ent in
-ĠKr ish
-hell o
-ff ield
-ok y
-ĠT ate
-Ġmap le
-Ġa ids
-chem ical
-33 4
-n uts
-ĠWar p
-Ġx x
-ĠRob b
-umer ous
-_- _
-ft ime
-ĠV W
-Ġw inger
-ĠD ome
-t ools
-ĠP V
-ĠGe orgetown
-Ġg eared
-Ġjihad ists
-Ġc p
-Ġster oids
-M other
-cler osis
-ĠDR M
-nes ia
-Ġl inger
-Ġimm ersive
-ĠC OUN
-Ġoutwe igh
-ens ual
-B and
-Ġtransform s
-mat ched
-ps ons
-ĠJud icial
-f actor
-Ġrefer ral
-Ġodd ly
-ĠW enger
-B ring
-ĠB ows
-60 2
-IC LE
-Ġl ions
-ĠAcad emic
-ĠTh orn
-ĠRa ider
-kef eller
-St orage
-L ower
-ĠOr t
-ĠEqu ality
-AL T
-ĠS OC
-T ypes
-Ġl yn
-ĠAss et
-co at
-TP P
-C VE
-ĠPione er
-app lication
-Mod ern
-ĠH K
-En vironment
-Al right
-R ain
-IP P
-ĠShi ite
-Ġm ound
-ĠAb ilities
-cond ition
-St aff
-Ġcompet ence
-ĠM oor
-ĠDi ablo
-Ġwith held
-Ġost ensibly
-ĠB rom
-Ġms g
-Ġden omin
-ĠRef erences
-ĠF P
-Ġplun ged
-Ġp amph
-m oving
-cent ral
-Ġdown right
-Ġf ading
-T al
-T yp
-ĠTh y
-uk es
-it he
-Ġo ve
-Ġbatt led
-Ġseaf ood
-Ġfig ur
-ĠR D
-c rop
-Ġsqu ads
-{ \
-à ¹
-ĠE h
-Ġinterview ing
-ĠQ in
-Ġas piring
-PL IC
-Ġcla uses
-ĠG ast
-ĠN ir
-Ġl uggage
-Ġh ose
-Ġsystem d
-Ġdesc ending
-ĠRev ised
-ĠR ails
-al ign
-70 9
-33 7
-Ġf ug
-charg ing
-t ags
-Ġut er
-k ish
-WAR NING
-49 0
-prof its
-Ġvoy age
-Ġa ce
-ĠV anguard
-ĠT anks
-ĠM uk
-Ġ2 26
-S afe
-Ar mor
-Ġvolcan ic
-Ġwom b
-ĠM IL
-Ġbegin ner
-ĠRec ogn
-ĠA AP
-PL AY
-) !
-Ġdetect ing
-c n
-Ġbre aches
-Bas ically
-ĠP ag
-ĠMunicip al
-ĠInd ie
-ĠL af
-ĠDis able
-ĠOl son
-Ġrest rained
-Ġrul ings
-Ġhum ane
-ev ents
-ĠCinem a
-display Text
-ĠH atch
-action Date
-onna issance
-Ġassault ing
-ĠL ug
-CH AT
-Ġvig orous
-ĠPer se
-Ġintoler ance
-ĠSnap chat
-ĠSh arks
-Ġd ummy
-ĠDi agn
-ĠGu itar
-im eters
-40 3
-RE G
-A x
-Ġsepar ates
-ĠMah m
-Ġt v
-j ah
-O OL
-C irc
-ĠWinds or
-uss ian
-Ġintu ition
-Ġdis dain
-ĠDon ovan
-Ġ2 21
-E mb
-Ġcondem ning
-Ġgener osity
-zz y
-Ġpant ies
-ĠPre vent
-Action Code
-AN A
-34 2
-external ActionCode
-Ġspec ifying
-Ġcryst all
-J ere
-Ġru pt
-ĠApp rentice
-Ġprof iling
-Ð º
-St rike
-Ġsid eline
-Ġoblig ated
-Ġocc ult
-Ġbureaucr atic
-ant ically
-rupt ed
-neg ative
-ĠEthiop ia
-ĠC ivic
-Ġins iders
-el igible
-ĠTV s
-ĠB AR
-ĠT I
-i ologist
-ĠA IR
-Ġsubstit uted
-Ar ab
-ĠS aul
-ĠY og
-p rem
-Ġbuild ers
-Ġstation ary
-Ġdoubt ful
-Ġvig orously
-Ġthr illing
-Ph ysical
-ĠCare y
-ĠHyd ra
-geon ing
-ĠS ly
-y ton
-Ġborrow ers
-ĠPark inson
-Ġ ë
-ĠJama ica
-Ġsat ir
-Ġinsurg ents
-ĠF irm
-Ġis ot
-ĠK arn
-our ning
-ak ens
-doc s
-l ittle
-ĠMon aco
-CL ASS
-Tur key
-L y
-ĠCon an
-ass ic
-Ġstar red
-ĠPac ers
-et ies
-Ġt ipping
-M oon
-ĠR w
-s ame
-Ġcav ity
-Ġgo of
-ĠZ o
-Sh ock
-um mer
-Ġemphas izes
-Ġreg rett
-Ġnovel ty
-Ġen vy
-ĠPass ive
-r w
-50 5
-Ġind ifferent
-ĠR ica
-ĠHim self
-ĠFred die
-Ġad ip
-ä¸ Ģ
-Ġbreak out
-Ġhur ried
-ĠHu ang
-ĠD isk
-Ġro aming
-?????- ?????-
-U V
-ĠRick y
-ĠS igma
-Ġmarginal ized
-Ġed its
-Ġ30 4
-mem ory
-Ġspec imen
-29 3
-ãģ ¯
-Ġvert ically
-Ġaud ition
-ĠHe ck
-Ġc aster
-ĠHold ings
-ad al
-ĠC ron
-ĠL iam
-Ġdef lect
-P ick
-ĠDeb ug
-RE F
-Ġvers atility
-ot hes
-class ified
-ĠMah ar
-ĠH ort
-C ounter
-st asy
-not iced
-33 1
-ĠSh im
-f uck
-ĠB ie
-Ġair ing
-ĠPro tein
-ĠHold ing
-Ġspect ators
-ili ated
-ĠThat cher
-n osis
-ãĥ¼ ãĥ³
-Te le
-B oston
-ĠTem pl
-st ay
-Ġdecl arations
-47 9
-Vol ume
-ĠDesign er
-ĠOver watch
-id ae
-Ġon wards
-Ġn ets
-ĠMan ila
-part icularly
-Ġpolit ic
-o other
-Ġport raits
-Ġpave ment
-c ffff
-Ġs aints
-Ġbegin ners
-ES PN
-Ġshort comings
-âķIJ âķIJ
-Ġcom et
-ĠOrgan ic
-qu el
-Ġhospital ized
-Bre ak
-Ġpe el
-dyl ib
-asp x
-ur ances
-ĠT IM
-P g
-Ġread able
-ĠMal ik
-Ġm uzzle
-Ġbench marks
-d al
-ĠV acc
-ĠH icks
-60 9
-ĠB iblical
-he ng
-Ġover load
-ĠCivil ization
-Ġimm oral
-Ġf ries
-ãĤ Ĵ
-Ġreprodu ced
-Ġform ulation
-j ug
-ire z
-g ear
-Ġco ached
-Mp Server
-ĠS J
-ĠK w
-In it
-d eal
-ĠO ro
-ĠL oki
-ĠSong s
-Ġ23 2
-ĠLou ise
-asion ally
-Ġunc ond
-olly wood
-Ġprogress ives
-ĠEn ough
-ĠDo e
-Ġwreck age
-Ġbr ushed
-ĠBase Type
-Ġz oning
-ish able
-het ically
-ĠC aucus
-ĠH ue
-Ġk arma
-ĠSport ing
-Ġtrad er
-Ġseem ing
-ĠCapt ure
-4 30
-b ish
-Ġt unes
-Ġindo ors
-ĠSp here
-ĠD ancing
-TER N
-Ġno b
-ĠG ST
-m aps
-Ġpe ppers
-F it
-Ġoverse es
-ĠRabb i
-ĠR uler
-vert ising
-off ice
-xx x
-Ġra ft
-Ch anged
-Ġtext books
-L inks
-ĠO mn
-ãĢ ij
-Ġinconven ience
-ĠDon etsk
-= ~
-Ġimplicit ly
-Ġboost s
-ĠB ones
-ĠBo om
-Cour tesy
-Ġsens ational
-AN Y
-Ġgre edy
-ed en
-Ġinex per
-ĠL er
-ĠV ale
-Ġtight en
-ĠE AR
-ĠN um
-Ġancest or
-S ent
-ĠH orde
-urg ical
-all ah
-Ġsa p
-amb a
-ĠSp read
-tw itch
-Ġgrand son
-Ġfract ure
-Ġmoder ator
-ĠSe venth
-ĠRe verse
-Ġestim ation
-Cho ose
-Ġpar ach
-Ġbar ric
-ãĢ IJ
-Ġcomp ass
-Ġall ergic
-âĢ ķ
-OT HER
-err illa
-Ġw agon
-Ġz inc
-Ġrub bed
-ĠFull er
-ĠLuxem bourg
-ĠHoo ver
-Ġli ar
-ĠEven ing
-ĠCob b
-est eem
-Ġselect or
-ĠB rawl
-is ance
-ĠE k
-Ġtro op
-Ġg uts
-ĠApp eal
-ĠTibet an
-Ġrout ines
-ĠM ent
-Ġsummar ized
-steam apps
-Ġtr anqu
-Ġ19 29
-or an
-ĠAut hent
-Ġg maxwell
-Ġappre hens
-Ġpo ems
-Ġsa usage
-ĠWeb ster
-ur us
-Ġthem ed
-Ġl ounge
-Ġcharg er
-Sp oiler
-Ġsp illed
-h og
-ĠSu nder
-ĠA in
-ĠAng ry
-Ġdis qual
-ĠFrequ ency
-ĠEther net
-Ġhel per
-Per cent
-Ġhorr ifying
-Ġa il
-ĠAll an
-EE E
-ĠCross ing
-44 9
-Ġh olog
-ĠPuzz les
-ĠGo es
-eren n
-60 4
-ãģ ı
-ĠRaf ael
-Ġatt en
-ĠE manuel
-Ġup ro
-ĠSus p
-P sych
-ĠTr ainer
-ĠN ES
-ĠHun ts
-bec ue
-Ġcounsel or
-R ule
-Ġtox ins
-Ġb anners
-r ifice
-Ġgreet ing
-Ġfren zy
-Ġall ocate
-Ġ* )
-ex pr
-50 3
-ĠCh ick
-ĠT orn
-Ġconsolid ation
-ĠF letcher
-sw itch
-fr ac
-cl ips
-ĠMcK in
-ĠLun ar
-Mon th
-IT CH
-Ġscholar ly
-rap ed
-39 8
-Ġ19 10
-Ġe greg
-Ġin secure
-Ġvict orious
-cffff cc
-Ġsing led
-Ġel ves
-ĠW ond
-bur st
-Ġcam oufl
-ĠBL ACK
-Ġcondition ed
-ç ī
-ans wered
-Ġcompuls ory
-asc ist
-Ġpodcast s
-ĠFrank furt
-bn b
-Ġne oliberal
-ĠKey board
-ĠBel le
-w arm
-Ġtrust s
-Ġins ured
-ĠBu cc
-us able
-60 7
-ĠPl ains
-Ġ18 90
-Ġsabot age
-Ġlod ged
-f elt
-Ġg a
-ĠN arc
-ĠSal em
-Ġsevent y
-ĠBl ank
-p ocket
-Ġwhis per
-Ġm ating
-om ics
-ĠSal man
-ĠK ad
-Ġan gered
-Ġcoll isions
-Ġextraord inarily
-Ġcoerc ion
-G host
-b irds
-è Ģ
-k ok
-Ġper missible
-avor able
-Ġpo inters
-Ġdiss ip
-ac i
-Ġtheat rical
-ĠCos mic
-Ġforget ting
-Ġfinal ized
-å¤ §
-y out
-l ibrary
-Ġbo oming
-ĠBel ieve
-ĠTe acher
-ĠL iv
-ĠGOOD MAN
-ĠDomin ican
-OR ED
-ĠPart ies
-Ġprecip itation
-ĠSl ot
-R oy
-ĠComb ined
-Ġinteg rating
-Ġch rome
-Ġintest inal
-ĠRe bell
-Ġmatch ups
-Ġblock buster
-ĠLore n
-ĠLe vy
-Ġpre aching
-ĠS ending
-ĠPur pose
-ra x
-f if
-Ġauthor itative
-ĠP ET
-ast ical
-Ġdish on
-Ġchat ting
-Ġ"$ :/
-Connect ion
-Ġrecre ate
-Ġdel inqu
-Ġbro th
-ĠD irty
-ĠAd min
-z man
-Ġscholars hips
-Ġ25 3
-cont act
-als a
-7 67
-c reen
-abb age
-Ġ19 15
-Ġbl ended
-Ġal armed
-L anguage
-35 6
-Ġbl ends
-ĠCh anged
-W olf
-Ġhe pat
-Creat ing
-Ġper secut
-Ġsweet ness
-art e
-Ġforfe iture
-ĠRober to
-im pro
-N FL
-ĠMag net
-Det ailed
-Ġinsign ificant
-ĠPOL IT
-ĠBB Q
-ĠC PS
-Ġse aw
-amin er
-m L
-end if
-f inals
-Ġ26 5
-u ish
-Ġ} )
-ĠPro blems
-Ġem blem
-Ġserious ness
-Ġpars ing
-Ġsubst itution
-Ġpress ured
-Ġrecy cled
-ale b
-Rub y
-Ġprof iciency
-Dri ver
-ĠW ester
-: '
-AF TA
-Ġm antle
-ĠClay ton
-fl ag
-Ġpractition er
-c overed
-ĠSt ruct
-add afi
-4 25
-ĠTown ship
-ĠHyd ro
-Lou is
-34 3
-Ġcond o
-ĠT ao
-Ġutil ization
-Ġnause a
-ĠDem s
-rid ges
-p ause
-Ġform ulas
-Ġchall enger
-37 6
-Ġdefect ive
-ĠRail way
-ĠPub Med
-Ġyog urt
-l bs
-ĠNor folk
-OP E
-ĠMood y
-Ġdistribut or
-Ġscroll s
-Ġextract s
-St an
-Ġv iability
-Ġexp oses
-Ġstar vation
-ĠStep s
-ĠD odd
-f ew
-ST D
-33 2
-Ġclos ures
-Ġcomplement ary
-ĠS asha
-ump y
-Ġmon et
-Ġartic ulate
-ĠDo ct
-k iller
-Ġsc rim
-Ġ2 64
-Ġprost itutes
-Ġse vered
-Ġattach ments
-Ġcool ed
-L ev
-ĠF alk
-f ail
-Ġpolic eman
-ĠD ag
-Ġpray ed
-ĠK ernel
-Ġcl ut
-Ġc ath
-Ġan omaly
-St orm
-em aker
-ĠBreak fast
-ul i
-o ire
-J J
-h z
-Oper ation
-ĠS ick
-35 4
-ĠGuatem ala
-R ate
-Ġexp osures
-f aces
-ĠArch ae
-ra f
-ĠM ia
-Ġ20 25
-Ġop aque
-Ġdisgu ised
-ĠHead quarters
-S ah
-Ġp ots
-9 78
-ĠM alf
-Ġfrown ed
-Ġpoison ous
-ĠCon vers
-ee ks
-Ġcr ab
-." "
-Ġtre ason
-Ġr anc
-Ġescal ating
-Ġwar r
-Ġmob s
-Ġl amps
-ĠSun shine
-ĠBrun swick
-Ph ones
-Ġspe lled
-ĠSk ip
-Ġ20 50
-Ġ19 11
-ĠPl uto
-ĠAm end
-Ġme ats
-38 7
-Ġst omp
-ĠZh ou
-ĠLevi athan
-ĠHaz ard
-ad v
-ĠOr well
-Ġal oud
-Ġb umper
-ĠAn arch
-ub untu
-ĠSer ious
-f itting
-ĠOption al
-ĠCec il
-RE AM
-Ġser otonin
-Ġcultiv ate
-ag ogue
-} \
-Ġmos ques
-ĠSun ny
-Ġre active
-rev olution
-ĠL up
-ĠFed ora
-Ġdefense man
-ĠV ID
-ist ine
-Ġdrown ing
-ĠBroad casting
-Ġthr iller
-ĠS cy
-Ġacceler ating
-Ġdirect s
-od ied
-b ike
-d uration
-Ġpain fully
-R edd
-Ġproduct ions
-Ġg ag
-Ġwh ist
-Ġs ock
-Ġinf initely
-ĠConc ern
-ĠCit adel
-Ġlie u
-Ġcand les
-ogene ous
-arg er
-Ġheaven ly
-inflamm atory
-Per formance
-C s
-ruct ose
-az aki
-Ġp essim
-Ġinf erence
-Ġpow d
-ĠZ oe
-Ġpain ts
-Ġd azz
-pt a
--------- ---
-Ġins pir
-ĠExper imental
-ĠKn ife
-reg or
-b ors
-Ġshow ers
-rom eda
-Ġs aint
-Ġben ign
-ĠJ iang
-Ġenvision ed
-Ġsh roud
-IF T
-H O
-Ġsh uff
-ĠI CC
-Ġse greg
-Ġrevis it
-ighth ouse
-L i
-Ġsub strate
-ĠSe as
-ĠRew ard
-ĠH ep
-ĠBr ass
-s bm
-Ġelim inates
-Ġst amina
-ĠV AT
-ĠLo an
-Ġconst raint
-Ġappropri ated
-Ġp es
-ĠA LE
-r anging
-Ġ40 4
-39 2
-Ġintellectual s
-ach u
-Ġrestruct uring
-ĠLe vin
-Ġrun es
-Ġdelight ful
-Ġcarbohyd rates
-ĠMod els
-ĠExp o
-Ġtransport ing
-all oc
-Ġring ing
-S amsung
-Ġscarce ly
-ĠURL s
-ĠM AS
-Ġprot otypes
-Ġnarr ator
-ĠCPU s
-cd n
-ĠBart on
-Ġdecided ly
-ĠSh u
-ix ir
-oc ious
-ĠMy st
-N intendo
-Ġre use
-Ġforg iven
-F ew
-in ical
-n at
-Ġseam less
-ĠEv a
-ĠE VE
-ĠJ O
-land ers
-Ġso fter
-neg ie
-Ġtrans ient
-Ġorb ital
-Ġfulf il
-ĠK om
-Hop efully
-Ġdynam ically
-ĠHun ger
-å Ľ
-ĠArmen ia
-el man
-ber to
-Ġp ige
-ĠID s
-lim it
-Ġve ins
-Ġso aring
-p acks
-Gold en
-ĠCr ab
-ist or
-ĠR PM
-Ġ$ $
-g ression
-Ġjihad ist
-Ġgam ble
-Ġcare g
-Ġinf lated
-F ace
-ĠFire arms
-ĠEm manuel
-â Ŀ
-Ġsh ocks
-gr ab
-Ġspl end
-ĠHP V
-ab ortion
-Ab ove
-Ent ity
-play ers
-Ġcomm enced
-ul ence
-Ġfulfill ment
-Ġembod iments
-ĠW elfare
-Ġha il
-Ġ< @
-tt en
-Ġcat cher
-ĠJ azeera
-Ġvolcan o
-Ġstabil ize
-ĠHand ler
-Ġintens ified
-ĠAb rams
-Ġhum iliation
-p aced
-60 5
-ĠCent OS
-Spe cific
-Ġhe ed
-ĠC AM
-ĠGal ile
-D ie
-Ġabol ished
-ĠThom son
-ĠTe achers
-ĠW ass
-j ong
-ĠIS BN
-ĠAll ies
-sh ake
-å ·
-v ict
-How ard
-Ġde em
-Ġexceed ingly
-ĠSmart stocks
-ib e
-Ġdoor way
-Ġcompet ed
-ig mat
-Ġnational ists
-Ġg room
-ĠKe en
-Ġdispos able
-de cl
-ĠT olkien
-ĠSche me
-Ġb iod
-Ġav id
-ĠEl on
-ag ar
-ĠT SA
-R oman
-Ġartific ially
-Ġadvis ors
-X L
-ĠInf erno
-36 6
-Ġted ious
-ĠPhot ography
-ĠCar rie
-Ġtro pe
-ĠSand ra
-Ġdec imal
-Que en
-ĠGund am
-ĠO M
-ote ch
-N BA
-Ġ19 32
-Ġent renched
-ĠMar ion
-Ġfr aternity
-Lab our
-Hen ry
-Ġlat itude
-E ither
-Ġenh ances
-ĠPot ential
-Ġsh ines
-id ad
-Ġbread th
-Ġcapac ities
-ĠðŁ ĻĤ
-ĠBron x
-Ġsex es
-Ġdifferent iation
-Ġheavy weight
-ĠT aj
-d ra
-Ġmigr ate
-Ġexhaust ion
-ĠR UN
-els ius
-ĠCu omo
-Ġgu itars
-Ġcl ones
-ĠSom ew
-ĠP ry
------------- -
-Ġwarr anted
-cy cles
-Ġsalv age
-Ġdis ks
-R ANT
-ĠNGO s
-ĠMart ian
-":[ {"
-Ġadd icts
-oj ure
-il let
-Ġamazing ly
-art ments
-p ixel
-ĠGPU s
-Lay out
-è £
-ĠTam il
-ĠBas il
-Ġimpart ial
-ĠSt ructure
-f ork
-b ryce
-Ġr idge
-ĠHamb urg
-ri ous
-Ġbl itz
-cig arettes
-Ġcan ned
-40 2
-Ġiron ically
-Ġcompassion ate
-ĠHaw kins
-. #
-ĠCat hedral
-Ġrall ied
-in ternal
-Ġqu ota
-st akes
-T EXT
-m om
-Ġcomple tes
-Ġ23 8
-Ġsh rug
-ãĥ ij
-ĠN inth
-Ġrev ise
-ĠProv ider
-Ġtre acher
-Ġqu asi
-ĠPR ES
-Ġdep osition
-Ġconfidential ity
-iss ors
-Ġim balance
-Ġspan ning
-Ġang ular
-ĠC ul
-commun ication
-ĠNor a
-ĠGen ius
-op ter
-Ġs acked
-Sp ot
-Ġfine ly
-ĠCH R
-28 2
-w aves
-Pal est
-ĠRo hing
-N L
-è ¿
-Ġsh itty
-ĠSc alia
-4 75
-Pro gress
-Ġreferen cing
-Ġclass rooms
-ab ee
-Ġs od
-hes ion
-70 8
-ĠZucker berg
-ĠFin ish
-ĠScot ia
-ĠSav ior
-ĠInstall ation
-an tha
-( -
-Ġ30 2
-ĠP unk
-Ġcr ater
-yout u
-Ġro ast
-Ġinflu encing
-Ġd up
-ĠJ R
-ĠG rav
-Ġstat ure
-Ġbath rooms
-A side
-W iki
-me an
-ĠZ ak
-ĠOn es
-ĠN ath
-Ġhyper t
-Ġcommence ment
-C ivil
-Ġmoder ately
-Ġdistribut ors
-Ġbreast feeding
-Ġ9 80
-ĠS ik
-ĠC ig
-ĠAM ER
-R IP
-ĠCare er
-ust ing
-Ġmess ed
-Ġe h
-ĠJ ensen
-/ $
-Ġblack mail
-Ġconvers ions
-Ġscientific ally
-Ġmant ra
-p aying
-Ġiv ory
-ĠCour ts
-OU GH
-aunt let
-Ser ial
-B row
-ĠH undreds
-3 23
-Ġpe e
-Ġlin ux
-Ġsub mer
-ĠPrinc ipal
-48 5
-ĠD SL
-ĠCous ins
-Ġdoctr ines
-ĠAthlet ics
-Ġ3 15
-ĠK arma
-Ġatt ent
-ur ger
-Ġpresc ribe
-Ġenc aps
-ĠC ame
-Ġsecret ive
-ĠCr imes
-d n
-C lean
-ĠEgypt ians
-ĠCar penter
-Ġ ll
-H um
-ĠMil o
-Ġcapital ists
-Ġbrief ed
-T we
-ĠBas in
-elve t
-M os
-Ġplun ge
-ĠKa iser
-ĠFu j
-ill in
-Ġsafegu ards
-Ġo ste
-ĠOpportun ity
-ĠM afia
-ĠCall ing
-ap a
-ur ban
-br ush
-ill ard
-c é
-int elligence
-ĠL ob
-ĠDru id
-Ġsm oother
-Ġfoot ing
-Ġmotor ists
-arc ity
-Ġmascul inity
-Ġm ism
-Ġabdom inal
-ĠTa vern
-ĠR oh
-Ġesc apes
-s igned
-Anth ony
-Ġsacrific ing
-Ġintim acy
-Ġan terior
-ĠK od
-Ġmot if
-Ġg raz
-Ġvisual ization
-Ġguitar ist
-ĠTro tsky
-m agic
-D ar
-ĠMor i
-Ġw ards
-Ġtoile ts
-l est
-Ġtele port
-ĠSund ays
-ĠPl at
-ET S
-Ġe Sports
-Pat rick
-ĠK atherine
-en ko
-Ġhas sle
-ĠM ick
-gg les
-Ġh ob
-aint ain
-Ġair borne
-Ġsp ans
-Ġch ili
-Ġa perture
-Ġvolunte ered
-ĠInc ident
-ĠF res
-ĠVeter an
-augh tered
-ing o
-Ġun insured
-CL OSE
-Ġf use
-Ġer otic
-Ġadvert ise
-ra ising
-Text ure
-Ġatt ends
-ĠRE AL
-udd led
-Ġsm oot
-Ġ30 5
-ĠWill is
-Ġbl ond
-An alysis
-ĠV T
-on ica
-Ġstrongh old
-R F
-N M
-. >>
-Ġprosper ous
-Ġbo asted
-29 2
-ĠManufact uring
-PR ESS
-g ren
-Ġpharm acy
-ĠRoc kefeller
-k ai
-Ġth umbs
-ĠH ut
-Ġmother board
-Ġguard ians
-ĠAl ter
-ll ular
-Ġsh ack
-Ġwise ly
-Ġback bone
-erv a
-Ġsu icides
-ĠMcG regor
-ij ah
-E mer
-ĠB rav
-Ġdesign ate
-P OST
-produ ced
-Ġcleans ing
-irl wind
-ex istent
-ĠHum ph
-ĠPay ne
-Ġv ested
-Å ¡
-Ġstring ent
-ion a
-Ġuns ub
-Ġsum med
-ĠHer cules
-sub ject
-ĠR agnar
-ĠN os
-Ġcharacter ization
-Ġsav vy
-ĠDaw son
-ĠCas ino
-Ġf ri
-ĠBar rier
-Ġmis information
-Ġins ulation
-Ġcorrid ors
-Ġair planes
-ĠNo ct
-ah i
-Ġ19 16
-k b
-arm ac
-Ġsh un
-Ġsche ma
-Ġhorr ified
-Ġ23 9
-aund ers
-N B
-i ates
-er ity
-ĠSh ard
-Ġr arity
-Ġgroup ed
-ĠGh ana
-again st
-ĠBi ological
-ĠA ware
-ow ell
-Ï Ħ
-ĠBe au
-sh aw
-H ack
-ĠJul ius
-US S
-ol son
-aun a
-c ru
-ĠMaur ice
-ĠI k
-Ġsequ encing
-Ġradical s
-Ġ( ?,
-v irtual
-Ġany ways
-Ġreper c
-Ġhand lers
-Ġhes itant
-é ĥ
-ĠM F
-ple mentation
-ass ociated
-Ġcampaign ed
-ĠY ue
-ut ations
-ĠY oga
-Ġsim mer
-Ġro ds
-Ġmel ody
-Ġconv oy
-v ideos
-Ġscreen ed
-N eg
-ochem ical
-Ġ( ))
-Ġultr as
-Ġant ip
-ĠIsland ers
-70 4
-Ġfet ish
-Ġridic ulously
-ĠK art
-Ġmitochond rial
-Ġinterf ering
-Build er
-Ġover fl
-Ġac ne
-ĠM ud
-ĠK err
-f lex
-ĠPost al
-ĠBalt ic
-47 7
-ĠPers ons
-our age
-H B
-ĠM use
-ĠImm ortal
-ĠDri ving
-Ġpet itions
-Ġsubsc ript
-Ġs orce
-ĠProcess or
-ut on
-S ony
-Ġph on
-Ġr aced
-ĠAnth rop
-Ġday time
-ĠEx ercise
-Add ing
-Ġeng ages
-ĠQual comm
-Ġmir acles
-Ġmem es
-ĠDr ink
-ĠOri oles
-Ġhair s
-ĠPol ar
-ath om
-Ġsl ippery
-ĠR emy
-Ġcar amel
-ĠY EAR
-Ġal k
-I gn
-a ution
-ĠMer lin
-ĠC ran
-Ġap ologies
-Ġ4 10
-Ġout ing
-ĠMem ories
-app ointed
-Ġcount ered
-u ld
-pos ing
-Ġfire wall
-ĠW ast
-ĠW et
-work ed
-se ller
-Ġrepe aled
-ere o
-ass uming
-BL IC
-m ite
-ĠCEO s
-ĠChap el
-ellig ent
-________________ ________
-D og
-Ġw art
-Ġsubsc riber
-s ports
-Ġbe gged
-ĠM V
-Ġsem if
-eth ical
-Ġpre ach
-Ġrev ital
-Ġpun itive
-Ġshort cuts
-Ġinstit uted
-ĠWars aw
-Ġabdom en
-ĠK ING
-Ġsuper intendent
-Ġf ry
-ĠGe o
-T OR
-Ġcontrad ictions
-apt ic
-Ġlandsc apes
-b ugs
-Ġcl ust
-Ġvol ley
-c ribed
-Ġt andem
-Ġrob es
-WH AT
-Ġpromot er
-Ġel oqu
-review ed
-ĠD K
-ĠPl ato
-Ġf ps
-T ank
-ĠDer rick
-Ġpriorit ize
-as per
-ĠHond uras
-ĠCom pleted
-ne c
-Ġm og
-n ir
-ĠMay o
-DE F
-st all
-in ness
-ĠVolks wagen
-Ġprec aution
-ĠM ell
-i ak
-ist ries
-Ġ24 8
-Ġoverl apping
-Sen ate
-ĠEnh ance
-res y
-rac ial
-OR TS
-ĠM ormons
-Str ong
-ĠCo ch
-Mex ico
-ĠMad uro
-Ġj ars
-Ġcan e
-W ik
-oll a
-iff erence
-Ġphysic ist
-ĠMag gie
-Ġ28 5
-Ġdep iction
-ĠMcL aren
-J u
-Ġsl ows
-Ġcommission ers
-ĠWill ow
-ĠExpl os
-hov ah
-Ġtechn ician
-Ġhom icides
-ĠFl av
-ĠTr uman
-Ġ100 00
-u ctor
-Ġsh ader
-News letter
-45 7
-Ġre ver
-Ġhard ened
-Ġwhere abouts
-Ġrede velop
-Ġcar bs
-Ġtra vers
-Ġsqu irrel
-Ġfoll ower
-Ġs ings
-50 8
-Ġrabb its
-emon ium
-Ġdocument ing
-Ġmisunder stood
-) '
-R ick
-gg ies
-Ġprem ie
-Ġsk ating
-Ġpass ports
-Ġf ists
-aged don
-H aw
-AC P
-0 80
-ĠThough ts
-ĠCarl son
-Ġpriest hood
-h ua
-Ġdun geons
-ĠLo ans
-Ġant is
-Ġfamiliar ity
-ĠS abb
-op al
-ĠIn k
-st rike
-Ġc ram
-Ġlegal ized
-Ġcu isine
-Ġfib re
-Tra vel
-ĠMon ument
-OD Y
-eth y
-Ġinter state
-ĠP UR
-em porary
-ĠArab ian
-develop ed
-Ġsadd le
-Ġg ithub
-ĠOff er
-ĠIS P
-ro let
-ĠSUP ER
-ĠDen is
-Ġmultipl ier
-Ġstir red
-Interest ingly
-Ġcustom ary
-Ġbill ed
-he x
-Ġmultipl ied
-Ġfl ipping
-ĠCros by
-Ġfundament als
-ia e
-ĠPlay ed
-ĠAt om
-am azon
-ĠFl am
-ee z
-activ ated
-Ġtables poon
-Ġliberal ism
-ĠPal in
-ĠP atel
-N um
-ĠT AM
-Ġs urn
-ĠRel oaded
-Ġco ined
-" ],
-ĠCl ash
-ĠAg u
-Ġprag matic
-ĠActiv ate
-Ġ8 02
-Ġtrail ers
-Ġsil hou
-Ġprob es
-Ġcirc us
-ĠB ain
-ĠLind say
-ĠAb bey
-Del ivery
-Ġconcess ion
-Ġgast ro
-ĠSpr ite
-Ä Ł
-and el
-Ġg imm
-Ġaut obi
-ĠT urtle
-Ġwonder fully
-ĠHar am
-ĠWorld wide
-ĠHand le
-Ġtheor ists
-Ġsle ek
-ĠZh u
-ograph ically
-EG A
-ĠOwn ers
-ath s
-ĠAntar ctic
-n atal
-=" "
-fl ags
-`` ``
-Ġs ul
-K h
-Ġpot assium
-Ġlinem an
-Ġcere al
-ĠSe asons
-Ġ20 22
-Ġmat hematic
-Ġastron omers
-prof essional
-Ġf ares
-cknow led
-Ġch i
-Ġyoung sters
-Ġmistaken ly
-Ġhem isphere
-ĠDiv inity
-r one
-Ġ" ,
-r ings
-Ġattract s
-v ana
-å ¹
-C AP
-Ġplay list
-Ġpor ch
-ãģ £
-Ġincorpor ates
-Ġso ak
-Ġassert ing
-ĠTerror ism
-ĠP ablo
-J a
-ces ter
-Ġfear ing
-ĠPr ayer
-Ġescal ated
-G W
-Ġro be
-ĠBright on
-ac ists
-ĠSym phony
-ĠDwar f
-ĠPar ade
-ĠLe go
-Ġinex pl
-Ġl ords
-le af
-RA G
-l iber
-Ġcig ars
-ĠJe hovah
-60 6
-WIND OWS
-ĠLiber ia
-eb us
-He avy
-Ġl ubric
-ĠR W
-angu ages
-Ġnarrow ed
-com puter
-ĠE mber
-Ġmurder ing
-Ġdown stream
-ĠT uls
-ĠT ables
-Top ic
-ĠAcc uracy
-= /
-l ost
-ĠRe i
-Ġprogress es
-b ear
-Ġestablish ments
-Just in
-ĠPe ach
-ĠG omez
-å ¿
-ĠTri angle
-Id ent
-ĠH ive
-Res ources
-Ġmix es
-ĠAss uming
-M u
-Ġhyp oc
-Ġs ane
-ĠW an
-id ious
-Su ccess
-Ġ io
-Ang el
-Ġdanger ously
-ĠCreat ure
-W ORK
-: [
-ĠKat rina
-List ener
-M iller
-ĠId lib
-h ang
-Ġcircum vent
-h ref
-Ġcel estial
-ĠWe eks
-ĠP ug
-ĠDal ton
-Ġsubpoen a
-uk u
-Ġpers isted
-pe i
-old ing
-ĠDoc uments
-ĠH ast
-ĠC ENT
-Ġprim er
-Ġsyn onymous
-Ġn ib
-om bs
-Ġnot ation
-ĠD ish
-ĠAt mosp
-Ġforb id
-ĠAN G
-pat tern
-l os
-Ġproject iles
-b rown
-." ,
-ĠVen om
-Ġfierce ly
-ub lished
-ĠU ran
-ĠNic arag
-4 10
-ĠC AL
-OT OS
-ĠMir acle
-ĠEn chant
-Ġguard ing
-app end
-Att ach
-Ġlevel ed
-Ġcond oms
-ih ilation
-64 9
-Ġnight mares
-ĠTHE Y
-ĠST ART
-ĠK inn
-Ġroomm ate
-Ġhy giene
-o pping
-J ob
-Ġl vl
-ĠV ER
-ĠKe eping
-ab etic
-Ġformat ting
-eral a
-Ġrev isions
-Ġres urg
-T el
-ĠGood man
-35 3
-p od
-Ġind isp
-ĠTrans lation
-Ġg own
-ĠM und
-Ġc is
-Ġby stand
-col lect
-ĠPun jab
-act ively
-ĠG amb
-te ll
-Ġimport ing
-g encies
-Ġloc om
-ĠBr ill
-H oly
-ĠBer ger
-Ġshow down
-Ġrespond ers
-IL Y
-Ġt akedown
-le ted
-Ġmat tered
-Ġpredict ive
-Ġover lay
-G PU
-ĠV ick
-Ġconvey ed
-T ab
-pe er
-Sc an
-Ġdefensive ly
-v ae
-Ġappro ving
-Ġt iers
-ĠV ia
-quer ade
-ĠSaud is
-Ġdemol ished
-ĠProp he
-Ġmon o
-Ġhospital ity
-H AM
-ĠAri el
-M OD
-ĠTor ah
-Ġbl ah
-ĠBel arus
-erent ial
-ĠT uc
-Ġbank er
-39 7
-Ġmosqu it
-ĠScient ist
-ĠMus ical
-Ġh ust
-Sh ift
-Ġtor ment
-Ġstand off
-E duc
-ĠF og
-Ġampl ifier
-Sh ape
-Inst ance
-ĠCrit ics
-Ġda emon
-H ouston
-Ġmatt ress
-ĠID F
-Ġobsc ene
-ĠA mer
-hett i
-Ġcomp iling
-35 2
-vere tt
-ĠRed uction
-ist ration
-ĠBl essed
-ĠB achelor
-3 16
-Ġpr ank
-ĠVul can
-dd ing
-Ġm ourning
-ĠQu int
-ĠBl aster
-test ing
-Ġsed iment
->> >
-ĠE ternity
-ĠWH ERE
-ĠM aze
-Ġreact ing
-ĠAl v
-oms day
-ĠC RA
-Ġtransl ator
-Ġbog us
-at u
-We bsite
-oll s
-Ġbapt ism
-Ġs ibling
-ĠAut umn
-ve z
-ãģ® é
-gu ards
-Ge org
-assad ors
-ĠFre ud
-Ġcontin ents
-ĠReg istry
-Bern ie
-ĸļ 士
-Ġtoler ant
-ĠU W
-Ġhor ribly
-99 5
-ĠMID I
-Ġimpat ient
-oc ado
-er i
-ĠWor st
-ĠNor ris
-ĠTalk ing
-Ġdef ends
-ens able
-Ġ20 21
-Ġanat omy
-L ew
-Ġdraw er
-ĠCan berra
-Ġpatri otic
-é¾įå ĸļ士
-ĠAv g
-AR M
-Ġundis closed
-Ġfare well
-45 9
-b able
-ĠAll ison
-OL OG
-Ġcon co
-t ight
-ĠAC PI
-ĠM ines
-l ich
-ĠâĶ ľ
-represent ed
-200 000
-Ġenthusi ast
-OT S
-b il
-ĠIng redients
-Ġinvent or
-ĠMy SQL
-³³ ³
-ĠAB OUT
-with in
-Ġm k
-B ul
-ĠF ake
-Ġdracon ian
-W a
-hel m
-ĠTer ran
-erv ille
-Ġcommon place
-SI ZE
-Ġ" <
-re place
-ograph s
-ĠSE LECT
-inc ible
-ĠMost ly
-ĠShe ffield
-ĠID E
-ugg le
-Ġcit ations
-h urst
-ĠUn ix
-Ġunle ash
-ĠP iper
-ĠN ano
-Ġsucc umb
-Ġreluct ance
-Ġ25 00
-ĠMer chant
-Ġwire t
-Ġcomb os
-ĠBirth day
-Ġchar coal
-ĠU PS
-ĠFair fax
-Ġdrive way
-ĠT ek
-ĠP itch
-ove re
-Ġtechn icians
-ĠAct ual
-fl ation
-ĠF iscal
-ĠEm pty
-an amo
-Ġmag nesium
-Ġsl ut
-Ġgrow ers
-Invest igators
-( ):
-ĠS atellite
-ĠKe ynes
-miss ive
-l ane
-Ġb orough
-3 44
-ĠTE AM
-ĠBet hesda
-C V
-h ower
-ĠR AD
-Ġch ant
-ĠR iy
-Ġcompos itions
-Ġmild ly
-Ġmedd ling
-Ġag ility
-ane ers
-5 01
-Ġsyn th
-ling er
-29 1
-Ġex claimed
-Part y
-Ġcont amin
-ĠMan or
-ĠResp ond
-Ġpra ising
-Ġman ners
-fle et
-Sum mer
-ĠLy nd
-ĠDef initely
-gr im
-Ġbow ling
-st ri
-ç Ľ
-y nt
-Ġmand ates
-D IV
-Ġreconc ile
-view s
-ĠDam on
-vet te
-F lo
-ĠGreat est
-il on
-ic ia
-Ġportray al
-Ġcush ion
-50 4
-19 79
-oss al
-App lic
-sc ription
-Ġmit igation
-AT S
-p ac
-Ġer ased
-Ġdefic iencies
-ĠHolland e
-ĠX u
-Ġb red
-Ġpregn ancies
-f emin
-Ġem ph
-Ġpl anners
-Ġout per
-utter ing
-Ġperpet rator
-Ġm otto
-ĠEll ison
-ĠNE VER
-Ġadmitted ly
-AR I
-ĠAzerbai jan
-Ġmill isec
-Ġcombust ion
-ĠBott le
-ĠL und
-ĠP s
-ĠD ress
-Ġfabric ated
-Ġbat tered
-Ġs idel
-ĠNot ting
-Fore ign
-ĠJer ome
-0 20
-ĠAr bit
-Ġkn ots
-ĠR IGHT
-M oving
-ãģ Ļ
-Ġsur geries
-Ġcour thouse
-Ġm astered
-Ġhover ing
-ĠBr an
-ĠAl ison
-Ġsaf est
-m ilitary
-Ġbull ied
-Ġbar rage
-Read er
-ES E
-ĠGe ographic
-T ools
-3 14
-ĠGe ek
-ro th
-gl ers
-ĠF IN
-Ï ģ
-ĠA ston
-al tern
-48 8
-Ġveter in
-G amer
-Ġint el
-ren ches
-Sh ield
-Ġam nesty
-ĠB har
-Ġp iled
-Ġhonor able
-ĠInst itutes
-Ġso aked
-Ġcom a
-ĠE FF
-34 1
-by tes
-ĠG mail
-le in
-ĠCanad iens
-m aterial
-I l
-Ġinstruct ors
-ĠK Y
-Ġconce ive
-ub b
-ĠP ossible
-Ġeas ing
-ĠChrist ina
-Ġcar ic
-ĠHD R
-R OM
-Ġsho vel
-de lete
-Ġp uff
-ĠCh anging
-Ġseam lessly
-Att ribute
-Ġacqu isitions
-ak ery
-ĠE F
-Ġaut istic
-ĠT akes
-ĠPow der
-ĠSt ir
-5 10
-ĠBub ble
-sett ings
-ĠF owler
-Ġmust ard
-Ġmore over
-Ġcopyright ed
-ĠLED s
-15 00
-æ ī
-ĠH IS
-en f
-Ġcust od
-ĠH uck
-G i
-Ġim g
-An swer
-C t
-j ay
-ĠInf rastructure
-Ġfeder ally
-L oc
-Ġmicro bes
-Ġover run
-dd s
-ot ent
-adi ator
->>>> >>>>
-Ġtorn ado
-Ġadj ud
-Ġintrig ued
-Ġs i
-ĠRevel ation
-pro gress
-Ġburgl ary
-ĠSai yan
-ĠK athy
-Ġser pent
-ĠAndre as
-Ġcomp el
-ess ler
-ĠPl astic
-ĠAd vent
-ĠPos itive
-ĠQ t
-ĠHind us
-reg istered
-ular ity
-Ġrighteous ness
-Ġdemon ic
-u itive
-ĠB DS
-ĠGre gg
-c ia
-ĠCrus ade
-ĠSina i
-W ARE
-+ (
-Ġme ll
-Ġder ail
-y ards
-A st
-Ġnotice ably
-ĠO ber
-R am
-Ġun noticed
-Ġse q
-av age
-T s
-Ġ6 40
-Ġconced e
-Ġ] )
-F ill
-Ġcapt ivity
-ĠImprove ment
-ĠCrus ader
-ara oh
-M AP
-æ Ĺ
-Ġstr ide
-al ways
-F ly
-N it
-Ġal gae
-ĠCook ing
-ĠDo ors
-Mal ley
-Ġpolic emen
-ãģ į
-Ġastron aut
-access ible
-49 5
-ĠR AW
-cl iffe
-udic rous
-Ġdep ended
-al ach
-Ġvent ures
-ra ke
-Ġt its
-ĠH ou
-Ġcond om
-ormon al
-Ġind ent
-Ġupload ing
-Foot note
-Import ant
-Ġ27 1
-Ġmind ful
-Ġcont ends
-C ra
-Ġcal ibr
-ĠO ECD
-plug in
-F at
-ĠIS S
-ĠDynam ics
-ans en
-68 6
-' ),
-Ġsp rite
-Ġhand held
-ĠH ipp
-=~ =~
-Tr ust
-Ġsem antics
-ĠBund es
-ĠRen o
-ĠLiter ature
-s ense
-G ary
-ĠA eg
-ĠTr in
-EE K
-Ġcler ic
-ĠSS H
-Ġch rist
-Ġinv ading
-ib u
-Ġen um
-aur a
-Ġal lege
-ĠInc redible
-B BC
-Ġth ru
-Ġsa iled
-Ġem ulate
-Ġin security
-Ġc rou
-Ġaccommod ations
-Ġincompet ent
-Ġsl ips
-ĠEarth qu
-s ama
-IL LE
-Ġi Phones
-as aki
-Ġby e
-Ġar d
-Ġext ras
-Ġsl aughtered
-Ġcrowd funding
-res so
-Ġfil ib
-ĠER ROR
-ĠT LS
-e gg
-ĠIt al
-Ġen list
-ĠCatal onia
-ĠSc ots
-Ġser geant
-Ġdiss olve
-N H
-Ġstand ings
-ri que
-I Q
-Ġbenef iciary
-Ġaqu arium
-You Tube
-ĠPower Shell
-Ġbright est
-ĠWar rant
-S old
-Writ ing
-Ġbegin nings
-ĠRes erved
-ĠLatin os
-head ing
-Ġ4 40
-Ġrooft op
-AT ING
-Ġ3 90
-VP N
-G s
-k ernel
-turn ed
-Ġprefer able
-Ġturn overs
-ĠH els
-S a
-ĠShin ji
-ve h
-ĠMOD ULE
-V iol
-Ġex iting
-Ġj ab
-ĠVan illa
-Ġac ron
-ĠG ap
-ber n
-A k
-ĠMc Gu
-Ġend lessly
-ĠFar age
-ĠNo el
-V a
-M K
-Ġbr ute
-ĠK ru
-ĠES V
-ĠOl ivia
-âĢ ł
-ĠK af
-Ġtrust ing
-Ġh ots
-3 24
-Ġmal aria
-Ġj son
-Ġp ounding
-ort ment
-Count ry
-Ġpostp oned
-Ġunequ iv
-? ),
-ĠRo oney
-udd ing
-ĠLe ap
-ur rence
-sh apeshifter
-ĠH AS
-os ate
-Ġca vern
-Ġconserv atism
-ĠB AD
-Ġmile age
-Ġarrest ing
-V aults
-Ġmix er
-Dem ocratic
-ĠB enson
-Ġauth ored
-8 000
-Ġpro active
-ĠSpirit ual
-t re
-Ġincarcer ated
-ĠS ort
-Ġpe aked
-Ġwield ing
-re ciation
-×Ļ ×
-P atch
-ĠEm my
-Ġex qu
-tt o
-ĠRat io
-ĠP icks
-ĠG ry
-ph ant
-Ġf ret
-Ġeth n
-Ġarch ived
-% -
-c ases
-ĠBl aze
-Ġim b
-c v
-y ss
-im ony
-Ġcount down
-Ġaw akening
-ĠTunis ia
-ĠRe fer
-ĠM J
-Ġun natural
-ĠCar negie
-iz en
-ĠN uggets
-he ss
-Ġev ils
-64 7
-Ġintrodu ctory
-l oving
-ĠMcM ahon
-Ġambig uity
-L abel
-ĠAlm ighty
-Ġcolor ing
-ĠCl aus
-set ting
-N ULL
-ĠF avorite
-ĠS IG
-> (
-ĠSh iva
-ĠMay er
-Ġstorm ed
-ĠCo verage
-we apons
-igh am
-Ġun answered
-Ġle ve
-Ġc oy
-c as
-b ags
-as ured
-Se attle
-ĠSant orum
-ser ious
-Ġcourage ous
-ĠS oup
-Ġconfisc ated
-Ġ// /
-Ġuncon ventional
-Ġmom s
-ĠRohing ya
-ĠOrche stra
-ĠPot ion
-Ġdisc redit
-ĠF IL
-f ixed
-ĠDe er
-do i
-ĠDim ension
-Ġbureaucr ats
-et een
-Ġaction Group
-oh m
-Ġb umps
-ĠUt ility
-Ġsubmar ines
-ren heit
-re search
-ĠShap iro
-Ġsket ches
-Ġde ceptive
-ĠV il
-es ame
-ĠEss entially
-Ġramp age
-isk y
-Ġmut tered
-th ritis
-Ġ23 6
-f et
-b ars
-Ġpup il
-ĠTh ou
-o S
-s ong
-Ġfract ured
-Ġre vert
-pict ure
-Ġcrit erion
-us her
-Ġreperc ussions
-ĠV intage
-ĠSuper intendent
-Offic ers
-Ġflag ged
-Ġbl ames
-Ġin verse
-ograp hers
-Ġmakes hift
-Ġdev oid
-Ġfoss ils
-ĠArist otle
-ĠFund s
-Ġde pleted
-ĠFl u
-ĠY uan
-Ġw oes
-Ġlip id
-Ġsit u
-requ isites
-Ġfurn ish
-ĠSam ar
-Ġshame ful
-Ġadverse ly
-Ġad ept
-Ġrem orse
-Ġmurder ous
-uck les
-ĠE SL
-Ġ3 14
-s ent
-Ġred ef
-ĠC ache
-ĠP urs
-ig ans
-Ġ4 60
-Ġpres criptions
-Ġf res
-F uck
-ocr ates
-Tw enty
-ĠWe ird
-ĠT oggle
-ĠC alled
-itiz ens
-Ġp oultry
-Ġharvest ing
-ãĤ¦ ãĤ¹
-Bott om
-Ġcaution ed
-t n
-39 6
-ĠNik ki
-Ġeval uations
-Ġharass ing
-Ġbind ings
-ĠMon etary
-Ġhit ters
-Ġadvers ary
-un ts
-Ġset back
-Ġenc rypt
-ĠC ait
-Ġl ows
-eng es
-ĠN orn
-Ġbul bs
-Ġbott led
-ĠVoy ager
-3 17
-Ġsp heres
-p olitics
-Ġsubt ract
-Ġsens ations
-Ġapp alling
-Ġ3 16
-Ġenvironment ally
-ĠST EM
-Ġpub lishes
-5 60
-Ġdilig ence
-48 4
-Ġadv ises
-Ġpet rol
-Ġimag ining
-Ġpatrol s
-ĠInt eger
-ĠAs hes
-act us
-ĠRad iant
-ĠL T
-it ability
-ht aking
-Set ting
-Ġnu anced
-ĠRe ef
-ĠDevelop ers
-N i
-pie ces
-99 0
-Lic ense
-Ġlow ers
-ĠOtt oman
-3 27
-oo o
-Ġqu itting
-mark ets
-Beh ind
-Ġbas in
-Ġdoc s
-an ie
-fl ash
-ct l
-Ġcivil ized
-ĠFuk ushima
-"] ,"
-ĠK S
-ĠHonest ly
-ar at
-Ġconstruct s
-ĠL ans
-ĠD ire
-ĠLI KE
-ĠTrou ble
-Ġwith holding
-ĠOb livion
-Ġsan ity
-any a
-Con st
-Ġgro cer
-ĠC elsius
-Ġrecount ed
-ĠW ife
-B order
-ate red
-h appy
-Ġspo iler
-Ġlog ically
-H all
-Ġsucceed ing
-Ġpoly morph
-Ġax es
-ĠShot gun
-ĠS lim
-ĠPrin ciples
-ĠL eth
-art a
-Ġsc or
-Sc reenshot
-Ġrelax ation
-#$ #$
-Ġdeter rent
-idd y
-Ġpower less
-Ġles bians
-Ġch ords
-ĠEd ited
-se lected
-Ġseparat ists
-000 2
-Ġair space
-Ġturn around
-Ġc unning
-P ATH
-P oly
-Ġbomb ed
-Ġt ion
-x s
-Ġwith hold
-Ġw aged
-ĠLiber ties
-Fl ag
-Ġcomfort ing
-45 4
-ĠI ris
-are rs
-Ġr ag
-Ġrel ocated
-ĠGu arant
-Ġstrateg ically
-Ġgam ma
-uber ty
-ĠLock heed
-g res
-Ġgr illed
-ĠLow e
-st ats
-ĠR ocks
-Ġsens ing
-Ġrent ing
-ĠGe ological
-ا Ø
-ot rop
-Ġse w
-Ġimproper ly
-48 6
-Ġâĸ ł
-Ġstar ving
-ĠB j
-Disc ussion
-3 28
-ĠCom bo
-ĠFix es
-N AT
-Ġstri ving
-th ora
-Ġharvest ed
-ĠP ing
-Ġplay ful
-Ġaven ues
-Ġoccup ational
-Ġw akes
-ĠCou rier
-Ġdrum mer
-ĠBrow ser
-ĠH outh
-it u
-Ġapp arel
-p aste
-Ġhun ted
-ĠSecond ly
-l ain
-X Y
-ĠP IN
-ic ons
-Ġcock tails
-Ġs izable
-Ġhurd les
-est inal
-ĠRecre ation
-Ġe co
-64 8
-ĠD ied
-m int
-Ġfinger prints
-Ġdis pose
-ĠBos nia
-ts y
-22 00
-Ġins pected
-ĠF ou
-Ġf uss
-Ġamb ush
-ĠR ak
-Ġmanif ested
-Pro secut
-Ġsuff ice
-ren ces
-Ġcompens ated
-ĠC yrus
-Ġgen us
-ĠWolver ine
-ĠTrend s
-Ġh ikes
-ĠSe en
-Ġen rol
-C old
-Ġpol itely
-ĠSl av
-ĠRu pert
-Ġey ewitness
-ĠAl to
-Ġun comp
-Ġposter ior
-M ust
-ĠHer z
-Ġprogress ively
-Ġ23 4
-Ġind ifference
-ĠCunning ham
-Ġacadem ia
-Ġse wer
-Ġast ounding
-ĠA ES
-r ather
-Ġeld est
-Ġclim bs
-ĠAdd s
-Ġout cry
-Ġcont ag
-ĠH ouses
-Ġpe pt
-ĠMel ania
-interest ed
-ĠU CH
-ĠR oots
-ĠHub bard
-ĠT BD
-ĠRoman ian
-fil ename
-St one
-ĠIm pl
-Ġchromos ome
-C le
-d x
-Ġscram bled
-ĠP t
-Ġ24 2
-OP LE
-Ġtremend ously
-St reet
-Ġcra ving
-Ġbund led
-ĠR G
-p ipe
-Ġinj uring
-Ġarc ane
-Part icip
-ĠHero ic
-st y
-Ġto pping
-ĠTemp est
-rent ices
-b h
-Ġpar anoia
-ĠUnic ode
-Ġegreg ious
-Ġ\ '
-ĠOsw ald
-Ġgra vel
-ĠSim psons
-Ġbl and
-ĠGuant anamo
-Writ er
-lin ers
-ĠD ice
-J C
-Ġpar ity
-Ġs ided
-Ġ23 7
-ĠPyr rha
-at ters
-d k
-F ine
-comp an
-Ġform ulated
-ĠId ol
-il ers
-hem oth
-ĠF av
-Ġintr usion
-Ġcar rots
-ĠL ayer
-ĠH acker
-Ġ ----------------
-Ġmoder ation
-é ģ
-oc oc
-Ġcharacter ize
-ĠTe resa
-Ġsocio economic
-Ġper k
-ĠParticip ation
-tr aining
-ĠPaul o
-ph ys
-Ġtrust worthy
-Ġembod ied
-ĠMer ch
-c urrency
-ĠPrior ity
-Ġte asing
-Ġabsor bing
-Ġunf inished
-ĠCompar ison
-Ġdis ple
-writ ers
-Ġprofess ions
-ĠPengu in
-Ġang rily
-ĠL INK
-68 8
-ĠCor respond
-Ġprev ailed
-Ġcart el
-l p
-as ms
-ĠRed emption
-ĠIslam ists
-effect s
-d ose
-ĠL atter
-ĠHal ifax
-Ġv as
-ĠTop ics
-ĠN amed
-advert ising
-zz a
-IC ES
-Ġret arded
-ach able
-ĠPupp et
-ĠItem Level
-Ġret ract
-Ġident ifiable
-A aron
-ĠB uster
-s ol
-hel le
-as semb
-H ope
-r anged
-B a
-ĠP urch
-é Ģ
-ĠSir i
-Ġarri vals
-Ġ19 12
-Ġshort ened
-Ġ3 12
-Ġdiscrep ancy
-ĠTem perature
-ĠWal ton
-Ġkind erg
-p olit
-Ġrem ix
-Ġconnect ors
-ãĥĺ ãĥ©
-ĠKazakh stan
-dom inated
-Ġsu gars
-im ble
-ĠPan ic
-ĠDem and
-ĠCol ony
-on en
-ĠM ER
-7 75
-ur ia
-aza ar
-ĠDeg ree
-P ri
-Ġsun shine
-Ġ25 1
-Ġpsychedel ic
-Ġdigit ally
-ĠBra un
-Ġsh immer
-Ġsh ave
-ĠTel esc
-ĠAst ral
-ĠVenezuel an
-ĠO G
-Ġc rawling
-Int eg
-ĠFe ather
-Ġunfold ing
-Ġappropri ation
-Ġè£ı è
-ĠMob ility
-ĠN ey
-- .
-b ilt
-L IN
-ĠT ube
-ĠCon versely
-Ġkey boards
-ĠC ao
-Ġover th
-Ġla ure
->> \
-ĠV iper
-ach a
-Off set
-ĠR aleigh
-ĠJ ae
-J ordan
-j p
-Ġtotal itarian
-Connect or
-Ġobserv es
-ĠSpart an
-ĠIm mediately
-ĠSc al
-C ool
-Ġt aps
-Ġro ar
-P ast
-Ġch ars
-ĠB ender
-ĠShe ldon
-Ġpain ter
-Ġbe acon
-ĠCreat ures
-Ġdownt urn
-Ġh inder
-ĠAnd romeda
-Ã Ľ
-cc oli
-ĠF itness
-et rical
-Ġutil izes
-Ġsen ate
-Ġen semble
-Ġche ers
-T W
-Ġaff luent
-k il
-ry lic
-ord ering
-Com puter
-Ġgru esome
-ost ics
-ĠUb isoft
-ĠKel ley
-Ġw rench
-Ġbourgeois ie
-IB LE
-ĠPrest on
-w orn
-ar ist
-reat ing
-Ġst ained
-ar ine
-Ġsl ime
-EN N
-Ġche sts
-Ġground water
-ann ot
-ĠTr ay
-ĠLoc ke
-ĠC TR
-Ġd udes
-ĠEx ternal
-ĠDec oder
-Ġpar amed
-ĠMed line
-80 9
-ĠD inner
-rup al
-g z
-ĠG um
-ĠDem o
-j ee
-Ġd h
-ber man
-arch s
-Ġen qu
-ĠEp stein
-Ġdevast ation
-Ġfriends hips
-ĠAr d
-Ġ23 1
-ĠRub in
-ĠDist ance
-Ġsp urred
-Ġd ossier
-Ġover looking
-\\\\\\\\ \\\\\\\\
-Fore st
-ĠCom es
-\ ",
-ĠIran ians
-Ġf ixtures
-L aughs
-Ġcur ry
-ĠKing ston
-Ġsqu ash
-Ġcat alogue
-Ġabnormal ities
-Ġdigest ive
-.... .....
-Ġsubord inate
-og ly
-Ġ24 9
-M iddle
-Ġmass ac
-Ġburg ers
-Ġdown stairs
-Ġ19 31
-39 4
-ĠV G
-Ġl asers
-ĠS ikh
-ĠAlex a
-der ived
-Ġcycl ist
-ãģ® éŃĶ
-onel iness
-!!!! !!!!
-Ġbuff s
-leg ate
-Ġrap ing
-Ġrecomm ending
-ro red
-Ġmult icultural
-un ique
-Ġbusiness men
-Ġune asy
-ĠM AP
-Ġdisp ersed
-cipl ine
-J ess
-ĠK erala
-å §
-Ġabst raction
-Sur v
-U h
-Ġprin ters
-ij a
-ow der
-Ġanalog ous
-ĠA SP
-af er
-Ġunfold ed
-Ġlevel ing
-Ġbre ached
-ĠH earing
-Ġn at
-Ġtransl ating
-crit ical
-Ġant agonist
-ĠYes terday
-Ġfuzz y
-w ash
-m ere
-Ġbe wild
-ĠM ae
-V irgin
-ph rase
-Ġsign aled
-ĠH IGH
-Ġprot ester
-Ġgar ner
-unk nown
-Ġk ay
-Ġabduct ed
-Ġst alking
-am n
-Ġdes erving
-ĠR iv
-ĠJ orge
-Ġscratch ing
-ĠS aving
-ip ing
-Ġte ase
-Ġmission ary
-ĠMor row
-T IME
-P resent
-Ġchem otherapy
-tern ess
-ĠH omes
-ĠP urdue
-Ġst aunch
-ĠWhit ney
-ĠTH ERE
-Î ¼
-iat us
-ĠErn est
-ĠDe ploy
-Ġcove ted
-F ML
-ĠDial ogue
-Ġex ited
-f ruit
-Ġner d
-":" ","
-Ġv ivo
-ru ly
-4 60
-ĠAm en
-rehens ible
-Ġâ ĺ
-D IR
-Ġad herence
-Ġche w
-ĠCo ke
-ĠSerge i
-dig ital
-ĠNe ck
-g ently
-enth al
-/ )
-Ġwe ary
-Ġgu ise
-ĠConc ord
-ĠOn ion
-at cher
-Ġb inge
-ĠDirect ive
-Ġman ned
-ans k
-Ġill usions
-Ġbillion aires
-38 3
-oly n
-odynam ic
-ĠWhe at
-ĠA lic
-Ġcol oured
-ĠN AFTA
-ab o
-Ġmac ros
-ind ependent
-s weet
-Ġsp ac
-ĠK abul
-Ġ Ä
-em e
-Ġdict ated
-Ġsh outs
-= {
-Ġr ipping
-ĠSh ay
-ĠCr icket
-direct ed
-Ġanalys ed
-ĠWAR RANT
-ag ons
-ĠBlaz ers
-Ġche ered
-Ġar ithmetic
-ĠTan z
-37 3
-ĠFl ags
-Ġ29 5
-Ġw itches
-ĠIn cluded
-ĠG ained
-ĠBl ades
-G am
-ĠSam antha
-ĠAtl antis
-ĠPr att
-Ġspo iled
-ĠI B
-ĠRam irez
-Pro bably
-re ro
-ĠN g
-ĠWar lock
-t p
-Ġover he
-Ġadministr ations
-Ġt int
-Ġreg iment
-Ġpist ols
-Ġblank ets
-Ġep ist
-Ġbowl s
-Ġhydra ulic
-Ġde an
-Ġj ung
-Ġasc end
-70 5
-ĠSant iago
-Ã ®
-Ġun avoid
-ĠSh aman
-re b
-Ġstem ming
-99 8
-ĠM G
-st icks
-esthes ia
-ER O
-Ġmor bid
-ĠGr ill
-ĠP oe
-any l
-Ġdele ting
-ĠSurve illance
-Ġdirect ives
-Ġiter ations
-ĠR ox
-ĠMil ky
-F ather
-Ġpat ented
-44 7
-Ġprec ursor
-Ġm aiden
-ĠP hen
-ĠVe gan
-ĠPat ent
-K elly
-Redd itor
-Ġn ods
-Ġvent ilation
-ĠSchwar z
-Ġw izards
-Ġomin ous
-ĠHe ads
-ĠB G
-Ġl umber
-ĠSp iel
-Ġis Enabled
-Ġancest ral
-ĠSh ips
-Ġwrest ler
-ph i
-Ġy uan
-ĠRebell ion
-Ġice berg
-Ġmag ically
-Ġdivers ion
-ar ro
-yth m
-ĠR iders
-ĠRob bie
-ĠK ara
-ĠMain tenance
-ĠHer b
-Ġhar ms
-p acked
-ĠFe instein
-Ġmarry ing
-Ġbl ending
-ĠR ates
-Ġ18 80
-Ġwr ink
-ĠUn ch
-ĠTor ch
-desc ribed
-Ġhuman oid
-ilit ating
-ĠCon v
-ĠFe ld
-IGH TS
-Ġwhistlebl ower
-ort mund
-ets y
-arre tt
-ĠMon o
-ĠI ke
-ĠC NBC
-ĠW AY
-ĠMD MA
-ĠIndividual s
-Ġsupplement al
-Ġpower house
-ĠSt ru
-F ocus
-aph ael
-ĠCol leg
-att i
-Z A
-Ġp erenn
-ĠSign ature
-ĠRod ney
-Ġcub es
-idd led
-ĠD ante
-ĠIN V
-iling ual
-ĠC th
-Ġso fa
-Ġintimid ate
-ĠR oe
-ĠDi plom
-ĠCount ries
-ays on
-Ġextrad ition
-Ġdis abling
-ĠCard iff
-Ġmemor andum
-ĠTr ace
-Ġ?? ?
-se ctor
-ĠRou hani
-ĠY ates
-ĠFree ze
-Ġbl adder
-M otor
-ĠProm ise
-ant asy
-Ġforesee able
-ĠC ologne
-cont ainer
-ĠTre es
-ĠG ors
-ĠSin clair
-Ġbar ring
-key e
-Ġsl ashed
-ĠStat istical
-é ĩ
-Ġâĸ º
-All ows
-Ġhum ility
-Ġdr illed
-ĠF urn
-44 3
-Ġse wage
-Ġhome page
-Ġcour tyard
-Ġv ile
-Ġsubsid iaries
-aj o
-direct ory
-Ġam mon
-V ers
-charg es
-Ġ} }
-ĠCh ains
-Ġ24 6
-n ob
-Ġper cept
-Ġg rit
-Ġfisher men
-ĠIraq is
-ĠDIS TR
-ĠF ULL
-ĠEval uation
-g raph
-at ial
-Ġcooper ating
-Ġmel an
-Ġenlight ened
-Ġal i
-t ailed
-Ġsal ute
-Ġweak est
-ĠBull dogs
-U A
-ĠAll oy
-Ġsem en
-oc ene
-ĠWilliam son
-s pr
-, âĢĶ
-ĠG F
-itt ens
-Be at
-ĠJ unk
-iph ate
-ĠFarm ers
-ĠBit coins
-ig ers
-d h
-ĠL oyal
-p ayer
-Ġentert ained
-Ġpenn ed
-Ġcoup on
-Que ue
-Ġweaken ing
-c arry
-Ġunderest imate
-Ġshoot out
-Ġcharism atic
-ĠProced ure
-Ġprud ent
-in ances
-Ġric hes
-Ġcort ical
-Ġstr ides
-Ġd rib
-ĠOil ers
-5 40
-ĠPer form
-ĠBang kok
-Ġe uth
-S ER
-Ġsimpl istic
-t ops
-camp aign
-Q uality
-Ġimpover ished
-ĠEisen hower
-Ġaug ment
-ĠH arden
-Ġinterven ed
-Ġlist ens
-ĠK ok
-Ġs age
-Ġrub bish
-ĠD ed
-Ġm ull
-pe lling
-Ġvide ot
-Produ ction
-D J
-m iah
-Ġadapt ations
-Ġmed ically
-Ġboard ed
-Ġarrog ance
-Ġscra pped
-Ġopp ress
-FORM ATION
-Ġj unction
-4 15
-EE EE
-S kill
-Ġsub du
-ĠSug gest
-ĠP ett
-Ġle tt
-ĠMan ip
-ĠC af
-ĠCooper ation
-T her
-Ġreg ained
-¶ æ
-ref lect
-Ġth ugs
-ĠShel by
-Ġdict ates
-ĠWe iner
-ĠH ale
-Ġbatt leground
-s child
-Ġcond ol
-h unt
-osit ories
-Ġacc uses
-Fil ename
-Ġsh ri
-Ġmotiv ate
-Ġreflect ions
-N ull
-ĠL obby
-¥ µ
-ĠS ATA
-ĠBack up
-Ñ ĥ
-n in
-ĠCor rection
-Ġju icy
-ut ra
-ĠP ric
-Ġrest raining
-ĠAir bnb
-ĠAr rest
-Ġappropri ations
-Ġsl opes
-Ġmans laughter
-Ġwork ings
-ĠH uss
-ĠF rey
-Le ave
-ĠHarm ony
-ĠF eder
-Ġ4 30
-Ġt rench
-Ġglad ly
-Ġbull pen
-ĠG au
-b ones
-Ġgro ove
-Ġpre text
-ã ħĭ
-Ġtransm itter
-ĠComp onent
-Ġunder age
-ĠEm pires
-T ile
-Ġo y
-ĠMar vin
-ĠC AS
-Ġbl oss
-Ġrepl icated
-ĠMar iners
-Marc us
-ĠBl ocks
-Ġliber ated
-Ġbutter fly
-Fe el
-Ġfer mentation
-Ġyou tube
-Ġoff end
-ĠTer m
-res ist
-Ġcess ation
-Ġinsurg ency
-Ġb ir
-ĠRa ise
-59 5
-Ġhypothes es
-50 2
-Ġpl aque
-ocr at
-Ġjack ets
-ĠHuff Post
-am ong
-Ġconf er
-48 7
-ĠL illy
-Ġadapt ing
-ĠF ay
-Ġsh oved
-ve c
-Ġref ine
-Ġg on
-Ġgun men
-z ai
-ĠShut tle
-ĠI zan
-Ġ19 13
-Ġple thora
-· ·
-Ġ5 10
-Ġp uberty
-Ġ24 1
-ĠWe alth
-ĠAl ma
-ĠM EM
-ĠAd ults
-C as
-pr ison
-R ace
-Ġwater proof
-Ġathlet icism
-Ġcapital ize
-ĠJu ice
-Ġillum inated
-ĠP ascal
-Ġirrit ation
-ĠWitness es
-ad le
-ĠAst ro
-Ġf ax
-ĠEl vis
-Prim ary
-ĠL ich
-ĠEl ves
-Ġres iding
-Ġst umble
-3 19
-ĠP KK
-Ġadvers aries
-D OS
-ĠR itual
-Ġsm ear
-Ġar son
-ident al
-Ġsc ant
-Ġmon archy
-Ġhal ftime
-Ġresid ue
-Ġind ign
-ĠSh aun
-ĠEl m
-aur i
-A ff
-W ATCH
-ĠLy on
-hel ps
-36 1
-Ġlobby ist
-Ġdimin ishing
-Ġout breaks
-Ġgo ats
-f avorite
-ĠN ah
-son ian
-ĠBo oster
-Ġsand box
-ĠF are
-ĠMalt a
-Ġatt Rot
-ĠM OR
-ld e
-Ġnavig ating
-T ouch
-Ġunt rue
-ĠDis aster
-Ġl udicrous
-Pass word
-ĠJ FK
-blog spot
-4 16
-ĠUN DER
-ern al
-Ġdelay ing
-T OP
-Ġimpl ants
-ĠAV G
-ĠH uge
-att r
-Ġjournal istic
-ĠPe yton
-ĠI A
-R ap
-go al
-ĠProgram me
-Ġsm ashing
-w ives
-print ln
-ĠPl ague
-in us
-EE P
-Ġcru iser
-ĠPar ish
-umin ium
-Ġoccup ants
-ĠJ ihad
-m op
-Ġp int
-Ġhe ct
-ĠMe cca
-direct or
-ĠFund ing
-ĠM ixed
-Ġst ag
-T ier
-Ġg ust
-Ġbright ly
-ors i
-Ġup hill
-R D
-Ġles ions
-ĠBund y
-liv ious
-Ġbi ologist
-ĠFac ulty
-ĠAuthor ization
-Ġ24 4
-All ow
-ï ¸
-ĠGi ul
-Ġpert inent
-ot aur
-es se
-ĠRo of
-Ġunman ned
-35 1
-ĠSh ak
-ĠO rient
-Ġend anger
-D ir
-Ġrepl en
-ed ient
-Ġtail or
-Ġgad gets
-Ġaud ible
-âĺ Ĩ
-N ice
-Ġbomb ard
-ĠR ape
-Ġdef iance
-ĠTW O
-ĠFilip ino
-Ġunaff ected
-erv atives
-Ġso ared
-ĠBol ton
-Ġcomprom ising
-ĠBrew ers
-R AL
-ĠA HL
-icy cle
-Ġv ampires
-Ġdi pped
-oy er
-ĠX III
-Ġsidew ays
-ĠW aste
-ĠD iss
-ĠâĶľ âĶĢâĶĢ
-$ .
-Ġhabit ats
-ĠBe ef
-tr uth
-tr ained
-spl it
-R us
-And y
-ĠB ram
-RE P
-p id
-è£ ħ
-ĠMut ant
-An im
-ĠMar ina
-Ġfut ile
-hig hest
-f requency
-Ġepile psy
-Ġcop ing
-Ġconc ise
-Ġtr acing
-ĠS UN
-pan el
-ĠSoph ie
-ĠCrow ley
-ĠAd olf
-ĠShoot er
-Ġsh aky
-ĠI G
-ĠL ies
-ĠBar ber
-p kg
-Ġupt ake
-Ġpred atory
-UL TS
-/ **
-Ġintox icated
-ĠWest brook
-od der
-he ment
-Ġbas eman
-AP D
-st orage
-ĠFif ty
-ed itor
-G EN
-UT ION
-ir ting
-Ġse wing
-r ift
-Ġag ony
-ĠS ands
-Ġ25 4
-C ash
-Ġl odge
-Ġp unt
-N atural
-ĠIde as
-Ġerrone ous
-ĠSens or
-ĠHann ity
-Ġ19 21
-Ġm ould
-ĠG on
-kay a
-Ġanonym ously
-ĠK EY
-Ġsim ulator
-W inter
-Ġstream ed
-50 7
-? ",
-Ġte ased
-Ġco efficient
-Ġwart ime
-ĠTH R
-' '.
-ĠBank ing
-mp ire
-Ġf andom
-Ġl ia
-G a
-Ġdown hill
-Ġinterpre ting
-Ind ividual
-N orm
-Ġjealous y
-bit coin
-Ġple asures
-ĠToy s
-ĠChev rolet
-ĠAd visor
-IZ E
-Ġrecept ions
-70 6
-C ro
-Ġ26 2
-Ġcit rus
-ir u
-Review er
-ject ed
-U ES
-an z
-19 81
-ĠWork er
-Ġcompl ied
-ores cent
-contin ental
-T on
-ĠPr ism
-ĠShe ep
-Ġ28 8
-n ox
-ĠV og
-O rd
-Ġreal ms
-te k
-Ġirrig ation
-Ġbicy cles
-Ġelectron ically
-p oly
-t all
-() );
-Ġaest hetics
-ĠInteg rated
-Expl ore
-Ġd unk
-47 6
-p ain
-ĠJac ques
-ĠD mit
-Fram es
-Ġreun ited
-Ġhum id
-D ro
-P olitical
-Ġyouth ful
-Ġent ails
-Ġmosqu ito
-36 3
-spe cies
-Ġcoord inating
-ĠMay hem
-ĠMagn us
-M ount
-Impro ved
-ĠST ATE
-ATT LE
-Ġflow ed
-Ġtack led
-Ġfashion ed
-Ġre organ
-iv ari
-f inger
-Ġreluct antly
-et ting
-ĠV and
-you ng
-ĠGar land
-Ġpresum ption
-Ġamen ities
-ĠPle asant
-on ential
-ĠO xy
-Ġmor als
-ĠY ah
-Read y
-Sim on
-En h
-D emon
-Ġcl ich
-Mon itor
-ĠD U
-Ġwel comes
-Ġstand out
-Ġdread ful
-Ġban anas
-Ġball oons
-h ooting
-bas ic
-Ġsuff ix
-Ġd uly
-can o
-Ch ain
-at os
-Ġgeop olitical
-Ġ( &
-ĠGem ini
-ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ
-Ġacqu itted
-L uck
-prot ect
-10 24
-Ġsc arcity
-Ġmind fulness
-ec ided
-D N
-pr ime
-ĠPres idents
-ĠVID EO
-Ġ( âĪĴ
-add ock
-N OR
-ĠP ru
-p un
-ĠL OL
-)) ))
-ĠL iqu
-ĠS AS
-Ġsty ling
-Ġpunish ments
-Ġnum b
-Ġasc ertain
-ĠRock ies
-f lu
-Th umbnail
-Ġperpet rated
-ĠSem i
-Ġdis arm
-ĠOld er
-ĠEx ception
-Ġexponent ially
-ĠCommun ities
-Ġabol ish
-ĠPart ner
-pt oms
-Ġ7 77
-ĠFo ley
-ĠC ases
-Ġgre ase
-ĠReb irth
-G round
-Ġ; )
-ĠDoct rine
-ik ini
-Y e
-ĠBl ossom
-Ġpers ists
-b ill
-Ġinf usion
-Ġbud dies
-9 11
-ĠPat ient
-Ġdem os
-Ġacquaint ance
-ĠP aw
-at ari
-Ġx ml
-Ġfasc ination
-ĠSer ve
-Ï Ĥ
-br anded
-Ġa z
-Return s
-Ġover shadow
-Ġro am
-Ġspeed y
-n umbered
-hel ial
-Ġdisc iple
-Ġass urances
-g iven
-pect ing
-ĠN atalie
-çĶ °
-Ġmosquit oes
-rote in
-Ġnumer ic
-Ġindepend ents
-Ġtrans itional
-Ġreaction ary
-ĠMech dragon
-do ctor
-Ġshort est
-Ġsequ ential
-ĠB ac
-ĠAccount s
-ãģ Į
-ach y
-ract ive
-ĠReg iment
-Ġbreat htaking
-ffic iency
-ĠB ates
-Ġ3 11
-Ġward robe
-ft s
-ĠBer k
-Sim ply
-ĠRivers ide
-iver ing
-ident ial
-lu cent
-Ġen riched
-ĠCon ver
-ĠG iving
-ãĥ Ļ
-Ġlegal ize
-ĠF TC
-Ġfre aking
-M ix
-Ġter restrial
-es ian
-ci ents
-W ing
-LO AD
-Ġled ge
-ĠViol ent
-ĠMet all
-Ġ30 8
-Ġs outheastern
-hett o
-M eat
-Ġslow down
-Ġret reated
-Jere my
-end as
-**** *
-er ic
-Ġre ins
-opp able
-ĠHuman ity
-ear ances
-rig an
-C amera
-Ġwa ivers
-s oc
-Ġalter ation
-trans form
-ĠC emetery
-50 6
-Ġindef inite
-Ġstim ulating
-y g
-60 3
-ĠS op
-Ġdescript ive
-Ph ase
-ĠEd mund
-Ġpneum onia
-vent us
-A mb
-Ġlabor atories
-ĠEx clusive
-ug ar
-W ere
-Ġmalf unction
-Ġhomosexual s
-Ġ---- ---
-un i
-Ġturb ines
-ĠEqu ity
-D u
-Ġmind ed
-ĠR H
-ĠBlack hawks
-Ġfe ats
-Ġ17 00
-re pl
-36 2
-lad en
-Ġindisp ensable
-ly ss
-tt i
-Ġre el
-Ġdiver ted
-Ġlik eness
-Ġsubscript ions
-Ġfing ert
-Ġfil thy
-dest ruct
-d raft
-ĠBernard ino
-l aunch
-Ġper plex
-ĠS UM
-car b
-Ġswe ater
-ĠVent ure
-ĠJ ag
-ĠCele b
-ĠV oters
-Ġstead fast
-Ġathlet ics
-ĠHans on
-ĠDr ac
-Tr acker
-Ġcomm end
-ĠPres idency
-ĠD ID
-in formed
-Ġweb page
-P retty
-Ġforce fully
-ãĥĥ ãĤ¯
-Ġrel ocation
-Ġsat ire
-â ī
-ĠSunder land
-æ Ħ
-V oice
-???? ????
-Ġinform ant
-Ġbow el
-ĠUn iform
-Ġ ..."
-Ġpur ge
-Ġpic nic
-ĠU mb
-ĠU PDATE
-ĠSapp hire
-ĠSt all
-le arn
-Ġobject ively
-Ġob liter
-Ġlooph ole
-Ġjour neys
-Ġo mission
-Pro s
-ĠSid ney
-pl oma
-Ġspray ed
-Ġg uru
-Ġtra itor
-Ġtim et
-Ġsn apping
-ĠSe vent
-urn al
-ĠUk ip
-Ġb owed
-por al
-l iberal
-R os
-Quest ions
-i OS
-Ġsummar ize
-ST AT
-Ġ18 50
-ap est
-Ġl ender
-ĠVari able
-br inging
-ĠL ORD
-, )
-Ġcollaps es
-x iety
-ĠN ed
-Y D
-ĠSch a
-Ġantib ody
-Ġdis band
-y re
-ill usion
-Ġro ver
-s hed
-ĠHiro sh
-cc i
-Ġcal am
-ĠMort on
-P interest
-Ġ19 28
-ĠE uras
-ord es
-Ġf ences
-ĠIn ventory
-ĠVal encia
-ĠU d
-ĠT iff
-Ġsqu e
-Ġqu otation
-Ġtroubles ome
-er ker
-QU EST
-ĠKing doms
-s outh
-Ġle vy
-Pr ince
-ĠSt ing
-Ġnick named
-Ġapp e
-Ġphot ographic
-Ġcorp us
-re ference
-ĠT rog
-U nt
-) =(
-ĠLat via
-Ġactiv ating
-Ġlicense e
-Ġdispar ities
-ĠNews letter
-ãĥĥ ãĥĪ
-Ġfree ing
-ĠJe ep
-ĠPer ception
-ins k
-Ġsil icone
-ĠHay den
-Le an
-ĠSuz uki
-ibr arian
-66 8
-Ġsp or
-Ġcorrel ations
-ag hetti
-Ġtu ber
-ĠIP CC
-il us
-ĠV u
-Ġwealth iest
-ĠCarb uncle
-an za
-Ġfool ed
-ĠZ ur
-Ġd addy
-ran o
-il ian
-Ġknock out
-f man
-requ ired
-ĠWik ileaks
-ĠD uffy
-ON T
-Ġins ol
-ĠObject s
-Ġb ou
-ĠNord ic
-ĠIns ert
-sc an
-Ġd ancers
-Ġid iots
-major ity
-ĠNev ille
-ĠFree BSD
-Ġt art
-pan ic
-69 0
-Ġcoc oa
-Ġsam pled
-Ġlook up
-Ind ust
-Ġinject ions
-gen re
-Ġa u
-Ġroad way
-Ġgen itals
-K ind
-ĠEx aminer
-ĠY az
-F resh
-Ġpar alysis
-ĠAl uminum
-Ġre ap
-ok é
-Ġsl oppy
-ĠTun nel
-pos ium
-ner y
-en ic
-Ġher bal
-ĠOut er
-ĠBuild er
-Ġinc ur
-Ġide ologies
-Ġback ups
-cons uming
-ĠDet ect
-de ck
-ĠKN OW
-ĠG ret
-ĠM IC
-Ġtough ness
-ĠEx hibit
-Ġh ive
-L es
-ĠSCH OOL
-ĠAt ari
-ald e
-ĠN ull
-and estine
-m ouse
-Ġbrig ade
-48 9
-Ġrev ol
-ĠLaw son
-ĠW ah
-op oly
-eb ted
-ĠS aunders
-Ġ3 13
-ĠW inc
-Ġtab oo
-ĠHel met
-Ġw edge
-ch ip
-ĠT ina
-b g
-Ġinf uri
-r n
-Ġanomal ies
-ĠSy nc
-ĠEx am
-ĠComm it
-ĠDi ary
-ĠALS O
-ĠDe bor
-omed ical
-Ġcomprehens ion
-6 55
-Ġempower ing
-Ġ ire
-Ġju ices
-ĠE TH
-ĠBox ing
-=" /
-Ġfacilit ated
-p oke
-ĠPars ons
-ĠMod er
-tra vel
-Ġcivil izations
-Ġliber tarians
-Ġrun e
-ĠCl arks
-at hed
-Ġcampaign ers
-ĠDis patch
-ĠFah renheit
-ĠCap com
--------- --
-Ġl ace
-Ġdr aining
-Ġl iner
-ĠArt ificial
-é n
-t ask
-] ).
-ĠGM O
-ĠOper ator
-ord inary
-ĠInf luence
-ĠU ps
-Ġpot ency
-uss en
-osp ons
-ĠSw im
-ĠDead line
-Un ity
-Ġcul inary
-Ġenlight enment
-Ġwe arer
-Ġmin ed
-Ġp ly
-Ġinc est
-ĠDVD s
-W alk
-B TC
-Tr ade
-Ġdev al
-ib and
-ĠOvers ight
-Palest inian
-Ġd art
-Ġm ul
-L R
-Ġrem ovable
-ĠReal ms
-ì Ŀ
-Ġmisc ar
-ĠV ulkan
-68 5
-è re
-ĠS ap
-Ġmer ging
-ĠCar ly
-che ster
-Ġbr isk
-Ġlux urious
-ĠGener ator
-Ġbit terness
-Ġed ible
-Ġ24 3
-T G
-Ġrect angle
-With No
-bel ow
-J enn
-Ġdark est
-Ġh itch
-Ġdos age
-Ġsc aven
-ĠK eller
-ĠIllust rated
-Certain ly
-ĠMaver icks
-Marg inal
-Ġdiarr hea
-Ġenorm ously
-Ġ9 99
-sh r
-qu art
-Ġadam ant
-ĠM ew
-Ġren ovation
-Ġcerv ical
-ĠPercent age
-en ers
-ĠKim ber
-Ġflo ats
-Ġde x
-ĠW itcher
-ĠSwan sea
-d m
-Ġsal ty
-y ellow
-Ġca pe
-ĠDr ain
-ĠPaul a
-ĠTol edo
-les i
-Mag azine
-ĠW ick
-ĠM n
-ĠA ck
-ĠR iding
-AS ON
-Ġhom ophobic
-AR P
-Ġwand ered
-C PU
-ood oo
-ĠP ipe
-Ġtight ening
-ĠBut t
-3 18
-Ġdesert ed
-S ession
-Ġfacilit ating
-J ump
-Ġemer gencies
-OW ER
-Ġexhaust ive
-ĠAF TER
-Ġheart beat
-ĠLab el
-ack y
-ĠCert ified
-ilt ration
-Z e
-ĠU tt
-Ġ13 00
-Ġpres ume
-ĠDis p
-Ġsur ged
-Ġdoll s
-Col umb
-Ġchim pan
-ĠR azor
-Ġt icks
-Ġcouncill or
-Ġpilgr image
-ĠReb els
-ĠQ C
-ĠA uction
-x ia
-ik k
-b red
-Ġinsert ion
-Ġco arse
-d B
-SE E
-ĠZ ap
-ĠF oo
-Ġcontem por
-ĠQuarter ly
-ot ions
-ĠAl chemist
-ĠT rey
-ĠDu o
-S weet
-80 4
-ĠGi ov
-Ġfun n
-N in
-h off
-Ġram ifications
-Ġ19 22
-ĠExper ts
-az es
-Ġgar ments
-ar ial
-ĠN ab
-Ġ25 7
-ĠV ed
-Ġhum orous
-ĠPom pe
-Ġn ylon
-Ġlur king
-ĠSerge y
-ĠMatt is
-Ġmisogyn y
-ĠComp onents
-ĠWatch ing
-ĠF olk
-ract ical
-B ush
-Ġt aped
-Ġgroup ing
-Ġbe ads
-Ġ20 48
-Ġcon du
-quer que
-Read ing
-Ġgriev ances
-Ult ra
-Ġend point
-H ig
-ĠSt atic
-ĠScar borough
-L ua
-ĠMess i
-a qu
-ĠPsy Net
-ĠR udd
-Ġa venue
-v p
-J er
-Ġsh ady
-ĠRes ist
-ĠArt emis
-Ġcare less
-Ġbro kers
-Ġtemper ament
-Ġ5 20
-T ags
-ĠTurn ing
-Ġut tered
-Ġp edd
-Ġimpro vised
-Ġ: (
-Ġtab l
-Ġpl ains
-16 00
-press ure
-ĠEss ence
-marg in
-friend s
-ĠRest oration
-Ġpoll ut
-ĠPok er
-ĠAugust ine
-ĠC IS
-ĠSE AL
-or ama
-Ġth wart
-se ek
-Ġp agan
-Â º
-cp u
-Ġg arn
-Ġass ortment
-ĠI LCS
-t ower
-Recomm ended
-Ġun born
-ĠRandom Redditor
-ĠRandomRedditor WithNo
-Ġparaly zed
-Ġeru ption
-Ġinter sect
-ĠSt oke
-ĠS co
-B ind
-å ¾
-ĠP NG
-ĠNeg ative
-ĠNO AA
-Le on
-Ġall oy
-ĠL ama
-ĠD iversity
-5 75
-Ġunderest imated
-ĠSc or
-Ġm ural
-Ġb usted
-so on
-l if
-Ġnone x
-Ġall ergy
-ĠUnder world
-ĠR ays
-ĠBl asio
-Ġh rs
-ĠD ir
-Ġ3 27
-by ter
-Ġrepl acements
-Ġactiv ates
-ri ved
-M H
-Ġp ans
-ĠH I
-Ġlong itudinal
-Ġnu isance
-al er
-Ġsw ell
-ĠS igned
-s ci
-ĠIs les
-ĠA GA
-Ġdef iant
-Ġson ic
-oc on
-K C
-ĠA im
-t ie
-ah ah
-Ġm L
-D X
-Ġb isc
-ĠBill board
-ĠSY STEM
-NE Y
-ga ard
-Ġdist ressed
-former ly
-Al an
-Ġche fs
-Ġopt ics
-ĠC omet
-ĠAM C
-Ġredes igned
-irm ation
-Ġsight ings
-38 2
-3 11
-ĠW B
-Ġcont raction
-ĠT OTAL
-D ual
-Ġstart led
-Ġunderstand ably
-Ġsung lasses
-ETH OD
-Ġd ocker
-Ġsurf ing
-ĠH EL
-ĠSl ack
-ton es
-Ġsh alt
-Vis ual
-49 8
-Dep artment
-c ussion
-Ġunrest ricted
-Ġt ad
-Ġre name
-employ ed
-Ġeduc ating
-Ġgrin ned
-bed room
-ĠActiv ities
-ĠV elvet
-ĠSW AT
-Ġsh uffle
-ig or
-Ġsatur ation
-F inding
-c ream
-ic ter
-Ġv odka
-tr acking
-te c
-Ġfore ground
-iest a
-Ġve hement
-ĠEC B
-ĠT ie
-E y
-Ġt urtles
-ĠRail road
-ĠKat z
-ĠFram es
-Ġmen ace
-ĠFell owship
-ĠEss ential
-ugg ish
-Ġdri p
-ch witz
-ĠKy oto
-s b
-ĠN ina
-Param eter
-Ġal arms
-ĠCl aud
-Ġpione ering
-Ġchief ly
-ĠSc ream
-Col lection
-Ġthank fully
-ĠRonald o
-åŃ IJ
-st rip
-ĠDisney land
-com mercial
-See ing
-S oul
-Ġevac uate
-Ġc iv
-ĠAs he
-Ġdiv ides
-ĠD agger
-rehens ive
-Ġber ries
-ĠD F
-Ġs ushi
-Ġplur ality
-W I
-Ġdisadvant aged
-Ġbatt alion
-ob iles
-45 1
-Ġcl ing
-Ġunden iable
-ĠL ounge
-Ġha unt
-p he
-Ġquant ify
-Ġdiff ered
-Ġ[* ]
-ĠV iz
-c um
-sl ave
-Ġvide og
-Ġqu ar
-Ġbund les
-ĠAl onso
-t ackle
-Ġneur onal
-Ġlandsl ide
-conf irmed
-ĠDep th
-Ġrenew ables
-B ear
-ĠMaced onia
-Ġjer seys
-Ġb unk
-ĠSp awn
-ĠControl s
-ĠBuch anan
-Ġrobot ics
-Ġemphas izing
-ĠTut orial
-h yp
-ist on
-Ġmonument al
-æ °
-ĠCar ry
-Ġt bsp
-en ance
-H ill
-art hed
-Ġro tten
-De an
-Ġtw isting
-Ġgood will
-Ġimm ersion
-L iving
-Ġbr ushes
-ĠC GI
-ĠAt k
-tr aditional
-Ġph antom
-ĠSt amina
-Ġexpans ions
-ĠMar in
-Ġembark ed
-ĠE g
-int estinal
-ĠPE OPLE
-ĠBo oth
-ĠApp alach
-Ġreleg ated
-V T
-M IT
-Ġmust er
-Ġwithdraw ing
-Ġmicrosc ope
-ĠG athering
-ĠC rescent
-ĠArgent ine
-ĠDec re
-ĠDomin ic
-Ġbud s
-ant age
-ĠI on
-Ġwid ened
-ONS ORED
-ĠGl oves
-iann opoulos
-raz en
-fe el
-Ġrepay ment
-Ġhind sight
-ĠRE ALLY
-ĠPist ol
-ĠBra h
-Ġwat ts
-Ġsurv ives
-Ġfl urry
-iss y
-Al ert
-ĠUrug uay
-Ph oenix
-S low
-ĠG rave
-ĠF ir
-Ġmanage able
-Ġtar iff
-ĠU DP
-ĠPist ons
-ĠNiger ian
-Ġstrike outs
-Ġcos metics
-whel ming
-f ab
-c ape
-pro xy
-Ġre think
-Ġover coming
-sim ple
-Ġw oo
-Ġdistract ing
-ĠSt anton
-ĠTuls a
-ĠD ock
-65 9
-Ġdisc ord
-ĠEm acs
-ĠV es
-ĠR OB
-Ġreass uring
-Ġcons ortium
-Muslim s
-3 21
-Ġprompt s
-se i
-ĠH itch
-imp osed
-ĠF ool
-Ġindisc rim
-wr ong
-bu querque
-D avis
-! ]
-Ġtim eless
-ĠNE ED
-Ġpestic ide
-Ġrally ing
-ĠCal der
-Ġå ¤
-Ġx p
-ĠUn le
-ĠEx port
-lu aj
-B uff
-)
-B oot
-ĠChrys ler
-or ative
-M ess
-Ġneglig ible
-ert odd
-ĠMush room
-ĠG ale
-g c
-ĠCos by
-ĠR ural
-rit ical
-B ell
-Ġturb ine
-00 200000
-Ġlegit imately
-ĠAnim ated
-T ED
-ĠThe odore
-c onduct
-ĠH ier
-Ġcounterfe it
-ĠAlger ia
-Ġun beat
-cont roller
-Ġun res
-Ġscram bling
-ĠFall on
-T es
-Ġam ber
-Ġroy alties
-ĠShel ter
-ĠL ester
-Ġclass ify
-Rem ote
-Ġun heard
-Ġcontrovers ies
-Ġenrich ment
-ĠYan kee
-g amer
-Ġpl atinum
-Ġec ology
-ĠS ark
-Ġunt ouched
-Ġsuper visors
-Ġ" %
-Ġf ooth
-Ġcomm ons
-Ġnarc otics
-Ġind ices
-ĠP ly
-Ġaddition ally
-ĠGaw ker
-ĠE Q
-Pl aying
-Ġcave at
-ĠAbs olute
-oss us
-B aby
-Ġr ation
-Ġres in
-Ġcalib ration
-ĠNew port
-Ġkn ocks
-v t
-Ġcomp ost
-Sc ene
-Ġsar cast
-Ġkiss es
-Ġn s
-all i
-ĠMar cel
-ĠP iet
-iat rics
-Ġsurround s
-ĠRep rodu
-ĠPhill ies
-Ġuncertain ties
-ĠE ur
-ĠRom ance
-ĠH ath
-ĠNeed s
-ĠCl oak
-Ġcre m
-que ue
-Ġ3 55
-Ġup front
-] );
-Ġrecip roc
-Ġ19 27
-Ġ11 00
-ut su
-Ġdep ressive
-ow ment
-F ans
-Ġme ch
-Ġann ihil
-Ġcounter terrorism
-ĠFig ures
-b old
-ĠMo ines
-ĠDri vers
-Ġmanuscript s
-ĠCrypt o
-Ġhyp not
-redd its
-Ġprosec utions
-Ġdiver t
-CR IP
-ĠB ene
-ĠRe ggie
-Ġtax ing
-ĠMor ales
-ent ing
-t ur
-sign ificant
-ĠPR OV
-Ġstr ands
-Ġp ouch
-ĠR ookie
-» Ĵ
-Ġnic er
-he my
-h w
-EC A
-Ġintimid ated
-Ġstr icter
-Ġmicro bial
-det ails
-Ġv ows
-Ġqu ake
-hh hh
-Ġrein vent
-U b
-Ġrel inqu
-ĠBuff ett
-lic ensed
-itte red
-ĠPic ard
-Ġche wing
-u cl
-organ ic
-Ġlocal ized
-ĠEconom ist
-Ġacqu ainted
-Def inition
-s ed
-Crit ics
-Ġc c
-45 3
-38 1
-Ġfell ows
-Ġcheck points
-0 25
-Ġre election
-Ġmed iated
-ĠK DE
-Ġhurd le
-Ġtext ing
-Per fect
-Ġtrust ees
-fect ure
-Ġd ich
-mon ary
-Ġdist inctions
-Ġ14 00
-Ġus her
-Ġparas ites
-ĠSh aring
-ĠV im
-Ġbar becue
-ĠMin isters
-ere lla
-Ġe b
-Ġm c
-ĠSome how
-ĠIn sect
-ch anges
-b road
-ĠBy z
-Ġgrap es
-66 9
-Ġ= ================
-Ġass imil
-Ġhaun ting
-Ġfire power
-Ġdef amation
-em phasis
-Ġcomp ose
-Ġallerg ies
-Ġstr ang
-roll ers
-b ang
-Ġbrew ers
-ron gh
-ri ot
-p oor
-c old
-S ample
-Ġbu oy
-0 40
-ĠCourt ney
-Ġ26 8
-ĠWed ding
-70 2
-Ġobsess ive
-Ġbra king
-ĠL al
-an ical
-å ¦
-at en
-Con struction
-Ġclin ically
-iers hip
-N ames
-ĠDisc uss
-ĠRam os
-Ġloc ale
-ĠAgric ultural
-En able
-Ġhorse power
-ent ure
-P ref
-C ourt
-Ġstaff ing
-Ġfut uristic
-dri vers
-ĠMarket place
-æĪ ¦
-Friend s
-Ġdam ning
-ĠCustom ers
-Ġwe eds
-ĠM ai
-Ġag ile
-ĠT att
-ic ent
-R anked
-cro ft
-ĠKat y
-Ext reme
-Ġcar ve
-ĠR over
-ĠBy ron
-37 2
-Ġconduct s
-r atch
-it ia
-ĠPump kin
-Sad ly
-Rel oaded
-P olicy
-Ġl ick
-pe ak
-is ks
-ĠCD s
-ĠEn cyclopedia
-in itial
-C os
-ĠAware ness
-ĠD ram
-$$ $$
-Ġr iff
-Ġscript ure
-run ners
-Ġbo iler
-ons on
-o in
-Ġham string
-Ġcat aly
-ĠArch bishop
-ch all
-Ġf aux
-ok in
-local host
-ĠN AME
-ad obe
-S AN
-am ate
-Ġscram ble
-Ġcar c
-ĠMan ifest
-ĠCed ar
-ĠSer gio
-l ater
-ff er
-Ġgrapp ling
-ĠDe utsche
-agon ists
-ĠNew sp
-Ġpret ended
-arch ment
-Ġcur ated
-Ġhead phone
-ĠUn common
-ĠS IGN
-A gent
-Ġdead lines
-Ġhorizont ally
-ĠM AT
-ĠSum mers
-Ġord ained
-ĠLast ly
-ĠKend all
-Ġfr ig
-ĠMach ina
-ĠWater loo
-ĠMex icans
-Ġprotect or
-Ġgl are
-} "
-Prem ium
-Ġr ift
-ĠTelesc ope
-Met al
-Ġrec apt
-Ġ; ;
-Ġincl ination
-Ġimp oses
-ing en
-^ {
-Ġh aste
-Ġd olphins
-Ġcomm uters
-pl anned
-c ong
-m x
-ĠU pload
-Ġext rap
-ĠTuc son
-ĠExpl oration
-efe ated
-Ġsl ender
-70 3
-ĠB uk
-is el
-Ġcompet itiveness
-ch lor
-ĠP ermanent
-ĠE verett
-ĠSpecial ist
-ĠS OL
-Ġcy an
-ĠEx actly
-U F
-ĠL IFE
-ary l
-on et
-ĠEmploy ee
-aw ed
-ĠRat ings
-Ġextra vag
-ul hu
-ĠPl ane
-Ġelev ate
-ĠCoord inator
-ĠWat kins
-Ġex cludes
-Ġsent ient
-Ġep och
-Ġall oc
-Pre viously
-ĠSh y
-ĠSlov akia
-L OCK
-Ġmarked ly
-Ġkn ob
-Ġadventure rs
-ĠBe en
-ĠCost s
-amm ers
-Ġon slaught
-ĠSupport ed
-ĠT au
-ik arp
-ĠS overe
-ĠHam pton
-ãĤ ī
-Pre v
-ĠW orse
-Ġc ottage
-ĠH ades
-le z
-b owl
-Ġfrag rance
-ĠL ok
-EM OTE
-ĠPet ro
-Ġ19 25
-ĠP end
-produ cing
-Ġrel ocate
-v ati
-p ole
-Ġsem in
-ĠN UM
-Ġrock ed
-b uff
-b ly
-Rep ly
-ĠH ai
-Ġartic ulated
-ĠIslam abad
-66 5
-ĠClaim s
-Des ktop
-Ġtrust ee
-Ġscript ing
-ĠS ob
-ĠAs ylum
-STD OUT
-ĠCl own
-ĠD ortmund
-ĠDev on
-l ite
-ĠMar ble
-Ġb unker
-Ġcre st
-Ġarous al
-ĠS ears
-ĠBudd y
-ered ith
-ĠP olly
-Ġdec ode
-ĠV ish
-ĠRef lect
-an on
-Ġrefund s
-imm ers
-H M
-Ġwip ing
-Ġpuzz led
-Ġmat te
-un o
-P ierre
-) ),
-Ġt ainted
-Ġsymbol ism
-ĠF raz
-Ġprotest ors
-ethe us
-%% %%
-W ra
-Ġl ax
-ad em
-atur ation
-ãĥ ĵ
-ĠTra iler
-ĠE NG
-ĠBows er
-Ġatt m
-D ur
-80 7
-Ġsid x
-Ġc ider
-ĠA ffect
-Ġw oven
-ĠBark er
-ben ef
-Ġdst g
-ĠRy u
-> [
-Ġsq or
-S audi
-Ġis tg
-Ġindul ge
-pro c
-Ġdisg usted
-Ġcomp ounded
-Ġn em
-Ġschool ing
-ĠC ure
-process ing
-S ol
-Ġpro verb
-it ized
-ĠAlv arez
-Ġscar f
-Ġrect angular
-re ve
-Ġh ormonal
-ĠSt ress
-itiz en
-Ġ4 25
-girl s
-ĠNo ir
-ĠR app
-Ġmar ches
-ch urch
-ĠUs es
-Ġ40 5
-ĠBer m
-Ġord inances
-ĠJud gment
-Charg es
-ĠZ in
-Ġdust y
-Ġstraw berries
-Ġper ce
-ĠTh ur
-ĠDebor ah
-net flix
-ĠLam bert
-Ġam used
-ĠGu ang
-Y OU
-R GB
-ĠC CTV
-Ġf iat
-r ang
-Ġf ederation
-ĠM ant
-ĠB ust
-ĠM are
-respect ive
-ĠM igration
-ĠB IT
-59 0
-Ġpatriot ism
-Ġout lining
-reg ion
-ĠJos é
-Ġbl asting
-ĠEz ra
-B s
-Ġundermin es
-ĠSm ooth
-Ġcl ashed
-rad io
-Ġtransition ing
-ĠBucc aneers
-ĠOw l
-Ġplug s
-Ġh iatus
-ĠPin ball
-Ġm ig
-ĠNut r
-ĠWolf e
-Ġinteg ers
-Ġor bits
-ĠEd win
-ĠDirect X
-b ite
-Ġbl azing
-v r
-Ed ge
-ĠP ID
-ex it
-ĠCom ed
-ĠPath finder
-ĠGu id
-ĠSign s
-ĠZ er
-ĠAg enda
-Ġreimburse ment
-M esh
-i Phone
-ĠMar cos
-ĠS ites
-h ate
-en burg
-Ġs ockets
-p end
-Bat man
-v ir
-ĠSH OW
-Ġprovision al
-con n
-ĠDeath s
-AT IVE
-Pro file
-sy m
-J A
-Ġnin ja
-inst alled
-id ates
-eb ra
-ĠOm aha
-Ġse izing
-ĠBe asts
-Ġsal ts
-M ission
-Gener ally
-ĠTr ilogy
-he on
-leg ates
-Ġd ime
-Ġf aire
-par able
-G raph
-Ġtotal ing
-Ġdiagram s
-ĠYan uk
-ple t
-ĠMe h
-Ġmyth ical
-ĠStep hens
-aut ical
-ochem istry
-Ġkil ograms
-Ġel bows
-anc ock
-ĠB CE
-ĠPr ague
-Ġimpro v
-ĠDev in
-Ġ" \
-par alle
-Ġsuprem acists
-ĠB illion
-Ġreg imen
-inn acle
-Ġrequ isite
-ang an
-ĠBur lington
-ain ment
-ĠObject ive
-oms ky
-G V
-Ġun ilateral
-Ġt c
-Ġh ires
-ment al
-Ġinvol untary
-Ġtrans pl
-ĠASC II
-Â ¨
-Ev ents
-Ġdoub ted
-ĠKa plan
-ĠCour age
-ig on
-ĠMan aging
-ĠT art
-Ġfalse hood
-ĠV iolet
-Ġair s
-Ġfertil izer
-Brit ain
-Ġaqu atic
-ou f
-W ords
-ĠHart ford
-Ġeven ings
-ĠV engeance
-qu ite
-G all
-ĠP ret
-Ġp df
-ĠL M
-ĠSo chi
-ĠInter cept
-9 20
-Ġprofit ability
-ĠId le
-ĠMac Donald
-ĠEst ablishment
-um sy
-Ġgather ings
-ĠN aj
-Charl ie
-Ġas cent
-ĠProt ector
-Ġal gebra
-Ġbi os
-for ums
-EL S
-Introdu ced
-Ġ3 35
-Ġastron omy
-Cont ribut
-ĠPol ic
-Pl atform
-Ġcontain ment
-w rap
-Ġcoron ary
-ĠJ elly
-man ager
-Ġheart breaking
-c air
-ĠChe ro
-c gi
-Med ical
-ĠAccount ability
-! !"
-oph ile
-Ġpsych otic
-ĠRest rict
-Ġequ itable
-iss ues
-Ġ19 05
-ĠN ek
-c ised
-ĠTr acking
-Ġo zone
-Ġcook er
-ros is
-Ġre open
-Ġinf inity
-ĠPharm aceutical
-ens ional
-Att empt
-ĠR ory
-Mar co
-Ġawa its
-H OW
-t reated
-Ġbol st
-Ġreve red
-Ġp ods
-opp ers
-00 10
-Ġampl itude
-ric an
-SP ONSORED
-Ġtrou sers
-Ġhal ves
-ĠK aine
-ĠCut ler
-ĠA UTH
-Ġsplend id
-Ġprevent ive
-ĠDud ley
-if acts
-umin ati
-ĠY in
-Ġad mon
-ĠV ag
-Ġin verted
-Ġhast ily
-ĠH ague
-L yn
-Ġled ger
-Ġastron omical
-get ting
-Ġcirc a
-ĠC ic
-ĠTenn is
-Lim ited
-Ġd ru
-ĠBY U
-Ġtrave llers
-Ġp ane
-ĠInt ro
-Ġpatient ly
-Ġa iding
-Ġlo os
-ĠT ough
-Ġ29 3
-Ġconsum es
-Source File
-Ġ"" "
-Ġbond ing
-Ġtil ted
-Ġmenstru al
-ĠCel estial
-UL AR
-Plug in
-Ġrisk ing
-N az
-ĠRiy adh
-Ġacc redited
-Ġsk irm
-é Ľ
-Ġexam iner
-Ġmess ing
-Ġnear ing
-ĠC hern
-ĠBeck ham
-Ġsw apped
-Ġgo ose
-K ay
-Ġlo fty
-ĠWal let
-Ġ[ '
-Ġap ocalypse
-Ġb amboo
-ĠSP ACE
-ĠEl ena
-Ġ30 6
-ac ons
-Ġtight ened
-Ġadolesc ence
-Ġrain y
-Ġvandal ism
-ĠNew town
-Ġcon ject
-c akes
-Ġche ated
-Ġmoder ators
-par ams
-E FF
-Ġdece it
-ĠST L
-ĠTanz ania
-ĠR I
-Ġ19 23
-ĠEx ile
-the l
-Ġthe olog
-Ġquir ky
-ĠIr vine
-Ġneed y
-or is
-U m
-K a
-Ġmail box
-3 22
-Ġb os
-ĠPet ra
-K ING
-Ġenlarg ed
-O ften
-Ġbad ass
-Ġ3 43
-ĠPl aces
-ĠC AD
-Ġpr istine
-Ġinterven ing
-d irection
-Ġl az
-ĠD SM
-Ġproject ing
-ĠF unk
-ag og
-pay ment
-n ov
-Ġch atter
-AR B
-Ġexam inations
-ĠHouse hold
-ĠG us
-F ord
-4 14
-B oss
-Ġmy stic
-Ġle aps
-ĠB av
-ul z
-b udget
-Foot ball
-Ġsubsid ized
-Ġfirst hand
-Ġcoinc ide
-oc ular
-Con n
-ĠColl abor
-Ġfool s
-am ura
-ah ar
-r ists
-Ġsw ollen
-Ġexp ended
-ĠP au
-s up
-Ġsp ar
-Ġkey note
-s uff
-Ġunequ al
-Ġprogress ing
-str ings
-ĠGamer gate
-Dis ney
-ĠEle ven
-om nia
-Ġscript ed
-Ġear ners
-bro ther
-ĠEn abled
-æ ³
-Ġlar vae
-ĠL OC
-m ess
-Wil son
-ĠTem plate
-success fully
-Ġparam ount
-Ġcamoufl age
-Ġbind s
-ĠQu iet
-ĠSh utterstock
-r ush
-Ġmasc ot
-fort une
-ĠCol t
-ĠBe yon
-hab i
-Ġha irc
-Ġ26 7
-ĠDe us
-Ġtw itch
-Ġconcent rating
-Ġn ipples
-c ible
-Ġg ir
-N Z
-M ath
-n ih
-Requ ired
-Ġp onder
-ĠS AN
-Ġwedd ings
-Ġl oneliness
-N ES
-ĠMah jong
-69 5
-add le
-ĠGar ner
-ĠC OUR
-Br idge
-Ġsp ree
-ĠCald well
-Ġbri bery
-Ġ���� ����
-plug ins
-Ġr acket
-Ġchamp agne
-vers ible
-V ote
-Ġmod ifiers
-May or
-6 80
-Ġassemb lies
-ĠS ultan
-ĠN ing
-ĠLad ies
-Ġsulf ur
-Ġor bs
-Ġ---- -
-____ ___
-ĠJournal ism
-Ġes ports
-Ġl ush
-Ġh ue
-Ġspect ral
-H onest
-ãĥ ı
-Ġbus hes
-Ġrein forcement
-Ġre opened
-ĠWhe els
-ĠM org
-rie ving
-Ġaux iliary
-Ġj Query
-ĠB AT
-tes que
-Ġver tex
-p ure
-f rey
-ãĤ º
-d os
-Ġty ph
-Ġc ull
-Ġe q
-Ġdec on
-Ġtoss ing
-Ġdispar ate
-ĠBr igham
-print f
-led ged
-Ġsu nd
-Ġco zy
-Ġhepat itis
-per forming
-Ġav al
-ĠG G
-f uture
-Ġpet ertodd
-ĠKos ovo
-Ġmagn ets
-Al ready
-ĠEd ison
-ĠCe res
-ĠRA ID
-Ġbrill iance
-57 6
-Ġder ives
-Ġhypert ension
-ĠÎ Ķ
-Ġlamb da
-Ġfl air
-Ġmission aries
-Ġrap es
-ĠSt arter
-ĠMon ths
-Ġdef y
-Ġseism ic
-ĠR aphael
-Ġeuro zone
-65 6
-z sche
-Ġscr atched
-Ġb ows
-ĠLenn on
-ĠGa ia
-Ġdri pping
-f acts
-A le
-Ġfrog s
-ĠBre ast
-ogene ity
-ĠProsecut or
-Ġampl ified
-ĠHod g
-ĠF n
-Th ousands
-ĠNI H
-ĠMonitor ing
-FT WARE
-ĠPri ebus
-ĠG rowing
-hun ter
-Ġdiagn ose
-ĠM ald
-ĠL R
-Ġcrown ed
-Ġburst ing
-Ġdiss olution
-j avascript
-Ġuseful ness
-ĠExec ution
-: (
-ĠIv ory
-a ah
-Ġpersecut ed
-viol ence
-ist as
-ĠCr ate
-Ġimpuls es
-ĠSp ani
-ed es
-Hand le
-ĠZ erg
-think able
-Last ly
-Ġspont aneously
-Ġinconven ient
-Ġdismiss ing
-Ġpl otted
-Ġeight y
-Ġ7 37
-r ish
-ĠThor nton
-ath am
-Ġsit com
-V en
-Rec ipe
-t el
-l und
-Ġcle ars
-ĠSas uke
-Ġ25 8
-Ġopt ing
-Ġen raged
-est hetic
-ĠA e
-uch s
-Pre p
-Fl ow
-Ġrun off
-ĠE ating
-ĠG iles
-ĠAct ing
-res ources
-ib aba
-Ġr pm
-Ġske wed
-ĠBl anc
-ĠS akuya
-Ġhot ter
-Ġ19 24
-op ian
-ck o
-Ġcr umbling
-Ġcapt ains
-ĠAppropri ations
-le aders
-dro pping
-an uts
-Ġrevers ing
-ĠP ose
-ĠS ek
-Sc ot
-ĠIde a
-c ise
-ĠSloven ia
-Ġ3 17
-Do ctor
-Ġcro cod
-ald i
-Se a
-ĠFar rell
-Ġmerc enaries
-ĠR NC
-ĠGu ess
-Ġp acing
-M achine
-Streamer Bot
-ĠChar ity
-Ġ29 8
-Ġcann ons
-ĠTob y
-TPP StreamerBot
-ĠPass ion
-cf g
-Th om
-Ġbad ges
-ĠBern stein
-. âĢĵ
-ĠP OP
-ĠCon j
-Ġinitial ization
-Ġbiod iversity
-D ub
-Ġfeud al
-Ġdisclaim er
-Ġc row
-Ġign ition
-ar f
-S HA
-Ġk Hz
-h azard
-ĠArt ists
-oe uv
-67 9
-ĠRud y
-N ine
-ĠRam adan
-å ½
-itt o
-Ġadren aline
-C ert
-Ġsmell ed
-Ġimp unity
-Ġag endas
-ĠRe born
-ĠCon cent
-ĠSe ems
-Ġo mega
-ĠDust in
-Ġback er
-ĠSau ce
-ĠBoy le
-W IN
-Ġsp ins
-Ġpa uses
-u pt
-Ġshred ded
-Ġstra pped
-ĠCor ruption
-Ġscr atches
-Ġn i
-Ġatt ire
-ĠS AF
-Factory Reloaded
-ĠI PS
-Ġ( %
-Ġsem inar
-f ocus
-c ivil
-Ġ18 60
-int osh
-Ġcontin ual
-Ġabbre vi
-ĠS ok
-oc obo
-X M
-Ġfr antic
-Ġunavoid able
-Ġar tery
-Ġannot ations
-b ath
-Cl imate
-Ġd ors
-ĠSl ide
-co ord
-ĠRel oad
-ĠL DL
-ĠLove craft
-Ġunim agin
-Ġresemb led
-Ġbarr acks
-n p
-Ġsurrog ate
-Ġcategor ized
-ãĤ ©
-Ġvacc inated
-Ġdrain age
-Ġind ist
-ĠWhats App
-Ġ18 70
-oler ance
-inv oke
-am orph
-Ġrecon nect
-Ġem anc
-Ġblind ness
-Ġ12 80
-intern et
-c ollar
-Ġalt ru
-Ġab yss
-ĠT RI
-65 7
-Ġinf used
-HE AD
-Ġforest ry
-ĠWood y
-ĠC i
-w i
-s am
-78 4
-hol iday
-Ġmog ul
-ĠF ees
-ĠD EN
-In ternal
-ur bed
-f usc
-at om
-ĠIll usion
-Ġpoll ed
-Ġfl ap
-Ġco ax
-L GBT
-An aly
-ĠSect ions
-ĠCalif orn
-em n
-Ġh ither
-ĠN IGHT
-Ġn ailed
-ĠPip eline
-39 1
-o of
-ĠPr imal
-vere nd
-Ġsl ashing
-Ġret ri
-avi our
-Ġdepart ing
-g il
-IS C
-Ġmid way
-Ġultras ound
-Ġbeh aving
-ĠT ara
-class es
-V irtual
-ĠColon ial
-Ġstri pping
-Ġorchestr ated
-ĠGra ves
-45 2
-ĠIron ically
-ĠWrit ers
-Ġl ends
-ĠMan z
-Ġra ven
-Ġoxid ative
-Ġ26 6
-EL F
-act ually
-asc ar
-D raft
-Ġfavour able
-Ġhumili ating
-Ġf idelity
-ĠH of
-ĠX uan
-49 6
-Ġlay ered
-at is
-79 0
-Ġpay check
-it on
-K ar
-ĠVM ware
-ĠFar mer
-Ġserv ic
-gl omer
-Ġsl ump
-ĠFab ric
-ĠD OC
-est ing
-Ġreass ure
-Ġph yl
-v olt
-it ory
-R ules
-Ġoxid ation
-Ġpri zed
-Ġmist ress
-ĠDj ango
-WAR N
-å ij
-Ġenc ode
-ĠFeed back
-Ġstupid ity
-I an
-ĠYugoslav ia
-× ¨
-ac l
-UT E
-19 77
-Ġqual ifies
-Ġpuls es
-pret ty
-Ġfro ze
-Ġs s
-Iter ator
-Ġur gently
-Ġm ailed
-ĠCh am
-Ġsust aining
-Ġbas il
-Ġpupp ies
-il ant
-ĠP LEASE
-l ap
-ace ous
-F ear
-ĠMaster y
-aut omatic
-ĠT AG
-Ġant im
-ag les
-47 3
-fram es
-Ġwh ispers
-ĠWho ever
-Ġbra very
-ĠUK IP
-ract ions
-"" "
-Ġt ame
-Ġpart ed
-every thing
-CON T
-Ġind ebted
-Ġadd r
-re k
-IR ED
-Ġem inent
-cl inton
-Ġo usted
-Ġreview er
-Ġmelt down
-Ġre arr
-ĠY ao
-the real
-aby te
-Ġst umbling
-Ġbat ches
-Ġ25 9
-Ġcontrace ptive
-Ġprost itute
-ens is
-De cl
-ĠSt rikes
-M ilitary
-ĠO ath
-v acc
-pp ings
-05 2
-Ġpart Name
-amp ing
-Rep orts
-K I
-CH R
-Ġsubt ly
-sw ers
-Bl ake
-us ual
-Ġcontest ants
-Ġcart ridges
-ĠGRE AT
-Ġbl ush
-ĠâĢ º
-47 2
-Ġreason ed
-ãĥ ¤
-paralle led
-Ġd yn
-ag ate
-Ġnight ly
-å Ĩ
-55 6
-Ġsem antic
-ĠAdv oc
-Ġ !!
-Ġdisag rees
-ĠB W
-V eh
-Ġharm ing
-Ġembr aces
-Ġstri ves
-Ġin land
-ĠK ard
-Ġhe ats
-ĠGin ny
-ut an
-ern aut
-yl ene
-ĠE lev
-J D
-Ġh ars
-ĠStar r
-Ġsk ysc
-Ġcollabor ators
-Us ually
-Ġrev olutions
-ĠSTAT S
-Ġdism antle
-Ġconfident ly
-Ġkin etic
-Al i
-Ġpercent ile
-Ġextract ing
-ill ian
-est ead
-Ġphysic ists
-ĠMarsh al
-Ġfell owship
-Ġd ashed
-ĠU R
-ĠSi oux
-ĠComp act
-am ide
-P ython
-ĠLe igh
-ĠPharm ac
-ist rates
-her ical
-Ġf ue
-ĠE min
-Ġ( {
-ĠNeighbor hood
-Ġdisrupt ing
-ĠD up
-Ġg land
-ĠSe v
-ĠMar ian
-arg on
-ĠD und
-Ġ< !--
-Ġstr and
-Ġstadium s
-z os
-Ġpsych osis
-ĠR ack
-Ġbrilliant ly
-ï¸ ı
-Ġsubmer ged
-ĠInst it
-ĠCh ow
-Ġc ages
-ĠH ats
-ĠU rs
-Ġdil uted
-us at
-ien ne
-ĠMembers hip
-ĠBur k
-Ġ ie
-Ġarche type
-D rug
-ult on
-ĠSp ock
-ĠMcK ay
-ĠDep end
-F eatured
-S oc
-19 78
-ĠB ere
-Ġrelent lessly
-Ġcripp ling
-Ġar thritis
-çĶ Ł
-ĠTrop ical
-ĠBul g
-ĠCher yl
-Ġadm irable
-Ġsub title
-Over ride
-Ġorig inating
-ĠC CP
-Ġsw ore
-ĠSo le
-ĠDis orders
-3 29
-Ġprocess ion
-Ġref urb
-Ġimm ersed
-requ ently
-Ġskept ics
-Ġcer amic
-m itter
-en stein
-b elt
-ĠT IT
-b idden
-Ġf ir
-m ist
-> ]
-Ġwe ave
-ĠParad ox
-Ġentr usted
-ĠBarcl ays
-Ġnovel ist
-og ie
-80 6
-Ġnin ety
-Ġdisag reements
-@@@@ @@@@
-ĠAus chwitz
-c ars
-ĠL ET
-t ub
-arant ine
-P OS
-Ġback story
-Ġcheer ful
-ĠR ag
-ek a
-bi ased
-Ġinexper ienced
-ak ra
-ĠW itt
-t an
-Ġrap ist
-Ġplate au
-ch al
-ĠInqu is
-exp ression
-Ġc ipher
-Ġsh aving
-add en
-re ly
-( \
-ism a
-ĠReg ulatory
-CH AR
-ily n
-N VIDIA
-G U
-Ġmur m
-la us
-Christ opher
-Ġcontract ual
-ĠPro xy
-ĠJa ime
-ĠMethod ist
-Ġstew ards
-st a
-per ia
-Ġphys iology
-Ġbump ed
-Ġf ructose
-Austral ian
-ĠMet allic
-ĠMas querade
-ar b
-Ġprom ul
-Ġdown fall
-Ġbut cher
-Ġb our
-ĠIN FORMATION
-ĠB is
-pect s
-ad ena
-Ġcontempl ating
-ar oo
-cent ered
-ĠPe aks
-Us ed
-Ġmod em
-Ġg enders
-Ġ8 000
-37 1
-Ġm aternity
-ĠR az
-Ġrock ing
-Ġhandgun s
-ĠD ACA
-Aut om
-ĠN ile
-Ġtum ult
-ĠBenef it
-ĠAppro ach
-works hop
-ĠLe aving
-G er
-inst ead
-Ġvibr ations
-Ġrep ositories
-49 7
-ĠA unt
-ĠJ ub
-ĠExp edition
-Al pha
-Ġs ans
-Ġoverd ue
-Ġoverc rowd
-Ġlegisl atures
-Ġp aternal
-ĠLeon ardo
-Ġexp ressive
-Ġdistract ions
-Ġsil enced
-tr ust
-Ġb iking
-Ġ5 60
-Ġpropri et
-Ġimp osition
-Ġcon glomer
-Ġ= ================================================================
-ĠTe aching
-ĠY ose
-int ensive
-T own
-Ġtroll ing
-ĠGr ac
-ĠAS US
-Y o
-Ġspecial s
-ĠNep h
-ĠGod zilla
-Dat abase
-ĠHe gel
-Ġ27 2
-19 76
-ĠGl oria
-Ġdis emb
-ĠInvestig ations
-ĠB ane
-ag ements
-St range
-Ġtre asury
-ĠPl ays
-Ġundes irable
-Ġwid ening
-Ġverb ally
-Ġinf ancy
-Ġcut ter
-f ml
-Ġ21 00
-prot otype
-f ine
-Ġdec riminal
-Ġdysfunction al
-Ġbes ie
-ĠErn st
-z eb
-Ġnort heastern
-Ġa ust
-por ate
-ĠMar lins
-Ġsegreg ated
-ew orld
-ĠMa her
-Ġtra verse
-Ġmon astery
-ur gy
-G ear
-s and
-Com pl
-ĠE MP
-Ġpl ent
-ĠMer cer
-Ġ27 6
-TA BLE
-Config uration
-H undreds
-Ġpr ic
-Ġcollabor ating
-ĠPar amount
-ĠCumm ings
-Ġ( <
-Ġrecord er
-Ġfl ats
-Ġ4 16
-wh ose
-Font Size
-ĠOr bit
-Y R
-Ġwr ists
-Ġb akery
-) }
-ĠB ounty
-ĠLanc aster
-Ġend ings
-acc ording
-ĠSal am
-e asy
-75 5
-ĠBur r
-ĠBarn ett
-onom ous
-Un ion
-Ġpreced ence
-ĠScholars hip
-ĠU X
-Ġroll out
-Ġbo on
-al m
-ĠCan ter
-æ µ
-Ġround ing
-Ġcl ad
-Ġv ap
-ĠF eatured
-is ations
-Ġ5 40
-pol ice
-Ġunsett ling
-Ġdr ifting
-ĠLum ia
-ĠObama Care
-ĠF avor
-Hy per
-ĠRoth schild
-ĠMil iband
-an aly
-ĠJul iet
-H u
-Ġrec alling
-a head
-69 6
-Ġunf avorable
-Ġd ances
-O x
-Ġleg ality
-Ġ40 3
-rom ancer
-Ġinqu ire
-ĠM oves
-\ ">
-ĠVari ant
-ĠMess iah
-ĠL CS
-ĠBah á
-75 6
-Ġeyeb row
-ĠÂ ¥
-ĠMc F
-ĠFort y
-M as
-Ġpan icked
-Ġtransform ations
-q q
-Ġrev olves
-ring e
-ĠA i
-ax e
-Ġon ward
-ĠC FR
-ĠB are
-log in
-Ġliqu ids
-Ġde comp
-second ary
-il an
-ĠCon vert
-ami ya
-Ġprosecut ing
-Ġâī ¡
-ĠYork ers
-ĠByr ne
-sl ow
-aw ei
-J ean
-Ġ26 9
-ĠSky dragon
-Ġ é
-ĠNicarag ua
-ĠHuck abee
-ĠHigh ly
-Ġamph ib
-ĠPast or
-ĠL ets
-Ġbl urred
-Ġvisc eral
-ĠC BO
-Ġcollabor ated
-z ig
-Leg al
-Ġapart heid
-Ġbr id
-Ġpres et
-ĠD ET
-ĠAM A
-× Ķ
-arch ing
-auc uses
-build er
-Ġpo etic
-Ġem ulator
-ĠMole cular
-Ġhon oring
-ise um
-Ġtract or
-ĠCl uster
-ĠCal m
-ared evil
-Ġsidew alks
-Ġviol in
-Ġgeneral ized
-ĠAle c
-Ġemb argo
-Ġfast ball
-ĠHT TPS
-ĠL ack
-ĠCh ill
-ri ver
-C hel
-ĠSw arm
-ĠLev ine
-ro ying
-L aunch
-Ġkick er
-Ġadd itive
-ĠDe als
-W idget
-cont aining
-Ġescal ate
-ĠOP EN
-Ġtwe aked
-Ġst ash
-Ġsp arks
-ĠEs sex
-ĠE cc
-Ġconv ict
-Ġblog ging
-I ER
-ĠH L
-Ġmurd erers
-75 9
-ĠH ib
-Ġde pl
-ĠJ ord
-S ac
-Ġdis sect
-ĠHow e
-os her
-Ġcustom izable
-ĠFran z
-Ġat ro
-Ä ĩ
-Ġ000 4
-Ġout post
-R oss
-Ġglyph osate
-ĠHast ings
-ĠBE FORE
-Ġsh ove
-o pped
-ĠSc ala
-Ġam ulet
-an ian
-Ġexacerb ated
-Ġe ater
-47 1
-UM E
-Ġpul p
-izont al
-ĠZ am
-ĠAT I
-imm une
-aby tes
-Ġunnecess arily
-ĠC AT
-ĠAx is
-Ġvisual ize
-Ã ī
-ĠRad ical
-f m
-Doc uments
-ĠFor rest
-Ġcontext ual
-ĠSy mbol
-Ġtent ative
-ĠDO ES
-ĠGood s
-Ġintermitt ent
-} :
-medi ated
-Ġridic ule
-Ġathe ism
-Ġpath ogens
-ĠM um
-Ġre introdu
-Ġ30 7
-i HUD
-Ġflash light
-Ġsw earing
-Ġp engu
-B u
-Ġrot ated
-ĠCr ane
-Ġ() );
-Ġfashion able
-Ġendors ing
-46 3
-) [
-Ġingest ion
-Ġcook s
-Ġ9 50
-ot omy
-ĠIm am
-Ġk a
-Ġte aser
-ĠGhost s
-ĠãĤ µ
-19 69
-Ï ĥ
-ub by
-Ġconver ter
-zan ne
-end e
-ĠPre par
-ĠNic kel
-ĠChim era
-h im
-ĠTyr ann
-ĠSabb ath
-ĠNich ols
-Ġra pt
-ih ar
-Ġshe lling
-Ġillum inate
-Ġdent ist
-ut or
-ĠInteg ration
-Ġwh ims
-ĠLiter ary
-Be aut
-Ġp archment
-ag ara
-Br and
-Ġder og
-âĢ¦ )
-ĠNor se
-Ġunw itting
-Ġc uc
-Ġborder line
-Ġupset ting
-Ġrec ourse
-Ġd raped
-ĠRad ar
-Ġcold er
-ĠPep si
-im inary
-], [
-65 8
-V i
-ĠF rem
-ĠP es
-Ġveter inary
-ĠT ED
-ĠEp idem
-n ova
-k id
-Ġdev out
-o ct
-j ad
-M oh
-ĠP AY
-Ġge ometric
-Ġ3 23
-Ġcircum ference
-ich ick
-19 75
-ĠY uri
-ĠSh all
-ĠH over
-un in
-S pr
-Ġg raft
-ĠHapp iness
-Ġdisadvant ages
-att acks
-Ġhub s
-ĠStar Craft
-é ĸ
-Ġgall eries
-ĠKor ra
-Ġgrocer ies
-ĠGors uch
-Ġrap ists
-Ġfun gi
-ĠTyph oon
-V ector
-ĠEm press
-b attle
-4 68
-Ġparas ite
-ĠBom ber
-S G
-ex ist
-ĠP f
-Ġun se
-Ġsurge ons
-B irth
-ĠUn sure
-ĠPrint ed
-ĠBehavior al
-ĠA ster
-Pak istan
-Ġun ethical
-Ġs v
-ĠIo T
-Ġlay outs
-P ain
-Ġconst ants
-ĠL W
-ĠB ake
-Ġtow els
-Ġdeterior ation
-ĠBol ivia
-Ġblind ed
-ĠW arden
-ĠMist ress
-Ġon stage
-Ġcl ans
-ĠB EST
-19 60
-Ġant ique
-Ġrhet orical
-ĠPer cy
-ĠRw anda
-, .
-B ruce
-Ġtra umat
-ĠParliament ary
-Ġfoot note
-id ia
-ĠLear ned
-se eking
-gen ic
-Ġdim ensional
-H ide
-èĢ ħ
-Ġintrig ue
-in se
-Ġle ases
-Ġapp rentices
-w ashing
-Ġ19 26
-V ILLE
-Ġsw oop
-s cl
-Ġbed rooms
-on ics
-ĠCr unch
-comp atible
-Ġincap ac
-ĠYemen i
-ash tra
-z hou
-d anger
-Ġmanifest ations
-ĠDem ons
-AA F
-Secret ary
-ACT ED
-L OD
-Ġam y
-ra per
-eth nic
-4 17
-Ġpos itives
-Ġ27 3
-ĠRefuge es
-Ġus b
-ĠV ald
-odd y
-ĠMahm oud
-As ia
-Ġskull s
-ĠEx odus
-ĠComp et
-ĠL IC
-ĠM ansion
-ĠA me
-Ġconsolid ate
-storm s
-ont ent
-99 6
-Ġcl en
-Ġm ummy
-fl at
-75 8
-ĠV OL
-oter ic
-n en
-ĠMin ute
-S ov
-Ġfin er
-R h
-ly cer
-Ġreinforce ments
-ĠJohann es
-ĠGall agher
-Ġgym n
-S uddenly
-Ġext ortion
-k r
-i ator
-T a
-Ġhippocamp us
-N PR
-ĠComput ing
-Ġsquare ly
-Ġmod elling
-ĠFor ums
-ĠL isp
-ĠKrish na
-Ġ3 24
-Ġr ushes
-Ġens ued
-Ġcre eping
-on te
-n ai
-il ater
-ĠHorn ets
-Ġob livious
-IN ST
-55 9
-Ġjeopard y
-Ġdistingu ishing
-j ured
-Ġbeg s
-sim ilar
-ph ot
-5 30
-ĠPark way
-Ġs inks
-ĠHearth stone
-ib ur
-ĠBat on
-Av oid
-Ġd ancer
-Ġmag istrate
-ary n
-Ġdisturb ances
-ĠRom ero
-Ġpar aph
-Ġmis chief
-âĸ ĵ
-ĠSh aria
-Ġur inary
-r oute
-iv as
-f itted
-Ġeject ed
-ĠAl buquerque
-Ġ4 70
-Ġirrit ated
-ĠZ ip
-ĠB iol
-Ã į
-Ġden ounce
-Ġbin aries
-ĠVer se
-Ġopp os
-ĠKend rick
-ĠG PL
-Ġsp ew
-ĠEl ijah
-ĠE as
-Ġdr ifted
-so far
-Ġannoy ance
-ĠB ET
-47 4
-ĠSt rongh
-it ates
-ĠCogn itive
-oph one
-ĠIdent ification
-ocr ine
-connect ion
-Ġbox er
-ĠAS D
-ĠAre as
-Y ang
-t ch
-ull ah
-Ġdece ive
-Comb at
-ep isode
-cre te
-W itness
-Ġcondol ences
-ht ar
-Ġhe als
-Ġbuck ets
-ĠLA W
-B lu
-Ġsl ab
-ĠOR DER
-oc l
-att on
-ĠSteven son
-ĠG inger
-ĠFriend ly
-ĠVander bilt
-sp irit
-ig l
-ĠReg arding
-ĠPR OG
-Ġse aling
-start ing
-Ġcard inal
-ĠV ec
-ĠBe ir
-Ġmillisec onds
-we ak
-per se
-Ġster ile
-ĠCont emporary
-ĠPh ant
-ĠCl o
-Ġout p
-Ġex iled
-Ġ27 7
-Ġself ie
-Ġman ic
-Ġn ano
-ter ms
-Alex ander
-Ġres olves
-Ġmillenn ia
-Ġexpl odes
-Ġconst ellation
-Ġadul tery
-m otion
-D OC
-Ġbroad casters
-Ġkinderg arten
-ĠMay weather
-ĠE co
-ich o
-Ġ28 7
-l aun
-Ġm ute
-Ġdisc reet
-Ġpres chool
-Ġpre empt
-De lete
-ĠFre ed
-P i
-H K
-Ġblock er
-ĠC umber
-Ġw rought
-d ating
-Ġins urer
-Ġquot as
-Ġpre ached
-Ġev iction
-ĠReg ina
-ĠP ens
-Ġsevent een
-ĠN ass
-D ick
-Ġfold s
-Ġd otted
-ĠA ad
-Un iversal
-Ġp izz
-ĠG uru
-Ġso ils
-Ġno vice
-ĠNe ander
-Ġst ool
-Ġdeton ated
-ĠPik achu
-ĠMass ive
-IV ER
-ĠAb del
-Ġsubdu ed
-Ġtall est
-Ġprec arious
-Ġa y
-r ification
-ĠOb j
-c ale
-Ġun question
-cul osis
-ad as
-igr ated
-D ays
-Ġque ens
-ĠGaz ette
-ĠCol our
-ĠBow man
-ĠJ J
-ï ve
-Ġdomin ates
-Stud ent
-Ġm u
-Ġback log
-ĠElect ro
-Tr uth
-48 3
-Ġcond ensed
-r ules
-ĠCons piracy
-Ġacron ym
-hand led
-ĠMat te
-j ri
-ĠImp ossible
-l ude
-cre ation
-Ġwar med
-ĠSl ave
-Ġmis led
-Ġfer ment
-ĠK ah
-ink i
-ke leton
-cy l
-ĠKar in
-Hun ter
-Reg ister
-ĠSur rey
-Ġst ares
-ĠW idth
-ĠN ay
-ĠSk i
-Ġblack list
-uck et
-Ġexp ulsion
-im et
-Ġret weet
-vant age
-Fe ature
-Ġtro opers
-Ġhom ers
-9 69
-Ġconting ency
-ĠW TC
-ĠBrew er
-fore ign
-W are
-S olar
-Ġund ue
-RE C
-ulner able
-path ic
-ĠBo ise
-Ġ3 22
-Ġarous ed
-ĠY ing
-ä¸ į
-uel ess
-Ġp as
-Ġmor p
-Ġfl oral
-Ex press
-ud ging
-k B
-ĠGr anted
-Ø ¯
-ĠMich a
-ĠGoth ic
-ĠSPEC IAL
-ĠRic ardo
-F ran
-Ġadminister ing
-6 20
-por a
-ĠÂ ®
-Ġcomprom ises
-Ġb itten
-Ac cept
-Th irty
-Ð ²
-Ġmater ially
-ĠTer r
-ig matic
-ch ains
-Ġdo ve
-stad t
-Mar vel
-FA ULT
-Ġwind shield
-Ġ3 36
-ad ier
-Ġsw apping
-Ġflaw less
-ĠPred ator
-ĠMiche le
-Ġprop ulsion
-ĠPsych ic
-Ġassign ing
-Ġfabric ation
-Ġbar ley
-l ust
-Ġtow ering
-Ġalter cation
-ĠBent ley
-Sp here
-Ġtun a
-ĠClass es
-Fre edom
-un er
-L ady
-v oice
-Ġcool est
-or r
-Ġpal p
-$ {
-Ġhyster ia
-ĠMet atron
-p ants
-Ġspawn ing
-Exper ts
-ĠInvest ors
-ĠAn archy
-Ġshr unk
-ĠVict im
-Ġ28 9
-Ġec stasy
-ĠB inding
-58 5
-ĠMel ody
-57 8
-ot ally
-ĠE tsy
-lig a
-Ġapplaud ed
-Ġswe ating
-Ġredist ributed
-Ġpop corn
-Ġsem inal
-f ur
-ĠNeuro science
-R and
-ĠO st
-ĠMadd en
-ĠIncre asing
-ĠDaw kins
-ĠSub way
-Ġar sen
-cons erv
-B UR
-Ġsp iked
-ĠLy ft
-ĠImper ium
-ĠDrop box
-Ġfav oured
-Ġencomp asses
-gh ost
-Ġins pires
-Ġbur geoning
-ĠY oshi
-ĠVert ical
-ĠAud itor
-Ġint ending
-Ġfilib uster
-Bl oom
-f ac
-ĠCav s
-ign ing
-Ġcowork ers
-ĠBarb arian
-rem ember
-FL AG
-Ġaudit ory
-ason ry
-Col lege
-Ġmut ed
-gem ony
-ob in
-ĠPsych o
-9 68
-Ġlav ish
-Ġhierarch ical
-ĠDr one
-ou k
-Ġcripp led
-ĠMax im
-Sl ot
-Ġqu iz
-ĠV id
-if ling
-Ġarchae ologists
-Ġabandon ment
-d ial
-le on
-ĠF as
-T ed
-Ġr aspberry
-Ġmaneu vers
-Ġbehavi ours
-Ġins ure
-Ġrem od
-Sw itch
-h oe
-Ġsp aced
-Ġafford ability
-ĠF ern
-not ation
-ĠBal anced
-Ġoccup ies
-en vironment
-Ġneck lace
-Ġsed an
-F U
-ĠBrav o
-Ġab users
-ĠAn ita
-met adata
-ĠG ithub
-ait o
-ĠF aster
-ĠWass erman
-ĠF lesh
-Ġth orn
-r arily
-ĠMer ry
-w ine
-Ġpopul ace
-ĠL ann
-Ġrepair ing
-Ġpsy che
-Ġmod ulation
-aw aru
-âĢĭ âĢĭ
-ari j
-Ġdecor ations
-Ġapolog ise
-ĠG arg
-app ly
-Ġgive away
-ĠFl an
-ĠWy att
-U ber
-Ġauthor ised
-ĠMor al
-HAHA HAHA
-activ ate
-Ġtorped o
-ĠF AR
-Ġam assed
-ĠA ram
-ark in
-ĠVict ims
-st ab
-Ġo m
-ĠE CO
-Ġopio ids
-Ġpurpose ly
-ĠV est
-Ġer g
-at an
-ĠSur gery
-Ġcorrect ing
-ĠOrt iz
-ĠBe et
-Ġrev oke
-Ġfre eway
-ĠH iggins
-F ail
-ĠFar ms
-ĠAT P
-h ound
-Ġp oking
-ĠCommun ists
-mon ster
-iment ary
-Ġunlock ing
-Ġunf it
-we ed
-en ario
-at ical
-ĠEnlight enment
-ĠN G
-ĠComp ensation
-de en
-ĠWid ow
-ĠCind y
-ĠAfter wards
-Ġ6 000
-ikh ail
-ag ically
-Ġrat ified
-Ġcasual ty
-H OME
-p sey
-f ee
-Ġspark ling
-Ġd é
-Ġconcert ed
-C atal
-Ġcomp lying
-ĠA res
-ĠD ent
-Sh ut
-Ġsk im
-ad minist
-Ġhost ilities
-ĠG ins
-Ġ6 08
-Ġm uddy
-ĠMc Int
-ĠDec ay
-5 25
-Ġconspic uous
-ĠEx posure
-Ġresc ind
-Ġwear able
-Ġ3 28
-our met
-ah s
-ĠRob ots
-Ġe clips
-inst ance
-ĠRE PORT
-ĠApp l
-0 30
-ĠSk ies
-01 00
-Ġfall acy
-S ocket
-ĠRece iver
-Ġsol ves
-ĠButter fly
-ĠSho pping
-ĠFI RE
-65 4
-Med ic
-Ġsing ers
-ĠNeed less
-'' ''
-isher s
-ĠD ive
-58 8
-Ġselect ively
-Ġcl umsy
-88 9
-Ġpurch aser
-ear ned
-ard y
-Ġbenef iting
-eng lish
-Ġyield ing
-ĠP our
-Ġspin ach
-Ġdel ve
-ĠC rom
-6 10
-Ġexport ing
-ĠMA KE
-Ġ26 3
-Ġg rop
-Ġenv oy
-ĠInqu iry
-ĠLu igi
-d ry
-ĠT uring
-Thumbnail Image
-ĠVar iety
-Ġfac et
-Ġfl uffy
-Ġexcerpt s
-Ġsh orth
-ĠOl sen
-CL UD
-Ġrel iant
-ĠUN C
-T our
-Ġbat hing
-Comp any
-Ġglobal ization
-P red
-ĠMalf oy
-Ġh oc
-j am
-craft ed
-ĠBond s
-ĠKiss inger
-Eng land
-Ġorder ly
-cat entry
-Ġ26 1
-Ġexch anging
-ĠInt ent
-ĠAmend ments
-D OM
-Ġst out
-³³³³³³³³ ³³³³³³³³
-ĠAir bus
-Ġ27 8
-hy de
-P oll
-Item ThumbnailImage
-Ġlooph oles
-ĠPill ar
-Ġexpl or
-St retch
-A part
-Ġun married
-Lim it
-ĠTransform ers
-Ġintellect ually
-unct ure
-18 00
-Ġd arn
-B razil
-Ġleft over
-ber us
-f red
-Mine craft
-3 26
-ĠForm s
-Ġproof s
-ĠDes igned
-Ġindex es
-ĠSupp ose
-EM S
-ĠL oving
-ĠBon nie
-im ating
-OT US
-Ġconduct or
-Ġbehav ed
-ĠF ren
-Ġsy nerg
-Ġmillenn ium
-Ġcater ing
-ĠL auder
-W r
-ĠY iannopoulos
-ĠAT F
-Ġensl aved
-Ġawaken ed
-D VD
-ĠED ITION
-ĠConc ert
-ĠChall enger
-ĠH aku
-umer ic
-Ġdep recated
-ĠSH AR
-4 12
-Ġdy stop
-Ġtremb ling
-Ġdread ed
-ĠSp ac
-p adding
-Re pl
-ĠG arrison
-M ini
-Ġun paralleled
-am ar
-URR ENT
-w reck
-c ertain
-t al
-ĠC LS
-app ings
-Ġsens ed
-Ġf encing
-ĠPas o
-ĠDes k
-Ġsc off
-Ġcontem plate
-ĠL iga
-l iquid
-75 7
-Ġapp rentice
-ĠUCH IJ
-5 70
-ĠTh ousand
-ĠIll um
-Ġchampion ed
-ãĤ Į
-Ġelect ors
-Ġ3 98
-ĠH ancock
-round ed
-ĠJ OHN
-Ġuns atisf
-Ġqual ifier
-ĠGad get
-EN E
-Ġdead liest
-ĠPl ants
-Ġ ions
-Ġacc ents
-Ġtwe aking
-Ġsh aved
-F REE
-ĠCh aser
-Again st
-9 60
-Ġmeth amphetamine
-Ġnormal ized
-Ġ$ \
-ĠPre cision
-ĠGu am
-Ġch oked
-ĠX II
-ĠCast ing
-Tor rent
-Ġscal p
-ĠJagu ar
-w it
-Ġsem ic
-ix ie
-ĠG ould
-Ġconf ines
-N usra
-ĠL on
-ĠJ ugg
-y cle
-ĠCod ec
-E gypt
-Ġrest rain
-ĠAl iens
-Ġch oking
-ĠD unk
-ĠBell a
-ab c
-Ġsl ang
-Ġneuro trans
-s av
-Ġempower ment
-â ĨĴ
-Ġclim bers
-ĠM im
-ĠF ra
-ros se
-Cap ital
-ĠCth ulhu
-Inter face
-Ġprof icient
-ĠIN TO
-Ġ3 18
-ront al
-5 80
-ĠDes pair
-K enn
-Ġscrim mage
-ĠCo at
-as ions
-Ġwall paper
-ĠJ ol
-Ġresurg ence
-Ġant iv
-ĠB alls
-² ¾
-Ġbuff ers
-Ġsub system
-ĠSt ellar
-ĠL ung
-A IDS
-Ġerad icate
-Ġblat antly
-Ġbehav es
-ĠN un
-Ġant ics
-ex port
-DE V
-w b
-Ġph p
-ĠInteg rity
-Ġexplore r
-Ġrev olving
-auth ored
-g ans
-Ġbas k
-Ġas ynchronous
-å į
-TH ING
-69 8
-G ene
-ĠR acer
-ĠN ico
-iss ued
-Ġser mon
-p ossibly
-Ġsize of
-Ġentrepreneur ial
-ox in
-ĠMin erva
-Ġpl atoon
-n os
-ri ks
-A UT
-ĠAval anche
-ĠDes c
-ij 士
-ĠP oc
-Ġconf erred
-Î »
-Ġpat ched
-F BI
-66 2
-Ġfract ures
-Ġdetect s
-Ġded icate
-Ġconstitu ent
-Ġcos mos
-W T
-Ġswe ats
-Ġspr ung
-b ara
-s olid
-Ġuns us
-Ġbul ky
-ĠPhilipp e
-ĠFen rir
-Ġtherap ists
-ore al
-^^ ^^
-Ġtotal ed
-Ġboo ze
-ĠR PC
-Prosecut ors
-Ġdis eng
-ĠSh ared
-Ġmotor cycles
-Ġinvent ions
-Ġlett uce
-ĠMer ge
-ĠJ C
-Ġspiritual ity
-ĠWAR NING
-Ġunl ucky
-ĠT ess
-Ġtong ues
-ĠD UI
-T umblr
-Ġle ans
-Ġinv aders
-Ġcan opy
-ĠHur ricanes
-ĠB ret
-ĠAP PLIC
-id ine
-ick le
-Reg arding
-Ġve ggies
-Ġe jac
-ju ven
-F ish
-D EM
-ĠD ino
-Th row
-ĠCheck ing
-be ard
-( &
-Ġj ails
-Ġh r
-trans fer
-iv ating
-Ġfle ets
-ĠIm ag
-ĠMc Donnell
-Ġsnipp et
-Is a
-ĠCh att
-ĠSt ain
-ĠSet FontSize
-ĠO y
-ĠMathemat ics
-49 4
-Ġelectro ly
-ĠG ott
-ĠBr as
-B OOK
-ĠF inger
-d ump
-Ġmut ants
-Ġrent als
-Ġinter tw
-Ġc reek
-ail a
-Bro ther
-ĠDisc ord
-pe e
-raw ler
-Ġcar p
-Ġ27 9
-ãĤ· ãĥ£
-rel ations
-Ġcontr asts
-Col umn
-Ġrec onnaissance
-Ġun know
-Ġl ooting
-Ġregul ates
-Ġopt imum
-ĠChero kee
-ĠA ry
-Lat est
-Ġroad side
-Ġd anced
-ĠUnic orn
-A cknowled
-Ġuncont roll
-ĠM US
-at io
-ch ance
-ha ven
-VAL UE
-Ġfavour ites
-Ġceremon ial
-b inary
-pe ed
-wood s
-EM P
-Ġv ascular
-Ġcontempl ated
-Ġbar ren
-ĠL IST
-Y ellow
-ospons ors
-Ġwhisk y
-ĠM amm
-ĠDeV os
-min imum
-H ung
-44 2
-P ic
-ĠSnap dragon
-77 6
-Ġcar ving
-Ġund ecided
-Ġadvantage ous
-Ġpal ms
-ĠA Q
-Ġst arch
-L oop
-Ġpadd le
-Ġfl aming
-ĠHor izons
-An imation
-bo ost
-Ġprob abilities
-ĠM ish
-Ġex odus
-ĠEditor ial
-Ġfung us
-Ġdissent ing
-ĠDel icious
-rog ram
-ĠD yn
-d isk
-t om
-Ġfab rics
-ĠC ove
-ĠB ans
-Ġsoft en
-ĠCON S
-Ġin eligible
-Ġestim ating
-ĠLex ington
-pract ice
-of i
-Ġshe dding
-ĠN ope
-Ġbreat hed
-ĠCorinth ians
-y ne
-ek i
-B ull
-Ġatt aching
-reens hots
-Ġanaly se
-ĠK appa
-Ġuns ustainable
-Ġinter pol
-ank y
-he mer
-Ġprot agonists
-Ġform atted
-ĠBry ce
-ĠAch illes
-ĠAb edin
-sh ock
-Ġb um
-b os
-qu a
-ĠW arn
-q t
-ĠDi abetes
-8 64
-ĠIn visible
-Ġvan ish
-Ġtrans mitting
-Ġmur ky
-ĠFe i
-Ġawa ited
-ĠJur assic
-umm ies
-Ġmen acing
-g all
-C ath
-B uilt
-ild o
-ĠV otes
-Ġon t
-Ġmun itions
-ĠFre em
-ÃŃ n
-Ġdec ency
-lo pp
-ie ved
-ĠG ord
-Ġun thinkable
-ĠNews week
-Ġ3 21
-He at
-Ġpresent er
-ji ang
-Ġpl ank
-ĠAval on
-Ġben z
-ĠR out
-Ġslam ming
-ĠD ai
-ou ter
-ĠCook ie
-ĠAlic ia
-ge y
-Ġvan ity
-Ġow l
-á µ
-t ested
-ĠAw akens
-Ġcan v
-Ġblind ly
-ĠRid ley
-ĠEm ails
-Requ ires
-ĠSer bian
-ograp hed
-if rame
-eter ia
-Ġaltern ating
-qu iet
-Ġsoc iology
-ĠUn lock
-ĠCommun ism
-Ġo ps
-Ġatt ribution
-Ġab duction
-ĠAb ram
-Ġsidel ined
-ĠB OOK
-Ġref ining
-ĠFe eling
-ĠOs lo
-ĠPru itt
-r ack
-ang ible
-Ġcaut iously
-ĠM ARK
-eed s
-M ouse
-ĠStep h
-ĠP air
-S ab
-99 7
-ĠBa al
-B ec
-Ġcomm a
-ĠP all
-ĠG ael
-Ġmisunder stand
-ĠP esh
-Order able
-Ġdis mal
-ĠSh iny
-% "
-Ġreal istically
-Ġpat io
-ĠG w
-ĠVirt ue
-Ġexhaust ing
-wh atever
-oph ys
-y ip
-4 18
-Ad just
-ĠWa iting
-ess on
-ĠMaz da
-ĠDo zens
-Ġstream lined
-Ġincompet ence
-ĠM eth
-Ġeth os
-ON ES
-Ġincent iv
-Ġgr itty
-ĠBut cher
-Head er
-Ġexp onential
-Ã Ł
-Ġcorrel ate
-Ġcons ensual
-s ounding
-R ing
-Orig in
-Ġcon clusive
-fe et
-ac ly
-ĠF ernandez
-Buy able
-Ġd ucks
-aunt lets
-Ġel ong
-Ġ28 6
-Ġsim ul
-G as
-ĠK irst
-Ġprot r
-ĠRob o
-ĠAo E
-op ol
-Ġpsych ologically
-sp in
-ilater ally
-ĠCon rad
-W ave
-44 1
-ĠAd vertisement
-ĠHarm on
-ĠOri ental
-is Special
-Ġpresum ptive
-Ġw il
-ĠK ier
-ne a
-Ġp pm
-Ġhar bour
-ĠW ired
-comp any
-Ġcor oner
-atur days
-ĠP roud
-ĠN EXT
-ĠFl ake
-val ued
-ce iver
-Ġfra ught
-Ġc asing
-Ġrun away
-Ġg in
-ĠLaure nt
-ĠHar lem
-ĠCur iosity
-qu ished
-Ġneuro science
-ĠH ulu
-Ġborrow er
-Ġpetition er
-ĠCo oldown
-W ARD
-Ġinv oking
-conf idence
-For ward
-Ġst s
-pop ulation
-Delivery Date
-Fil m
-ĠC ov
-quick Ship
-quickShip Available
-prim ary
-isSpecial Orderable
-inventory Quantity
-channel Availability
-BO X
-ĠMulti player
-ĠJen ner
-77 8
-ĠM d
-Ġ~ /.
-M N
-Ġchild ish
-Ġantioxid ant
-ĠChrom ebook
-Ġ27 4
-Ġscreen play
-Ġadvent urous
-ĠRelations hip
-respons ive
-ming ton
-Ġcorner stone
-ĠF ey
-F IR
-Ġrook ies
-ĠF eaturing
-Ġorig inate
-Ġelectro des
-ant es
-Ġscript ures
-Ġgl ued
-Ġdiscont ent
-Ġaff licted
-lay out
-B rave
-Ġm osa
-ĠQuant ity
-ĠH ik
-w inner
-H ours
-Ġent ail
-ĠCell s
-olog ue
-Ġv il
-Ġpre acher
-Ġdecor ative
-d ifferent
-Ġprejud ices
-ĠSm oking
-ĠNotting ham
-so Type
-Ġrhyth ms
-ĠAl ph
-bl ast
-Ste el
-ĠDaniel le
-Ġstr ife
-Ġrem atch
-so DeliveryDate
-ĠF ork
-t rip
-ol ulu
-hes es
-C G
-ĠPOLIT ICO
-ost a
-ĠDr ift
-é¾įå ¥
-é¾įå¥ ij士
-Ġvet ting
-ĠJin ping
-ĠRec ession
-Min or
-ĠF raud
-enf ranch
-Ġconven ed
-ĠNA ACP
-ĠMill ions
-ĠFarm ing
-ĠW oo
-ĠFl are
-rit o
-imm igrant
-Ġvac ancy
-ĠHE AD
-ĠV aj
-eg al
-ĠV igil
-Stud y
-Ġru ining
-Ġr acks
-Ġhe ater
-ĠRand olph
-ĠBr ush
-ĠT ir
-Ø ¨
-Ġc ov
-% ]
-Ġrecount s
-ĠO PT
-ĠM elt
-Ġtr uce
-Ġcas inos
-Ġcrus ade
-Ġcarn age
-Ġstri pe
-ĠK yl
-Text ures
-Ġ6 98
-Ġpro clamation
-Ġgood ies
-Ġ........ ..
-pro claimed
-P olit
-Ġtop ical
-Ġspecial ize
-ĠA min
-g m
-Ġanch ored
-Ġbear ings
-s ample
-ĠHigh land
-ĠAut ism
-Ġmerc enary
-Ġinterview er
-L ER
-ĠSom ers
-Ġembry o
-ĠAss y
-Ġ28 1
-ĠEd iting
-ĠCh osen
-6 60
-Ġp ci
-ĠThunder bolt
-BI LL
-Ġchuck led
-jri wal
-h of
-Ġearth ly
-() {
-ind ependence
-Ġdisp ers
-ĠV endor
-ĠG areth
-Ġp als
-P enn
-ĠSub mit
-ic um
-Th u
-Ġcl andestine
-Ġcann ibal
-ĠCl erk
-E Stream
-gal itarian
-âĻ ¥
-g ew
-Ġhor rend
-ĠL ov
-ĠRe action
-ocr in
-Class ic
-Ġecho ing
-Ġdiscl osing
-ĠIns ight
-og un
-ĠInc arn
-upload s
-pp erc
-guy en
-Ġ19 01
-ĠB ars
-68 7
-Ġb ribes
-ĠFres no
-ur at
-ĠRe ese
-Ġintr usive
-Ġgri pping
-ĠBlue print
-ĠR asm
-un ia
-man aged
-ĠHeb do
-Ġ3 45
-Ġdec oding
-Ġpo ets
-Ġj aws
-ĠF IGHT
-am eless
-ĠMead ows
-ĠHar baugh
-Inter view
-ĠH osp
-ĠB RA
-Ġdelet ion
-m ob
-W alker
-ĠMoon light
-ĠJ ed
-ĠSoph ia
-Ġus ur
-Ġfortun ately
-ĠPut ting
-ĠF old
-Ġsan itation
-Ġpart isans
-IS ON
-B ow
-ĠCON C
-ĠRed uced
-ĠS utton
-Ġtouch screen
-Ġembry os
-âĢ¢âĢ¢ âĢ¢âĢ¢
-ĠK rug
-com bat
-ĠPet roleum
-Ġam d
-ĠCos mos
-Ġpresc ribing
-Ġconform ity
-ours es
-Ġplent iful
-Ġdis illusion
-ĠEc ology
-itt al
-Ġf anc
-Ġassass inated
-regn ancy
-Ġperenn ial
-ĠBul lets
-Ġst ale
-Ġc ached
-ĠJud ith
-ĠDise ases
-All en
-Ġl as
-Ġsh ards
-ĠSu arez
-ĠFriend ship
-inter face
-ĠSupp orters
-add ons
-46 2
-ĠIm ran
-ĠW im
-Ġnew found
-ĠM b
-An imal
-Ġd arling
-and e
-Ġrh y
-ĠTw isted
-pos al
-yn ski
-Var ious
-× ľ
-ĠK iw
-uy omi
-Ġwell being
-ĠL au
-an os
-Ġunm ist
-Ġmac OS
-Ġrest room
-ĠOl iv
-ĠAir ways
-Ġtimet able
-9 80
-Ġrad ios
-v oy
-ias co
-Ġcloud y
-ĠDraw ing
-Any thing
-Sy ria
-ĠH ert
-st aking
-Ġun checked
-Ġb razen
-ĠN RS
-69 7
-onom ic
-est ablish
-Ġl eng
-Ġdi agonal
-ĠF ior
-L air
-ĠSt ard
-Ġdef icient
-jo ining
-be am
-Ġomn ip
-Ġbl ender
-Ġsun rise
-Mo ore
-ĠF ault
-ĠCost ume
-ĠM ub
-Fl ags
-an se
-Ġpay out
-ĠGovern ors
-ĠD illon
-ĠBan ana
-N ar
-Ġtra iled
-Ġimperial ist
-um ann
-ats uki
-4 35
-ĠRoad s
-Ġsl ur
-ĠIde ally
-Ġt renches
-C trl
-Ġmir rored
-ĠZ el
-ĠC rest
-Comp at
-ĠRoll s
-sc rib
-ĠTra ils
-omet ers
-w inter
-Ġimm ortality
-il ated
-Ġcontrad icts
-un iversal
-ill ions
-ĠM ama
-opt im
-AT URE
-Ġge o
-et ter
-ĠCar lo
-4 24
-Ġcanon ical
-ĠStrongh old
-n ear
-Ġperf ume
-Ġorche stra
-od iac
-Ġup he
-Ġreign ing
-vers ive
-Ġc aucuses
-ĠD EM
-Ġinsult ed
-Ġ---- --
-ĠCr ush
-Ġroot ing
-ĠWra ith
-Ġwh ore
-Ġto fu
-C md
-ĠB ree
-Ġ$ _
-Ġr ive
-ĠAd vertising
-Ġw att
-ĠH O
-Ġpersu asive
-ĠParam eters
-Ġobserv ational
-ĠN CT
-ĠMo j
-ĠSal on
-Ġtr unc
-Ġexqu isite
-ĠMar a
-Ġpo op
-ĠAN N
-Ex c
-ĠWonder ful
-ĠT aco
-Ġhome owner
-ĠSmith sonian
-orpor ated
-mm mm
-Ġlo af
-ĠYam ato
-ĠInd o
-Ġcl inging
-á s
-Ġimm utable
-h ub
-Or ange
-Ġfingert ips
-ĠWood en
-ĠK idd
-ĠJ PM
-ĠDam n
-C ow
-c odes
-48 2
-Ġiniti ating
-ĠEl k
-ĠCut ting
-Ġabsent ee
-ĠV ance
-ĠLil ith
-G UI
-Ġobsc ured
-Ġdwar ves
-ĠCh op
-ĠB oko
-Val ues
-Ġmult imedia
-Ġbrew ed
-Reg ular
-CRIP TION
-ĠMort al
-Ġa pex
-Ġtravel er
-Ġbo ils
-Ġspray ing
-Rep resent
-ĠStars hip
-4 28
-Ġdisappro val
-Ġshadow y
-Ġlament ed
-ĠRe place
-ĠFran ç
-67 7
-d or
-Ġunst oppable
-Ġcoh orts
-gy n
-ĠClass ics
-ĠAm ph
-Ġsl uggish
-ĠAdd iction
-ĠPad res
-Ġins cription
-Ġin human
-min us
-ĠJere miah
-at ars
-Ter ror
-ĠT os
-ĠSh arma
-ast a
-c atch
-Ġpl umbing
-ĠTim bers
-Sh ar
-H al
-ĠO sc
-Ġcou pling
-hum ans
-Ġsp onge
-Ġid ols
-ĠSp a
-ĠAdv ocate
-ĠBe ats
-lu a
-Ġtick ing
-Ġload er
-ĠG ron
-8 10
-Ġstim ulated
-Ġside bar
-ĠManufact urer
-ore And
-19 73
-Ġpra ises
-ĠFl ores
-dis able
-ĠElect rical
-ra ise
-E th
-Ġmigr ated
-Ġlect urer
-K ids
-ĠCa vern
-Ġk ettle
-Ġgly c
-ĠMand ela
-ĠF ully
-å§ «
-FIN EST
-Ġsquee zing
-ĠRy der
-amp oo
-oreAnd Online
-Inst oreAndOnline
-Buyable InstoreAndOnline
-Ġcommem orate
-ĠRamp age
-Aust in
-ĠSh roud
-ĠRu ins
-9 15
-ĠK H
-Ġwater front
-ĠE SC
-b aby
-ĠC out
-ĠEm blem
-Ġequival ents
-49 2
-Un ique
-ĠNiet zsche
-brow ser
-Ġim itation
-ĠWere wolf
-ĠKir in
-ac as
-' ,"
-ĠÃ ¾
-Review ed
-Ġc unt
-Ġvo ic
-ĠLen ovo
-Ġbond ed
-48 1
-Ġinhib itors
-Ġendeav ors
-ĠHav ana
-ĠSt out
-ĠJ olly
-A ctor
-*/ (
-Ġoccur rences
-ĠT ens
-Incre ased
-ĠACT ION
-Ġ ãĢĮ
-ĠRank ings
-ĠB reat
-Ġ30 9
-D ou
-Ġimpact ing
-ĠDuc hess
-pre fix
-Q B
-Ġsummon ing
-Ġbest owed
-ĠKe pler
-ĠPOW ER
-c ube
-ĠK its
-ĠG rip
-Ġop ium
-Ġrep utable
-t oc
-ich ael
-ĠR ipple
-Ġcaf é
-ĠZ oom
-ĠBur ma
-Ġwa ive
-Ġst alls
-Ġdem eanor
-inc erity
-Ġfluor ide
-ĠSH OULD
-Par is
-Ġlong ing
-Ġpl at
-Ġgross ly
-Ġbull s
-Ġshowc asing
-ex pected
-ĠG addafi
-engine ering
-Re peat
-ĠK ut
-Ġconce ivable
-Ġtrim med
-osc ope
-ĠCand idate
-ĠT ears
-rol og
-Lew is
-S UP
-Ġroad map
-Ġsal iva
-Ġtrump et
-Jim my
-Ġmirac ulous
-Ġcolon ization
-Ġam put
-ĠGN OME
-ate ch
-D ifferent
-ĠE LE
-ĠGovern ments
-ĠA head
-ãħĭ ãħĭ
-word press
-L IB
-ĠIn clude
-ĠDor othy
-0 45
-ĠColomb ian
-Ġle ased
-88 4
-Ġde grading
-ĠDa isy
-i ations
-Ġbapt ized
-Ġsurn ame
-co x
-Ġblink ed
-ãĥ ¢
-Ġpoll en
-Ġder mat
-Ġre gex
-ĠNich olson
-ĠE ater
-ç ľ
-rad or
-Ġnarrow er
-Ġhur ricanes
-Ġhalluc inations
-r idden
-ISS ION
-ĠFire fly
-Ġattain ment
-Ġnom inate
-Ġav ocado
-ĠM eredith
-Ġt s
-Ġreve rence
-Ġe uph
-Ġcr ates
-ĠT EXT
-Ġ4 43
-Ġ3 19
-J SON
-iqu ette
-Ġshort stop
-ic key
-Ġpro pelled
-Ġap i
-ĠTh ieves
-77 9
-Ġovers aw
-Ġcol i
-ĠNic ola
-Ġover cl
-ik awa
-ĠC yr
-Ġ38 4
-78 9
-ĠAll ows
-10 27
-Det roit
-TR Y
-set up
-ĠSocial ism
-Sov iet
-s usp
-ĠAP R
-ĠShut down
-Ġal uminium
-zb ek
-ĠL over
-GGGG GGGG
-Ġdemocr acies
-Ġ19 08
-ĠMer rill
-ĠFranco is
-gd ala
-Ġtraff ickers
-ĠT il
-ĠGo at
-Ġsp ed
-ĠRes erv
-Ġpro d
-55 2
-Ġc ac
-ĠUn iv
-ĠSch we
-Ġsw irling
-ĠWild erness
-ĠEgg s
-Ġsadd ened
-Ġarch aic
-H yd
-Ġexcess ively
-B RE
-Ġaer ospace
-ĠVo ices
-Cra ig
-Ġign ited
-In itially
-ĠMc A
-Ġhand set
-Ġreform ing
-Ġfrust rations
-ĠDead pool
-ĠBel ichick
-ract or
-ĠRagnar ok
-ĠD rupal
-ĠApp roximately
-19 20
-ĠHub ble
-arm or
-ĠSar as
-ĠJon as
-Ġnostalg ic
-Ġfeas ibility
-Sah aran
-Ġorb iting
-Ġ9 70
-R u
-Ġsh in
-ĠInvestig ators
-Ġinconsist encies
-ĠP AN
-B G
-Ġgraz ing
-Ġdetect ors
-ĠStart up
-ĠFun ny
-ĠNa omi
-Consider ing
-Ġh og
-ut f
-ce mic
-Ġfort ified
-ĠFun ctions
-Ġcod ec
-nut rition
-H at
-" !
-micro soft
-55 8
-ĠTh in
-ĠA CE
-Al ias
-ĠO PS
-p apers
-P K
-ãĢ İ
-Ġimpro bable
-N orthern
-equ al
-Ġlook out
-Ġty res
-ĠMod ified
-ĠK op
-Abs olutely
-Ġbuild up
-sil ver
-Ġaud i
-Ġgro tesque
-ĠSab er
-ĠPres byter
-ON Y
-Ġglac iers
-ĠSho als
-ĠK ass
-ĠH RC
-ĠNic ol
-ĠL unch
-ĠF oss
-âĸ Ĵ
-AD RA
-ĠOne Plus
-o ing
-ground s
-Ġincident al
-Ġdatas ets
-68 9
-ĠClarks on
-Ġassemb ling
-ĠCorrect ions
-Ġdrink ers
-Ġqual ifiers
-Ġle ash
-Ġunf ounded
-ĠH undred
-Ġkick off
-T i
-Ġrecon cil
-ĠGr ants
-ĠCompl iance
-ĠDexter ity
-Ġ19 06
-w arn
-D allas
-Max imum
-n ard
-av ia
-be aut
-ens itivity
-tr ace
-Ġpione ers
-ĠF ract
-ãĢ ı
-Ġpre cept
-Ġgloss y
-ĠI EEE
-Ac ross
-Ġ6 80
-S leep
-che on
-Ġsatir ical
-ĠMin otaur
-ĠCla ude
-Ġr é
-ape go
-Ġcar rot
-ĠSem in
-ino a
-Ġz o
-Ind ependent
-Ġdiagn oses
-ĠC ue
-M AR
-Ġrend ition
-ĠK ik
-Ġpath ology
-Ġselect s
-Link edIn
-Ġass ay
-ĠD res
-Ġtext ual
-post ed
-IT AL
-ĠM aul
-N eal
-Ġinter connected
-Ġerr atic
-ĠVir us
-Ġ5 30
-Ġenvironmental ists
-ĠP helps
-Ġeng agements
-ĠIN ST
-Ġeconom ical
-nox ious
-Ġg earing
-izz y
-Ġfavor ably
-ĠMcG ill
-T erm
-Ġh anged
-Ġball park
-ĠRe yes
-Ġbe ware
-ĠP sal
-ĠMass acre
-q i
-Ġin accessible
-acly sm
-Ġfr ay
-ill ac
-Ġbitter ly
-ĠCert ification
-Mich igan
-Ġir respective
-al ore
-Em pty
-Ġendorse ments
-Ġund et
-f g
-equ ipped
-Ġmerc iless
-ĠC ust
-Ġimm ature
-Ġvou cher
-ĠBlack well
-Ñ ı
-h awk
-dis ciplinary
-ile e
-ĠMak oto
-ĠD ude
-ãĥĩ ãĤ£
-Y ears
-Ġin ver
-Ġsh aman
-ĠY ong
-ip el
-ell en
-ĠCath y
-br ids
-Ġs arc
-65 1
-N ear
-Ġground work
-Ġam az
-Ġ4 15
-ĠHunting ton
-hew s
-ĠB ung
-Ġarbit rarily
-ĠW it
-ĠAl berto
-Ġdis qualified
-best os
-46 1
-Ġp c
-Ġ28 4
-ro bat
-Rob in
-Ġh ugs
-ĠTrans ition
-ĠOcc asionally
-Ġ3 26
-ĠWh ilst
-ĠLe y
-Ġspaces hip
-cs v
-Ġun successfully
-ĠA u
-le ck
-ĠWing ed
-ĠGrizz lies
-. �
-Ġne arer
-ĠSorce ress
-ĠInd igo
-El se
-8 40
-let es
-Co ach
-Ġup bringing
-ĠK es
-Ġseparat ist
-Ġrac ists
-Ġch ained
-Ġabst inence
-lear ning
-Ġrein stated
-Ġsymm etry
-Ġremind ers
-ĠChe vy
-Ġm ont
-Ġexempl ary
-ĠT OR
-Z X
-Ġqual itative
-ĠSt amp
-ĠSav annah
-ĠRoss i
-Ġp aed
-Ġdispens aries
-ĠWall s
-ĠCh ronic
-Ġcompliment ary
-ĠBeir ut
-Ġ+ ---
-igs list
-Ġcrypt ographic
-mas ters
-ĠCap itals
-Ġmax imal
-Ġent ropy
-Point s
-Ġcombat ants
-l ip
-ĠGl ob
-ĠB MC
-ph ase
-th ank
-HT TP
-Ġcomm uter
-Ġ\( \
-.. /
-ĠReg ener
-ĠDO I
-ĠActiv ision
-Ġsl it
-os al
-RE M
-Ġch ants
-Y u
-Ke ys
-Bre xit
-ĠFor ced
-Ari zona
-Ġsquad ron
-IS O
-ĠMal one
-Ġ3 38
-Ġcontrast ing
-Ġt idal
-Ġlib el
-Ġimpl anted
-Ġupro ar
-ĠC ater
-Ġpropos itions
-M anchester
-ĠEuro s
-it amin
-G il
-ĠEl ven
-ĠSe ek
-ĠB ai
-Ġredevelop ment
-ĠTown s
-ĠL ub
-! ",
-al on
-K rist
-Ġmeas urable
-Ġimagin able
-Ġapost les
-Y N
-7 60
-Ġster oid
-Ġspecific ity
-ĠL ocated
-ĠBeck er
-ĠE du
-ĠDiet ary
-uts ch
-ĠMar ilyn
-Ġbl ister
-ĠM EP
-ĠK oz
-ĠC MS
-y ahoo
-ĠCar ney
-Ġbo asting
-ĠC aleb
-By te
-read s
-ad en
-Pro blem
-ĠWood ward
-S we
-S up
-ĠK GB
-Set up
-Ġtac it
-Ġret ribution
-Ġd ues
-ĠM ü
-. ?
-ä¸ Ń
-p ots
-Ġcame o
-ĠP AL
-educ ation
-A my
-like ly
-g ling
-Ġconstitution ally
-ĠHam m
-ĠSpe ak
-Ġwid gets
-br ate
-Ġcra ppy
-ĠI ter
-Ġanticip ating
-ĠB out
-P ixel
-ĠY ep
-ĠLaur ie
-Ġh ut
-Ġbullet in
-ĠSal vation
-Ġch ats
-ear able
-Honest ly
-AL TH
-onse qu
-c ult
-isco very
-ovy ch
-Ġse lves
-ĠSat oshi
-S ounds
-Ġconver gence
-ĠRosen berg
-19 74
-Ġnas al
-Ġfull est
-Ġfer ocious
-x us
-ist e
-AM S
-Ġlobb ied
-Ġso othing
-ĠGun n
-t oday
-0 24
-Ġinspir ational
-ĠN BN
-p b
-g ewater
-or ah
-all owed
-ĠCol iseum
-Ġspecial izing
-Ġinsane ly
-ĠT ape
-del ay
-Ġt arn
-ĠP ound
-Ġmel anch
-Ġdeploy ments
-il and
-Ġless en
-Ġfur ry
-ĠUE FA
-Ġblood shed
-ĠMe ier
-ither ing
-Ġhe irs
-ĠJ aw
-ax ter
-ĠPublic ations
-Ġal ters
-int ention
-ĠWinc hester
-d etermination
-ĠLif etime
-th in
-Mon ster
-7 80
-Ġapprox imation
-Ġsuper markets
-ĠSecond s
-or os
-h uge
-Ġb ribe
-ĠLIM ITED
-un ed
-Ġmis interpret
-ĠIn jury
-Ġ3 67
-Ġthreshold s
-ĠCarn ival
-Ġgastro intestinal
-Ġguid eline
-Ġde ceived
-f eatures
-Ġpurported ly
-ĠRon nie
-ĠNew t
-Ġsp acious
-as us
-Ġsuperhero es
-ĠCyn thia
-le gged
-k amp
-ch io
-Ġth umbnail
-ĠShir ley
-ill ation
-Ġshe ds
-ĠZ y
-E PA
-Ġdam s
-Ġy awn
-n ah
-ĠPe ggy
-ĠE rie
-ĠJu ventus
-ĠF ountain
-r x
-don ald
-al bum
-ĠComp rehensive
-Ġc aching
-ĠU z
-ulner ability
-ĠPrinc iple
-ĠJ ian
-ing ers
-cast s
-ĠOs iris
-ch art
-t ile
-ĠTiff any
-ĠPatt on
-ĠWh ip
-Ġovers ized
-J e
-ĠCind erella
-ĠB orders
-ĠDa esh
-M ah
-Ġdog ma
-Ġcommun ists
-v u
-Coun cil
-Ġfresh water
-Ġw ounding
-Ġdeb acle
-Ġyoung ster
-Ġthread ed
-ĠB ots
-ĠSav ings
-ãģ Ĥ
-ol ing
-oh o
-Ġillum ination
-M RI
-Ġlo osen
-tr ump
-ag ency
-ur ion
-Ġmoment arily
-ĠCh un
-ĠBud apest
-ĠAl ley
-D isk
-Ġaston ished
-ĠCon quer
-ĠAccount ing
-h aving
-ĠWe in
-ĠAl right
-Ġrev olver
-Ġdel usion
-Ġrelic s
-Ġad herent
-qu ant
-Ġhand made
-or io
-Ġcomb ating
-c oded
-Ġquad ru
-re th
-N ik
-ĠTrib al
-ĠMyster ious
-Ġin hal
-ĠWin ning
-ĠClass ification
-ch anged
-Ġun ab
-Ġsc orn
-icip ated
-w l
-ond uctor
-Ġrein forcing
-ĠChild hood
-an ova
-Ġadventure r
-Ġdoctor al
-ĠStrateg ies
-Ġengulf ed
-ĠEnc ounter
-Ġl ashes
-Crit ical
-ric ular
-ĠU TF
-oci ation
-check ing
-ĠConsult ing
-Run time
-per iod
-ĠAs gard
-Ġdist illed
-ĠPas adena
-ĠD ying
-ĠCOUN TY
-Ġgran ite
-Ġsm ack
-Ġparach ute
-ĠS UR
-Virgin ia
-ĠF urious
-78 7
-ĠO kin
-Ġcam el
-ĠM bps
-19 72
-ĠCh ao
-ĠC yan
-j oice
-ef er
-ĠW rap
-ĠDeb ate
-S eg
-Ġfore arm
-ĠIgn ore
-Ġtim estamp
-Ġprob ing
-ĠNo on
-ĠGra il
-f en
-Ġdorm ant
-ĠFirst ly
-ĠE ighth
-ĠH UN
-ĠDes ire
-or as
-Girl s
-ĠDes mond
-z ar
-am ines
-O AD
-exec ute
-Ġbo obs
-ĠAT L
-_ (
-Chel sea
-Ġmasturb ation
-ĠCo C
-Ġdestroy er
-ĠCh omsky
-Ġsc atter
-ĠAss ets
-79 6
-ĠC argo
-Ġrecept ive
-ĠSc ope
-Ġmarket ers
-Ġlaun chers
-Ġax le
-ĠSE A
-se q
-ĠM off
-f inding
-ĠGib bs
-Georg ia
-extreme ly
-N J
-Ġlab orers
-st als
-Ġmed iation
-ĠH edge
-at own
-Ġi od
-des pite
-v ill
-J ane
-ex istence
-Ġcoinc ided
-ĠUt ilities
-ĠChe ap
-Ġlog istical
-Ġcul mination
-ĠNic otine
-p ak
-F older
-Ġrod ents
-st uff
-Ġlaw fully
-Ġreper to
-io ch
-j j
-Dial ogue
-HH HH
-lic tion
-Look s
-Ġ29 7
-Ġtur rets
-ĠAb andon
-Ġinc ess
-ĠTraff ord
-Ġcur led
-Ġprefer ring
-Ġprivat ization
-Ġir resist
-ĠP anda
-ĠSh ake
-ĠMc Gr
-ãĥ Ħ
-und ers
-Ġdiscrim inated
-Ġbart ender
-I LE
-Atl antic
-Ġprop ensity
-ĠW iz
-ĠG im
-con ference
-Ġrein forces
-G h
-w agon
-Ġe erie
-F al
-Ġhug ged
-rac ist
-R IC
-F u
-Ġf iller
-ĠSt ub
-Ġeng raved
-ĠWrest le
-Ġimagin ative
-ĠPe er
-ĠFact ors
-an us
-ĠDrac ula
-mon itor
-Ġrou ters
-ib ia
-ĠBoo lean
-end ale
-ĠSl aughter
-ĠSh ack
-R FC
-ĠSpiel berg
-S ax
-ĠPH OTO
-ĠCl over
-ĠR ae
-Dep ending
-ĠMem or
-ar am
-Ġpier ced
-Ġcur tains
-v ale
-ĠInqu isition
-ĠP oke
-Ġforecast ing
-Ġcompl ains
-S ense
-ĠHer mes
-isc overed
-Ġb ible
-ĠMor ph
-Ġg erm
-78 5
-D ON
-Ġcon gen
-Ġcr ane
-ĠD PR
-Ġrespect fully
-R oom
-ĠN aw
-ĠDal ai
-re ason
-ĠAng us
-Educ ation
-ĠTitan ic
-Ë ľ
-Ġo val
-un ited
-Ġthird s
-Ġmoist ur
-ĠC PC
-M iami
-Ġtent acles
-ĠPol aris
-ex c
-ex clusive
-ĠPra irie
-Ġcol ossal
-ĠBl end
-sur prisingly
-ÃŃ s
-Ġindo ctr
-Ġbas al
-ĠMP EG
-und o
-Spl it
-Develop ment
-Ġlan tern
-19 71
-Ġprov ocation
-Ġang uish
-ĠB ind
-ĠLe ia
-duc ers
-ipp y
-conserv ancy
-Ġinitial ize
-ĠTw ice
-ĠSu k
-Ġpred ic
-Ġdi ploma
-Ġsoc iop
-Ing redients
-Ġhamm ered
-ĠIr ma
-Q aida
-Ġglim ps
-ĠB ian
-Ġst acking
-Ġf end
-gov track
-Ġun n
-dem ocratic
-ig ree
-Ġ5 80
-Ġ29 4
-Ġstraw berry
-ID ER
-Ġcher ished
-ĠH ots
-Ġinfer red
-Ġ8 08
-ĠS ocrates
-O regon
-ĠR oses
-ĠFO IA
-Ġins ensitive
-Ġ40 8
-Recomm end
-ĠSh ine
-Ġpain staking
-UG E
-ĠHell er
-ĠEnter prises
-I OR
-ad j
-N RS
-L G
-Ġalien ated
-Ġacknowled gement
-ĠA UD
-ĠRen eg
-Ġvou chers
-Ġ9 60
-Ġm oot
-ĠDim ensions
-Ġc abbage
-B right
-g at
-ĠK lu
-Ġlat ent
-Ġz e
-ĠM eng
-Ġdis perse
-Ġpand emonium
-H Q
-Ġvirt uous
-ĠLoc ations
-ee per
-prov ided
-Ġse ams
-ĠW T
-iz o
-PR OV
-Ġtit anium
-Ġrecol lection
-Ġcr an
-Ġ7 80
-ĠN F
-49 1
-64 2
-p acking
-59 8
-text ure
-Sp ider
-fre edom
-cipl ed
-ĠTAM ADRA
-âĻ ¦
-aut hent
-ĠW ANT
-r ified
-Ġr ites
-Ġuter us
-k iss
-Ġâī ¤
-Ġsk illet
-Ġdis enfranch
-ĠGa al
-Comp an
-Ġage ing
-gu ide
-B alt
-Ġiter ator
-Ġdiscretion ary
-t ips
-Ġprim ates
-ĠTechn ique
-ĠPay ments
-az el
-ĠR OCK
-stant ial
-0 60
-Ġd mg
-ĠJack ets
-ĠPlay off
-Ġnurs ery
-ĠSy mb
-art on
-Ġannex ation
-Color ado
-Ġco ils
-ĠSh oes
-âĦ¢ :
-ĠRo z
-COM PLE
-ĠEve rest
-ĠTri umph
-J oy
-G rid
-à ¼
-process or
-ĠPros per
-ĠSever us
-ĠSelect ed
-r g
-ĠTay yip
-St ra
-Ġski ing
-Ġ? )
-Ġpe g
-Tes la
-Ġtime frame
-Ġmaster mind
-ĠN B
-scient ific
-ĠSh it
-gener ic
-IN TER
-N UM
-Ġst roll
-ĠEn ix
-ĠM MR
-ĠE MS
-m ovie
-Ĥ ª
-Ġminim izing
-idd ling
-Ġilleg itimate
-Ġprot otyp
-Ġpremature ly
-Ġmanual s
-obb ies
-ĠCass idy
-D EC
-des ktop
-Ġaer os
-Ġscreen ings
-Ġdeb ilitating
-ĠGr ind
-nature conservancy
-Ġf ades
-ter mination
-assets adobe
-F actor
-Ġdefinitive ly
-P oké
-ap ult
-ĠLaf ayette
-C orn
-ĠCor al
-Ġstagn ant
-T ue
-Ġdissatisf action
-G ender
-Ġkid neys
-ĠG ow
-ĠDef eat
-ĠAsh ton
-Ġcart els
-Ġfore closure
-ĠExpl ore
-stre ngth
-ot in
-Ġveterin arian
-Ġf umble
-Ġpar ap
-ĠSt rait
-r ils
-Ġpr ick
-ĠBerm uda
-ĠAm munition
-skin ned
-Ġab ound
-ĠB raz
-Ġshar per
-ĠAsc ension
-Ġ9 78
-Ġpreview s
-Ġcommun ion
-ĠX Y
-Ġph ony
-Ġnewcom er
-Ġ3 32
-." ,"
-Ġredist ribution
-Prot ect
-ĠSo f
-K al
-Ġlip stick
-w orst
-Ġtang led
-Ġretrospect ive
-int eger
-Ġvolunte ering
-Ġ19 07
-Ġ --------------------
-ic hen
-Ġunve iling
-Ġsen seless
-Ġfisher ies
-\ -
-Ġh inges
-Ġcalcul us
-My th
-Ġund efeated
-Ġoptim izations
-Ġdep ress
-Ġbill board
-ĠY ad
-ĠPy ramid
-Is n
-I de
-Ġleg ion
-ĠK ramer
-ent anyl
-Ġpenet rating
-ĠHaw th
-ĠPR ODUCT
-ĠGer ard
-ĠP act
-ĠIn cluding
-ĠEl ias
-ĠEl aine
-vis ual
-Ġhum ming
-Ġcond esc
-ĠF asc
-ä¸ Ĭ
-Ġe galitarian
-Ġdev s
-ĠD ahl
-O ps
-D H
-ĠB ounce
-id ated
-ald o
-Ġrepublic an
-Ġh amb
-ĠS ett
-ograph ies
-CH APTER
-Ġtrans sexual
-Ġsky rocket
-ans wer
-Ġmark up
-Ø ª
-Ġhero ine
-Comp are
-ĠT av
-Be ast
-Ġsuccess ors
-Ġna ïve
-ĠBuck ley
-st ress
-me at
-Ġdownload able
-Ġindex ed
-Ġsc aff
-ĠL ump
-ĠHom o
-Stud io
-In sp
-Ġr acked
-far ious
-ĠPet ty
-Ex ternal
-Ġ19 09
-W ars
-com mit
-put ers
-Ġun ob
-ĠEr r
-ĠE G
-ĠAl am
-ĠSiber ia
-ĠAtmosp heric
-IS TER
-ĠSatan ic
-trans lation
-ĠL oud
-tra umatic
-l ique
-Ġreson ate
-ĠWel ch
-Ġspark ing
-ĠT OM
-t one
-Ġout l
-Ġhandc uffed
-ĠSer ie
-8 01
-Ġland marks
-ĠRee ves
-Ġsoft ened
-Ġdazz ling
-ĠW anted
-month s
-Mag ikarp
-Ġunt reated
-ĠBed ford
-M i
-ĠDynam o
-O re
-79 5
-Ġwrong ful
-Ġl ured
-Ġcort isol
-Ġve x
-d rawn
-ile t
-Download ha
-ĠF action
-Ġlab yrinth
-Ġhij acked
-w aters
-er ick
-Ġsuper iors
-ĠRow ling
-ĠGu inness
-Ġt d
-99 2
-Ġune arthed
-Ġcentr if
-Ġsham eless
-P od
-ĠF ib
-Ġ icing
-Ġpredict or
-Ġ29 2
-fore station
-con struct
-C and
-@ #
-Ġag itated
-Ġre pr
-OV A
-Ġkn itting
-ĠLim a
-Ġf odder
-68 4
-ĠPerson a
-k l
-7 01
-Ġbreak up
-á ¸
-Ġapp alled
-Ġantidepress ants
-ĠSus sex
-Har ris
-ĠTher mal
-ee ee
-U pload
-Ġg ulf
-Ġdoor step
-ĠSh ank
-L U
-ĠM EN
-ĠP ond
-s orry
-Ġmis fortune
-n ance
-Ġb ona
-M ut
-Ġde graded
-ĠL OG
-ĠN ess
-an imal
-Ġa version
-und own
-Ġsupplement ed
-ĠC ups
-Ġ50 4
-Ġdep rive
-ĠSpark le
-Å Ĥ
-ĠMed itation
-auth ors
-ĠSab an
-ĠN aked
-air d
-ĠMand arin
-ĠScript ures
-ĠPerson nel
-ĠMahar ashtra
-Ġ19 03
-ĠP ai
-ĠMir age
-omb at
-Access ory
-Ġfrag mented
-T ogether
-Ġbelie vable
-ĠGl adiator
-al igned
-ĠSl ug
-M AT
-Ġconvert ible
-ĠBour bon
-amer on
-ĠRe hab
-nt ax
-Ġpowd ered
-pill ar
-Ġsm oker
-ĠMans on
-ĠB F
-5 11
-ĠGood ell
-ĠD AR
-m ud
-g art
-Ġob edient
-ĠTrans mission
-ĠDon ation
-8 80
-Ġbother ing
-Material s
-ãĤ ±
-dest roy
-Ġfore going
-Ġanarch ism
-ĠK ry
-ice ps
-Ġl ittered
-ĠSch iff
-Ġanecd otal
-un its
-Ġf ian
-ĠSt im
-ĠS OME
-ĠInv aders
-Ġbehaviour al
-ĠVent ures
-Ġsub lime
-Ġfru ition
-ĠPen alty
-Ġcorros ion
-¶ ħ
-Ġlik ened
-Ġbesie ged
-ween ey
-ĠCre ep
-Ġlinem en
-mult i
-ic ably
-ud der
-Ġvital ity
-Ġshort fall
-ĠP ants
-ap ist
-H idden
-ĠDro ps
-med ical
-Ġpron unciation
-ĠN RL
-Ġinsight ful
-J V
-ĠBe ard
-ĠCh ou
-Ġchar ms
-Ġb ins
-Ġamb assadors
-ĠS aturdays
-Ġinhib itor
-ĠFr anch
-6 01
-', '
-ĠCon or
-art ney
-ĠX peria
-g rave
-be es
-ĠProtest ants
-Ġso aking
-ĠM andal
-Ġph ased
-Ġ6 60
-Ġsc ams
-Ġbuzz ing
-ĠItal ians
-ĠLoren zo
-ĠJ A
-Ġhes itated
-Ġcl iffs
-ĠG OT
-ingu ishable
-Ġk o
-Ġinter ruption
-Z ip
-Lear ning
-Ġundersc ores
-ĠBl ink
-K u
-57 9
-ĠAut ob
-I RE
-Ġwater ing
-Ġpast ry
-8 20
-Ġvision ary
-ĠTempl ar
-awa ited
-Ġpist on
-Ġant id
-current ly
-Ġp ard
-Ġw aging
-Ġnob ility
-ĠY us
-Ġinject ing
-f aith
-ĠP ASS
-å º
-Ġret ake
-ĠPR OC
-Ġcat hedral
-b ash
-Ġwrest lers
-Ġpartner ing
-Ġn oses
-Ġ3 58
-Trans form
-am en
-Ġb outs
-ĠId eal
-ĠConstant in
-Ġse p
-ĠMon arch
-att en
-ĠPe oples
-mod ified
-Ġmor atorium
-Ġpen chant
-Ġoffensive ly
-Ġprox ies
-ok ane
-ĠTaiwan ese
-ĠP oo
-ĠH OME
-us ional
-Ġver bs
-ĠO man
-vis ory
-Ġpersu asion
-Ġmult it
-Ġsc issors
-G ay
-ow ay
-oph ysical
-l us
-gn u
-Ġap ocalyptic
-Ġabsurd ity
-Ġplay book
-Ġautobi ography
-I UM
-Ġsne aking
-ĠSim ulation
-pp s
-ell ery
-Plan et
-Ġright fully
-Ġn iece
-ĠN EC
-ĠIP O
-ĠDis closure
-lean or
-ous y
-ST ER
-Ġ28 2
-Cru z
-Ch all
-64 3
-ĠSurv ive
-ĠF atal
-ĠAm id
-ap o
-We apons
-D EN
-7 70
-ĠGreen wald
-Ġlin en
-al os
-Ġpollut ants
-ĠPCI e
-k at
-Ġp aw
-ĠK raft
-C hem
-ĠTermin ator
-Ġre incarn
-Ġ] [
-ĠSe eds
-Ġsilhou ette
-ĠSt ores
-Ġgro oming
-ĠD irection
-ĠIs abel
-ĠBr idges
-ðŁ ij
-E ED
-ĠM orsi
-Ġval ves
-ĠRank ed
-ĠPh arma
-ĠOrgan izations
-Ġpenet rated
-ĠRod ham
-ĠProt oss
-Ġove rest
-Ġex asper
-ĠT J
-Ġ 000000
-Ġtrick le
-Ġbour bon
-WH O
-Ġw retched
-Ġmicrosc opic
-Ġcheck list
-Ġad orned
-R oyal
-Ad minist
-ĠRet irement
-ĠHig hest
-We ather
-ile ge
-Ġincre ments
-ĠC osponsors
-Ġmas se
-ĠS inn
-r f
-Ġh ordes
-as sembly
-75 4
-ĠNat asha
-ĠTY PE
-ĠGEN ERAL
-Ġarr anging
-Ġ40 7
-l ator
-Ġg lean
-Ġdisc redited
-Ġclin icians
-UN E
-Ġachie ves
-ĠEm erson
-com plex
-= [
-Ġprincip ally
-Ġfra il
-p icked
-Ġthan king
-Ġre cl
-ĠL AST
-Ġsupp ressing
-il ic
-Ġantidepress ant
-ĠLis bon
-Ġth or
-Ġsp a
-Ġking doms
-ĠPear ce
-em o
-Ġpl ung
-Ġdiv est
-Ġ ********************************
-b is
-osp els
-ad r
-Sp irit
-hall a
-P ink
-end ez
-Ġresurrect ed
-esc ape
-ĠRosen stein
-Ġge ological
-Ġnecess ities
-Ġcarn iv
-ĠE lys
-ĠBar ney
-Ġ29 6
-dig y
-ST ON
-D OWN
-Ġmil estones
-Ġk er
-Ġdismant ling
-Ġre prim
-Ġcross ings
-19 45
-Ġpatri archy
-Ġblasp hemy
-Ġ3 59
-met ry
-ĠOb esity
-ĠDiff erences
-bl ocking
-ãĥķ ãĤ¡
-ich ita
-ĠSab ha
-ph alt
-ĠCol o
-ual a
-effic ients
-ĠMed ina
-con sole
-55 7
-ĠHann ibal
-ĠHab it
-ĠF ever
-Ġthen ce
-Ġsyn agogue
-Ġessential s
-Ġw ink
-ĠTr ader
-ID A
-ĠSp oiler
-ĠIceland ic
-ĠHay ward
-Ġpe ac
-Ġmal ice
-Ġflash back
-Ġth w
-Ġlay offs
-L iquid
-Ġtro oper
-Ġh inge
-ĠRead ers
-Ph ill
-ĠB auer
-Cre ated
-Ġaud its
-ac compan
-Ġunsus pecting
-ier a
-6666 6666
-Ġbro ch
-Ġapprehend ed
-ĠM alk
-cer ning
-ĠCod ex
-O VER
-M arsh
-ĠD eng
-ĠExp ression
-Ġdisrespect ful
-Ġasc ending
-t ests
-ĠPlaint iff
-ster y
-ĠAl ibaba
-din and
-ĠDem psey
-Applic ations
-mor al
-Ġthrough put
-Ġquar rel
-Ġm ills
-Ġhe mor
-ĠC ASE
-terror ist
-st im
-ifest yle
-ro zen
-CE PT
-Ar k
-u ci
-lect ic
-Ġirrit ating
-she ets
-A y
-Ġrede emed
-Ġhorn y
-ĠTe ach
-ĠS ear
-dem ocracy
-4 65
-ĠRest ore
-Ġstand by
-ĠP is
-iff in
-Ġsleep y
-Ġextr ater
-Ġcompl iments
-Fram eworks
-Ġinstall s
-Ġb anging
-sur face
-found land
-Ġmetaph ysical
-Ġ28 3
-oul s
-dev ices
-Ar gs
-ĠSac rifice
-ĠMcC orm
-es on
-Cons ervative
-ĠM ikhail
-see ing
-is ively
-ĠRo oms
-ĠGener ic
-Ġenthusi astically
-Ġgri pped
-Ġcomed ic
-ĠElectric ity
-Ġgu errilla
-Ġdec oration
-ĠPerspect ive
-Ġconsult ations
-Ġun amb
-Ġplag iar
-Ġmagic ian
-Ġe rection
-ĠTour ism
-or ied
-ro xy
-11 00
-T am
-Ī è
-Î ³
-× ª
-ĠPred ators
-Nit rome
-Ġtelesc opes
-project s
-Ġun protected
-Ġst ocked
-ĠEnt reprene
-nex pected
-Ġwast ewater
-V ill
-Ġint imately
-Ġi Cloud
-ĠConst able
-Ġspo of
-Ġne farious
-Ġfin s
-Ġcens or
-ĠMod es
-ĠEs per
-ar bon
-Ġinter sections
-Ġlaud ed
-Ġphys i
-Ġgener ously
-ĠThe Nitrome
-ĠTheNitrome Fan
-Ġar isen
-ĠÙ Ī
-Ġg lands
-ĠPav ilion
-ĠGu pta
-Ġuniform ly
-Ġr amps
-ri et
-ĠWH EN
-ĠVan essa
-Ġrout ed
-Ġlim p
-ĠC PI
-p ter
-int uitive
-Ġv aping
-Ġexperiment ed
-ĠOlymp us
-ĠAm on
-Ġsight ing
-Ġinfiltr ate
-ĠGentle man
-Ġsign ings
-ĠMe ow
-ĠNav igation
-che cks
-4 33
-Ġel apsed
-ĠBulg arian
-esp ie
-ĠS OM
-d uring
-Ġsp ills
-anc a
-ĠPly mouth
-M AL
-Ġdomest ically
-ĠWater gate
-ĠF AM
-k illed
-ed ited
-ĠYour self
-Ġsynchron ization
-ĠPract ices
-ST EP
-Ġgen omes
-ĠQ R
-not ice
-Ġloc ating
-z in
-Ġ3 29
-al cohol
-Ġk itten
-V o
-Ġr inse
-Ġgrapp le
-ĠSc rew
-ĠD ul
-A IR
-Ġle asing
-ĠCaf é
-Ġro ses
-ĠRes pect
-Ġmis lead
-Ġperfect ed
-Ġnud ity
-Ġnon partisan
-ĠCons umption
-Report ing
-Ġnu ances
-Ġdeduct ible
-ĠSh ots
-Ġ3 77
-Ġæ ľ
-ano oga
-Ben ef
-ĠB am
-ĠS amp
-if ix
-Ġgal van
-ĠMed als
-rad ius
-Ġno bles
-Ġe aves
-igr ate
-K T
-ĠHar bour
-u ers
-Ġrisk ed
-re q
-Ġneuro t
-get table
-ain a
-Rom ney
-Ġunder pin
-Ġlo ft
-ĠSub committee
-ĠMong ol
-b iz
-Ġmanif ests
-ass isted
-ĠG aga
-Ġsy nergy
-Ġreligious ly
-ĠPre f
-ĠG erry
-T AG
-ĠCho i
-4 66
-beh ind
-ĠO u
-Gold Magikarp
-Ġhemor rh
-R iver
-Ġtend on
-Ġinj ure
-ĠF iona
-Ġp ag
-Ġag itation
-|| ||
-ur an
-ĠE SA
-Ġest eem
-Ġdod ging
-Ġ4 12
-r ss
-Ġce ases
-ex cluding
-Ġint akes
-Ġinsert s
-Ġemb old
-ĠO ral
-up uncture
-4 11
-ĠUn ified
-ĠDe le
-Ġfurn ace
-ĠCoy otes
-ĠBr ach
-L abor
-Ġhand shake
-Ġbru ises
-Gr ade
-éĹ ĺ
-ĠGram my
-ile en
-St ates
-ĠScandinav ian
-ĠKard ash
-8 66
-Ġeffort lessly
-ĠDI RECT
-ĠTH EN
-ĠMe i
-ert ation
-19 68
-Ġgro in
-w itch
-Requ irements
-98 5
-Ġroof s
-Ġest ates
-ĠH F
-Ġha ha
-Ġdense ly
-ĠO CT
-Ġpl astics
-Ġincident ally
-ĠTr acks
-ĠTax es
-Ġch anted
-Ġforce ful
-ĠBie ber
-ĠK ahn
-K ent
-ĠC ot
-lic ts
-F ed
-Ġhide ous
-ĠVer d
-ĠSynd icate
-ĠIl legal
-J et
-ĠD AV
-re asonable
-c rew
-Ġfundamental ist
-Ġtruth ful
-ĠJ ing
-Ġl il
-Ġdown ed
-Ġen chanted
-ĠPolic ies
-ĠMcM aster
-ĠH are
-ides how
-Ġpar ams
-en cers
-gorith m
-Ġallow ances
-Ġturb ulent
-Ġcomplex ities
-ĠK T
-Ġ3 37
-ĠGen etic
-F UN
-D oug
-t ick
-Ġg igs
-ument hal
-Ġpatriarch al
-Ġcal c
-, ...
-Ġc out
-ĠGu an
-Ġpath ological
-ĠR ivals
-Ġunder rated
-Ġflu orescent
-ĠJ iu
-arna ev
-ĠQu an
-Ġ4 29
-Ġ à¨
-M ario
-Con struct
-ĠC itation
-ĠR acial
-ĠR SA
-ĠF idel
-Ġ3 95
-Person ally
-C ause
-Ã »
-rad ical
-in en
-Ġvehement ly
-ĠPap a
-Ġintern ship
-Ġfl akes
-ĠRe ck
-Luck ily
-B ra
-20 20
-rav ings
-R N
-W onder
-Ser iously
-Ġre usable
-Ġpoll uted
-ĠP eng
-le igh
-ind le
-Ġcircuit ry
-ĠMad onna
-ĠB ART
-Res idents
-att ribute
-Phil adelphia
-Cl ub
-Ġplan ner
-Ġfr antically
-Ġfaith fully
-ĠTerrit ories
-ĠL AT
-ĠAnders en
-an u
-ĠP ARK
-ĠS ora
-i age
-ĠPlay offs
-ĠG CC
-4 27
-Ġab norm
-ĠL ever
-Ġdisob edience
-As ync
-ĠShe a
-V ert
-Ġsk irts
-ĠSaw yer
-x p
-Ġwors ening
-Ġsc apego
-ĠAng le
-oth al
-Ġtro ve
-ĠSt y
-ĠN guyen
-mar ine
-ide on
-Dep ths
-Bl og
-ĠIll uminati
-Ġtract s
-Ġorgan ise
-Ġo str
-F s
-Ġlever aging
-ĠD aredevil
-as ar
-Ġl ang
-Ġex termin
-urs ions
-ĠRom o
-ãĤ¤ ãĥĪ
-Ġcont ended
-Ġencounter ing
-ĠTable t
-ĠAltern ate
-sk ill
-Ġswe ets
-Ġco hesive
-cap acity
-Ġrep ud
-Ġl izard
-ro o
-Ġpilgr ims
-ĠR uff
-ĠInstr ument
-ĠLog o
-uit ous
-E H
-Ġsales man
-Ġank les
-L ed
-ĠPat ty
-ud os
-Own er
-Ġdiscrep ancies
-k j
-M U
-Ġuncond itional
-Dragon Magazine
-i ard
-O ak
-ĠConvers ation
-be er
-ĠOs aka
-D elta
-us ky
-Ġsecret ion
-Ġpl aza
-Ġm ing
-Ġde pletion
-ĠM ous
-ĠI TS
-ĠH imal
-ĠFle ming
-Ġcyt ok
-ĠH ick
-Ġbat ters
-ĠInt ellectual
-6 75
-é r
-IS ION
-ĠQu entin
-ĠCh apters
-ih adi
-Ġco aster
-WAY S
-ĠL izard
-ĠY or
-and ering
-S kin
-ha ust
-ab by
-Ġportray ing
-Ġwield ed
-d ash
-Ġprop onent
-Ġr ipple
-Ġgrap hene
-Ġfly er
-Ġrec urrent
-Ġdev ils
-Ġwater fall
-æĺ ¯
-go o
-Text Color
-Ġtam pering
-IV ES
-TR UMP
-ĠAb el
-ĠS AL
-ĠHend ricks
-ĠLu cius
-b ots
-Ġ40 96
-IST ORY
-Gu est
-ĠN X
-in ant
-Ben z
-ĠLoad ed
-ĠCle ver
-t reatment
-Ġta vern
-Ġ3 39
-ĠT NT
-ific antly
-Tem perature
-F el
-Ġunder world
-ĠJud ges
-Ġ< +
-Ġst ump
-Ġoccup ancy
-Ġab er
-ĠF inder
-) ",
-ĠN unes
-res et
-in et
-ect omy
-Ġwell ness
-ĠP eb
-quart ered
-and an
-Ġneg atives
-ĠTh iel
-ĠCl ip
-ĠL TD
-Ġbl ight
-Ġreperto ire
-K yle
-Ġqu er
-ĠC es
-Ġha pl
-98 9
-ĠTh ames
-isc opal
-Des k
-ivari ate
-ĠEx cellence
-found ation
-Ġâ ĩ
-X i
-Ġmyster iously
-esty les
-Ġper ish
-ĠEng els
-ĠDE AD
-09 0
-}} }
-ĠUn real
-Ġrest less
-ID ES
-orth odox
-ĠInter mediate
-Ġdin ners
-ĠTr out
-ĠSe ym
-ĠHall s
-og ged
-Ġtraged ies
-Ġdid nt
-67 6
-Ġail ments
-Ġobserv able
-ĠV ide
-ad apt
-ĠD usk
-Ġprofessional ism
-ĠPres cott
-ĠInd ies
-p ox
-ĠMe hran
-W ide
-Ġend emic
-ĠPar an
-B ird
-Ġped als
-ĠI U
-ĠAdam ant
-ĠH urt
-Ġcorrel ates
-urd en
-Ġspons oring
-cl imate
-ĠUnivers ities
-ĠK not
-enn es
-ĠDam ian
-ĠAx el
-S port
-Ġbar b
-ĠS no
-sh own
-ste en
-ud ence
-Ġnon violent
-Ġhom ophobia
-Ġbiom ass
-ĠDet ail
-Ġsrf N
-ĠT une
-accompan ied
-I ENCE
-Al bert
-ĠMong o
-z x
-ĠCer berus
-or bit
-c ens
-Ġsl ay
-SH ARE
-H Y
-Ġb rawl
-ĠPro be
-Ġnonex istent
-ĠClare nce
-ĠBlack burn
-Ġport als
-ĠR ita
-ĠRem ain
-ĠLe vant
-Ġtrick ed
-ĠF erry
-aver ing
-ĠStraw berry
-ĠAn swers
-Ġhorrend ous
-ĠA man
-Supp lement
-ĠT oad
-Ġpe eled
-Ġman oeuv
-ĠU zbek
-mond s
-ĠH ector
-Ġ40 2
-pe es
-fix es
-Ġd j
-Ġres umes
-Ġaccount ant
-Ġadvers ity
-Ġham pered
-ĠL arson
-Ġd oping
-part s
-H ur
-Ġbe arded
-Ġy r
-ĠPlug in
-å¥ ³
-Ġ/ **
-rol ley
-Ġwaters hed
-ĠSub mission
-if lower
-AS C
-Ġcho ir
-Ġsculpt ures
-m A
-incre asing
-ai i
-Ġsne akers
-Ġconfront s
-ĠEle phant
-ĠEl ixir
-Ġrec al
-ĠT TL
-w idget
-ĠW ax
-ĠGr ayson
-Ġha irst
-Ġhumili ated
-ĠWAR N
-app iness
-ĠT TC
-F uel
-Ġpol io
-Ġcomplex es
-Ġbab e
-ĠX IV
-P F
-). [
-P arts
-Ġ4 35
-M eg
-ĠY ards
-ĠAL P
-Ġy ells
-Ġprin ces
-Ġbull ies
-ĠCapital ism
-ex empt
-FA Q
-ĠSp onge
-ĠAl a
-Ġpleas antly
-Ġbu f
-Ġden ote
-Ġunp ublished
-Ġkne eling
-asc a
-Ġl apse
-al ien
-99 4
-Ġrefere es
-ĠLaw yers
-S anta
-Ġpuzz ling
-ĠProm etheus
-ĠPh araoh
-ĠDel ay
-Ġfacilit ates
-ĠC ES
-Ġjew els
-Ġbook let
-ond ing
-Ġpolar ization
-ĠMor an
-ĠSal ad
-ĠS OS
-ĠAdv ice
-PH OTOS
-IC AN
-iat ures
-ex press
-ĠWonder land
-ĠC ODE
-ĠCL ASS
-9 75
-Ġg rep
-ĠD iesel
-ĠGl ac
-! ?"
-Ġr m
-o ine
-disc rimination
-ĠN urse
-m allow
-Ġv ortex
-ĠCons ortium
-Ġlarge Download
-stra ight
-augh lin
-G rad
-Ġpublic ized
-ĠW aves
-ĠRed d
-Ġfest ivities
-ĠM ane
-ar ov
-Ġfleet ing
-ĠDr unk
-ug en
-C ele
-Ġchromos omes
-ĠD OT
--+-+ -+-+
-Ġbus iest
-ĠBe aver
-Sy rian
-ĠK yr
-k as
-ĠCross Ref
-19 50
-76 01
-Ġrepe aling
-ĠWin ners
-ĠMac ro
-ĠD OD
-bl ance
-S ort
-64 1
-Ġmet re
-ĠD irk
-Ġgo ggles
-Ġdraw backs
-Ġcomplain ant
-Ġauthor izing
-Ġantit rust
-oper ated
-Ġm ah
-Ġexagger ation
-Am azing
-ĠSer aph
-Ġha ze
-w ow
-Ġextingu ished
-Ġcan yon
-ĠB osh
-Ġv ents
-Ġsc rape
-Cor rect
-4 26
-Ġav g
-Dem and
-ĠâĪ ¼
-Ġmicrobi ota
-"} ],"
-ĠSt ev
-B io
-ĠPlan es
-Ġsuggest ive
-Ġdec ipher
-ĠRefuge e
-ĠKe jriwal
-ĠGreen peace
-Ġdecl ass
-ĠSound ers
-Ġth o
-Ġdec rypt
-Ġbr ushing
-ĠJane iro
-ip op
-S i
-8 77
-ĠGeoff rey
-Ġc pu
-ĠHaz el
-Ġview points
-Ġcris py
-ĠNot ification
-Ġsold er
-ĠMod est
-ĠHem isphere
-Ġcass ette
-in cludes
-Ġident ifiers
-ĠC ALL
-in cent
-T odd
-ĠSwe ep
-Ġ3 34
-b oss
-Ġsm ir
-gin x
-Ġtown ship
-Ġg rieving
-ĠMos que
-Net flix
-AS ED
-ĠMillenn ials
-oc om
-19 67
-Ġbold ly
-s leep
-Ġes che
-arij uana
-Ġsw irl
-ĠPen al
-Ġneglig ent
-ĠStephen son
-K ER
-ĠZ oro
-ris is
-Ġlocal ization
-ĠSeym our
-ĠAng lic
-red itation
-prot ection
-ĠPa ige
-Ġo mit
-ĠR ousse
-ĠT ub
-Ġinv itations
-t ty
-Ġm oss
-ph ysical
-C redits
-Ġan archy
-Ġchild care
-Ġl ull
-ĠM ek
-ĠL anguages
-lat est
-ĠSan ford
-Ġus ability
-Ġdiff use
-ĠD ATA
-Ġsp rites
-ĠVeget a
-ĠProm otion
-ãĥ¼ ãĤ¯
-rict ing
-z ee
-Tur kish
-ĠTD s
-pro ven
-57 1
-Ġsmug glers
-707 10
-Ġreform ed
-ĠLo is
-Ġun fl
-ĠWITH OUT
-ĠReturn ing
-ann ie
-ĠTom as
-Fr anc
-ĠProf it
-ĠSER V
-ĠR umble
-ik uman
-es an
-Ġt esters
-Ġgad get
-Ġbrace let
-ĠF SA
-comp onent
-Ġparamed ics
-Ġj an
-ĠRem em
-ĠSk inner
-Ġl ov
-ĠQu ake
-rom a
-Ġfl ask
-Pr inc
-Ġover power
-Ġlod ging
-ĠK KK
-ret te
-Ġabsor bs
-w rote
-Ġ ,"
-K ings
-ĠH ail
-ĠFall ing
-xt ap
-ĠHel ena
-ire ns
-L arry
-Ġpamph let
-ĠC PR
-G ro
-ĠHirosh ima
-Ġhol istic
-". [
-Ġdet achment
-Ġas pire
-Ġcompl icit
-ĠGreen wood
-Ġresp awn
-ĠSt upid
-ĠFin ished
-f al
-b ass
-Ġab hor
-Ġmock ery
-ĠFe ast
-VID EO
-Ġcon sec
-ĠHung ry
-P ull
-ĠH ust
-it ance
-? ãĢį
-) --
-ĠPar allel
-con v
-4 69
-ha ar
-w ant
-P aper
-m ins
-ĠTor o
-ĠTR UMP
-ĠR ai
-D W
-ĠW icked
-ĠL ep
-Ġfun ky
-Ġdetrim ent
-ios is
-ache v
-Ġde grade
-im ilation
-Ġret ard
-Ġfrag mentation
-Ġcow boy
-ĠY PG
-ĠH AL
-Parent s
-ĠS ieg
-ĠStra uss
-ĠRub ber
-× IJ
-Fr ag
-Ġp t
-Ġoption ally
-ĠZ IP
-ĠTrans cript
-ĠD well
-88 2
-M erc
-ĠM OT
-ãĥ¯ ãĥ³
-Ġhun ts
-Ġexec utes
-In cludes
-Ġacid ic
-ĠRespons ibility
-ĠD umb
-we i
-And erson
-ĠJas per
-ight on
-abs olutely
-Ad ult
-Ġpl under
-Mor ning
-ĠT ours
-ĠD ane
-Î º
-ĠT EST
-ĠG ina
-Ġcan ine
-aw an
-Ġsocial ists
-ĠS oda
-Ġimp etus
-ĠSupplement ary
-oli ath
-ĠKinn ikuman
-mitted ly
-second s
-Ġorganis ers
-Ġdocument aries
-Vari able
-GRE EN
-Ġres orts
-Ġbr agging
-Ġ3 68
-Art ist
-w k
-bl ers
-Un common
-ĠRet rieved
-Ġhect ares
-Ġtox in
-r ank
-Ġfaith s
-ĠG raphic
-Ġve c
-ĠL IA
-Af rican
-Ġard ent
-end iary
-L ake
-ĠD OS
-cient ious
-ĠOk awaru
-ĠAll y
-ĠTim eline
-D ash
-ĠI c
-contin ue
-Ġt idy
-Ġinstinct ively
-ĠP ossibly
-ĠOut door
-ĠWould n
-Ġl ich
-ĠBr ay
-ĠA X
-ĠÃ ī
-Ġ+ #
-\ '
-Direct ory
-ab iding
-Ġf eral
-ic ative
-but t
-Ġper verse
-S alt
-Ġwar ped
-Ġnin eteen
-Ġcabin ets
-Ġsrf Attach
-ĠSl oan
-Ġpower ing
-reg ation
-F light
-se vere
-Ġst ren
-Ġc og
-ap ache
-Ġâ Ŀ
-Ġcaf eteria
-p aces
-ĠGrim oire
-uton ium
-Ġr aining
-Ġcir cling
-Ġlineback ers
-c redit
-Ġrep atri
-ĠCam den
-lic ense
-Ġly ric
-Ġdescript or
-Ġval leys
-Ġre q
-Ġback stage
-ĠPro hibition
-ĠK et
-Op ening
-S ym
-æĸ ¹
-Ġserv ings
-Ġoverse en
-Ġaster oids
-ĠMod s
-ĠSpr inger
-ĠCont ainer
-è »
-ĠM ens
-Ġmult im
-Ġfire fighter
-pe c
-Ġchlor ine
-Ð ¼
-end i
-Ġsp aring
-Ġpolyg amy
-ĠR N
-ĠP ell
-Ġt igers
-Ġflash y
-ĠMad ame
-S word
-Ġpref rontal
-Ġpre requisite
-uc a
-Ġw ifi
-Ġmiscon ception
-Ġharsh ly
-ĠStream ing
-ot om
-ĠGiul iani
-foot ed
-Ġtub ing
-ind ividual
-z ek
-n uclear
-m ol
-Ġright ful
-49 3
-Ġspecial ization
-Ġpassion ately
-ĠVel ocity
-ĠAv ailability
-T enn
-Ġl atch
-ĠSome body
-Ġhel ium
-cl aw
-Ġdi pping
-XX X
-Ġinter personal
-7 10
-Ġsub ter
-Ġbi ologists
-ĠLight ing
-Ġopt ic
-Ġden im
-end on
-ĠC orm
-Ġ3 41
-ĠC oup
-Ġfear less
-Ġal ot
-ĠCliff ord
-ĠRun time
-ĠProv ision
-up dated
-lene ck
-Ġneur on
-Ġgrad ing
-ĠC t
-sequ ence
-in ia
-con cept
-Ġro aring
-ri val
-ĠCaucas ian
-Ġmon og
-key es
-Ġappell ate
-Ġlia ison
-EStream Frame
-ĠPl um
-! .
-Ġsp herical
-Ġper ished
-Ġbl ot
-Ġben ches
-Ġ4 11
-Ġpione ered
-Ġhur led
-Jenn ifer
-ĠYose mite
-Ch air
-Ġreef s
-Ġelect or
-ĠAnt hem
-65 2
-Ġun install
-Ġimp ede
-Ġbl inking
-Ġgot o
-Dec re
-A ren
-Ġstabil ization
-ĠDis abled
-ĠYanuk ovych
-Ġoutlaw ed
-ĠVent ura
-ten ess
-Ġplant ation
-Ġy acht
-ĠHu awei
-Ġsol vent
-Ġgr acious
-Ġcur iously
-Ġcapac itor
-Ġc x
-ĠRef lex
-Ph ys
-ĠC f
-pt in
-cons ervative
-Ġinv ocation
-c our
-F N
-ĠNew ly
-H our
-As ian
-ĠLe ading
-ĠAer ospace
-An ne
-Ġpre natal
-Ġdeterior ating
-H CR
-ĠNorm andy
-ol ini
-ĠAm bro
-9 10
-Ġset backs
-ĠT RE
-Ġs ig
-ĠSc ourge
-59 7
-79 8
-Game play
-Ġm sec
-M X
-Ġprice y
-ĠL LP
-aker u
-Ġover arching
-ĠB ale
-Ġworld ly
-Cl ark
-Ġscen ic
-Ġdisl iked
-ĠCont rolled
-T ickets
-ĠE W
-ab ies
-ĠPl enty
-Non etheless
-Ġart isan
-Trans fer
-ĠF amous
-Ġinf ield
-ble y
-Ġunres olved
-ĠML A
-ãĤ Ĥ
-Cor rection
-Ġdemocr at
-ĠMore no
-ro cal
-il ings
-Ġsail or
-Ġr ife
-h ung
-Ġtrop es
-Ġsn atched
-ĠL IN
-ĠB ib
-ES A
-ĠPre v
-ĠCam el
-run time
-Ġob noxious
-4 37
-Ġsum mers
-Ġunexpl ained
-ĠWal ters
-cal iber
-Ġg ull
-ĠEnd urance
-ä½ ľ
-Ġ3 47
-Ir ish
-Ġaer obic
-Ġcr amped
-ĠHon olulu
-à ©
-us erc
-ec ast
-AC Y
-ĠQu ery
-ãĤ¹ ãĥĪ
-Bet a
-Ġsuscept ibility
-ĠSh iv
-ĠLim baugh
-ĠÃ ĸ
-ĠN XT
-ĠM uss
-ĠBrit ons
-ES CO
-EG IN
-Ġ% %
-Ġsec ession
-ĠPat ron
-ĠLu a
-n aires
-ĠJPM organ
-us b
-ocy te
-Ġcouncill ors
-ĠLi ang
-f arm
-Ġnerv ously
-Ġattract iveness
-ĠK ov
-j ump
-Pl ot
-Ġst ains
-ĠStat ue
-ĠApost les
-he ter
-ĠSUP PORT
-Ġoverwhel m
-Y ES
-Ġ29 1
-d ensity
-Ġtra pping
-M it
-Ġf ide
-ĠPam ela
-atl antic
-Dam n
-Ġp ts
-OP A
-Ġserv icing
-Ġoverfl owing
-ul o
-ĠE rit
-t icket
-light ing
-ĠH mm
-ãĥ¼ ãĥ«
-im oto
-Ġchuck le
-4 23
-ãģ ķ
-sh ape
-Ġque ues
-Ġanch ors
-ãĤ¼ ãĤ¦ãĤ¹
-F er
-Ġaw oke
-Ġ6 66
-h ands
-Ġdiver gence
-Ġ50 5
-T ips
-Ġdep ot
-Ġske w
-ĠDel iver
-op ot
-Ġdiv ul
-ĠE B
-uns igned
-ĠUn i
-X box
-Ġfor ks
-Ġ7 02
-å ¯
-Ġpromot ers
-ĠV apor
-Ġlev ied
-sl ot
-Ġpig ment
-Ġcyl inders
-C RE
-Ġsn atch
-Ġperpet ually
-Ġl icking
-ĠFe et
-ĠKra ken
-ĠHold en
-ĠCLS ID
-m r
-Ġproject or
-Ġden otes
-Ġchap el
-ĠTor rent
-b ler
-R oute
-ĠDef endant
-ĠPublisher s
-ĠM ales
-ĠInn ov
-ĠAg ility
-rit er
-ty mology
-st ores
-L ind
-Ġf olly
-ĠZur ich
-B le
-Ġnurt ure
-Ġcoast line
-uch in
-D omin
-Ġfri vol
-ĠCons olid
-res ults
-M J
-Ġphyl ogen
-Ġha uled
-ĠW iley
-ĠJess ie
-ĠPrep are
-ĠE ps
-Ġtreasure r
-I AS
-Ġcolon ists
-Ġin und
-ĠWW F
-ĠCon verted
-6 000
-out side
-ĠApp earance
-ĠRel ic
-ĠM ister
-s aw
-Ġresult ant
-Ġadject ive
-ĠLaure l
-ĠHind i
-b da
-Pe ace
-Ġreb irth
-Ġmembr anes
-Ġforward ing
-Ġcoll ided
-ĠCar olyn
-K ansas
-5 99
-ĠSolid GoldMagikarp
-Be ck
-Ġstress ing
-ĠGo o
-ĠCooper ative
-Ġf s
-ĠAr chie
-L iter
-ĠK lopp
-J erry
-Ġfoot wear
-War ren
-Ġsc ree
-h are
-Under standing
-P ed
-Ġanth ology
-ĠAnn ounce
-M ega
-Ġflu ent
-Ġbond age
-ĠDisc ount
-il ial
-C art
-ĠNight mares
-Sh am
-ĠB oll
-uss ie
-H ttp
-Atl anta
-Ġun recogn
-ĠB id
-Ġunder grad
-Ġforg iving
-ĠGl over
-AAAA AAAA
-4 45
-V G
-pa io
-kill ers
-Ġrespons ibly
-Ġmobil ize
-Ġeffect ed
-ĠL umin
-Ġk ale
-Ġinfring ing
-ann ounced
-Ġf itt
-b atch
-ĠT ackle
-ĠL ime
-ĠAP P
-uke mia
-Ġrub y
-Ġex oner
-ĠCas ual
-0 70
-Ġpel vic
-Ġautom ate
-ĠK ear
-ĠCoast al
-Ġcre ed
-Ġbored om
-ĠSt un
-ri ott
-Ĥ İ
-Ġregener ate
-Ġcomed ians
-ĠOP ER
-Sp ons
-id ium
-on is
-L ocated
-05 7
-Ġsusp ense
-ĠD ating
-C ass
-Ġneoc ons
-ĠShin zo
-Ġaw oken
-ch rist
-ĠMess ages
-att led
-ĠSpr ay
-ĠSp ice
-C W
-Ġshield ing
-ĠG aul
-Am id
-Ġparam ilitary
-Ġmult if
-ĠTan ner
-il k
-Ġgodd amn
-g ements
-Ġbe friend
-m obi
-Ġ3 88
-fold er
-acc a
-Ġins in
-g ap
-N ev
-fif th
-Ġpsychiat ry
-b anks
-TH IS
-Ġhar b
-ac qu
-Ġfac ade
-ĠPower Point
-80 3
-Ġbl uff
-Sh ares
-Ġfavor ing
-El izabeth
-Ãį Ãį
-Ġr anger
-77 2
-ĠAr che
-h ak
-ĠGen etics
-ĠF EMA
-Ġev olves
-Ġest e
-ĠP ets
-ĠM é
-ĠInterest ing
-ĠCanter bury
-ch apter
-ĠStar fleet
-Sp anish
-Ġdraw back
-ĠNor wich
-9 70
-n orth
-ag anda
-Ġtransform ative
-ram ids
-bi ology
-ad ay
-Ġpropag ation
-ĠGam ma
-ĠDen ise
-ĠCalcul ator
-ent imes
-ĠB ett
-Ġapp endix
-ĠHD D
-AK ING
-Ġst igmat
-Ġhol ster
-Ġord inarily
-Ch ance
-ĠCont rary
-Ġad hesive
-Ġgather s
-6 12
-re au
-ony ms
-ew ays
-Ġindu ces
-Ġinterchange able
-se m
-Wh it
-Ġtr ance
-Ġincorpor ation
-ĠExt ras
-Fin ancial
-Ġawkward ly
-ĠStur geon
-ĠH Y
-Norm ally
-ĠEnd ing
-ĠAss ist
-enc rypted
-Ġsub jug
-Ġn os
-Ġfan atic
-C ub
-C U
-?" .
-Ġirre versible
-å Ĥ
-03 1
-ĠH AR
-sp read
-ul ia
-= $
-Sc ope
-L ots
-Ġlif estyles
-ol on
-Ġf eds
-Ġcongrat ulate
-web kit
-Ġindist inguishable
-ĠSw ing
-Ġcommand ments
-qu ila
-ab ella
-m ethyl
-ann abin
-Ġo vere
-Ġlob ster
-ĠQU EST
-ĠCONT IN
-bern atorial
-:::: ::::
-ĠTra ve
-ĠSam oa
-AN I
-75 2
-Ð ´
-userc ontent
-ĠMod erate
-y eah
-ĠK itt
-Ġwe e
-Ġstuff ing
-ĠInter vention
-ĠD ign
-Ġware houses
-ĠF iji
-Ġpel lets
-Ġtake away
-ĠT ABLE
-ĠClass ical
-col lection
-Ġland fall
-ĠMus cle
-Ġsett les
-ĠAD V
-Ġ3 44
-L aura
-Ġf ared
-ĠPart ial
-4 36
-oss ibility
-ĠD aly
-ĠT arant
-ĠFu ji
-am l
-c ence
-55 1
-ĠProced ures
-ĠO CD
-ĠU D
-t in
-Q UI
-ach o
-4 38
-Ġgl itches
-Ġenchant ment
-Ġcalcul ates
-IR O
-ĠH ua
-alys es
-ĠL ift
-um o
-Ġle apt
-Ġhypothes ized
-ĠGust av
-it ans
-VERS ION
-æ ł
-Rog er
-Ġr and
-ĠAd apter
-Ġ3 31
-ĠPet ition
-k ies
-M ars
-Ġunder cut
-ze es
-ĠLy ons
-ĠDH CP
-Miss ing
-Ġretire es
-Ġins idious
-el i
-> )
-. ãĢį
-Ġfinal ists
-ĠA ure
-Ġacc user
-Ġwas tes
-ĠY s
-ĠL ori
-Ġconstitu encies
-Ġsupp er
-Ġmay hem
-or ange
-Ġmis placed
-Ġmanager ial
-Ġex ce
-ĠCL I
-Ġprim al
-ĠL ent
-Cry stal
-h over
-ĠN TS
-end um
-Ġd w
-ĠAl c
-n ostic
-Ġpres erves
-ĠTs arnaev
-Ġtri pled
-rel ative
-Arc ade
-k illing
-ĠW EEK
-ĠH anna
-D ust
-Com pleted
-ģ «
-Ġappro ves
-ĠSur f
-ĠLuther an
-ven ants
-Ġrobber ies
-we ights
-soft ware
-at ana
-ug al
-Ġgrav y
-ĠC ance
-OLOG Y
-ly ak
-Ton ight
-Ġunve il
-Ġ19 04
-ĠMin ion
-ent ious
-st ice
-pack ages
-ĠG EAR
-Ġg ol
-ĠHutch inson
-ĠProf ession
-ĠG UN
-ĠDiff erence
-ĠTsuk uyomi
-ĠLes bian
-6 70
-Ġfug itive
-ĠPlan etary
--------------------------------- ------------------------
-Ġacc rued
-Ġch icks
-Ġsto pp
-Ġblock ers
-C od
-Ġcomment ers
-ĠSomew here
-ĠPhot ographer
-the me
-Ġmay oral
-w u
-Ġanten nas
-Ġrev amped
-ĠSubject s
-it é
-im ura
-Ġentr ances
-liter ally
-Ġten ets
-ĠO MG
-ĠMP H
-ĠDon key
-ĠOff ense
-Ġ" +
-Sn ap
-ĠAF B
-Ġan imate
-ĠS od
-His panic
-Ġinconsist ency
-D b
-F Y
-Ex port
-Ġa pe
-Ġpear l
-ib el
-ĠPAC s
-Ġ{ \
-Ġact u
-ĠHS BC
-camp us
-Ġpay off
-Ġde ities
-ĠN ato
-ou ple
-Ġcens ored
-ĠCl ojure
-Ġconf ounding
-en i
-Ġreck on
-op he
-Ġspot ting
-Ġsign ifies
-Ġprop el
-Ġfest ive
-S uggest
-Ġpled ging
-ĠB erman
-Ġrebell ious
-Ġovershadow ed
-Ġinfiltr ated
-j obs
-67 2
-Ġscal able
-Ġdomin ion
-ĠNew foundland
-ĠMead ow
-Ġpart itions
-AM I
-Ġsupplement ary
-str ument
-Ġhair y
-Ġperpet uate
-Ġnuts hell
-ĠPot ato
-ĠHob bit
-Ġcur ses
-Flo at
-Ġquiet er
-Ġfuel ing
-Ġcaps ules
-ĠL ust
-ĠH aunted
-Exec utive
-Ġchild birth
-G re
-Ġrad iant
-å İ
-Ġm alls
-Ġin ept
-ĠWarrant y
-Ġspect ator
-E h
-t hens
-Ġculmin ating
-æ ©
-ary a
-ãĤ ®
-ilit arian
-ĠOR IG
-ĠSp ending
-pt ives
-ĠS iren
-ĠRec ording
-ay ne
-Ġv im
-Ġspr ang
-T ang
-ĠM FT
-mor ning
-ĠWe ed
-m peg
-cess ion
-ĠCh ung
-7 30
-w arning
-56 2
-handed ly
-P oor
-P olitics
-: #
-Ġp ian
-Ġfec es
-ĠDocument ation
-Ġban ished
-Ġ3 99
-ĠAR C
-Ġhe inous
-J ake
-ĠAm ir
-way ne
-v re
-os henko
-Ġnotebook s
-Ġfound ational
-Ġmarvel ous
-ixt ape
-Ġwithdraw als
-Ġh orde
-ĠD habi
-is able
-ĠK D
-Ġcontag ious
-ĠD ip
-ĠAr rows
-Ġpronoun s
-Ġmorph ine
-ĠB US
-68 2
-Ġk osher
-fin ished
-ĠInstr uments
-Ġf used
-yd en
-ĠSal mon
-F ab
-aff ected
-K EN
-C ENT
-Dom ain
-Ġpoke mon
-ĠDr inking
-G rowing
-ĠInvestig ative
-ĠA ether
-em i
-Ġtabl oid
-Ġrep ro
-ĠNot withstanding
-ĠBers erker
-Ġdram as
-Ġclich é
-Ġb ung
-ĠU RI
-ĠD os
-0 44
-Ġpast ors
-Ġl s
-Ġac rylic
-aun ts
-Ed ward
-Ġmajor ities
-B ang
-Ġfield ing
-ĠRepl acement
-ĠAl chemy
-pp ard
-ĠRome o
-ĠSan ct
-ĠLav rov
-ib ble
-Inst ruct
-Ġimp ractical
-ĠPlay boy
-ce phal
-Ġsw aps
-Ġk an
-ĠThe o
-Ġillust rating
-Ġdismant led
-ĠTrans gender
-ĠG uth
-UG H
-Ġtriumph ant
-Ġencomp ass
-Ġbook mark
-udd in
-j er
-Ġpred icate
-ES H
-Ġwhen ce
-ĠAB E
-Ġnon profits
-Se qu
-Ġdi abetic
-Ġp end
-Ġheart felt
-sh i
-Ġinter acts
-ĠTele com
-Ġbombard ment
-dep ending
-ĠLow ry
-ĠAd mission
-ĠBl ooming
-ust ration
-ene gger
-B rew
-Ġmol ten
-ĠNer d
-P IN
-âĸ Ģ
-ave ment
-Ġtou red
-Ġco efficients
-ĠTray von
-ans son
-Ġsand y
-t old
-fl ows
-Ġpop ulous
-ĠT inder
-ĠBl iss
-R achel
-Min imum
-Ġcontest ant
-ĠRed uce
-ĠMor se
-ĠGrass ley
-ĠClick er
-Ġexp r
-Ġs incerity
-Ġmar qu
-Ġelic it
-ĠPro position
-ĠDemon ic
-Ġtac os
-G reek
-Ġpost war
-Ġin sofar
-ĠP ork
-Ġ35 2
-doctor al
-walk ing
-Ġmid term
-ĠSam my
-sight ed
-ĠTR ANS
-ic i
-AL D
-ĠUS L
-ĠF ISA
-ĠAm pl
-ĠAlex andra
-ine lli
-Tr ain
-Ġsign ify
-ĠVers us
-Ġob fusc
-Ġk h
-Ġagg ro
-ĠRen ault
-Ġ3 48
-5 18
-ox icity
-0 22
-ĠTw ist
-Ġgoof y
-D ynamic
-Ġbrief ings
-m ight
-8 99
-Ġderog atory
-T ro
-Ġfor ging
-ĠKor an
-ĠMar ried
-ĠBuc s
-Ġpal ate
-ĠCon version
-m able
-4 13
-Ġ( _
-Ġs iph
-ĠN EO
-col lege
-Ġmarg inally
-Ġfl irt
-ĠTra ps
-ĠP ace
-é »Ĵ
-Ġgoalt ender
-Ġforb ids
-Ġcler ks
-ĠT ant
-ĠRobb ins
-ĠPrint ing
-Ġpremie red
-Ġmagn ification
-ĠT G
-ĠR ouse
-ĠM ock
-odynam ics
-Ġpre clude
-ism o
-ĠPul itzer
-Ġaval anche
-ĠK odi
-rib une
-ĠL ena
-Elect ric
-Ġref inery
-Ġend owed
-Ġcounsel ors
-Ġd olphin
-ĠM ith
-Ġarm oured
-hib ited
-Beg in
-ĠP W
-O il
-ĠV or
-ĠShar if
-ĠFraz ier
-est ate
-Ġj ams
-Pro xy
-Ġband its
-ĠPresbyter ian
-ĠPrem iere
-t iny
-ĠCru el
-Test ing
-Ġhom er
-ĠV ERS
-ĠPro l
-ĠDep osit
-ĠCoff in
-Ġsemin ars
-Ġs ql
-ĠDef endants
-Altern atively
-ĠR ats
-ç «
-ethy st
-' >
-Ġiss uer
-58 9
-Ġch aired
-ĠAccess ories
-man ent
-Ġmar row
-ĠPrim ordial
-C N
-Ġlimit less
-ĠCarn age
-Ġund rafted
-q v
-IN ESS
-on ew
-Ġco hesion
-98 7
-Ġne cks
-Ġfootball er
-ĠG ER
-Ġdetect able
-ĠSupport ing
-ĠCS V
-oc ally
-k Hz
-Ġund e
-Ġsh one
-Ġbud ding
-tra k
-Stand ing
-ĠStar craft
-ĠKem p
-Ben ch
-Ġthw arted
-ĠGround s
-ath i
-L isa
-Dial og
-ĠS X
-V ision
-Ġingen ious
-Ù IJ
-Ġfost ering
-ĠZ a
-ĠIn gram
-Ġ" @
-N aturally
-6 16
-0 35
-ĠF AC
-H mm
-55 4
-Ġacceler ator
-ĠV end
-Ġsun screen
-Ġtuber culosis
-rav iolet
-ĠFunction al
-ĠEr rors
-ed ar
-19 66
-ĠSpect re
-ĠRec ipes
-88 5
-ĠM ankind
-L iverpool
-Ġ| --
-Ġsubst itutes
-ĠX T
-w ired
-Ġinc o
-ĠAf gh
-E va
-ic c
-S ong
-K night
-Ġdilig ently
-ĠBroad cast
-A id
-Ġaf ar
-ĠH MS
-aton in
-ĠGr ateful
-Ġfire place
-ĠOm ni
-e uro
-ĠF RE
-ĠSh ib
-ĠDig est
-t oggle
-Ġheads ets
-Ġdiff usion
-ĠSqu irrel
-ĠF N
-Ġdark ened
-out her
-Ġsleep s
-ĠX er
-gun s
-Ġset ups
-Ġpars ed
-Ġmamm oth
-ĠCur ious
-g ob
-ĠFitz patrick
-ĠEm il
-im ov
-........ .....
-ĠB enny
-Second ly
-Ġheart y
-Ġcons on
-st ained
-Ġgal actic
-cl ave
-Ġplummet ed
-Ġp ests
-Ġsw at
-Ġrefer rals
-ĠLion el
-h oly
-Ġunder dog
-ĠSl ater
-ĠProv ide
-ĠAm ar
-ress or
-å Į
-ong a
-Ġtim id
-Ġp iety
-ĠD ek
-Ġsur ging
-az o
-Ġ6 10
-Ġdes ks
-ĠSp okane
-ĠAn field
-Ġwars hips
-ĠCob ra
-Ġar ming
-clus ively
-ĠBad ge
-ag ascar
-ĠPR ESS
-ĠMcK enzie
-ĠFer dinand
-burn ing
-Af ee
-Ġtyr ann
-ĠI w
-ĠBo one
-100 7
-ĠRe pt
-Ċ Âł
-Ġcar avan
-ĠD ill
-ĠBundes liga
-Ch uck
-Ġheal er
-ãĥ¼ãĥ Ĩ
-ĠH obby
-Ġneg ate
-Ġcrit iques
-section al
-mop olitan
-Ġd x
-Ġouts ourcing
-ĠC ipher
-t ap
-Sh arp
-Ġup beat
-Ġhang ar
-Ġcru ising
-ĠNi agara
-Ġ3 42
-ill us
-ĠS v
-Ġsubt itles
-Ġsqu ared
-Ġbook store
-Ġrevolution aries
-ĠCarl ton
-ab al
-Ut ah
-Ġdesp ise
-ĠU M
-cons ider
-aid o
-Ġc arts
-ĠT urtles
-Tr aining
-Ġhonor ary
-Â ¢
-Ġtri angles
-4 22
-Ġreprint ed
-Ġgrace ful
-ĠMong olia
-Ġdisrupt ions
-ĠB oh
-Ġ3 49
-Ġdr ains
-Ġcons ulate
-Ġb ends
-Ġm afia
-ur on
-ĠF ulton
-m isc
-Ġren al
-Ġin action
-ck ing
-Ġphot ons
-Ġbru ised
-ĠC odes
-og i
-Ġn ests
-ĠLove ly
-ĠLib re
-ĠD aryl
-Ġ# ##
-S ys
-. ,"
-Ġfree zes
-est ablishment
-and owski
-Ġcum bers
-ĠSt arg
-ĠBom bs
-Ġleg ions
-Ġhand writing
-Ġgr un
-ĠC ah
-sequ ent
-Ġm oth
-ĠMS M
-Ins ert
-F if
-Ġmot el
-Ġdex ter
-ĠB ild
-hearted ly
-Ġpro pe
-ĠText ure
-ĠJ unction
-ynt hesis
-oc ard
-ĠVer a
-ĠBar th
-Ġμ g
-Ġl ashed
-Ġ35 1
-ĠZ amb
-ĠSt aples
-ĠCort ex
-ĠCork er
-Ġcontinu um
-ĠWR ITE
-unt a
-rid or
-Ġde ems
-0 33
-ĠG OLD
-p as
-Ġrep ressive
-ãĥĨ ãĤ£
-Ġbaff led
-Sc ar
-Ġc rave
-Ġ ______
-Ġentrepreneurs hip
-ĠDirector ate
-Ġ' [
-Ġv ines
-Ġasc ended
-ĠGR OUP
-ĠGood bye
-Ġdo gged
-ãĥ´ ãĤ¡
-Man ufact
-Ġunimagin able
-ri ots
-ier rez
-Ġrel ativity
-ĠCraft ing
-ra ught
-ud en
-c ookie
-Ġassass ins
-Ġdissatisf ied
-ac ci
-Ġcondu it
-Sp read
-ĠR ican
-n ice
-izz le
-Ġsc ares
-ĠWH Y
-ph ans
-5 35
-Ġprot racted
-ĠKrist en
-5 36
-ĠSc rib
-ĠNe h
-Ġtwent ies
-Ġpredic ament
-Ġhandc uffs
-Ġfruit ful
-ĠU L
-ĠLud wig
-Ġatt est
-ĠBre aker
-Ġbi ologically
-ĠDeal er
-Ġrenov ations
-f w
-ess en
-Al ice
-ĠHen ri
-Ġun ilaterally
-ĠS idd
-h ai
-ĠSt retch
-S ales
-Ġcumbers ome
-ĠJ avier
-Ġtrend y
-Ġrot ting
-ĠChall enges
-Ġscra ps
-Ġfac ets
-ĠVer onica
-ĠVer ge
-ĠS ana
-Al ien
-ĠR ih
-Ġrad ial
-ect ar
-Ġ6 30
-cl i
-Mar ie
-Ġwild fire
-ĠCat o
-h ander
-Ġwait ress
-Ġch ops
-ĠS ECTION
-Ġblunt ly
-ĠCat alog
-n ian
-stud y
-Ġpat rolling
-ĠT enth
-nex us
-ĠN ON
-op sy
-Ġsc athing
-s ie
-Ġdeterior ated
-V B
-Naz is
-Ġdep ictions
-Ġauthent icated
-ĠCon ce
-k rit
-Ġpromul g
-ĠL ONG
-U FC
-ĠVis itors
-ĠRec all
-Ġrehab ilit
-ĠSL I
-Ġglac ier
-ĠB ite
-Ġ50 3
-Ġvom it
-Ġfer mented
-ĠKh alid
-Ġgrad ed
-ĠMag icka
-ĠIch igo
-power ful
-ic ators
-75 3
-Ġsh rew
-Ġ35 6
-Ġlegal izing
-Ġall otted
-ĠArch demon
-ith ing
-igg urat
-V OL
-Le od
-Ġo ily
-Ġindu cing
-Ġamy gdala
-Ġadm ins
-ĠAcqu isition
-C AN
-Ġsche matic
-Ġmo an
-ĠCamer oon
-Ġt ink
-Ġmer ry
-Ġbutter flies
-ĠGo ff
-Ġworks pace
-ĠCor ona
-Ġj avascript
-ĠD olphin
-ĠCant or
-4 64
-to e
-AP S
-ĠAg ing
-Ġpadd ed
-ĠZ heng
-ĠHe ld
-Ġest ranged
-Ġ7 70
-. }
-ĠDun ham
-Ġsm okes
-Ġcap itals
-und ai
-Sh in
-ĠFound ing
-Ġent itle
-Ġcenter piece
-D iscover
-Ġthere to
-al ert
-ĠN ou
-ĠAnaly st
-l c
-F H
-FI ELD
-ĠP OV
-gr ay
-Ġar cs
-ĠH OT
-Ġr s
-Ġoblig atory
-ĠArchitect s
-ĠS ven
-ĠF EC
-0 200
-Christ mas
-ĠAlban ia
-rat om
-58 7
-Ġhard ships
-Ġaut os
-ĠCharg es
-Ġap es
-Ġ3 76
-wal let
-Ġintox ication
-Ġgobl in
-Ġ5 70
-++++++++ ++++++++
-ĠYel p
-ĠMag netic
-ĠBr iggs
-R ail
-Ġspawn s
-ĠW iggins
-Ġshowc ased
-Ġres orted
-ub en
-Ġwh ipping
-Ġim itate
-Ġdigest ion
-ĠUS PS
-ĠG est
-Ġye a
-ĠT ight
-ind al
-ic as
-` .
-C AST
-'' ;
-ĠF et
-opath ic
-In valid
-Ġregrett ed
-Ġbro ccoli
-ĠSc ores
-e ve
-Ġpost ings
-Ġaccum ulating
-Ġneed less
-elf th
-Ġmay ors
-Ġsc rib
-Ġanecd otes
-Ġbot ched
-ĠRib bon
-ĠConstant ine
-i uses
-ess es
-Ġdev ise
-Comp ared
-Ġp udding
-Ġg arg
-Ġev oke
-79 7
-Ġdet ox
-9 09
-ĠPie ces
-ĠMcC artney
-Ġmet ast
-ĠK rypt
-P OR
-Ġt ending
-ĠMerch ants
-Pro of
-ĠV arg
-ĠPort able
-ãĥ¼ãĥĨ ãĤ£
-B rain
-25 00
-Ġfol iage
-Ø ¹
-Ġment ors
-ĠA ires
-Ġminimal ist
-Ġing ested
-ĠTro jan
-ĠQ ian
-inv olved
-0 27
-Ġer oded
-RA FT
-Ġbl urry
-M ob
-Ġbuff et
-ĠFn atic
-ae a
-KN OWN
-ĠIn it
-s afety
-en um
-ACT ION
-ĠCrus her
-ĠD ates
-Ġ ................
-c alling
-ak ov
-Ġvent ured
-Ġ5 55
-au ga
-H art
-ĠA ero
-M AC
-Ġthin ly
-Ġar ra
-ST ATE
-ild e
-ĠJac qu
-ĠFem ales
-Ġthe orem
-Ġ3 46
-Ġsmart est
-ĠPU BLIC
-ĠK ron
-ĠB its
-ĠV essel
-ĠTele phone
-Ġdec ap
-Ġadj unct
-ĠS EN
-mer ga
-Ġred acted
-Ġpre historic
-Ġexplan atory
-ĠRun s
-ĠUtt ar
-ĠM anny
-ĠAUTH OR
-ĠUnle ashed
-ĠBow ling
-be ans
-79 3
-Ġunivers es
-Ġsens it
-ĠK ung
-re peat
-ctr l
-Ġp aced
-Ġfull er
-Cl ock
-Ġrec omb
-ĠF aul
-ĠB unker
-Ġpool ed
-Ġan a
-ĠM outh
-LL OW
-hum ane
-Ġbull do
-ĠMicha els
-f am
-Ġwreck ed
-Ġport rays
-ĠWh ale
-ĠH es
-Ġguess es
-ĠBrow se
-ĠL APD
-Ġconsequ ential
-ĠInn ocent
-ĠD RAG
-Ġtrans gress
-ĠO aks
-Ġtri via
-ĠRes on
-ĠA DS
--- +
-ĠT oll
-Ġgrasp ing
-ĠTHE M
-ĠT ags
-ĠCon clusion
-Ġpract icable
-Ġho op
-Ġunintention ally
-Ġign ite
-ĠM ov
-ur ized
-le hem
-Ter min
-Ġcolour ful
-ĠLin ear
-ĠEll ie
-G y
-Ġman power
-Ġj s
-Ġem oji
-ĠSHAR ES
-_ .
-0000 7
-Ġsophistic ation
-Ġunders core
-Ġpract ise
-Ġbl ob
-op ens
-Uk raine
-Ke eping
-Y C
-J R
-ult imate
-Cl aim
-Ġautom obiles
-99 3
-ste el
-Ġpart ing
-ĠL ank
-... ?
-Ġ38 5
-Ġremem brance
-Ġe ased
-Ġcov ari
-ĠS ind
-Effect ive
-Ġdisse mination
-ĠMo ose
-ĠCl apper
-br ates
-App ly
-Ġinv is
-Ġwors ened
-âĢĶ -
-Ġlegisl ator
-ĠL ol
-ĠRow e
-Ġdealers hip
-um ar
-id ences
-Ġinvestig ates
-Ġc ascade
-Ġbid der
-ĠB EN
-Iron ically
-Ġpres iding
-Ġd ing
-Ġcontrad icted
-Ġshut s
-ĠF IX
-Ġ3 66
-Dist rict
-Ġsin ful
-ĠChar isma
-o ops
-Ġtot ality
-Ġrest itution
-ĠOpt imus
-ĠD ah
-Ġcl ueless
-urn ed
-Ġnut rit
-Ġland owners
-Ġfl ushed
-Ġbroad en
-m ie
-Ġprint ln
-Ġn ig
-ĠCorp us
-J en
-Ġprot o
-ĠWik imedia
-ĠPal o
-C OR
-Ġstory lines
-Ġevangel icals
-ĠDar rell
-Ġrot or
-ĠH W
-sk illed
-ery l
-Ġbe gg
-ĠBl umenthal
-Ġwe aving
-Ġdown wards
-ĠJack et
-ĠANG EL
-Te chnology
-Ġes oteric
-alde hyde
-Ġfur iously
-Ġforeign er
-We ak
-CH O
-ĠH ound
-Exper ience
-ĠPlay station
-ĠM IA
-ĠU ng
-cl oth
-ag all
-Ġcal ming
-iz ens
-St ruct
-ĠW itches
-ĠCeleb ration
-Ġ........ ......
-pt roller
-ĠTC U
-Ġb unny
-ãĥ į
-ut orial
-Ġup scale
-ĠSt a
-ĠCol ossus
-Ġchlor ide
-ĠZ ac
-ĠRe asons
-ĠBrook ings
-ĠWH ITE
-][ /
-ĠL ose
-9 05
-Ġunders ide
-ern els
-Ġv ape
-do zen
-upp et
-ĠST OP
-mat ical
-ĠStat ements
-hed dar
-P AC
-Custom er
-Ġmem os
-ĠP J
-end ars
-ĠLim its
-l augh
-Ġstabil ized
-ĠALE C
-Y A
-Up grade
-al am
-Ġtechn o
-Ġan ew
-fore seen
-Ġcolleg iate
-ĠPy ro
-ĠD ism
-Ġfront line
-Ġammon ia
-I U
-Qu ite
-John ny
-ass in
-G OP
-ĠSt yles
-ĠSovere ign
-acter ial
-5 49
-ĠR IP
-ĠL ists
-Ġ3 64
-ĠRece p
-s ocket
-ĠByr d
-ĠCand le
-An cient
-Ġappell ant
-en forcement
-ace a
-ans ki
-Ġold s
-88 6
-Ġsl urs
-Ġem pires
-Ġbuck le
-Ġalien ation
-ĠAber deen
-Ġunic orn
-Ġoverr iding
-ĠL X
-pp a
-Ġdesp ised
-ĠB ugs
-ĠB ST
-S outhern
-5 33
-Ġhall mark
-ĠPost er
-Ġstem med
-Ġprincip als
-ĠT ECH
-ĠSand wich
-It aly
-Ġche esy
-ĠSet TextColor
-ĠProt ective
-ĠC ohn
-J O
-apt op
-Re ason
-Lead er
-ĠUnder stand
-ĠFr idays
-ĠContin uous
-Ġcl ipping
-ĠR ye
-Ġber th
-tim er
-ann is
-re act
-Ġbuff alo
-ĠPar as
-Ġ6 55
-Ġpres ided
-ĠSun rise
-Ġve ts
-Ġcl oves
-ĠMcC ull
-Stre ngth
-G AN
-Ġill iter
-ĠPric ing
-l é
-Ġresist or
-Ġbr un
-ĠSuff olk
-Ñ ĭ
-ĠL iver
-Re leased
-Ġwhat s
-8 60
-ĠMe asures
-Ġden ouncing
-ĠRy zen
-Ġsou ven
-Ġcareg ivers
-ch ini
-ĠScar lett
-Ġt rough
-Cong ratulations
-Ġtax is
-ĠTrad ition
-j it
-Ġtable top
-Ġhither to
-Ġdis information
-off ensive
-h ra
-ĠDISTR ICT
-Ġcompl icate
-chen ko
-ĠRecon struction
-Ġpalp able
-Ġa usp
-Ġ4 28
-Ġshowc ases
-ĠPublic ation
-know ledge
-inn on
-4 19
-Ġretri eval
-and ers
-Ġref ute
-Ġinqu ired
-g ur
-Ġneg ativity
-Ġcons erve
-Ġafter life
-Ġpres upp
-ĠGill espie
-Ġm t
-ĠD N
-T ap
-Ġper pend
-ĠS my
-does n
-Ġsp illing
-Ġhyp ers
-K ate
-® ,
-ke pt
-ĠP owered
-Ġj a
-ĠK lux
-ard e
-ab an
-Ġ4 44
-Ġflatt ened
-ĠImprove ments
-urg a
-ĠK und
-Ġins cribed
-Ġfac ult
-Ġunpre pared
-ĠCons umers
-Ġsatisf ies
-Ġpul monary
-Ġinf iltration
-Ġex ternally
-Ġcongrat ulations
-ag han
-Ġair liner
-Ġfl ung
-Ġfly ers
-G D
-Ġsnipp ets
-Ġrec ursive
-Ġmaster ing
-L ex
-Ġovert ly
-v g
-Ġluck ily
-Ġenc ro
-ĠLanc et
-ĠAbyss al
-function al
-Ġs ow
-Ġsqu id
-Ġnar ration
-Ġn aughty
-ĠHon our
-ĠSpart ans
-Ġsh atter
-ĠTac oma
-ĠCal ories
-ĠR aces
-Sub mit
-Ġpurpose fully
-w av
-ĠY ok
-F est
-ĠG err
-Met ro
-Ġit iner
-f amous
-Ġ" {
-in line
-was her
-Iss ue
-ĠCL IENT
-oz o
-Vers ions
-7 25
-ĠGl ock
-Ġshield ed
-ĠPC R
-ENC Y
-ĠWe ld
-ĠSim pl
-Ġredirect ed
-ĠK ham
-Ġ( >
-Ġlab ou
-Ġdi apers
-ss l
-Ġcell ar
-organ isms
-ore sc
-ĠBer ks
-did n
-Sh ipping
-C hest
-Ġund one
-Ġmillion aire
-Ġc ords
-ĠYoung er
-appropri ately
-Ġsequ els
-u ve
-ant icipated
-Ġle wd
-ĠSh irt
-ĠDmit ry
-V eter
-Ġsl aying
-ĠY ar
-Ġcompl ication
-I owa
-ĠEric a
-ĠBL M
-g irlfriend
-b odied
-6 26
-19 63
-Ġintermedi ary
-Ġcons olation
-M ask
-ĠSi em
-ow an
-Beg inning
-Ġfix me
-Ġculmin ated
-Ġcon duc
-ĠVolunte er
-Ġpos itional
-Ġgre ets
-ĠDefin itions
-Ġthink er
-Ġingen uity
-Ġfresh men
-ĠMom ents
-Ġ35 7
-ate urs
-ĠFed Ex
-s g
-69 4
-Ġdwind ling
-ĠBO X
-sel age
-Ġt mp
-Ġst en
-ĠS ut
-Ġneighbourhood s
-Ġclass mate
-f ledged
-Ġleft ists
-Ġclim ates
-ATH ER
-ĠScy the
-ul iffe
-Ġs ag
-Ġho pped
-ĠF t
-ĠE ck
-ĠC K
-ĠDo omsday
-k ids
-Ġgas ped
-Ġmon iker
-ĠL od
-ĠC FL
-t ions
-r ums
-fol ios
-Ġm d
-Ġunc anny
-Ġtrans ports
-ĠLab rador
-Ġrail ways
-Ġappl iance
-ĠCTR L
-æ Ģ
-Pop ulation
-ĠConfeder acy
-Ġunb earable
-Ġdors al
-ĠIn form
-op ted
-ĠK ILL
-Mar x
-Ġhypoc ritical
-q us
-ĠN umerous
-ĠGeorg ian
-ĠAmbro se
-ĠL och
-Ġgu bernatorial
-ĠX eon
-ĠSupp orts
-ens er
-ee ly
-ĠAven ger
-19 65
-Ar my
-Ġju xtap
-Ġcho pping
-ĠSpl ash
-ĠS ustainable
-ĠFin ch
-Ġ18 61
-ict ive
-at meal
-ĠG ohan
-Ġlights aber
-ĠG PA
-ug u
-ĠRE PL
-vari able
-Ġher pes
-Ġdesert s
-ac iously
-Ġsitu ational
-week ly
-ob l
-Ġtext ile
-ĠCorn wall
-Ġcontrace ptives
-ĠA ke
-] -
-ä¹ ĭ
-: ,
-ĠW em
-ĠB ihar
-Ġ' .
-Ġbe re
-Ġanal ogue
-ĠCook ies
-Ġtake off
-Whe el
-Ġmaj estic
-Ġcomm uting
-0 23
-ĠCor pse
-ass ment
-min i
-Ġgor illa
-ĠAl as
-ere e
-Ġacquaint ances
-ĠAd vantage
-Ġspirit ually
-Ġey ed
-pm wiki
-ĠE nder
-Ġtrans lucent
-Ġnight time
-ĠIM AGES
-5 45
-ĠK amp
-ĠFre ak
-Ġ ig
-Port land
-4 32
-ĠM ata
-Ġmar ines
-Ġh ors
-ater asu
-ĠAtt ribution
-Ġ-------- -
-Ġk ins
-ĠBEL OW
-++ +
-Ġre eling
-ol ed
-Ġcl utter
-ĠRel ative
-Ġ4 27
-B US
-Ġa vert
-ĠChe ong
-ĠA ble
-ĠPry or
-Develop er
-Ġen cyclopedia
-ĠUSA F
-ĠG arry
-Sp ain
-Bl ocks
-Ġexp osition
-ĠGamer Gate
-W OR
-Ġstockp ile
-Ġclot hed
-ĠT one
-ĠR ue
-t umblr
-Ġtreacher ous
-Ġf rying
-Ñ Į
-ĠS ph
-Ġrest raints
-Ġemb odies
-ĠG es
-S afety
-Ġnegoti ators
-min ing
-ĠAppalach ian
-L OS
-ĠJenn a
-Ġpass ers
-ç ĭ
-sn ap
-Ġshort en
-creat or
-Ġinn umerable
-uther land
-67 4
-ĠW OM
-ĠAs cend
-ĠArm ory
-ĠTrans action
-K ick
-Ġsuit case
-day Name
-Ġwaste ful
-mar riage
-ĠMcC abe
-ite ch
-ĠO ss
-Cl osure
-ĠTreasure r
-Ġindec ent
-ĠD ull
-Ġresid ences
-19 59
-ĠS ettlement
-Ham ilton
-Ġself ies
-ĠRank ing
-ĠBark ley
-ĠB ore
-ĠW CS
-ĠMar itime
-ĠH uh
-ĠForest ry
-Ġcultiv ating
-ĠBall ard
-Ġg arrison
-ĠSD L
-9 30
-Ġnas cent
-Ġirresist ible
-Ġaw fully
-\/ \/
-Ġequ ate
-Ġanthrop ology
-ĠSylv ia
-Ġintest ine
-Ġinnoc uous
-cess ive
-ag ra
-ĠMet roid
-G rant
-8 55
-ģ ĸ
-Ġ" _
-ãĥĥ ãĥī
-Ġappra isal
-ĠFred dy
-04 6
-Ġ40 6
-Ġ18 30
-Ġd ocking
-St atic
-Ġp ont
-ĠVolt age
-ĠSt ead
-ĠMort gage
-ĠJon ah
-Y L
-CLASS IFIED
-Ġas bestos
-nik ov
-Ġcoll agen
-ĠOrb ital
-P ocket
-7 99
-Ġhy brids
-inc hes
-Ġinv oice
-und y
-Ġinequ alities
-T rend
-w ashed
-B ALL
-Ġluc id
-ĠComment ary
-Ġw itty
-Br andon
-Ġbru ising
-Ġ6 20
-es cent
-box ing
-P OL
-Ġ3 78
-R ect
-Ġlic ences
-ĠMcG ee
-p ressed
-D anny
-Ġj ammed
-ord inate
-Ġle th
-Ġdistingu ishes
-ĠYam aha
-IL S
-ĠH ume
-ĠC ategories
-Rober ts
-Ch art
-Ġbeet le
-ĠGra veyard
-Ġ($ )
-o ÄŁ
-Ġtw ilight
-are lla
-á ½
-Ġbooth s
-ĠH HS
-ĠFeld man
-Ġexcav ation
-Ġphilosoph ies
-at ography
-ĠGar age
-te chnology
-Ġunfor gettable
-Ġver ifying
-Ġsubord inates
-E ls
-Ġne b
-G aming
-EN A
-ĠAchieve ment
-it ters
-ĠG abe
-Ġd umps
-for cer
-Ġpo ignant
-ĠM BA
-ĠHe idi
-ime i
-Ġm ages
-Ġliber ate
-Ġcircum cised
-ĠMer maid
-ĠMat th
-t ogether
-ĠW ichita
-Ġstore front
-ĠAd in
-V II
-Four th
-Ġexplore rs
-W ER
-Not able
-Bro ok
-m ens
-F aith
--------- -
-ĠJ ou
-¬ ¼
-Ġpine apple
-Ġam alg
-el n
-ark able
-ĠãĤµ ãĥ¼ãĥĨãĤ£
-ĠãĤµãĥ¼ãĥĨãĤ£ ãĥ¯ãĥ³
-Ġov arian
-ĠE choes
-Ġhairc ut
-Ġp av
-Ġch illed
-anas ia
-Ġsty led
-Ġd ab
-ni per
-Ġminister ial
-ĠD UP
-T an
-Ġsul ph
-ĠD eter
-ĠBo hem
-od an
-Ġeduc ator
-â ĵĺ
-sp ir
-Ch icken
-ĠE leanor
-Ġqu i
-Ġheav iest
-Ġgrasp ed
-U RA
-Ġcro oked
-Jess ica
-pro blem
-Ġpred etermined
-Ġman iac
-Ġbreath s
-ĠLauder dale
-Ġh obbies
-y z
-Cr ime
-Ġcharism a
-d L
-Ġle aping
-Ġk ittens
-Ang elo
-ĠJ ACK
-ĠSu zanne
-Ġhal ting
-ENT ION
-Ġswall owing
-ĠEarthqu ake
-Ġeight eenth
-ĠN IC
-ĠIN F
-ĠCons cious
-Ġparticular s
-circ le
-7 40
-Ġbene volent
-Ġ7 47
-Ġ4 90
-Ġr undown
-ĠVal erie
-ĠB UR
-Ġcivil isation
-ĠS chn
-W B
-ot ide
-intern ational
-Ġj ohn
-Ġ19 02
-Ġpe anuts
-Ġflav ored
-k us
-Ġro ared
-Ġcut off
-é £
-Ġorn ament
-Ġarchitect ures
-Ġ3 69
-ol or
-ĠWild e
-ĠC RC
-ĠAdjust ed
-Ġprov oking
-land ish
-Ġrational ity
-Ġjust ifies
-Ġdisp el
-Ġa meric
-ĠPol es
-Ø ©
-Ġen vis
-ĠD oodle
-ä½ ¿
-igs aw
-auld ron
-Techn ical
-T een
-up hem
-ĠX iang
-Ġdetract ors
-ĠZ i
-ĠJournal ists
-Ġconduc ive
-ĠVolunte ers
-Ġs d
-Know ing
-Ġtrans missions
-ĠPL AN
-ĠL IB
-Ġall uded
-Ġob e
-Ġd ope
-ĠGold stein
-Ġwavelength s
-ĠDest ination
-nd a
-ug i
-Ġattent ive
-ĠLe an
-ral tar
-Ġman g
-mb uds
-ak ings
-b ender
-Ġacc ol
-Ġcraw led
-N OW
-Min nesota
-Ġflour ished
-ĠZ up
-ĠSuper visor
-ĠOliv ier
-Ex cellent
-Ġwid en
-D one
-Ġw ig
-Ġmiscon ceptions
-Cor p
-W an
-Ġvener able
-ĠNot ably
-ĠKling on
-an imate
-Bo ost
-ĠS AY
-miss ing
-ibli ography
-mel on
-Ġpay day
-Ø ³
-bo le
-Ġve iled
-ĠAl phabet
-It alian
-Ġever lasting
-ĠR IS
-ĠC ree
-rom pt
-Ġh ating
-Ġgrin ning
-Ġge ographically
-OS H
-Ġwe eping
-ĠÂłĠÂłĠÂłĠÂł ĠÂłĠÂłĠÂłĠÂł
-Ġimpe cc
-Let ter
-Ġblo ated
-PL A
-ĠFe in
-Ġper sever
-Th under
-Ġa ur
-ĠR L
-Ġpit falls
-âĸ º
-Ġpredomin ant
-Ġ5 25
-7 18
-AP E
-7 14
-Ġfarm land
-ĠQ iao
-Ġv iolet
-ĠBah amas
-Ġinflic ting
-ĠE fficiency
-Ġhome brew
-Ġundert ook
-Ġcur ly
-ĠHard ing
-man ia
-59 6
-Ġtem pered
-Ġhar rowing
-ĠP ledge
-ĠFranken stein
-è ª
-M otion
-Ġpredict ably
-ĠExpl osion
-oc using
-er d
-col o
-FF ER
-Ġback field
-ĠV IDE
-ue bl
-N arr
-ĠArg ument
-Ġgen omic
-Ġbout ique
-Ġbatt ed
-ĠB inary
-Ġg amb
-ĠRh ythm
-67 3
-Ġa float
-ĠOlymp ia
-Y ING
-Ġend if
-is in
-Ġwin ters
-Ġsc attering
-I v
-D istance
-Ġtr u
-ĠCom fort
-Ġne xus
-Ġair flow
-ĠByz antine
-p ayers
-con i
-ĠB etsy
-D eal
-ĠN ug
-ĠContin ent
-red ibly
-Ġoptim izing
-al beit
-Ġec static
-ĠPro to
-ç ·
-iv ot
-âĸ Ħ
-em p
-rou nder
-Ġcl out
-ĠI ST
-66 3
-ĠDoll ars
-ĠD AC
-Ġsubsc ribed
-Ġrehears al
-Ġam ps
-ĠSh ang
-es m
-Ġspr inkle
-Ġassail ant
-ĠO o
-ĠCoin base
-T act
-Ġret ina
-Ġn uns
-R ON
-att o
-Ġj ug
-ĠSV G
-Ġb ikini
-ĠFI LE
-ĠFound ers
-ep ort
-ĠK P
-Ġrest ores
-ĠTh ick
-Ġash ore
-Ġappro vals
-R ender
-M AG
-G raham
-ĠCort ana
-ãĥ³ ãĤ¸
-ss h
-or ians
-ars ity
-ĠInsp ired
-u pper
-Ġsign alling
-Ġreb uke
-Ġfl ares
-Ġdownt ime
-Stud ies
-Ġstagn ation
-ĠSequ ence
-Ġgr unt
-Ġass ures
-ĠPL A
-59 2
-Ġintra ven
-d epend
-Sus an
-ĠManz iel
-Man ia
-Cont ract
-Ġsl ams
-Ġcult ured
-Ġcred itor
-L IST
-ĠH UM
-ĠChatt anooga
-serv ed
-Ġclo aked
-ĠF TP
-p owder
-ĠSt ella
-uct ive
-Ġcheap ly
-ĠMU CH
-ĠGalile o
-Ġsu ites
-spe ech
-Ġdeliber ations
-ĠCh ips
-« ĺ
-Bal ance
-ĠWyn ne
-ĠAk ron
-Ass et
-Ġhon oured
-Ġed ged
-Like wise
-anim ous
-ĠW age
-ĠEz ek
-ad vertisement
-ĠRT X
-ĠM AD
-Ġmigr ating
-ĠS QU
-Ġ4 75
-Ed ited
-Ġshorth and
-ĠBas ics
-Ġcro tch
-ĠEV EN
-Ġv m
-effic iency
-Ġcal ves
-ĠF rie
-ĠBrill iant
-Ġstri kers
-Ġrepent ance
-Ġarter ies
-r l
-B ed
-h ap
-Ġcrypt ography
-ĠSab res
-Ġ4 14
-vi ks
-ih ara
-aps es
-T alking
-Ġintertw ined
-Ġdoc ks
-Ġalle le
-ĠArt ifact
-ĠH IM
-t orn
-ç ķ
-Ġop acity
-ĠE ly
-os uke
-Ġn ipple
-Ġhand written
-ĠV K
-ĠChamber lain
-ĠLa os
-ig raph
-g row
-Ġtr illions
-Ġdescend ant
-ĠSail or
-as uring
-Ġce ilings
-ĠWare house
-f lying
-ĠGl ow
-Ġn ont
-Ġmiscar riage
-Ġrig s
-Ġmin istries
-Ġelabor ated
-Ġdel usional
-ĠHum ane
-Ġ3 79
-n ets
-Ġblack out
-add ers
-Ġn p
-ĠT ire
-ro sc
-Ġsub div
-Ġlink age
-Ġchron ological
-ĠHER O
-Ġres ettlement
-ĠVin yl
-Ġpast oral
-ĠMob il
-ĠBar bar
-Co oldown
-ĠF ritz
-c riminal
-re pe
-Ġbell ig
-ĠBre ed
-Ġ4 18
-Ġsem blance
-ij k
-Ġcur tail
-Ġclin ch
-cont ained
-ĠProm pt
-ast on
-Ġw i
-Ġpursu its
-5 15
-ĠGl oss
-Ġfl ips
-Ġcoup ons
-Ġcl oning
-ĠLike ly
-Rem oved
-ĠQu artz
-r ices
-ĠSpe ars
-Ġp ious
-Ġdep reciation
-ĠD are
-oun ces
-am az
-O nt
-Ġp innacle
-d ocker
-0 26
-ĠW yr
-ĠPro per
-Ë Ī
-n il
-By tes
-Ġseek er
-t rial
-Ġunf olds
-ĠMar se
-Ġextravag ant
-ĠSurviv ors
-RED ACTED
-ĠSpeed way
-ĠCra igslist
-sub mit
-ĠGener ations
-Ġup holding
-Ġblood stream
-ĠMiss ions
-ĠL awn
-Ġlim bo
-ene i
-H uh
-ĠWild cats
-pre p
-ĠMark us
-ĠFor bidden
-rit ic
-IN O
-Ġexhib iting
-requ ent
-ch uk
-Ġhabit ual
-ĠComp atibility
-Dr ag
-RIP T
-uj ah
-GR OUND
-Ġdelinqu ent
-Ġburn er
-Ġcontempor aries
-Ġgimm ick
-load s
-Ġno zzle
-p odcast
-ĠW ak
-ĠStat en
-ĠK uh
-ãģ ĵ
-inter rupted
-Ġinv incible
-ĠBurn ett
-cig arette
-ĠPeb ble
-ĠTem porary
-ĠMar ino
-58 2
-Ġwast eland
-ident ly
-T x
-Ġr ite
-ĠPan asonic
-ĠM iddles
-ĠHort on
-ae us
-Ġc uring
-Ġm ats
-Ġadj ourn
-Ġfears ome
-pe z
-bo ats
-Ġpro pell
-Ġconflic ted
-ĠAng er
-Ġinsurg ent
-K arl
-Ġco ales
-Ġsouth western
-Ġdis su
-ĠO vert
-******** ****
-Ġbox ed
-ĠBr une
-aa a
-Ġgard ening
-ĠEng el
-tr acks
-Ġpur ified
-Ġplace holder
-ĠL ikes
-Ġd an
-G ab
-Ġe ct
-ĠF aw
-ĠEl iot
-Ġ' ,
-otrop ic
-ĠRu in
-hed on
-Ġca ul
-Ġa ft
-ĠCad illac
-gh a
-ass ian
-ud eb
-ĠT ick
-Ġadjust s
-AR GET
-5 37
-isc he
-ant y
-ĠFried rich
-ĠBl izz
-ĠA OL
-Camp aign
-Ġmamm al
-ĠVe il
-ĠK ev
-ĠMaur it
-ĠDam ien
-N ation
-E astern
-Ġ{ :
-Ġ= ================================
-Ġstereotyp ical
-Ġatt ic
-ĠCy borg
-requ ire
-Ġaward ing
-ĠPap ua
-bt n
-b ent
-B oo
-Ġ( =
-ĠX ander
-ĠSomers et
-Ġcatch y
-Ġcert ify
-STR UCT
-Ġit al
-Ġt ides
-ĠBr ands
-G ray
-comp etitive
-Ġcur ator
-ĠD G
-omin ium
-ĠGM Os
-ci ating
-ĠCarm en
-ow ard
-Balt imore
-Ġr gb
-C u
-Ġwip es
-spe ll
-IT NESS
-Ġsummar izes
-ĠRe vis
-Ġwhistlebl owers
-ĠBre ach
-Ġcro chet
-k os
-ews ki
-Ġrep et
-Ġcrim son
-ĠKar achi
-read able
-dim ension
-ĠI gor
-ild ed
-ĠZ ed
-ĠKe ane
-ĠCos metic
-DE P
-Ġretreat ing
-ĠU A
-ens ical
-Ġd usk
-ĠDick ens
-Ġaren as
-ĠPass age
-level s
-Ġcur v
-P ope
-Ġch ores
-ĠEl ise
-ĠComp ass
-b ub
-Ġmamm alian
-ĠSans krit
-ĠAN C
-ĠCr ack
-Q ual
-L aun
-amp unk
-Ġlearn ers
-Ġglam orous
-Ġfur the
-erm ott
-c and
-Gener ic
-Ġnarr ated
-Ġdisorder ly
-ĠTrans actions
-ĠDet ention
-ĠR oku
-Ä į
-Ġunder statement
-ĠS aur
-ĠRodrig o
-ĠAS AP
-S in
-Ġre joice
-Method s
-Ġelectro de
-Ġworsh ipped
-Ġid i
-ĠPhys icians
-Ġpop up
-Ġde ft
-ĠRem oval
-ĠBu enos
-ver bs
-Ġfun k
-ush a
-rict ion
-ore a
-ĠBang alore
-ĠKen obi
-zz i
-Ġnorm ative
-Ġgobl ins
-Ġcaf es
-ĠUN CLASSIFIED
-ĠF ired
-S IGN
-Ġs clerosis
-ĠV oter
-ĠSon ny
-ĠExt end
-ĠEV s
-Ar senal
-Ġp si
-Ġwid est
-ĠT us
-Ġlo oms
-Ġjust ifying
-ĠGr anger
-è ¯
-Ref er
-58 3
-Ġflour ishing
-ab re
-Ġr ave
-ĠCont ra
-Ġ18 98
-Add s
-Ġf ul
-ĠCo oke
-some one
-= #
-67 1
-Ġy ak
-Ġar te
-ĠMis cellaneous
-ĠDet ection
-ĠCl ancy
-â ģ
-ass ies
-Ġval iant
-ĠFemin ist
-cor ruption
-V el
-P ear
-Ġsucc inct
-Ġquick est
-k w
-Ġsp itting
-ĠL ibraries
-åħ ī
-ant z
-D ad
-ĠSpec ifications
-rup ulous
-and r
-RES ULTS
-Ġsnow ball
-Ġpred is
-ĠB axter
-ĠNurs ing
-ĠCh aff
-s we
-Ġout age
-Ġnest ing
-Ġnotor iety
-tr igger
-on ite
-j on
-Ġf ou
-ook ed
-ĠCelebr ity
-re ality
-Ġfat ig
-Ġhug ging
-Ġbother s
-ĠPan zer
-ĠCh andra
-fig ured
-Ġvol ts
-ĠCloud s
-Ġfee ble
-ĠCur ve
-ĠAs us
-78 6
-abs or
-ĠV ICE
-ĠH ess
-Ġmanufact ures
-Ġgri zz
-ĠPower ful
-ac id
-Ġsub sections
-ĠKrug man
-ĠAl ps
-is u
-Ġsequ est
-ĠUlt ron
-ĠT inker
-ĠGo ose
-Ġmism atch
-Att orney
-Ġmorph ology
-ĠSix ers
-ut tered
-ĠE LECT
-gr an
-Rus sell
-ĠG SL
-Ġfort night
-Ġ. )
-Ġapost le
-pr one
-el ist
-Unt itled
-ĠIm plementation
-ist ors
-Ġtank er
-Ġpl ush
-Ġattend ants
-ĠT ik
-ĠGreen wich
-ĠY on
-ĠSP L
-cell s
-unt led
-S olution
-ĠQu é
-Ġvac ated
-Ġupt ick
-ĠMer idian
-æ ĥ
-ĠDr ill
-9 25
-58 4
-Ġrenov ated
-ĠKub rick
-zy k
-Ġl ousy
-pp el
-ohyd rate
-ĠI zzy
-lesi astical
-CC C
-ĠAj ax
-Ġad apters
-ĠPetra eus
-Ġaffirm ation
-ĠST OR
-le ms
-ad oes
-ĠConstantin ople
-Ġp onies
-Ġl ighthouse
-Ġadherent s
-ĠBre es
-omorph ic
-Fight ing
-Ġpl aster
-ĠP VC
-ĠOb st
-Ġdear ly
-ĠTo oth
-icks on
-Ġsh aming
-P lex
-A gg
-ĠâĢ¦ "
-Ġsub reddits
-Ġpige on
-ĠResident ial
-ĠPass ing
-Ġl um
-ĠP ension
-Ġpessim istic
-Ġ4 32
-z inski
-c ade
-0 75
-Ġapolog ised
-iy ah
-Put ting
-Ġgloom y
-ĠLy me
-=-=-=-=- =-=-=-=-
-ĠT ome
-ĠPsych iatric
-ĠH IT
-c ms
-ap olog
-Ġbreak er
-Ġdeep en
-Ġtheor ist
-ĠHigh lands
-Ġb aker
-Ġst aples
-Ġinterf ered
-ĠAb ortion
-jo ined
-ch u
-Ġform ulate
-Ġvacc inations
-Ġban ter
-phe us
-Ġoutfield er
-ĠM eter
-Ġ# ####
-Ġ18 95
-Ġnarrow ing
-ĠST ORY
-f p
-ĠC ST
-ign ore
-Ġproclaim ing
-ĠR U
-ĠB ALL
-yn a
-65 3
-Ġpos it
-P RE
-59 4
-ĠRegist rar
-ĠPil grim
-ic io
-Ġpre tt
-Ġlif eless
-Ġ__ _
-Ne igh
-ĠCh urches
-orn o
-Ġor cs
-Ġkind red
-ĠAud it
-Ġmillenn ial
-ĠPers ia
-g ravity
-ĠDis ability
-ĠD ARK
-W s
-od on
-Ġgrand daughter
-ĠBro oke
-ĠA DA
-ER A
-Ġpick ups
-ĠWil kinson
-ĠSh ards
-ĠN K
-Ġexp el
-ĠKis lyak
-Ġj argon
-Ġpolar ized
-ian e
-Pub lisher
-Ġreb utt
-Ġapprehens ion
-ĠK essler
-Ġpr ism
-F UL
-19 64
-ĠL oll
-ä ¿
-le thal
-Å Ł
-Ġg hetto
-Ġb oulder
-ĠSlow ly
-ĠOsc ars
-ĠInst ruction
-ĠUl tr
-ĠM oe
-N ich
-ĠP ATH
-( *
-ĠRE LEASE
-un ing
-rou se
-en eg
-Ġre imb
-ĠDet ected
-Do S
-Ġster ling
-Ġaggreg ation
-ĠLone ly
-ĠAtt end
-hig her
-Ġairst rike
-ks on
-SE LECT
-Ġdef lation
-ĠHer rera
-C ole
-rit ch
-Ġadvis able
-F ax
-Ġwork around
-Ġp id
-mort em
-ers en
-Ġtyp o
-Ġal um
-78 2
-ĠJam al
-script s
-Ġcapt ives
-ĠPres ence
-ĠLie berman
-angel o
-Ġalcohol ism
-ass i
-Ġrec ite
-Ġgap ing
-Ġbask ets
-ĠG ou
-Brow ser
-ne au
-Ġcorrect ive
-und a
-sc oring
-ĠX D
-Ġfil ament
-Ġdeep ening
-ĠStain less
-Int eger
-Ġbu ggy
-Ġten ancy
-ĠMub arak
-Ġt uple
-ĠD roid
-ĠS itting
-Ġforfe it
-ĠRasm ussen
-ixt ies
-es i
-ĠKim mel
-Ġmetic ulously
-Ġap opt
-ĠS eller
-08 8
-ec ake
-hem atically
-T N
-Ġmind less
-Ġdig s
-ĠAcc ord
-ons ense
-em ing
-br ace
-Ġe Book
-ĠDist ribut
-ĠInvest ments
-w t
-] ),
-beh avior
-56 3
-Ġbl inding
-ĠPro testers
-top ia
-Ġreb orn
-ĠKel vin
-ĠDo ver
-ĠD airy
-ĠOut s
-Ġ[ /
-Ï Ģ
-b p
-ĠVan ity
-ĠRec ap
-ĠHOU SE
-ĠF ACE
-Ġ4 22
-69 2
-ĠAnt ioch
-cook ed
-Ġcoll ide
-Ġa pr
-Ġsle eper
-ĠJar vis
-Ġalternative ly
-ĠLe aves
-ĠM aw
-Ġantiqu ity
-ĠAdin ida
-Ġab user
-Poké mon
-Ġass orted
-ĠRev ision
-ĠP iano
-ĠG ideon
-O cean
-Ġsal on
-Ġbust ling
-ogn itive
-ĠRah man
-Ġwa iter
-Ġpres ets
-ĠO sh
-ĠG HC
-oper ator
-Ġrept iles
-Ġ4 13
-ĠG arr
-ĠCh ak
-Ġhas hes
-Ġfail ings
-Ġfolk lore
-Ġab l
-ĠC ena
-ĠMac Arthur
-ĠCOUR T
-Ġperipher y
-app ers
-Ġreck oned
-ĠInf lu
-ĠC ET
-Ġ3 72
-ĠDefin itive
-ass ault
-4 21
-Ġreservoir s
-Ġd ives
-ĠCo il
-DA Q
-Ġvivid ly
-ĠR J
-ĠBel lev
-Ġec lectic
-ĠShow down
-ĠK M
-ip ed
-reet ings
-ĠAs uka
-L iberal
-ĠÏ Ħ
-Ġbystand ers
-ĠGood win
-uk ong
-S it
-ĠT rem
-Ġcrim inally
-ĠCirc us
-ch rome
-88 7
-Ġnan op
-ĠOb i
-ĠL OW
-o gh
-ĠAuth ors
-ob yl
-Ur ban
-Ġt i
-ĠWe ir
-t rap
-ag y
-Ġparent heses
-Ġout numbered
-Ġcounter productive
-ĠTob ias
-ub is
-P arser
-ST AR
-Ġsyn aptic
-ĠG ears
-Ġh iber
-Ġdebunk ed
-Ġex alted
-aw atts
-H OU
-Ch urch
-ĠPix ie
-ĠU ri
-ĠForm ation
-ĠPred iction
-C EO
-Ġthro tt
-ĠBrit ann
-ĠMad agascar
-ë ĭ
-Ġbill boards
-ĠRPG s
-ĠBe es
-complete ly
-F IL
-Ġdoes nt
-ĠGreen berg
-re ys
-Ġsl ing
-Ġempt ied
-ĠPix ar
-ĠDh arma
-l uck
-ingu ished
-Ġend ot
-Ġbab ys
-05 9
-che st
-r ats
-Ġr idden
-Ġbeet les
-Ġillum inating
-Ġfict itious
-ĠProv incial
-Ġ7 68
-Ġshe pherd
-ĠR ender
-Ġ18 96
-C rew
-Ġmold ed
-ĠXia omi
-ĠSp iral
-Ġdel im
-Ġorgan ising
-Ġho ops
-ĠBe i
-z hen
-Ġfuck in
-Ġdec ad
-Ġun biased
-am my
-sw ing
-Ġsmugg led
-Ġk ios
-ĠP ERSON
-ĠInquis itor
-Ġsnow y
-Ġscrap ing
-ĠBurg ess
-P tr
-ag ame
-R W
-Ġdro id
-ĠL ys
-ĠCass andra
-Jac ob
-Ġ35 4
-Ġpast ure
-Ġfr anc
-ĠScot ch
-ĠEnd s
-ĠI GF
-def inition
-Ġhyster ical
-ĠBrown e
-77 1
-Ġmobil ization
-æ ķ
-iqu eness
-Th or
-Ġspear headed
-Ġembro iled
-Ġconject ure
-jud icial
-Ch oice
-Ġpaper back
-P ir
-Ġrec overs
-ĠSur ge
-ĠSh ogun
-ĠPed iatrics
-ãģ ł
-Ġsweep s
-ĠLabor atories
-ĠP acks
-al us
-add in
-Ġhead lights
-g ra
-Ev idence
-COL OR
-Ad min
-Ĭ ±
-Ġconco ct
-s ufficient
-Ġun marked
-Ġrich ness
-Ġdiss ertation
-Ġseason ing
-Ġg ib
-ĠM ages
-un ctions
-ĠN id
-che at
-ĠTM Z
-c itizens
-ĠCatholic ism
-n b
-Ġdisemb ark
-ĠPROG RAM
-a ques
-Ty ler
-Or g
-ĠSl ay
-ĠN ero
-ĠTown send
-IN TON
-te le
-Ġmes mer
-9 01
-Ġfire ball
-ev idence
-aff iliated
-ĠFrench man
-ĠAugust a
-0 21
-Ġs led
-Ġre used
-ĠImmun ity
-Ġwrest le
-assemb led
-Mar ia
-Ġgun shots
-ĠBarb ie
-Ġcannabin oids
-ĠTo ast
-ĠK inder
-IR D
-Ġre juven
-Ġg ore
-Ġrupt ure
-Ġbre aching
-ĠCart oon
-Ġ4 55
-ĠPale o
-6 14
-Ġspe ars
-ĠAm es
-ab us
-Mad ison
-GR OUP
-Ġab orted
-y ah
-Ġfel on
-Ġcaus ation
-Ġprep aid
-Ġp itted
-op lan
-ĠShel ley
-ĠRus so
-ĠP agan
-Ġwill fully
-ĠCan aver
-und rum
-ĠSal ary
-ĠAr paio
-read er
-ĠR ational
-ĠOver se
-ĠCa uses
-Ġ* .
-Ġw ob
-Ke ith
-ĠCons ent
-man ac
-77 3
-6 23
-Ġfate ful
-et imes
-Ġspir ited
-ĠD ys
-Ġhe gemony
-Ġboy cot
-ĠEn rique
-em outh
-Ġtim elines
-ĠSah ara
-ĠRel ax
-ĠQuin cy
-ĠLess ons
-ĠE QU
-SE A
-N K
-ĠCost co
-Incre ase
-Ġmotiv ating
-ĠCh ong
-am aru
-ĠDiv ide
-Ġped igree
-ĠTasman ia
-ĠPrel ude
-L as
-9 40
-57 4
-Ġch au
-ĠSp iegel
-un ic
--- >
-ĠPhil ips
-ĠKaf ka
-Ġuphe aval
-Ġsent imental
-Ġsa x
-ĠAk ira
-ser ial
-Mat rix
-Ġelect ing
-Ġcomment er
-ĠNeb ula
-ple ts
-ĠNad u
-ĠAd ren
-Ġen shr
-ĠR AND
-fin ancial
-ĠCly de
-uther ford
-Ġsign age
-Ġde line
-Ġphosph ate
-rovers ial
-f ascist
-ĠV all
-ĠBeth lehem
-Ġfor s
-Ġeng lish
-S olid
-N ature
-Ġv a
-ĠGu ests
-Ġtant al
-Ġauto immune
-;;;;;;;; ;;;;
-ĠTot ally
-ĠO v
-Ġdef ences
-ĠCoc onut
-Ġtranqu il
-Ġpl oy
-Ġflav ours
-ĠFl ask
-ãĤ¨ ãĥ«
-ĠWest on
-ĠVol vo
-8 70
-Ġmicro phones
-ver bal
-R PG
-Ġi ii
-; }
-0 28
-Ġhead lined
-Ġprim ed
-Ġho ard
-ĠSh ad
-ĠEN TER
-Ġtri angular
-Ġcap it
-l ik
-ĠAn cients
-Ġl ash
-Ġconv ol
-Ġcolon el
-en emy
-G ra
-Ġpub s
-ut ters
-Ġassign s
-ĠPen et
-ĠMon strous
-ĠBow en
-il ver
-H aunted
-ĠD ing
-start ed
-pl in
-Ġcontamin ants
-ĠDO E
-ff en
-ĠTechn ician
-R y
-Ġrob bers
-Ġhot line
-ĠGuard iola
-ĠKau fman
-row er
-ĠDres den
-ĠAl pine
-E lf
-Ġf mt
-ĠS ard
-urs es
-g pu
-Un ix
-Ġunequiv ocally
-ĠCitizens hip
-qu ad
-m ire
-ĠS weeney
-B attery
-6 15
-Ġpanc akes
-Ġo ats
-M aps
-ĠCont rast
-mbuds man
-ĠE PS
-Ġsub committee
-Ġsour cing
-Ġs izing
-ĠBuff er
-ĠMand atory
-Ġmoder ates
-ĠPattern s
-ĠCh ocobo
-ĠZ an
-ĠSTAT ES
-ĠJud ging
-ĠIn her
-* :
-Ġb il
-ĠY en
-Ġexh ilar
-oll ower
-z ers
-Ġsn ug
-max imum
-Ġdesp icable
-ĠP ACK
-ĠAn nex
-Ġsarcast ic
-Ġlate x
-Ġt amp
-ĠS ao
-b ah
-ĠRe verend
-ĠChin atown
-ĠA UT
-d ocumented
-ĠGA BA
-ĠCan aan
-ĠÙ ħ
-Ġgovern s
-pre v
-E sc
-ĠEst imates
-OS P
-Ġendeav our
-ĠCl osing
-omet ime
-every one
-Ġwor sen
-Ġsc anners
-Ġdev iations
-ĠRobot ics
-ĠCom pton
-Ġsorce rer
-Ġend ogenous
-Ġem ulation
-ĠPier cing
-ĠA ph
-ĠS ocket
-Ġb ould
-ĠO U
-ĠBorder lands
-Ġ18 63
-G ordon
-ĠW TO
-Ġrestrict s
-Ġmosa ic
-Ġmel odies
-ç Ħ
-T ar
-Ġdis son
-ĠProv ides
-Ġ ......
-b ek
-F IX
-Ġbro om
-ans hip
-Do ctors
-Ġner ds
-ĠReg ions
-na issance
-Ġmet e
-Ġcre pt
-pl ings
-Ġgirlfriend s
-kn it
-ig ent
-ow e
-Ġus hered
-ĠB az
-M obil
-4 34
-ĠPres ents
-orig in
-Ġins omnia
-ĠA ux
-4 39
-ĠCh ili
-irs ch
-G AME
-Ġgest ation
-alg ia
-rom ising
-$ ,
-c row
-ĠIn spection
-at omic
-Rel ations
-J OHN
-rom an
-ĠClock work
-ĠBak r
-m one
-M ET
-Ġthirst y
-Ġb c
-Ġfacult ies
-R um
-Ġnu ance
-ĠD arius
-ple ting
-fter s
-etch up
-Reg istration
-ĠK E
-R ah
-Ġpref erential
-ĠL ash
-ĠH H
-Val id
-ĠN AV
-Ġstar ve
-ĠG ong
-z ynski
-ĠAct ress
-Ġw ik
-Ġun accompanied
-lv l
-Br ide
-AD S
-ĠCommand o
-ĠVaugh n
-Wal let
-Ġho pping
-ĠV ie
-Ġcave ats
-Ġal as
-if led
-ab use
-66 1
-Ġib n
-Ġg ul
-Ġrob bing
-t il
-IL A
-Ġmit igating
-Ġapt ly
-Ġty rant
-Ġmid day
-ĠGil more
-ĠDe cker
-Ġ§ §
-part ial
-Ex actly
-Ġphen otype
-Ġ[+ ]
-ĠP lex
-ĠI ps
-vers ions
-Ġe book
-Ġch ic
-g ross
-":" "},{"
-ĠSur prisingly
-M organ
-Ġresid ues
-ĠConf ederation
-in feld
-Ġl yr
-mod erate
-Ġperpend icular
-V K
-Ġsynchron ized
-Ġrefres hed
-Ġad ore
-ĠTor ment
-ol ina
-Ġ26 00
-Item Tracker
-Ġp ies
-ĠF AT
-ĠR HP
-0 48
-ĠRES P
-ĠB J
-all ows
-P and
-Ġunw elcome
-ĠV oc
-ĠBast ard
-ĠO W
-ĠL AR
-ĠHeal er
-Environment al
-ĠKen yan
-ĠTr ance
-ĠP ats
-Ġali ases
-ĠGar field
-Ġcampaign er
-Ġadvance ments
-ĠOkin awa
-ĠC oh
-ows ky
-Ġstar ved
-Ġsize able
-Ġ: -)
-Ġm RNA
-Ġsusp ensions
-ist ar
-Scot land
-Pr in
--------------------------------- ----------------
-Ġ50 2
-Ġteasp oons
-Ġ10 50
-Ġcoerc ive
-ĠMason ic
-edd ed
-ĠPass enger
-Ġl att
-Ġbr aces
-ĠSt eal
-ĠNY T
-ĠK ats
-ĠCel est
-ae z
-T u
-ĠCoul ter
-ðŁ ĺ
-Fl ickr
-ĠWil mington
-ith s
-++ ;
-Ġv ending
-Ġneg ro
-ĠPh i
-ĠYellow stone
-Call back
-Ġsh ampoo
-ĠSh ades
-w at
-Ġsuper human
-Ġridic uled
-Ġhol iest
-om bo
-Ġintern s
-Ġh one
-ĠPar agu
-UR I
-Ġd angling
-ãĤ »
-so v
-ict ional
-av ailability
-Ġrev ocation
-Ġd ow
-in ic
-ĠTHE IR
-Ġis o
-Ġout ings
-ĠLeth al
-Ġ) ))
-Ġinacc ur
-Ġout landish
-Ġan us
-let ico
-id on
-l ol
-Ġun regulated
-Ġsuccumb ed
-Ġc uff
-ĠWast eland
-let al
-Ġsub str
-Ġcoff ers
-Ġautom akers
-ov i
-ĠX ue
-ĠDayton a
-Ġjar ring
-Ġf umes
-Ġdisband ed
-z ik
-itt on
-Ġstriking ly
-Ġsp ores
-Ad apter
-.) :
-ĠLynd on
-ival ry
-Ġor ally
-Ġtumult uous
-Ġdisple asure
-Ġcon es
-or rect
-Ġappe ase
-Ġder by
-ĠTrip oli
-ĠAl ess
-Ġp oked
-ĠGu ilty
-v P
-En ough
-Ġorig inals
-6 99
-Ġrabb i
-Ġproverb ial
-Ġpostp one
-el ope
-ĠMist y
-Ġstaff ed
-ĠUn employment
-redit ary
-Ġdilig ent
-re comm
-me asures
-as in
-8 25
-Ġpond s
-Ġmm ol
-ĠS AR
-ĠC ARE
-Ġ3 71
-Ġclen ched
-ĠCors air
-Ġcaric ature
-z n
-att ach
-ĠSch ro
-spe ak
-p ainted
-ĠS uc
-ĠE NT
-Ġcell ul
-ĠP aid
-di agn
-WH ERE
-Ġtext ed
-B arn
-Ġret racted
-ĠRe ferred
-S av
-Ġup keep
-Ġwork places
-ĠTok ens
-Ġampl ify
-cl inical
-Ġmult ic
-mber g
-Ġconvol uted
-Reg ion
-5 65
-ĠTop ic
-Ġsn ail
-Ġsal ine
-Ġins urrection
-ĠPet r
-f orts
-B AT
-ĠNav ajo
-Ġrud imentary
-ĠLak sh
-OND ON
-Me asure
-Ġtransform er
-ĠGodd ard
-Ġcoinc ides
-ir in
-R ex
-ĠB ok
-qu it
-Ġshotgun s
-Ġprolet arian
-Ġsc orp
-ĠAd a
-5 14
-Ġsl ander
-record ed
-Ġemb ell
-ris ome
-Ġapolog izing
-ĠMul cair
-ĠGib raltar
-Cl a
-Ġall ot
-ĠAtt ention
-Ġ4 33
-le ave
-Ġwh ine
-ĠIss a
-ĠFa ust
-ĠBar ron
-hen y
-Ġvictim ized
-J ews
-Ġnurt uring
-ett el
-W inged
-ĠSub tle
-Ġflavor ful
-ĠRep s
-eng ed
-call back
-Ġdirection al
-Ġcl asp
-ĠDirect ions
-plan et
-icult ure
-Hel per
-ic ion
-ac ia
-Ġç ¥ŀ
-Ġsur ges
-Ġcan oe
-ĠPrem iership
-be en
-Ġdef ied
-ĠTro oper
-Ġtrip od
-Ġgas p
-ĠE uph
-ĠAd s
-vern ight
-high ly
-R ole
-Ġent angled
-ĠZe it
-6 18
-ĠRust y
-Ġhaven s
-ĠVaugh an
-HA EL
-ĠSER VICE
-/ ,
-Ġstr icken
-Ġdel usions
-Ġb is
-ĠH af
-Ġgrat ification
-Ġent icing
-UN CH
-Ad ams
-ĠOL ED
-ĠBeet le
-Ġ18 99
-ĠSO FTWARE
-ateg or
-V L
-ĠTot em
-ĠG ators
-AT URES
-Ġimped ance
-Reg istered
-ĠC ary
-ĠAer ial
-on ne
-en ium
-Ġd red
-ĠBe g
-Ġconcurrent ly
-Ġsuper power
-ĠX an
-j ew
-imes ter
-ĠDick inson
-âĶ ģ
-F la
-Ġp ree
-ĠRoll ins
-© ¶æ
-Ġden omination
-ĠL ana
-5 16
-Ġinc iting
-sc ribed
-j uries
-ĠWond ers
-app roximately
-Ġsusp ending
-Ġmountain ous
-ĠL augh
-oid al
-N s
-Det ect
-) =
-ĠL uthor
-ĠSchwarz enegger
-ĠMull er
-ĠDev i
-ec ycle
-J ar
-6 13
-ĠL ongh
-B ah
-ĠSP ORTS
-n w
-Ġref inement
-Ġwater ways
-Ġd iner
-Bl ade
-68 3
-F ac
-Ġinitial s
-Ġro g
-Ġparan ormal
-B UT
-Ġ[ (
-ĠSw anson
-ĠM esh
-âĸ ¬
-Impro ve
-ĠRad iation
-ĠEst her
-ĠE sk
-ĠA ly
-ik y
-Ġir rad
-ĠBuck ingham
-Ġref ill
-Ġ. _
-Re pe
-CON CLUS
-Ġdifferent iated
-Ġchi rop
-ĠAt kins
-Pat tern
-Ġexc ise
-Ġcab al
-N SA
-ĠST A
-ĠS IL
-ĠPar aly
-Ġr ye
-ĠHow ell
-ĠCount down
-ness es
-alys ed
-Ġres ize
-ãĤ ½
-Ġbudget ary
-ĠStr as
-w ang
-Ġap iece
-Ġprecinct s
-Ġpe ach
-Ġsky line
-Ġ35 3
-pop ular
-App earances
-ĠMechan ics
-ĠDev Online
-S ullivan
-Z en
-Ġp u
-op olis
-5 44
-Ġde form
-Ġcounter act
-ĠL ange
-Ġ4 17
-Con sole
-77 4
-Ġnodd ing
-Ġpopul ism
-Ġhe p
-Ġcoun selling
-compl iance
-U FF
-Ġunden iably
-Ġrail ing
-ĠHor owitz
-ĠSim one
-ĠBung ie
-Ġa k
-ĠTal ks
-x ff
-fl ake
-Cr ash
-Ġsweat y
-Ġban quet
-ĠOFF IC
-Ġinvent ive
-Ġastron omer
-ĠStam ford
-ĠSc are
-ĠGRE EN
-olic ited
-Ġr usher
-Ġcent rist
-ight ing
-Ġsub class
-Ġdis av
-Ġdef und
-ĠN anto
-oci ate
-m ast
-Ġpac if
-Ġm end
-e ers
-imm igration
-ESS ION
-Ġnumber ing
-Ġlaugh able
-ĠEnd ed
-v iation
-em ark
-P itt
-Ġmetic ulous
-ĠL F
-Ġcongrat ulated
-ĠBir ch
-Ġsway ed
-Ġsemif inals
-Ġhum ankind
-m atter
-ĠEqu ip
-opa usal
-S aid
-ĠLay out
-Ġvo icing
-Ġth ug
-Ġporn ographic
-I PS
-Ġmo aning
-Ġgriev ance
-Ġconf essions
-esc al
-TEXT URE
-Aut hent
-os aurus
-P urchase
-Ġreleg ation
-al ter
-ĠÂł Âł
-Ġr iddled
-Ġo gre
-ĠLow ell
-Occ up
-E at
-ĠHy der
-ĠAdvis er
-Com merce
-H unt
-ĠOr th
-ĠComp etitive
-ĠCL A
-CD C
-Ġsal ads
-F le
-Ġindustrial ized
-` ,
-ĠO WN
-Ġbec k
-ĠPart icularly
-oub t
-Ġm M
-ĠHuss ain
-ĠChen nai
-Ġ9 20
-Ġappoint ing
-ĠCull en
-,,,, ,,,,
-Ġp ores
-ver ified
-Ġbi ochemical
-em ate
-Ġcoward ly
-ĠHels inki
-ĠEthiop ian
-S OURCE
-ER C
-est ro
-Ġbi otech
-ĠS our
-Ġbrew er
-Bloom berg
-Ġintens ify
-Gl ass
-an co
-ĠF DR
-gre SQL
-ĠF ires
-©¶æ ¥µ
-ec o
-100 1
-ĠHom eless
-Ġinstant aneous
-ĠH aste
-ig el
-D iamond
-Ġp aving
-Ġland fill
-Ġd ads
-h oun
-: ]
-Ġinc endiary
-ĠLiving ston
-ĠHil bert
-ĠChe cks
-st yles
-in ators
-ĠCl ive
-ph rine
-Ġchimpan zees
-Ġp all
-ĠJ M
-ĠAad haar
-ð Ŀ
-Ġachie vable
-dis abled
-P ET
-OOOO OOOO
-M ot
-Ġint angible
-Ġbal let
-ĠWe bs
-ĠEst imated
-Effect s
-Ġb ailed
-Josh ua
-Ġturb ulence
-Ġoccup ant
-ĠDay light
-Ġ36 1
-me et
-Ġstat ically
-Ġon look
-Ġk i
-il legal
-Ġvel vet
-Ġdehyd ration
-Ġacqu ies
-ĠRe z
-ak ura
-ĠU pton
-at ro
-Ġincomp rehensible
-Ġback door
-ĠRh ino
-7 27
-Ġmath s
-) +
-Ġhe resy
-Ġd f
-ĠRoc he
-ĠL ydia
-Ġpanc reat
-re ply
-arre ll
-Ġsolicit ation
-Ġcirc adian
-BI P
-Ġfor ay
-Ġcrypt ic
-iz u
-ime o
-ĠTom ato
-ĠH oms
-ex amination
-Ġqu arry
-ĠVal iant
-ĠJer icho
-ĠIN CLUD
-Ġ18 40
-5 19
-Ġres ists
-Ġsnap shots
-ĠSp ur
-ĠAnt iqu
-Log in
-Ġbest selling
-Ġant ic
-ĠS utherland
-ãĤ¢ ãĥ«
-Ġ~ /
-ĠP arm
-è ĥ
-P ages
-int ensity
-Ġimm obil
-Ġ18 65
-zz o
-Ġn ifty
-Ġf entanyl
-ĠPres ervation
-op hen
-Ġd arts
-ĠD inosaur
-po inters
-ĠR ite
-s uggest
-aware ness
-ĠSher idan
-Ġst ances
-Ġsor cery
-Ġper jury
-ĠNik ola
-ie ver
-Ġf iance
-ĠJordan ian
-ĠBall oon
-Ġn ab
-Ġk b
-Ġhuman ities
-ĠTan aka
-hill ary
-Ġconsult ancy
-ĠZ ub
-Ġrem ission
-Ġconf id
-CH Q
-ĠF ug
-Ġimpro vis
-Y ep
-/ _
-Ġunwilling ness
-Ġport folios
-05 5
-ĠInstruct or
-aim an
-Ġclaim ants
-M bps
-ĠBy e
-re ceived
-T weet
-Ġind emn
-ri z
-am ara
-N at
-Ġeval uates
-ĠL ur
-ep ad
-FO X
-ĠTh ro
-Ġrust y
-Ġbed rock
-ĠOp rah
-J B
-Ġmanip ulative
-Ġwill ful
-Ġrel apse
-Ġext ant
-The me
-S ensor
-ĠSt ability
-go vern
-Ġpo ppy
-Ġkn ack
-Ġins ulated
-ĠT ile
-ĠExt rem
-Ġunt old
-Ġconver ge
-Ġref uel
-ig roup
-Ġdistort ions
-Ġrav aged
-Ġmechan ically
-ĠRe illy
-ĠN ose
-ĠIncarn ation
-ĠBeck y
-abb ling
-Ġt aco
-Ġr ake
-Ġmelanch oly
-Ġillust rious
-ĠDart mouth
-Gu ide
-ĠR azer
-ĠBen z
-Ult imate
-ĠSur prise
-Ġpage ant
-off er
-Who ever
-Ġw iser
-Ġchem ist
-ĠHE LL
-ĠBul k
-Ġpl utonium
-ĠCO VER
-Ö ¼
-f ailed
-Ġtire lessly
-Ġinf ertility
-ĠTr ident
-ĠShow time
-ĠC iv
-V ice
-requ ires
-itt ance
-Ġun controlled
-interest ing
-56 1
-Ġinnov ate
-ateg ic
-L ie
-ĠS elling
-U l
-Ġsav ior
-ĠT osh
-Ġsw ast
-P ASS
-Ġr ink
-Ġcard io
-ĠI ro
-ud i
-Ġv antage
-Ġv ans
-ĠNi ño
-+ =
-Ġpropag ate
-< ?
-Ġmethod ological
-204 39
-Ġtrig lycer
-Ġing rained
-ĠAn notations
-arr anted
-6 17
-ĠS odium
-ĠA AC
-techn ical
-mult ipl
-Ġ3 73
-å ĭ
-Ġdec isively
-Ġboost ers
-Ġdessert s
-ĠGren ade
-Ġtest ifying
-ĠSc ully
-ID s
-Ġlock down
-ĠSc her
-ĠR é
-ĠWhit man
-ĠRams ay
-rem ote
-Ġh ikers
-ĠHy undai
-Ġcons cientious
-Ġcler ics
-ĠSiber ian
-ut i
-is bury
-Ġrel ayed
-Ġqu artz
-ĠC BI
-seek ers
-ull a
-Ġweld ing
-ĠSh al
-ble acher
-T ai
-ĠSam son
-Ġt umble
-ĠInvest or
-Ġsub contract
-ĠShin ra
-ow icz
-j andro
-d ad
-Ġtermin ating
-ĠNe ural
-ä» £
-Ġleak age
-ĠMid lands
-ĠCaucas us
-í ķ
-c it
-ll an
-iv ably
-ĠAlb ion
-Ġ4 57
-Ġregist rations
-Ġcomr ade
-Ġclip board
-0 47
-Ġdiscour aging
-ĠO ops
-Ad apt
-Ġem path
-n v
-ĠPR OT
-ĠDon n
-ĠP ax
-ĠB ayer
-t is
-Squ are
-Ġfoot prints
-part icip
-ĠChile an
-B rend
-ind ucing
-M agn
-Ġclub house
-ĠMagn um
-Ġenc amp
-ĠEth nic
-uch a
-ere y
-Ġw atered
-ĠCal ais
-Ġcomplex ion
-Ġsect s
-Ġren ters
-Ġbr as
-oÄŁ an
-Time out
-Man agement
-Ġinf ographic
-P okemon
-Cl ar
-Ġloc ality
-Ġfl ora
-as el
-P ont
-Ġpop ulate
-ĠO ng
-Ġsubs istence
-Ġa uctions
-ĠMcA uliffe
-ĠL OOK
-br inger
-Ġtit an
-Ġmanif old
-ĠâĹ ı
-Ġcalibr ated
-Ġcal iphate
-ĠSH E
-ĠCommission ers
-ce ivable
-j c
-W inner
-5 24
-Ġcond one
-Other wise
-Ġp iling
-Ġem body
-ĠCrime an
-ut ics
-ĠEx hibition
-Ġ4 26
-e ering
-Ġv ying
-ĠH UGE
-* =-
-Ġprin cipled
-à ¦
-Ġquir ks
-ĠEdit ors
-put ing
-G ES
-ĠF TA
-ठ¾
-add on
-ĠH AM
-ĠFrie za
-W oman
-. $
-Ġc rib
-ĠHer od
-Ġtim ers
-ĠSp aces
-ĠMac intosh
-at aka
-Ġgl ide
-Ġsmell ing
-ĠB AL
-Ġun su
-Ġcond os
-Ġbicy cl
-ĠRev ival
-55 3
-Ġjugg ling
-H ug
-ĠKardash ian
-ĠBalk ans
-mult iple
-Ġnutrit ious
-oc ry
-19 00
-Ġinteg rates
-Ġad joining
-ĠF older
-roll ment
-ven ient
-Ġu ber
-y i
-Ġwh iff
-ĠJu ven
-ĠB orough
-net te
-Ġb ilingual
-ĠSp arks
-ph thal
-man ufact
-Ġt outing
-ĠPH I
-Ke efe
-Rew ard
-Ġinf all
-ĠTem per
-typ ically
-ĠNik ol
-Ġregular s
-Ġpseud onym
-Ġexhib itions
-Ġbl aster
-Ġ40 9
-w arming
-Ġrever ber
-Ġrecip rocal
-Ġ6 70
-ip ient
-b ett
-ĠBe gins
-Ġit ching
-ĠPh ar
-Ass uming
-Ġem itting
-ĠML G
-Ġbirth place
-Ġt aunt
-ĠL uffy
-ĠAm it
-Ġcir cled
-ĠN ost
-enn ett
-Ġde forestation
-ĠHist orically
-ĠEvery day
-Ġovert ake
-79 2
-Ġn un
-ĠLuc ia
-Ġaccompan ies
-ĠSe eking
-ĠTr ash
-an ism
-R ogue
-Ġnorth western
-ĠSupplement al
-ĠNY U
-ĠF RI
-ĠSat isf
-x es
-5 17
-Ġreass ured
-Ġspor adic
-Ġ7 01
-Ġmed ial
-Ġcannabin oid
-Ġbarbar ic
-Ġep is
-ĠExplos ive
-ĠD ough
-Ġuns olved
-Support ed
-Ġacknowled gment
-sp awn
-Ġkit chens
-Ġ- =
-talk ing
-ic ist
-ĠPeg asus
-ĠPS U
-Ġphot on
-ĠAuthent ication
-R G
-@# &
-76 2
-ĠCl air
-Ġdi aper
-Ġbr ist
-ĠProsecut ors
-ĠJ em
-6 28
-ĠEvery where
-ĠJean ne
-equ ality
-ãĥ© ãĥ³
-object s
-ĠPel icans
-Ġ39 2
-Ġbl u
-b ys
-ĠA go
-Ġinstruction al
-Ġdiscrim inating
-ĠTR AN
-ĠCorn el
-ag os
-Ġty re
-Ġas piration
-ĠBrid gewater
-": -
-! ".
-ĠEn s
-ĠCoc o
-P ie
-Ġdet ach
-ĠC ouch
-Ġphys ique
-ĠOccup ations
-osc opic
-en ough
-B uzz
-App earance
-Y P
-Ġrac er
-Ġcompl icity
-r pm
-T oy
-Ġinterrupt s
-ĠCat alyst
-Ġut ilitarian
-imp act
-Ġsp aghetti
-Ġp orous
-Ġeste emed
-Ġinc iner
-ĠI OC
-7 48
-Ġesp resso
-ĠSm ile
-abil ia
-6 35
-Ġmathematic ian
-Ġ4 24
-ĠK L
-ĠH IP
-Ġover heard
-ĠT ud
-ĠT ec
-Ġqu izz
-Ġfl attering
-Ġcon n
-âĢ İ
-Ġatt aches
-ĠR OS
-ĠAC S
-Ġt cp
-ĠSh ame
-sk ip
-res pected
-ĠTrin idad
-gr ain
-Ġfooth old
-ĠUnch arted
-ĠJul io
-z l
-av ored
-ĠAn xiety
-er rors
-ĠCent auri
-its ch
-D addy
-Ġclutch ing
-ĠIm plement
-ĠGut ierrez
-Ġ7 60
-Ġtele portation
-end ra
-Ġrevers ible
-st ros
-Ad venture
-08 3
-Ġliber ating
-Ġas phalt
-ĠSp end
-AR DS
-im sy
-PR ES
-ĠEmer ging
-Ġwild fires
-Ġtechn ologically
-Ġem its
-ĠART ICLE
-Ġirregular ities
-Ġcher ish
-çī Ī
-Ġst ink
-ĠR ost
-Econom ic
-Ġcough ing
-ĠMcC ann
-pro perties
-ilant ro
-Ġreneg oti
-Trans lation
-Ġin quest
-ĠGra pe
-oot ers
-gu i
-ĠSwords man
-ace ae
-h itting
-Ġr c
-Ġexert ed
-ĠS AP
-it ent
-Ġperil ous
-Ġobsc urity
-Ġassass inate
-Ġab original
-Ġresc uing
-ĠSh attered
-lock ing
-all ion
-Ch anging
-ĠHar rington
-ĠB ord
-ĠAfgh ans
-Jam ie
-aret z
-ĠAugust us
-Ġ38 6
-8 30
-Ġj og
-ok ingly
-Tr igger
-ĠH OR
-Stat istics
-Ġviewers hip
-Ġadd itives
-h ur
-Ġmaxim izing
-ĠR ove
-ĠLou ie
-ĠBuck et
-ĠCHR IST
-ou sel
-Ġstre aks
-ir ted
-Ġt ert
-Ġcolonial ism
-Ġbur ying
-y k
-Cond ition
-ĠDPR K
-By Id
-75 1
-âĹ ¼
-Ġwor risome
-Ġvoc ational
-sl ice
-Ġsa ils
-ĠCorrection al
-95 4
-Ġt ul
-K id
-l uster
-Ġfam ilial
-ĠSp it
-ĠEp iscopal
-Specific ally
-ĠVol cano
-run s
-q s
-Ġve tted
-Ġcram med
-t rop
-here r
-Thank fully
-Ġper cussion
-Ġor anges
-Ġround up
-Ġ4 99
-x ious
-Char acters
-ĠZion ism
-ĠR ao
-ÃĽ ÃĽ
-W F
-Ġunintention al
-ONE Y
-Gr ab
-Com mercial
-Ġglut amate
-ĠMcK enna
-ru ciating
-ning ton
-ih u
-Ch an
-ĠSw ap
-Ġleaf lets
-Ġfunction ally
-er ous
-F arm
-Ġcal oric
-ĠLiter ally
-con cert
-Ġshe nan
-Ġrep aid
-ey es
-Ġbas hing
-ĠG orge
-Ġcollabor ations
-Ġun account
-itch ie
-Ġteam work
-pp elin
-Ġpip ing
-Ġmin ced
-Ġd iam
-ri eg
-Ġmasc ara
-Ġsuck er
-ĠMo ons
-App s
-ĠPe ck
-Ġper v
-ĠFl oat
-o ley
-ĠN ish
-im ize
-Ġarom atic
-u in
-end ish
-! /
-ĠB icycle
-ĠAS IC
-ile ged
-ĠQuad ro
-ios yn
-Ġlock out
-ĠW ink
-SP EC
-Attempt s
-Ġseed ed
-red o
-ias is
-Ġsn ag
-ãĥķ ãĤ©
-ãĤ ¶
-Ġground ing
-Ġrelie ver
-Ġfrivol ous
-ĠG ifts
-ĠF aces
-Es pecially
-Ġmicrobi ome
-im ag
-ĠSch l
-ĠP les
-ĠBle ach
-ĠIr win
-ĠE aton
-ĠDisc iple
-Ġmultipl ication
-Ġcoer ced
-Ġ4 19
-st h
-E vil
-B omb
-Ġex orc
-Ġstag gered
-L ESS
-Ġinert ia
-ĠED IT
-Ġgo b
-Tr aditional
-Ġclass y
-Lear y
-ĠP AGE
-yr s
-Ġtrans porter
-Ġmat ured
-Ġhij ab
-Ġbi ome
-Where as
-Ġex termination
-ĠT ues
-ĠT akeru
-ĠAud rey
-er ial
-ĠAd en
-aff les
-Ġnarciss istic
-ĠB aird
-UT F
-I re
-ĠCon nie
-Ch amp
-Ġwhis pering
-ĠH att
-D K
-Ġdis infect
-Ġdeduct ed
-Ġpart ake
-Ġdown grade
-ĠEs ports
-ĠContin uing
-Ġdemocr atically
-icro bial
-itt a
-Ġlim estone
-Ġexempt ed
-ĠFren zy
-H erm
-7 28
-Ġfled gling
-Met a
-765 61
-69 3
-% :
-w ake
-5 26
-ĠDis cipline
-Ġvirgin ity
-ĠLeg ions
-ĠFrank ie
-int ent
-Ġrest rooms
-ĠRou ter
-da q
-Ġobjection able
-âĨ ij
-w ark
-ĠRah ul
-g ain
-activ ation
-abs olute
-ĠAccess ed
-Ġ24 00
-ogg les
-Ġsecond ly
-ĠDEF ENSE
-Ġpost age
-wra pper
-sh arp
-7 29
-Ġcommun icates
-Ġadd on
-ĠMil itia
-H ong
-Ġsl umped
-ĠJP EG
-ĠI car
-ad ish
-68 1
-Ġmaj esty
-ĠWolf gang
-ĠEl astic
-u per
-Ġv iz
-Ġunconscious ly
-ĠST D
-ĠS ass
-Ġflower ing
-ĠHel ic
-ĠDra per
-ĠAm ateur
-Ġman ure
-Ġdis ingen
-ĠLe i
-br ing
-9 49
-Ġinhib ited
-Ġhead quartered
-Ġen igmatic
-�� �
-Ġred ress
-R H
-Ġratt led
-Ġd iction
-l io
-ĠT BA
-ĠSN AP
-C alling
-Ġfasc ists
-ĠD ove
-iew icz
-0 36
-Ġco asts
-ĠR ect
-Ġ) ]
-L ot
-6 29
-ĠS EM
-ĠPeters en
-ĠExpl ain
-ĠBo ards
-ĠBe zos
-ĠJ ournals
-Ġ20 24
-p arser
-Ġmist rust
-Ġgr ate
-ĠL ocked
-bo a
-S aint
-g aming
-Ġvow el
-in ately
-bl ow
-All ah
-Ġun matched
-Ġb ordering
-ĠExp end
-n r
-Or acle
-rou ch
-Ġcont iguous
-ac us
-Ġdist raught
-58 1
-Ġanat omical
-O X
-ap ixel
-8 33
-ĠPL US
-Ġres usc
-Ġab iding
-57 3
-Ġvac ancies
-Em ily
-Ġhyp othal
-ĠWer ner
-ĠWe e
-ĠDJ s
-5 13
-Ġwitch craft
-Ġac upuncture
-ent ary
-benef it
-Product s
-ĠP SP
-ĠMP G
-ĠJ inn
-ĠJ arrett
-Ġ4 45
-ĠIm aging
-ĠP yth
-Fin ish
-Ġte x
-Ġjuven iles
-Ġhero ism
-Ġdoubt less
-ĠA ki
-ĠT end
-ĠPatri arch
-Ġbit ters
-ĠTele communications
-it atively
-ag na
-Ġr g
-ĠS OLD
-Ġcomp ulsion
-ĠN asa
-ĠKath ryn
-Ġmillion aires
-Ġintrins ically
-Ġbolst ered
-time out
-fl o
-Ġtut or
-p our
-Stat ement
-Ġ{ *
-ĠRud olph
-ĠKimber ly
-rog ens
-adi q
-] +
-Ġindign ation
-Ġfract uring
-ĠRe leases
-ĠGr ain
-pro tein
-L ago
-Ġvac ations
-Ġboot ed
-ĠTH REE
-ĠH G
-oresc ence
-Ġt f
-Ġso ar
-iosyn cr
-Ġgl ances
-ĠSp oon
-ĠJ ury
-ĠCow boy
-Ġcreat ively
-Hig her
-Ġsolic itor
-Ġhaw k
-ac io
-89 6
-Ġsuperf lu
-Ġbombs hell
-ct ure
-Ġbroker age
-Ġraid ing
-Ġf rench
-Ġang led
-Trans action
-ĠGen ocide
-u pe
-ĠHait ian
-57 2
-! :
-Ġunwitting ly
-iter ator
-sc roll
-Ġtall ied
-Ġbi omedical
-ĠC ARD
-Ġe uphem
-Ġbrain storm
-a quin
-K o
-Mic helle
-ĠR unes
-ĠBall istic
-ud ers
-Ġmod esty
-ĠiP ads
-ĠEzek iel
-Y E
-Ġstars hip
-Ġpower fully
-Ġper l
-ĠSh ade
-ĠQu art
-ĠE EG
-Ġfisher man
-OS ED
-ĠTyp ical
-df x
-Ġmes hes
-Ġet ched
-worth iness
-Ġtopp led
-Ġ3 96
-or ius
-We iss
-Ġmy sql
-ĠVal halla
-Ù Ĵ
-le asing
-Ġrec omp
-rap nel
-S el
-04 3
-Ġder ailed
-ĠGu ides
-IR T
-Ġde human
-ĠBritt any
-" ))
-Ġex claim
-Ġb alk
-Ġ8 40
-CLA IM
-int el
-L AB
-Ġpe gged
-Ġast roph
-sm oking
-Ġrig ging
-Ġfix ation
-Ġcat apult
-ins ide
-ĠC ascade
-ĠBolshe vik
-G aza
-Dep th
-Ġloud spe
-Ġalmond s
-me yer
-l eness
-j en
-f resh
-Ġunbeat en
-ĠSqu id
-ĠPres umably
-Tim er
-B W
-Ġro sters
-Ġell ipt
-ĠHar riet
-dat abase
-ĠMut ual
-ĠComm odore
-uk ed
-kn ife
-ĠCOMM UN
-h ya
-Ġmel ts
-arch ives
-Ġrat ification
-Ġmultip lying
-Ġinter oper
-Ġasc ert
-w ings
-ver ting
-ĠScorp ion
-ay e
-ĠPorts mouth
-ĠM TA
-n it
-iaz ep
-Ġqu arantine
-Ġslides how
-Ġcent imeters
-Ġsyn opsis
-Ġsp ate
-th irst
-Ġnom inating
-ĠMel vin
-Pre view
-Ġthro b
-Ġgener ational
-ĠRad ius
-rest ling
-put able
-aw ar
-N ECT
-Ġunlaw fully
-ĠRevel ations
-Wik ipedia
-sur v
-Ġeye ing
-ij n
-ĠF W
-Ġbr unt
-Ġinter stellar
-Ġcl itor
-ĠCroat ian
-ĠCh ic
-ev a
-ĠDis app
-ĠA kin
-iner ies
-d ust
-Interest ed
-Ġgen esis
-ĠE ucl
-ö n
-p icking
-Ġmut ated
-Ġdisappro ve
-ĠHD L
-Ġ6 25
-Ì ¶
-c ancer
-Ġsqu ats
-Ġle vers
-Disc uss
-= ]
-D ex
-ĠVIDE OS
-A UD
-Ġtrans act
-ĠKin ect
-ĠK uala
-ĠC yp
-7 47
-Ġsh attering
-Ġarsen ic
-ĠInt ake
-ĠAngel o
-ĠQu it
-ĠK he
-Ġ18 93
-M aker
-0 29
-ĠPain ting
-Dis able
-9 16
-Ġanal ges
-Ġtact ile
-Ġprop hes
-Ġd iced
-ĠTravel s
-ĠHe ader
-ĠClub s
-Ass istant
-Ġinc rim
-Ġd ips
-Ġcruc ifix
-ĠShan ahan
-ĠInter pret
-Ġ40 90
-al ogy
-abb a
-Ġsimul ac
-hus band
-S IM
-Ġrecy cle
-uc er
-ed ged
-Ġre naissance
-ĠBomb ay
-Cath olic
-ĠL INE
-ĠCl othing
-re ports
-Ġpl aus
-Ġd ag
-ĠM ace
-Z I
-Ġintr uder
-ĠVeter inary
-g ru
-Ġsne aky
-ĠS ie
-ĠC innamon
-P OSE
-Ġcou rier
-ĠC NS
-Ġemanc ipation
-s it
-Ġplay through
-ĠFac ilities
-v irt
-ĠG auntlet
-Thom pson
-Ġunbeliev ably
-Param eters
-Ġst itching
-ign e
-ĠTH ESE
-Priv acy
-Ġshenan igans
-Ġvit ri
-ĠVal id
-59 1
-Ń ·
-ĠProt otype
-ink a
-SC P
-ĠT id
-è Ī
-old ed
-Ġindividual ity
-Ġbark ing
-Ġm ars
-ĠW D
-Ġ8 20
-Ġt ir
-Ġsl apping
-Ġdisgr untled
-ĠAng ola
-ri us
-ĠTorn ado
-ĠTh urs
-Ġcapt cha
-Ġang st
-ĠP og
-ĠAssass ins
-ĠAd idas
-Ġjoy ful
-Ġwh ining
-Emer gency
-Ġphosph orus
-Ġatt rition
-oph on
-ĠTimber wolves
-ĠJ ah
-ĠBr inging
-ĠW ad
-ĠEn sure
-oh l
-ĠX ie
-omm el
-c mp
-Ġz ipper
-Ġrel at
-ĠCor ridor
-m ilo
-T ING
-Av g
-Ġcro pped
-] }
-Ġr aged
-ĠLump ur
-ĠGuer rero
-our ke
-N ut
-Ġoff sets
-og lu
-dr m
-Ġmort als
-lat able
-Ġdismiss ive
-ä¸ ī
-Ġthro ats
-Ġchips et
-ĠSpot light
-Catal og
-art ist
-G b
-Ġch illy
-Ġst oked
-Ġ3 74
-W ard
-L atin
-Ġf iasco
-Ġble ach
-Ġb rav
-Enh anced
-Ġin oc
-ĠFior ina
-_ >
-Ġle ukemia
-Ġel uc
-Ġannoun cer
-ĠLith uan
-ĠArm ageddon
-å ĩ
-Len in
-ĠR uk
-Ġpe pp
-ĠRom antic
-ĠP IT
-ĠInter stellar
-ĠAt kinson
-R aid
-J s
-Go al
-C ourse
-Ġvan ishing
-es ley
-ĠR ounds
-Els a
-59 3
-Ġredund ancy
-ĠST AND
-Ġprop hetic
-Ġhabit able
-ry u
-Ġfaint ly
-M ODE
-Ġfl anked
-IR C
-Aw esome
-Ġsp urious
-ĠZ ah
-ĠMS G
-Ġsh ading
-Ġmotiv ational
-ĠSant ana
-ĠS PR
-Ġexc ruciating
-om ial
-ĠM iko
-ĠLe opard
-A byss
-Ġ[ |
-d irty
-Ġbath s
-Ġdem oral
-and re
-P B
-Ġun ification
-Ġsac rament
-Ġ[ &
-Ġpric eless
-Ġgel atin
-Ġeman ating
-ĠAll aah
-98 6
-Ġout burst
-Ġer as
-ĠX VI
-ĠSP I
-O tt
-ĠLaz arus
-PL IED
-F lying
-blog s
-W isconsin
-R aven
-Ġreb ate
-Ġcreep s
-ĠSp an
-ĠPain ter
-ĠKir a
-ĠAm os
-ĠCor vette
-Cons umer
-ĠRec over
-ck i
-Ġpes ky
-ĠIn vention
-Compan ies
-Ġchalleng ers
-ad emic
-ĠUkrain ians
-ĠNeuro log
-ĠFors aken
-Ġent rants
-Ġemb attled
-Ġdef unct
-ĠGlac ier
-Ġpo isons
-ĠH orses
-m akes
-ĠD irt
-Ġ4 23
-hh h
-ĠTrans formation
-QUI RE
-................ ..
-Ġtrave ller
-ĠSe xy
-ĠK ern
-ip olar
-Ġransom ware
-oooooooo oooooooo
-E c
-rub y
-Prof essional
-ĠOut break
-arg ument
-G rey
-ĠFif a
-ĠCH O
-ĠFOR M
-ĠAm trak
-- [
-Ġcr adle
-Ġantioxid ants
-ãģ®å ®
-7 36
-ĠNAS L
-ĠContribut ions
-Ind iana
-ĠST EP
-C SS
-Ġsal ient
-Ġall ocations
-yr ights
-Ġm ashed
-ĠCut ter
-Sex ual
-Ġp ounded
-Ġfan base
-Ġc asc
-ĠTrans parency
-Ġanaly tic
-ĠSummon er
-× ŀ
-ĠAD C
-det ail
-Ġvan quished
-Ġcr abs
-ar ie
-Dest roy
-ĠS ack
-Ġtrans istor
-Al abama
-ĠK oen
-ĠFisher ies
-c one
-Ġannex ed
-ĠM GM
-es a
-Ġf aked
-ĠCong ratulations
-Ġhind ered
-Ġcorrection al
-ĠI TV
-lee ve
-Ġin appropriately
-lic ks
-Ġtresp ass
-Ġp aws
-Ġnegoti ator
-ĠChrist ensen
-lim its
-ĠDian ne
-Ġeleg ance
-ĠContract s
-an ke
-Ob j
-Ġvigil ance
-Ġcast les
-ĠN AD
-ĠHol o
-Ġemph atically
-ĠTit us
-ĠServ ing
-ĠRich ie
-ĠP igs
-5 68
-Ġanim osity
-ĠAtt ributes
-ĠU riel
-M Q
-my ra
-ĠApplic ant
-Ġpsychiat rists
-ĠV ij
-ĠAb by
-ag ree
-P ush
-Ġk Wh
-hib a
-Ġinc ite
-ĠWe asley
-ĠTax i
-minist ic
-hy per
-ĠF arn
-Ġ6 01
-ĠNation wide
-F ake
-95 2
-Ġma ize
-Ġinteract ed
-Ġtransition ed
-Ġparas itic
-Ġharm onic
-Ġdec aying
-Ġbas eless
-ns ics
-Ġtrans pired
-Ġabund antly
-ĠFore nsic
-Ġtread mill
-ĠJ av
-ab and
-Ġssh d
-Ġfront man
-ĠJak arta
-oll er
-dro ps
-ĠSERV ICES
-rompt u
-oph ical
-h ospital
-bled on
-6 45
-Ġmid range
-ĠEV ENT
-cul ated
-raw led
-Ġper ched
-Ġover board
-ĠPe el
-ĠP wr
-ĠCar th
-ĠCOM PLE
-co e
-sh all
-Ġdeter rence
-M ETHOD
-ĠAbs ent
-M EN
-Ġs ill
-ĠLE VEL
-Y ork
-Ġsin ners
-ĠOP EC
-ĠN ur
-ĠDesign s
-se lection
-Ġunw orthy
-CH A
-Ġstreng thens
-88 3
-ed ly
-Ġslic ing
-Ġmal nutrition
-Ġfilm making
-ĠPol k
-ur ated
-Ġ4 21
-bre akers
-!' "
-Ġwet lands
-ĠDisc rimination
-Ġallow able
-Ġste ered
-ĠSic ily
-S AM
-Ġmust ache
-Ġm ids
-Ġcl ipped
-Ġcirc ulate
-Ġbr ittle
-ĠBuild ings
-ra ised
-ĠRound up
-Ġwealth ier
-Ġoverw rite
-Ġover powered
-ĠGerr ard
-s ites
-PD ATED
-Ġacute ly
-ĠGam ble
-Ġp im
-ĠK us
-Typ ically
-De ploy
-ĠMoroc can
-p otion
-com be
-Ġvigil ante
-Ġ36 3
-St ew
-ĠB agg
-Ġres ided
-ĠSp o
-Ġrem nant
-Ġempt iness
-br ainer
-Ġout patient
-pri ority
-Ġle ptin
-ĠPay ton
-ĠGle aming
-ĠS hed
-ĠPol o
-ĠMormon ism
-rest ricted
-arl ane
-w x
-Ġcreat ine
-ĠAn on
-ĠST UD
-ĠJ UL
-ĠT ee
-5 28
-08 9
-Ġhat ched
-Dis patch
-ĠCompos ite
-Ġ45 1
-p uff
-ĠX COM
-ĠOr n
-ĠTH ANK
-END ED
-ĠAshe ville
-ĠÃ ľ
-Ġman go
-ĠS lightly
-world ly
-ĠW ander
-ĠExp and
-ĠCh r
-M ist
-Ġorthodox y
-ĠUN ESCO
-reg ate
-Else where
-k ie
-ir led
-Ġtopp le
-Ġadopt ive
-ĠLeg s
-d ress
-ĠS agan
-b are
-ĠGl ou
-Cr unch
-Ġhelp ers
-Ġchron ically
-ĠH uma
-1 0000
-Ġaccommod ating
-äº Ķ
-Ġwrink les
-Ġdod ged
-four th
-Ġpre con
-Ġcompress or
-ĠK are
-Ġev ict
-ĠWar wick
-im ar
-Ġmodern ization
-Ġband wagon
-Ġref uted
-Ġnet ted
-ĠNa ples
-ĠGen ie
-per ors
-Ġfield ed
-Ġde re
-ĠPar ables
-le es
-Ġtr out
-asp ers
-Ġn ihil
-Ġhapp iest
-Ġflo ppy
-ĠLo ft
-ĠHe ard
-Ġun ison
-Ġl ug
-ĠRed mond
-class ic
-Supp orters
-SH IP
-G MT
-Ġfue lled
-ç IJ
-Ġd d
-ĠEmin em
-Ġ18 97
-NY SE
-Ġsecret aries
-ĠF IA
-ĠCanaver al
-F avorite
-Ġp omp
-Ġdetain ee
-ers hip
-aim on
-i our
-ĠA pex
-Ġplant ations
-am ia
-ac ion
-R ust
-Ġtow ed
-ĠTru ly
-5 77
-Ġshel tered
-r ider
-W o
-Ġl air
-ĠInt elligent
-impro ve
-m atically
-Ġet iquette
-ad ra
-all o
-ĠJun o
-any thing
-ĠStru ggle
-ĠPred ict
-ĠGr imes
-ĠAMER ICA
-ct x
-ĠSit uation
-W OOD
-Ġsol uble
-me ier
-Ġintoler able
-ang ering
-Ġun interrupted
-Ġtool tip
-Ġinterrog ated
-Ġgun ned
-ĠSne ak
-æŃ ¦
-Ġt ether
-Ġcr umble
-L ens
-Ġclust ered
-ĠSy l
-ĠHas an
-Ġdystop ian
-w ana
-Ġjoy stick
-ĠTh ib
-amm u
-Tom orrow
-5 46
-Ġoverc ame
-Ġminim ized
-cept or
-Run ner
-ENG TH
-ĠBrend a
-ĠAchieve ments
-Ġtor ches
-Ġrapp ort
-ĠInvestig ator
-ĠHand ling
-rel ation
-g rey
-8 15
-Ġk cal
-ĠComm ands
-d q
-Ġcur ls
-Ġbe arer
-Ġcyn icism
-it ri
-ĠUse ful
-B ee
-D CS
-Ġab ras
-P ract
-BIL ITIES
-7 12
-Ġdebug ger
-Ġdebt or
-ĠL ia
-ĠK ers
-Ġexacerb ate
-ĠSt acy
-ĠB land
-ĠSc enes
-Ġbranch ing
-âĸĪâĸĪâĸĪâĸĪ âĸĪâĸĪâĸĪâĸĪ
-ape ake
-Ġs alsa
-Ġmish and
-ĠKon ami
-ĠN ib
-Ġanecd ote
-Ġagree able
-Ï ī
-ĠNath aniel
-ĠHe isman
-ĠB eware
-Ġ18 86
-spect ive
-69 1
-5 22
-Ġinhib its
-Ġhas hing
-Ġ18 89
-å° Ĩ
-v ich
-P ure
-Ġsolid ly
-Ġaspir in
-im aru
-Ġstreet car
-ĠU CS
-ĠJ udd
-Ġflash backs
-p ins
-Ġ14 40
-ĠUN HCR
-ĠSym ptoms
-T IT
-5 38
-F ra
-% );
-Ġo oz
-Ġcur few
-Ġcal med
-Ġparticip ates
-Te X
-Ġnons ensical
-Ġfull back
-ĠDe L
-mon key
-h ari
-Ġmetabol ites
-Ġloot ed
-ĠAL WAYS
-ĠB CC
-L t
-oc het
-B one
-Ġveto ed
-Ġg cc
-ĠCL ICK
-Ġ18 88
-s af
-Ġstiff ness
-Ġlow ly
-ĠGe h
-vers on
-ors et
-Ġun foreseen
-Ġan esthesia
-ĠOpt ical
-Ġrecon structed
-ĠT up
-sh ows
-NEW S
-ĠNewsp aper
-ĠA SA
-ter a
-N umbers
-Ġinexpl icable
-× ij
-Ġhard ness
-unt arily
-ĠA cer
-grad ient
-ARD IS
-Ġwood land
-Ġmetaph ors
-ĠWem bley
-ĠPa vel
-phil is
-Ġre writing
-Ġpercept ual
-Ġ10 70
-worm s
-ĠDown s
-Ġunsur prisingly
-Ġtag ging
-fl ame
-Ġlit res
-Ġboun ces
-ĠB abe
-sh ut
-Ġoverd oses
-ĠShe ila
-ĠCh au
-ĠBl ess
-Capt ure
-ĠSign ificant
-ĠSc ion
-Ġ38 9
-ĠMc H
-ĠTitan ium
-ĠMe al
-amed a
-ag ents
-agg ressive
-B illy
-76 3
-ĠS aying
-DER R
-it one
-Coll ins
-B ound
-Ġbol ted
-ĠDM CA
-95 3
-Ġun iqueness
-Ġep igen
-un ci
-ant am
-Ġreck oning
-ch airs
-OG R
-ĠSen egal
-Ġ18 62
-re levant
-ĠÂ ¯
-Ġpharm acies
-ĠG eral
-v ier
-Y an
-OR PG
-Ġrab id
-b ending
-ĠUN ITED
-Ġ4 65
-As sembly
-Ġwe ep
-Ġbe hest
-ĠMother s
-ĠJ ace
-h id
-Ġwh irlwind
-ĠUN IVERS
-Ġut opian
-Ġkidn ap
-Ph ilipp
-K in
-89 3
-Ġlivest ream
-ĠM ISS
-Ġsub versive
-ĠTechn iques
-ĠJUST ICE
-ĠB ASE
-Ġ38 7
-Ġassail ants
-ĠHard core
-Ġsprink led
-ĠP se
-é ļ
-print ed
-ĠH au
-OR GE
-ĠT OUR
-Ġl aced
-Ġit ch
-G iving
-Ġport ed
-78 1
-//////////////// ////////////////
-bre eding
-Ġlog ger
-ĠH OL
-inn ie
-First ly
-Ġembry onic
-Ġdeleg ated
-p ai
-O IL
-Ġcentr ally
-ĠR x
-ĠSc outing
-D utch
-Ġhe reditary
-ĠCru iser
-s at
-5 29
-ĠMar riott
-other mal
-Ġprohib itions
-E arn
-ĠSt ab
-ĠColleg es
-ĠBel ief
-st retched
-ĠL H
-ĠEntity Item
-C IA
-Ġun rem
-Ġlaure ate
-Ġdenomin ations
-sum mary
-h ler
-S pect
-ĠK laus
-ĠBe ans
-Ġins ur
-ĠPA X
-Ġfield er
-ĠV et
-ĠSp arrow
-z ie
-ĠS Q
-ĠMond ays
-ĠOff line
-ĠLer ner
-ĠExt ensions
-Ire land
-Ġpatron age
-Ġcontrast ed
-ĠMan ia
-h irt
-Mos cow
-Ġcondem ns
-ĠAn ge
-Ġcomp osing
-ĠPe pe
-ĠP addock
-Ġheter ogeneity
-Ġide ologically
-Ġf ishes
-Ġcur sing
-ĠR utherford
-ĠFlo ating
-ĠAm elia
-Te a
-Syn opsis
-Ġstun ts
-Ġbe ad
-Ġstock ing
-ĠM ILL
-ob ook
-mass ive
-\ <
-Ġh ump
-ĠPref erences
-Engine Debug
-ge ist
-ĠNiet o
-ome ver
-ish y
-eval uate
-col onial
-Altern ative
-ĠGo Pro
-ĠV ortex
-ĠNET WORK
-ans ky
-Sec ure
-ĠTh rust
-Sn ake
-Ġparcel s
-Ġsam urai
-Ġactress es
-N ap
-M F
-ifer ation
-Be er
-5 23
-ĠI ly
-oint ment
-P ing
-Ġstri ped
-ĠMell on
-oss ession
-Ġneut ron
-end ium
-Ġa ph
-ĠFlav oring
-Ġ38 3
-Ġrespons iveness
-ĠJ indal
-ĠHitch cock
-Den ver
-ĠDRAG ON
-sm anship
-ĠDu pl
-Ġs ly
-Ġweb cam
-ĠTw ain
-ĠDar ling
-ili ate
-cons umer
-D IT
-Ġnames ake
-Ġun orthodox
-Ġfun er
-ĠPL oS
-ĠCONTR OL
-ozy g
-ogl obin
-F ACE
-ER G
-ĠD ia
-ĠF iesta
-ce le
-0 34
-Ġencl ave
-âĸ¬ âĸ¬
-on ement
-al ist
-M and
-Ġhome grown
-ĠF ancy
-Ġconcept ions
-ĠCont ains
-ure en
-Ġreiter ate
-Ġme ager
-Ġinstall ments
-Sp awn
-6 27
-Ġphot oc
-ĠCab rera
-ĠRos enthal
-ĠLans ing
-is ner
-Ġinvest s
-ĠUFO s
-EX P
-Hard ware
-Ġtr agically
-Ġconced es
-ie ft
-ch am
-bor gh
-ĠSch r
-ĠMel anie
-ĠH oy
-Ġvisit ation
-Ġid iosyncr
-Ġfract ions
-Ġfore skin
-ob os
-Ġpo aching
-ĠVI EW
-Ġstimul ates
-ĠG ork
-can on
-M IC
-ĠNem esis
-ĠInd ra
-ĠDM V
-Ġ5 29
-Ġinspect ing
-Ġgrand ma
-ĠW hedon
-ĠSh ant
-ĠP urg
-ik an
-ĠT eg
-ĠCL R
-z ac
-Vict oria
-ĠVer ify
-ion ics
-Ġpart ying
-ĠM ou
-col our
-Ġtestim onies
-l ations
-Ġpress uring
-hi ro
-ac ers
-Ġf id
-ang ler
-ĠCS I
-Ġhere after
-Ġdiss idents
-report ing
-iph any
-che v
-Ġsol itude
-Ġl obe
-Ġind is
-Ġcred ential
-re cent
-ad ult
-ĠNir vana
-ĠFranch ise
-L ayer
-H yp
-ĠBerks hire
-Ġwill s
-t if
-Ġtot em
-ĠJud ah
-rep air
-Inst ant
-5 48
-Ġemb assies
-Ġbott leneck
-Ġb ount
-Ġtyp ew
-ĠAl vin
-j ing
-im ilar
-R ush
-Ġbr im
-ĠHEL P
-A im
-] '
-Ġpass ively
-Ġbound ed
-ĠR ated
-Ġcriminal ity
-Ġbiom ark
-Ġdisp atcher
-ĠTow ards
-Ġ+ ++
-right eous
-f rog
-ĠP anc
-C arter
-0 32
-æ© Ł
-Ġult raviolet
-ĠLic ensed
-ĠT ata
-ĠBl essing
-ĠG AM
-Ġchem ically
-ĠSe af
-ĠRE LE
-ĠMerc enary
-capital ist
-Ġform ulations
-Ġann ihilation
-ĠVer b
-ĠAr gon
-Ġun loaded
-Ġmorp hed
-Ġconqu ering
-back er
-I ELD
-Ġtheft s
-Ġfront runner
-ĠRoy ale
-ĠFund amental
-el ight
-C hip
-necess ary
-ay n
-ĠSl ip
-Ġ4 48
-cern ed
-P ause
-Ġshock ingly
-ĠAB V
-Ġcomp osure
-7 33
-ĠMotors port
-ah ime
-Mur ray
-M ach
-Ġgr ids
-Ġdeb ian
-Ġfurther more
-Ġdexter ity
-ĠCollect ions
-os lov
-il age
-b j
-ĠMont eneg
-Ġstrut Connector
-Ġmassac res
-Ġbrief s
-fet ched
-uv ian
-ol ition
-Fail ure
-emon ic
-Ġfl ared
-Ġclaim ant
-Ġc ures
-Ġgive aways
-ĠSubst ance
-al ions
-Ġcr inge
-ĠK ul
-Ġarist ocracy
-ĠUl ster
-ol ated
-h ousing
-ĠM IS
-Ġgl ared
-ĠWil helm
-ne eds
-lam bda
-build ers
-ĠV IS
-Ġradi ator
-ĠGhost busters
-Ġ4 36
-act ual
-Ġher ds
-ç a
-watch ing
-Ġcounter ing
-Ch arge
-Ġchar red
-Ġwar heads
-Ġiod ine
-ĠM acy
-04 1
-Ġdepart ures
-ĠS ins
-Ġdy ed
-ĠConcept s
-g ado
-7 13
-Ġquot ations
-Ġg ist
-ĠChrist y
-Ġant igen
-ĠHem p
-ĠD rawn
-ĠB arg
-ez vous
-Ġp aternity
-Ġar du
-ĠAnch orage
-ĠR ik
-Ġover loaded
-ĠUs ername
-ĠTam my
-ĠN au
-ĠCell ular
-Ġw aning
-Ġrod ent
-ĠWor cester
-il ts
-ĠT ad
-Ġdwell ings
-Ġbull ish
-4 31
-Ġretali ate
-Ġmig raine
-ĠChev ron
-CH ECK
-Ġdon key
-c rim
-SP A
-ĠAn alog
-Ġmarqu ee
-ĠHa as
-B ir
-ĠGD DR
-ĠDownload s
-Ġwill power
-ĠFor th
-ĠRecord ed
-Ġimp ossibility
-ĠLog ged
-ĠFr anks
-ĠR att
-in itions
-Ġclean ers
-Ġsore ly
-Ġflick ering
-ĠEx amination
-c atching
-allow een
-Ms g
-Ġdun no
-F a
-Ġdys ph
-c razy
-.' '.
-Ġmain line
-Ġc s
-Ġp tr
-ĠW ally
-ig un
-95 1
-ĠBig foot
-f ights
-Ġretrie ving
-J r
-Ġdupl ication
-ĠExpl an
-Ġrel ational
-Ġqu aint
-Ġbisc uits
-Ġad o
-Ġsh udder
-Ġantid ote
-blood ed
-ks h
-Ġsa uces
-Ġrein vest
-Ġdispens ary
-ĠD iver
-Ġ9 000
-stud ent
-Ġin separ
-esc ap
-Ġtodd lers
-ĠGP IO
-ĠAss ignment
-head ers
-Ġlack luster
-Ġab ack
-95 6
-Ġtool bar
-7 45
-Ġo ust
-Ġcontempl ation
-ĠPRES IDENT
-Ġ4 58
-==== ==
-Ġguarantee ing
-ĠHe ist
-ĠCann es
-Ļ ½
-Ġcollabor ator
-ĠAm p
-Ġg ou
-ĠSH ALL
-st ories
-78 3
-Ġmobil ized
-Ġbro od
-ĠL U
-ĠðŁ ij
-Ġref in
-ĠAnthrop ology
-v ind
-ill i
-Ġwarrant ies
-ĠB abel
-Ġsw ath
-Ġc aches
-Ġantagon ists
-art ifacts
-Ġhot ly
-ĠSt arts
-ĠG ö
-z ag
-!! !!!
-Ġsc ourge
-Ġcons piring
-ru its
-re verse
-ĠShe en
-ĠJes uit
-ĠGiov anni
-ad ies
-Ġbutt ocks
-ear cher
-ac an
-Ġvolley ball
-Ġshroud ed
-Ġscore board
-b ats
-ĠI PM
-Ġass es
-Ġde regulation
-ĠTe legram
-ĠReb oot
-Ġ7 000
-ĠCan ary
-Ġk ernels
-ĠFranç ois
-ĠD uff
-ĠP on
-ĠLe ica
-ĠGar min
-Ġor phans
-ĠClaud ia
-Ġcal endars
-ĠLe ilan
-ent o
-R ocket
-Ġbr unch
-ĠHaw king
-ain ers
-Ġsens ibilities
-Ġk W
-ĠK and
-Ġre claimed
-Ġinteresting ly
-× ©
-rom y
-J M
-ĠEnhance ment
-b ush
-Sk ip
-Ġrapp ers
-Ġg azing
-p edia
-ath lon
-Rev olution
-Ġsn ipers
-Ġre verted
-Ġconglomer ate
-T erry
-79 4
-Ġhars her
-Ġdes olate
-ĠHit man
-Comm ission
-Ġ( /
-âĢ¦ ."
-Com par
-Ġampl ification
-om inated
-Ġreg ress
-ĠColl ider
-Ġinform ants
-Ġg azed
diff --git a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/special_tokens_map.json b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/special_tokens_map.json
deleted file mode 100644
index 773bd68cf09004..00000000000000
--- a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/special_tokens_map.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "bos_token": {
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- },
- "eos_token": {
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- },
- "unk_token": {
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- }
-}
diff --git a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/tokenizer_config.json b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/tokenizer_config.json
deleted file mode 100644
index 48314a4d098cbc..00000000000000
--- a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/tokenizer_config.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "add_bos_token": false,
- "add_prefix_space": false,
- "bos_token": {
- "__type": "AddedToken",
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- },
- "clean_up_tokenization_spaces": true,
- "eos_token": {
- "__type": "AddedToken",
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- },
- "errors": "replace",
- "model_max_length": 1024,
- "pad_token": null,
- "tokenizer_class": "GPT2Tokenizer",
- "unk_token": {
- "__type": "AddedToken",
- "content": "<|endoftext|>",
- "lstrip": false,
- "normalized": true,
- "rstrip": false,
- "single_word": false
- }
-}
diff --git a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/vocab.json b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/vocab.json
deleted file mode 100644
index a15dd0028acd1d..00000000000000
--- a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2/vocab.json
+++ /dev/null
@@ -1,50259 +0,0 @@
-{
- "!": 0,
- "!!": 3228,
- "!!!": 10185,
- "!!!!": 13896,
- "!!!!!": 50184,
- "!!!!!!!!": 34635,
- "!!\"": 37160,
- "!\"": 2474,
- "!\",": 40754,
- "!\".": 48220,
- "!'": 13679,
- "!'\"": 49296,
- "!)": 8133,
- "!),": 26290,
- "!).": 19588,
- "!,": 28265,
- "!--": 28112,
- "!.": 43179,
- "!/": 48443,
- "!:": 48725,
- "!?": 22857,
- "!?\"": 42720,
- "!]": 36463,
- "\"": 1,
- "\"!": 40484,
- "\"\"": 15931,
- "\"\"\"": 37811,
- "\"'": 30543,
- "\"(": 18109,
- "\")": 4943,
- "\"))": 48774,
- "\"),": 12340,
- "\").": 11074,
- "\");": 15341,
- "\",": 1600,
- "\",\"": 2430,
- "\"-": 26793,
- "\".": 1911,
- "\"...": 26214,
- "\".[": 42924,
- "\"/>": 26700,
- "\":": 1298,
- "\":\"": 2404,
- "\":\"\",\"": 34713,
- "\":\"\"},{\"": 47182,
- "\":\"/": 15473,
- "\":-": 48219,
- "\":[": 20598,
- "\":[\"": 26358,
- "\":[{\"": 32509,
- "\":{\"": 8351,
- "\";": 8172,
- "\">": 5320,
- "\"><": 22039,
- "\">": 23984,
- "\"?": 13984,
- "\"[": 17912,
- "\"]": 8973,
- "\"],": 33116,
- "\"],\"": 34171,
- "\"]=>": 23785,
- "\"}": 20662,
- "\"},": 25719,
- "\"},\"": 13018,
- "\"},{\"": 11919,
- "\"}],\"": 42785,
- "\"âĢ¦": 24426,
- "\"âĢĶ": 15327,
- "#": 2,
- "##": 2235,
- "###": 21017,
- "####": 4242,
- "########": 7804,
- "################": 14468,
- "################################": 29113,
- "#$": 29953,
- "#$#$": 34206,
- "$": 3,
- "$$": 13702,
- "$$$$": 36737,
- "$,": 47113,
- "$.": 35307,
- "${": 38892,
- "%": 4,
- "%\"": 39658,
- "%%": 16626,
- "%%%%": 36917,
- "%)": 4407,
- "%),": 15920,
- "%).": 18823,
- "%);": 49563,
- "%,": 7441,
- "%-": 33963,
- "%.": 7225,
- "%:": 48529,
- "%;": 26525,
- "%]": 39850,
- "&": 5,
- "&&": 25226,
- "'": 6,
- "'\"": 29653,
- "''": 7061,
- "''''": 39115,
- "''.": 35384,
- "'';": 44648,
- "')": 11537,
- "'),": 33809,
- "').": 27691,
- "');": 24036,
- "',": 3256,
- "',\"": 40264,
- "','": 41707,
- "'-": 29001,
- "'.": 4458,
- "'.\"": 30827,
- "'/": 26488,
- "':": 10354,
- "';": 17020,
- "'>": 44167,
- "'?": 30960,
- "']": 20520,
- "'d": 1549,
- "'ll": 1183,
- "'m": 1101,
- "'re": 821,
- "'s": 338,
- "'t": 470,
- "'ve": 1053,
- "(": 7,
- "(\"": 7203,
- "($": 16763,
- "(&": 39434,
- "('": 10786,
- "((": 19510,
- "()": 3419,
- "())": 28955,
- "());": 35430,
- "(),": 22784,
- "().": 22446,
- "():": 33529,
- "();": 9783,
- "(){": 39893,
- "(*": 46491,
- "(-": 32590,
- "([": 26933,
- "(\\": 38016,
- "(_": 28264,
- "({": 15090,
- ")": 8,
- ")!": 31520,
- ")\"": 16725,
- ")\",": 42501,
- ")'": 33047,
- ")(": 5769,
- "))": 4008,
- ")))": 22305,
- "))))": 35514,
- ")),": 36911,
- ")).": 29720,
- "));": 18125,
- ")*": 27493,
- ")+": 47762,
- "),": 828,
- "),\"": 27267,
- ")-": 13219,
- ")--": 42944,
- ").": 737,
- ").\"": 21387,
- ")...": 26513,
- ").[": 42669,
- ")/": 20679,
- "):": 2599,
- ");": 1776,
- ")": 36475,
- ")=": 47505,
- ")=(": 35793,
- ")?": 19427,
- ")[": 38381,
- ")\\": 19415,
- ")]": 15437,
- ")].": 25295,
- "){": 19953,
- ")|": 14726,
- ")}": 38165,
- ")âĢĶ": 27920,
- "*": 9,
- "*)": 28104,
- "**": 1174,
- "***": 8162,
- "****": 2466,
- "*****": 35625,
- "********": 4557,
- "************": 46068,
- "****************": 8412,
- "********************************": 17174,
- "*,": 25666,
- "*.": 24620,
- "*/": 16208,
- "*/(": 40278,
- "*:": 47026,
- "*=-": 48069,
- "+": 10,
- "+(": 33747,
- "+)": 28988,
- "++": 4880,
- "++)": 29577,
- "+++": 45340,
- "++++": 14030,
- "++++++++": 25128,
- "++++++++++++++++": 44627,
- "++;": 47253,
- "+,": 28200,
- "+.": 27613,
- "+=": 47932,
- ",": 11,
- ",\"": 553,
- ",'": 4032,
- ",'\"": 16078,
- ",''": 14004,
- ",)": 35751,
- ",,": 9832,
- ",,,,": 23846,
- ",,,,,,,,": 47682,
- ",-": 12095,
- ",.": 38508,
- ",...": 42303,
- ",[": 17414,
- ",âĢĶ": 34976,
- "-": 12,
- "-\"": 21215,
- "-$": 22799,
- "-'": 19355,
- "-(": 30420,
- "-)": 25106,
- "-+": 19529,
- "-+-+": 27097,
- "-+-+-+-+": 42744,
- "-,": 20995,
- "--": 438,
- "--+": 44785,
- "---": 6329,
- "----": 650,
- "-----": 30934,
- "------": 23031,
- "-------": 26866,
- "--------": 982,
- "---------": 45537,
- "----------": 35937,
- "-----------": 32284,
- "------------": 10541,
- "-------------": 32501,
- "--------------": 26171,
- "---------------": 24305,
- "----------------": 1783,
- "--------------------": 19351,
- "------------------------": 22369,
- "--------------------------------": 3880,
- "------------------------------------------------": 47232,
- "--------------------------------------------------------": 43801,
- "----------------------------------------------------------------": 10097,
- "-->": 46904,
- "-.": 34507,
- "->": 3784,
- "-[": 49146,
- "-|": 22831,
- ".": 13,
- ".\"": 526,
- ".\"\"": 32203,
- ".\")": 19570,
- ".\",": 33283,
- ".\",\"": 41424,
- ".\"[": 18161,
- ".#": 32535,
- ".$": 48082,
- ".'": 2637,
- ".'\"": 11496,
- ".''": 13531,
- ".''.": 50113,
- ".(": 12195,
- ".)": 2014,
- ".),": 12179,
- ".).": 15729,
- ".):": 47308,
- ".*": 15885,
- ".,": 1539,
- ".,\"": 44388,
- ".-": 7874,
- ".--": 9816,
- "..": 492,
- "...": 986,
- "...\"": 9313,
- "...)": 23029,
- "....": 1106,
- ".....": 12359,
- "......": 16317,
- ".......": 25780,
- "........": 2109,
- ".........": 34617,
- ".............": 44274,
- "................": 4181,
- "..................": 49129,
- "........................": 27754,
- "................................": 8864,
- "................................................................": 23193,
- "...?": 44825,
- "...]": 22345,
- "../": 40720,
- "./": 19571,
- ".:": 11207,
- ".;": 15089,
- ".<": 29847,
- ".": 25970,
- ".>>": 32756,
- ".?": 40791,
- ".[": 3693,
- ".]": 8183,
- "._": 13557,
- ".}": 44587,
- ".âĢĵ": 37585,
- ".âĢĶ": 13402,
- ".ãĢį": 43735,
- ".�": 40670,
- "/": 14,
- "/\"": 30487,
- "/#": 31113,
- "/$": 32624,
- "/(": 29006,
- "/)": 34729,
- "/*": 15211,
- "/**": 35343,
- "/+": 28404,
- "/,": 47454,
- "/-": 16327,
- "/.": 11757,
- "//": 1003,
- "///": 20379,
- "////": 9705,
- "////////": 16150,
- "////////////////": 27246,
- "////////////////////////////////": 49704,
- "/>": 15913,
- "/?": 20924,
- "/_": 47835,
- "/âĢĭ": 27643,
- "0": 15,
- "00": 405,
- "000": 830,
- "0000": 2388,
- "00000": 20483,
- "000000": 10535,
- "0000000": 24598,
- "00000000": 8269,
- "0000000000000000": 25645,
- "00007": 44808,
- "0001": 18005,
- "0002": 34215,
- "001": 8298,
- "0010": 37187,
- "002": 21601,
- "00200000": 36490,
- "003": 11245,
- "004": 22914,
- "005": 22544,
- "006": 28041,
- "007": 25816,
- "008": 25257,
- "009": 28694,
- "01": 486,
- "010": 20943,
- "0100": 39103,
- "011": 28555,
- "012": 30206,
- "013": 30273,
- "014": 28645,
- "015": 25150,
- "016": 27037,
- "017": 29326,
- "018": 29159,
- "019": 30484,
- "02": 2999,
- "020": 33618,
- "0200": 44613,
- "021": 46821,
- "022": 44087,
- "023": 45310,
- "024": 40839,
- "025": 36629,
- "026": 45987,
- "027": 44698,
- "028": 46957,
- "029": 48891,
- "03": 3070,
- "030": 39101,
- "031": 43637,
- "032": 49959,
- "033": 44427,
- "034": 49841,
- "035": 44215,
- "036": 48597,
- "04": 3023,
- "040": 36676,
- "041": 50049,
- "043": 48768,
- "044": 43977,
- "045": 40350,
- "046": 45438,
- "047": 48000,
- "048": 47202,
- "05": 2713,
- "050": 28669,
- "052": 37841,
- "055": 47838,
- "057": 43526,
- "059": 46712,
- "06": 3312,
- "060": 41322,
- "07": 2998,
- "070": 43509,
- "075": 46396,
- "08": 2919,
- "080": 33057,
- "083": 48290,
- "088": 46556,
- "089": 49352,
- "09": 2931,
- "090": 42534,
- "1": 16,
- "10": 940,
- "100": 3064,
- "1000": 12825,
- "10000": 49388,
- "1001": 47705,
- "1007": 44318,
- "101": 8784,
- "1016": 27956,
- "102": 15377,
- "1024": 35500,
- "1027": 40403,
- "103": 15197,
- "104": 13464,
- "105": 13348,
- "106": 15801,
- "107": 15982,
- "108": 15711,
- "1080": 24045,
- "109": 14454,
- "11": 1157,
- "110": 11442,
- "1100": 42060,
- "111": 16243,
- "1111": 26259,
- "112": 14686,
- "113": 16616,
- "114": 16562,
- "115": 15363,
- "116": 18298,
- "117": 17657,
- "118": 16817,
- "119": 16315,
- "12": 1065,
- "120": 10232,
- "1200": 27550,
- "121": 19244,
- "122": 18376,
- "123": 10163,
- "124": 17464,
- "125": 11623,
- "126": 19420,
- "127": 16799,
- "128": 12762,
- "129": 18741,
- "13": 1485,
- "130": 12952,
- "131": 22042,
- "132": 19924,
- "133": 16945,
- "134": 19880,
- "135": 17059,
- "136": 20809,
- "137": 19708,
- "138": 20107,
- "139": 20219,
- "14": 1415,
- "140": 15187,
- "141": 23756,
- "142": 23726,
- "143": 21139,
- "144": 18444,
- "145": 18781,
- "146": 20964,
- "147": 20198,
- "148": 18294,
- "149": 19442,
- "15": 1314,
- "150": 8628,
- "1500": 33698,
- "151": 24309,
- "152": 17827,
- "153": 21395,
- "154": 21526,
- "155": 18742,
- "156": 21599,
- "157": 18458,
- "158": 21273,
- "159": 19707,
- "16": 1433,
- "160": 14198,
- "1600": 36150,
- "161": 25948,
- "162": 25061,
- "163": 24136,
- "164": 23237,
- "165": 20986,
- "166": 23055,
- "167": 21940,
- "168": 14656,
- "169": 22172,
- "17": 1558,
- "170": 17279,
- "171": 27192,
- "172": 23628,
- "173": 25399,
- "174": 22985,
- "175": 17430,
- "176": 24096,
- "177": 22413,
- "178": 23188,
- "179": 21738,
- "18": 1507,
- "180": 15259,
- "1800": 39188,
- "181": 27057,
- "182": 24294,
- "183": 24839,
- "184": 22883,
- "185": 21652,
- "186": 25096,
- "187": 23451,
- "188": 20356,
- "189": 23362,
- "19": 1129,
- "190": 19782,
- "1900": 48104,
- "191": 26492,
- "192": 17477,
- "1920": 40454,
- "193": 24943,
- "194": 22913,
- "1945": 41931,
- "195": 22186,
- "1950": 42751,
- "1959": 45403,
- "196": 25272,
- "1960": 38503,
- "1963": 45192,
- "1964": 46477,
- "1965": 45271,
- "1966": 44227,
- "1967": 42830,
- "1968": 42246,
- "1969": 38391,
- "197": 24991,
- "1970": 30986,
- "1971": 41208,
- "1972": 41023,
- "1973": 40220,
- "1974": 40828,
- "1975": 38449,
- "1976": 38108,
- "1977": 37781,
- "1978": 37950,
- "1979": 33581,
- "198": 22337,
- "1980": 23664,
- "1981": 35411,
- "1982": 30763,
- "1983": 29279,
- "1984": 28296,
- "1985": 29110,
- "1986": 28054,
- "1987": 27301,
- "1988": 26709,
- "1989": 25475,
- "199": 19104,
- "1990": 19891,
- "1991": 24529,
- "1992": 23847,
- "1993": 24465,
- "1994": 22666,
- "1995": 21908,
- "1996": 22288,
- "1997": 21498,
- "1998": 21113,
- "1999": 18946,
- "2": 17,
- "20": 1238,
- "200": 2167,
- "2000": 11024,
- "200000": 33470,
- "2001": 14585,
- "2002": 16942,
- "2003": 16088,
- "2004": 15724,
- "2005": 14315,
- "2006": 13330,
- "2007": 12726,
- "2008": 11528,
- "2009": 10531,
- "201": 1264,
- "2010": 10333,
- "2011": 9804,
- "2012": 6999,
- "2013": 6390,
- "2014": 4967,
- "2015": 4626,
- "2016": 5304,
- "2017": 5539,
- "2018": 7908,
- "2019": 23344,
- "202": 19004,
- "2020": 42334,
- "203": 22416,
- "204": 18638,
- "20439": 47936,
- "205": 21261,
- "206": 22136,
- "207": 22745,
- "208": 21315,
- "209": 22567,
- "21": 2481,
- "210": 21536,
- "211": 21895,
- "212": 21777,
- "213": 26427,
- "214": 22291,
- "215": 23349,
- "216": 20666,
- "217": 24591,
- "218": 28727,
- "219": 28896,
- "22": 1828,
- "220": 17572,
- "2200": 34294,
- "221": 26115,
- "222": 23148,
- "223": 22047,
- "224": 24137,
- "225": 18182,
- "226": 24909,
- "227": 24403,
- "228": 23815,
- "229": 23539,
- "23": 1954,
- "230": 19214,
- "231": 25667,
- "232": 24339,
- "233": 25429,
- "234": 24409,
- "235": 22370,
- "236": 24940,
- "237": 24693,
- "238": 23721,
- "239": 23516,
- "24": 1731,
- "240": 16102,
- "241": 28872,
- "242": 27877,
- "243": 26660,
- "244": 25707,
- "245": 22995,
- "246": 26912,
- "247": 23753,
- "248": 23045,
- "249": 21626,
- "25": 1495,
- "250": 9031,
- "2500": 44688,
- "251": 28072,
- "252": 22800,
- "253": 28592,
- "254": 24970,
- "255": 13381,
- "256": 11645,
- "257": 28676,
- "258": 25600,
- "259": 25191,
- "26": 2075,
- "260": 21719,
- "261": 30057,
- "262": 29119,
- "263": 29558,
- "264": 18897,
- "265": 22980,
- "266": 25540,
- "267": 25674,
- "268": 25022,
- "269": 26276,
- "27": 1983,
- "270": 20233,
- "271": 28977,
- "272": 29807,
- "273": 27367,
- "274": 28857,
- "275": 23195,
- "276": 27988,
- "277": 27019,
- "278": 25870,
- "279": 26050,
- "28": 2078,
- "280": 21033,
- "281": 30368,
- "282": 32568,
- "283": 30290,
- "284": 30336,
- "285": 26279,
- "286": 27033,
- "287": 27800,
- "288": 25270,
- "289": 27693,
- "29": 1959,
- "290": 24369,
- "291": 33551,
- "292": 32759,
- "293": 31675,
- "294": 27696,
- "295": 25710,
- "296": 27137,
- "297": 26561,
- "298": 27728,
- "299": 22579,
- "3": 18,
- "30": 1270,
- "300": 6200,
- "3000": 23924,
- "301": 18938,
- "302": 22709,
- "303": 22572,
- "304": 21288,
- "305": 22515,
- "306": 20548,
- "307": 22996,
- "308": 21495,
- "309": 26895,
- "31": 3132,
- "310": 26717,
- "311": 36244,
- "312": 27970,
- "313": 25838,
- "314": 33638,
- "315": 27936,
- "316": 33400,
- "317": 34125,
- "318": 36042,
- "319": 35175,
- "32": 2624,
- "320": 19504,
- "321": 36453,
- "322": 37283,
- "323": 32637,
- "324": 33916,
- "325": 26582,
- "326": 39195,
- "327": 34159,
- "328": 34256,
- "329": 37967,
- "33": 2091,
- "330": 26073,
- "331": 31697,
- "332": 32148,
- "333": 20370,
- "3333": 24840,
- "334": 31380,
- "335": 27326,
- "336": 29211,
- "337": 31496,
- "338": 28460,
- "339": 29626,
- "34": 2682,
- "340": 23601,
- "341": 33660,
- "342": 31575,
- "343": 32118,
- "344": 33535,
- "345": 27712,
- "346": 30557,
- "347": 30995,
- "348": 28978,
- "349": 27371,
- "35": 2327,
- "350": 14877,
- "351": 35273,
- "352": 33394,
- "353": 33319,
- "354": 32182,
- "355": 28567,
- "356": 32066,
- "357": 27277,
- "358": 31128,
- "359": 30743,
- "36": 2623,
- "360": 15277,
- "361": 35195,
- "362": 35667,
- "363": 35447,
- "364": 26780,
- "365": 24760,
- "366": 32459,
- "367": 27824,
- "368": 27412,
- "369": 30803,
- "37": 2718,
- "370": 20167,
- "371": 38056,
- "372": 36720,
- "373": 34770,
- "374": 31020,
- "375": 22318,
- "376": 32128,
- "377": 26514,
- "378": 30695,
- "379": 29088,
- "38": 2548,
- "380": 23734,
- "381": 36626,
- "382": 36243,
- "383": 34741,
- "384": 22842,
- "385": 27203,
- "386": 21734,
- "387": 32220,
- "388": 30460,
- "389": 29769,
- "39": 2670,
- "390": 25964,
- "391": 37710,
- "392": 32321,
- "393": 26007,
- "394": 34626,
- "395": 31010,
- "396": 34107,
- "397": 33372,
- "398": 31952,
- "399": 28771,
- "4": 19,
- "40": 1821,
- "400": 7029,
- "4000": 27559,
- "401": 21844,
- "402": 32531,
- "403": 31552,
- "404": 26429,
- "405": 26598,
- "406": 29703,
- "407": 30120,
- "408": 26200,
- "409": 29416,
- "41": 3901,
- "410": 33289,
- "411": 42224,
- "412": 39226,
- "413": 44103,
- "414": 37309,
- "415": 35038,
- "416": 35218,
- "417": 38547,
- "418": 39667,
- "419": 45068,
- "42": 3682,
- "420": 27211,
- "421": 46636,
- "422": 44361,
- "423": 43356,
- "424": 40090,
- "425": 32114,
- "426": 42780,
- "427": 42363,
- "428": 40173,
- "429": 11785,
- "43": 3559,
- "430": 31794,
- "431": 50080,
- "432": 45331,
- "433": 42117,
- "434": 47101,
- "435": 40064,
- "436": 43690,
- "437": 43284,
- "438": 43704,
- "439": 47106,
- "44": 2598,
- "440": 25644,
- "441": 39710,
- "442": 39506,
- "443": 34938,
- "444": 30272,
- "445": 43489,
- "446": 27260,
- "447": 34825,
- "448": 31115,
- "449": 31911,
- "45": 2231,
- "450": 17885,
- "451": 36330,
- "452": 37730,
- "453": 36625,
- "454": 34229,
- "455": 30505,
- "456": 29228,
- "457": 33032,
- "458": 29334,
- "459": 33459,
- "46": 3510,
- "460": 34716,
- "461": 40652,
- "462": 39997,
- "463": 38380,
- "464": 44578,
- "465": 42018,
- "466": 42199,
- "467": 24669,
- "468": 38472,
- "469": 42947,
- "47": 2857,
- "470": 27790,
- "471": 38339,
- "472": 37856,
- "473": 37804,
- "474": 38652,
- "475": 32576,
- "476": 35435,
- "477": 32883,
- "478": 29059,
- "479": 31714,
- "48": 2780,
- "480": 22148,
- "481": 40271,
- "482": 40149,
- "483": 38783,
- "484": 34137,
- "485": 32642,
- "486": 34251,
- "487": 35133,
- "488": 33646,
- "489": 35890,
- "49": 2920,
- "490": 31503,
- "491": 41289,
- "492": 40256,
- "493": 43134,
- "494": 39449,
- "495": 33781,
- "496": 37747,
- "497": 38073,
- "498": 36260,
- "499": 28324,
- "5": 20,
- "50": 1120,
- "500": 4059,
- "5000": 27641,
- "501": 33548,
- "502": 35126,
- "503": 31938,
- "504": 33580,
- "505": 31654,
- "506": 35638,
- "507": 35378,
- "508": 33042,
- "509": 29022,
- "51": 4349,
- "510": 33690,
- "511": 41647,
- "512": 25836,
- "513": 48645,
- "514": 47396,
- "515": 45969,
- "516": 47493,
- "517": 48170,
- "518": 44085,
- "519": 47785,
- "52": 4309,
- "520": 31211,
- "522": 49542,
- "523": 49803,
- "524": 48057,
- "525": 39088,
- "526": 48531,
- "528": 49351,
- "529": 49721,
- "53": 4310,
- "530": 38612,
- "533": 44994,
- "535": 44465,
- "536": 44468,
- "537": 46096,
- "538": 49561,
- "54": 4051,
- "540": 35005,
- "544": 47576,
- "545": 45326,
- "546": 49489,
- "548": 49934,
- "549": 44966,
- "55": 2816,
- "550": 22730,
- "551": 43697,
- "552": 40427,
- "553": 48096,
- "554": 44218,
- "555": 31046,
- "556": 37864,
- "557": 41948,
- "558": 40486,
- "559": 38605,
- "56": 3980,
- "560": 34135,
- "561": 47915,
- "562": 43918,
- "563": 46572,
- "565": 47372,
- "568": 49211,
- "57": 3553,
- "570": 39254,
- "571": 42875,
- "572": 48724,
- "573": 48638,
- "574": 46900,
- "575": 36189,
- "576": 37452,
- "577": 49447,
- "578": 38907,
- "579": 41734,
- "58": 3365,
- "580": 39322,
- "581": 48630,
- "582": 46044,
- "583": 46239,
- "584": 46352,
- "585": 38905,
- "586": 29796,
- "587": 44617,
- "588": 39118,
- "589": 44169,
- "59": 3270,
- "590": 36993,
- "591": 48952,
- "592": 45839,
- "593": 49051,
- "594": 46438,
- "595": 35124,
- "596": 45734,
- "597": 43239,
- "598": 41292,
- "599": 43452,
- "6": 21,
- "60": 1899,
- "600": 8054,
- "6000": 43434,
- "601": 41706,
- "602": 31418,
- "603": 35642,
- "604": 31916,
- "605": 32417,
- "606": 33206,
- "607": 31980,
- "608": 28688,
- "609": 31751,
- "61": 5333,
- "610": 39132,
- "612": 43610,
- "613": 47512,
- "614": 46841,
- "615": 47007,
- "616": 44214,
- "617": 47941,
- "618": 47448,
- "62": 5237,
- "620": 38850,
- "623": 46872,
- "625": 26704,
- "626": 45191,
- "627": 49856,
- "628": 48200,
- "629": 48602,
- "63": 5066,
- "630": 30005,
- "635": 48250,
- "64": 2414,
- "640": 31102,
- "641": 42759,
- "642": 41290,
- "643": 41813,
- "644": 29173,
- "645": 49259,
- "646": 27720,
- "647": 33981,
- "648": 34287,
- "649": 33300,
- "65": 2996,
- "650": 17544,
- "651": 40639,
- "652": 43193,
- "653": 46435,
- "654": 39111,
- "655": 35916,
- "656": 37466,
- "657": 37680,
- "658": 38431,
- "659": 36445,
- "66": 2791,
- "660": 39885,
- "661": 47159,
- "662": 39380,
- "663": 45791,
- "665": 36879,
- "666": 27310,
- "6666": 19060,
- "66666666": 41977,
- "667": 28933,
- "668": 35809,
- "669": 36657,
- "67": 3134,
- "670": 43798,
- "671": 46250,
- "672": 43864,
- "673": 45758,
- "674": 45385,
- "675": 42444,
- "676": 42548,
- "677": 40179,
- "678": 30924,
- "679": 37601,
- "68": 3104,
- "680": 37397,
- "681": 48564,
- "682": 43950,
- "683": 47521,
- "684": 41580,
- "685": 35978,
- "686": 33808,
- "687": 39925,
- "688": 34427,
- "689": 40523,
- "69": 3388,
- "690": 35844,
- "691": 49541,
- "692": 46589,
- "693": 48528,
- "694": 45214,
- "695": 37381,
- "696": 38205,
- "697": 40035,
- "698": 39357,
- "699": 47325,
- "7": 22,
- "70": 2154,
- "700": 9879,
- "701": 41583,
- "702": 36680,
- "703": 36809,
- "704": 32869,
- "705": 34801,
- "706": 35402,
- "707": 24038,
- "70710": 42877,
- "708": 32583,
- "709": 31495,
- "71": 4869,
- "710": 43147,
- "712": 49517,
- "713": 50055,
- "714": 45722,
- "718": 45720,
- "72": 4761,
- "720": 23906,
- "725": 45151,
- "727": 47760,
- "728": 48524,
- "729": 48555,
- "73": 4790,
- "730": 43916,
- "733": 49995,
- "736": 49150,
- "74": 4524,
- "740": 45598,
- "745": 50150,
- "747": 48882,
- "748": 48246,
- "75": 2425,
- "750": 15426,
- "751": 48365,
- "752": 43665,
- "753": 44550,
- "754": 41874,
- "755": 38172,
- "756": 38219,
- "757": 39251,
- "758": 38569,
- "759": 38314,
- "76": 4304,
- "760": 40761,
- "7601": 42752,
- "762": 48194,
- "763": 49641,
- "765": 29143,
- "76561": 48527,
- "767": 32059,
- "768": 30610,
- "77": 3324,
- "770": 41820,
- "771": 46761,
- "772": 43571,
- "773": 46871,
- "774": 47582,
- "775": 34483,
- "776": 39509,
- "777": 29331,
- "778": 39761,
- "779": 40393,
- "78": 3695,
- "780": 40873,
- "781": 49703,
- "782": 46519,
- "783": 50165,
- "784": 37688,
- "785": 41172,
- "786": 46302,
- "787": 41019,
- "789": 40401,
- "79": 3720,
- "790": 37750,
- "792": 48156,
- "793": 44750,
- "794": 50242,
- "795": 41544,
- "796": 41060,
- "797": 44673,
- "798": 43240,
- "799": 45455,
- "8": 23,
- "80": 1795,
- "800": 7410,
- "8000": 33942,
- "801": 41531,
- "802": 30863,
- "803": 43564,
- "804": 36088,
- "805": 28256,
- "806": 37988,
- "807": 36928,
- "808": 28362,
- "809": 34583,
- "81": 6659,
- "810": 40215,
- "815": 49503,
- "82": 6469,
- "820": 41739,
- "825": 47338,
- "83": 5999,
- "830": 48341,
- "833": 48634,
- "84": 5705,
- "840": 40675,
- "85": 5332,
- "850": 25764,
- "855": 45432,
- "86": 4521,
- "860": 45039,
- "864": 39570,
- "866": 42240,
- "87": 5774,
- "870": 46951,
- "875": 31360,
- "877": 42802,
- "88": 3459,
- "880": 41655,
- "882": 42980,
- "883": 49287,
- "884": 40353,
- "885": 44230,
- "886": 44980,
- "887": 46660,
- "888": 28011,
- "889": 39121,
- "89": 4531,
- "893": 49682,
- "896": 48712,
- "899": 44093,
- "9": 24,
- "90": 3829,
- "900": 12865,
- "901": 46815,
- "905": 44928,
- "909": 44675,
- "91": 6420,
- "910": 43234,
- "911": 35549,
- "915": 40248,
- "916": 48894,
- "92": 5892,
- "920": 37128,
- "925": 46351,
- "93": 6052,
- "930": 45418,
- "94": 5824,
- "940": 46899,
- "949": 48581,
- "95": 3865,
- "950": 31027,
- "951": 50119,
- "952": 49234,
- "953": 49649,
- "954": 48372,
- "956": 50148,
- "96": 4846,
- "960": 39277,
- "968": 38956,
- "969": 38819,
- "97": 5607,
- "970": 43587,
- "975": 42716,
- "978": 32196,
- "98": 4089,
- "980": 40022,
- "985": 42250,
- "986": 49087,
- "987": 44183,
- "989": 42520,
- "99": 2079,
- "990": 34155,
- "992": 41561,
- "993": 44821,
- "994": 42691,
- "995": 33438,
- "996": 38565,
- "997": 39647,
- "998": 34808,
- "999": 17032,
- "9999": 24214,
- ":": 25,
- ":\"": 11097,
- ":#": 43922,
- ":'": 32105,
- ":(": 37498,
- ":,": 45299,
- ":-": 21912,
- ":/": 14079,
- "://": 1378,
- "::": 3712,
- "::::": 24022,
- "::::::::": 43661,
- ":[": 33250,
- ":\\": 7479,
- ":]": 47715,
- ":{": 29164,
- ";": 26,
- ";\"": 26033,
- ";;": 7665,
- ";;;;": 14223,
- ";;;;;;;;": 25887,
- ";;;;;;;;;;;;": 46939,
- ";}": 46956,
- "<": 27,
- "": 3556,
- "<<": 16791,
- "": 47934,
- "<|endoftext|>": 50256,
- "=": 28,
- "=\"": 2625,
- "=\"\"": 33151,
- "=\"#": 25698,
- "=\"/": 35922,
- "=#": 46249,
- "=$": 43641,
- "='": 11639,
- "=(": 16193,
- "=-": 10779,
- "=-=-": 16822,
- "=-=-=-=-": 27584,
- "=-=-=-=-=-=-=-=-": 46402,
- "=/": 33223,
- "==": 855,
- "===": 18604,
- "====": 1421,
- "======": 50155,
- "========": 2559,
- "============": 25609,
- "================": 4770,
- "================================": 10052,
- "================================================================": 23926,
- "=>": 14804,
- "=[": 41888,
- "=\\\"": 17553,
- "=]": 48874,
- "={": 34758,
- "=~": 31820,
- "=~=~": 33813,
- ">": 29,
- ">\"": 24618,
- ">(": 33994,
- ">)": 43734,
- ">,": 22330,
- ">.": 28401,
- ">:": 31175,
- "><": 6927,
- ">": 12240,
- ">>": 4211,
- ">>>": 33409,
- ">>>>": 16471,
- ">>>>>>>>": 33717,
- ">>\\": 34516,
- ">[": 36937,
- ">]": 37981,
- "?": 30,
- "?!": 12248,
- "?!\"": 30823,
- "?\"": 1701,
- "?\",": 35379,
- "?\".": 43634,
- "?'": 8348,
- "?'\"": 26989,
- "?)": 10091,
- "?),": 33924,
- "?).": 29865,
- "?,": 21747,
- "?:": 27514,
- "??": 3548,
- "???": 28358,
- "????": 9805,
- "?????": 19622,
- "?????-": 25658,
- "?????-?????-": 31666,
- "????????": 35709,
- "?]": 26398,
- "?ãĢį": 42943,
- "@": 31,
- "@#": 41573,
- "@#&": 48193,
- "@@": 12404,
- "@@@@": 22675,
- "@@@@@@@@": 37991,
- "A": 32,
- "AA": 3838,
- "AAA": 29697,
- "AAAA": 17922,
- "AAAAAAAA": 43488,
- "AAF": 38540,
- "AB": 6242,
- "ABC": 24694,
- "ABLE": 17534,
- "AC": 2246,
- "ACA": 26576,
- "ACC": 26861,
- "ACE": 11598,
- "ACH": 16219,
- "ACK": 8120,
- "ACP": 33056,
- "ACT": 10659,
- "ACTED": 38542,
- "ACTION": 44710,
- "ACY": 43300,
- "AD": 2885,
- "ADA": 26853,
- "ADD": 29266,
- "ADE": 19266,
- "ADRA": 40517,
- "ADS": 47149,
- "ADVERTISEMENT": 19053,
- "AE": 14242,
- "AF": 8579,
- "AFP": 17449,
- "AFTA": 32106,
- "AG": 4760,
- "AGE": 11879,
- "AGES": 25552,
- "AH": 18429,
- "AI": 20185,
- "AIDS": 39338,
- "AIN": 29833,
- "AIR": 42149,
- "AK": 10206,
- "AKING": 43602,
- "AL": 1847,
- "ALD": 44071,
- "ALE": 21358,
- "ALK": 28082,
- "ALL": 7036,
- "ALLY": 19807,
- "ALS": 23333,
- "ALSE": 23719,
- "ALT": 31429,
- "ALTH": 40818,
- "AM": 2390,
- "AMA": 25087,
- "AMD": 28075,
- "AME": 10067,
- "AMES": 29559,
- "AMI": 43870,
- "AMP": 23518,
- "AMS": 40834,
- "AMY": 29428,
- "AN": 1565,
- "ANA": 31574,
- "ANC": 20940,
- "ANCE": 19240,
- "AND": 6981,
- "ANE": 30525,
- "ANG": 15567,
- "ANGE": 27746,
- "ANI": 43664,
- "ANK": 15154,
- "ANN": 22846,
- "ANS": 15037,
- "ANT": 8643,
- "ANY": 31827,
- "AP": 2969,
- "APD": 35349,
- "APE": 45721,
- "APH": 31300,
- "API": 17614,
- "APP": 24805,
- "APS": 44580,
- "APTER": 29485,
- "AR": 1503,
- "ARA": 24401,
- "ARB": 37304,
- "ARC": 25793,
- "ARCH": 31315,
- "ARD": 9795,
- "ARDIS": 49608,
- "ARDS": 48294,
- "ARE": 12203,
- "ARGET": 46095,
- "ARI": 33604,
- "ARK": 14175,
- "ARM": 33456,
- "ARP": 36035,
- "ARR": 26465,
- "ARS": 27415,
- "ART": 7227,
- "ARY": 13153,
- "AS": 1921,
- "ASC": 42643,
- "ASE": 11159,
- "ASED": 42827,
- "ASH": 11211,
- "ASHINGTON": 19436,
- "ASON": 36033,
- "ASS": 10705,
- "AST": 11262,
- "ASY": 26483,
- "AT": 1404,
- "ATA": 13563,
- "ATCH": 11417,
- "ATE": 6158,
- "ATED": 11617,
- "ATER": 23261,
- "ATES": 29462,
- "ATH": 12599,
- "ATHER": 45226,
- "ATING": 33881,
- "ATION": 6234,
- "ATIONAL": 29912,
- "ATIONS": 18421,
- "ATIVE": 37045,
- "ATOR": 25633,
- "ATS": 33586,
- "ATT": 17139,
- "ATTLE": 35455,
- "ATURE": 40086,
- "ATURES": 47471,
- "AU": 26830,
- "AUD": 48877,
- "AUT": 39371,
- "AV": 10116,
- "AW": 12298,
- "AX": 25922,
- "AY": 4792,
- "AZ": 22778,
- "Aaron": 34451,
- "Ab": 4826,
- "Ability": 22453,
- "About": 8585,
- "Above": 32397,
- "Abs": 24849,
- "Absolutely": 40501,
- "Abstract": 23839,
- "Abyss": 49073,
- "Ac": 12832,
- "Acc": 17320,
- "Accept": 38855,
- "Access": 15457,
- "Accessory": 41629,
- "According": 4821,
- "Account": 30116,
- "Acknowled": 39482,
- "Across": 40553,
- "Act": 6398,
- "Action": 12502,
- "ActionCode": 31573,
- "Activ": 25526,
- "Active": 13739,
- "Activity": 16516,
- "Actor": 40277,
- "Actually": 26417,
- "Ad": 2782,
- "Adam": 23159,
- "Adams": 47462,
- "Adapt": 48003,
- "Adapter": 47307,
- "Add": 4550,
- "Added": 13003,
- "Adding": 32901,
- "Additional": 17699,
- "Additionally": 23216,
- "Address": 20231,
- "Adds": 46245,
- "Adjust": 39668,
- "Admin": 46787,
- "Administ": 41862,
- "Adult": 42995,
- "Adv": 22856,
- "Advanced": 28809,
- "Adventure": 48289,
- "Advertisement": 4723,
- "Advertisements": 14592,
- "Af": 17584,
- "Afee": 44314,
- "Aff": 35191,
- "African": 43032,
- "After": 3260,
- "Ag": 10262,
- "Again": 15316,
- "Against": 39276,
- "Age": 23396,
- "Agent": 36772,
- "Agg": 46384,
- "Ah": 10910,
- "Aid": 44245,
- "Aim": 49945,
- "Air": 16170,
- "Ak": 33901,
- "Al": 2348,
- "Alabama": 49177,
- "Alan": 36235,
- "Albert": 42590,
- "Ale": 37474,
- "Alert": 36420,
- "Alex": 15309,
- "Alexander": 38708,
- "Ali": 37893,
- "Alias": 40489,
- "Alice": 44484,
- "Alien": 44501,
- "All": 3237,
- "Allah": 48620,
- "Allen": 39989,
- "Allow": 35265,
- "Allows": 34934,
- "Almost": 23379,
- "Along": 24035,
- "Alpha": 38077,
- "Already": 37447,
- "Alright": 31442,
- "Also": 7583,
- "Alt": 29161,
- "Altern": 23081,
- "Alternative": 49788,
- "Alternatively": 44163,
- "Although": 7003,
- "Always": 30374,
- "Am": 5840,
- "Amazing": 42770,
- "Amazon": 24888,
- "Amb": 35649,
- "Americ": 5477,
- "America": 18165,
- "American": 7437,
- "Americans": 17636,
- "Amid": 43541,
- "Among": 14311,
- "Amount": 31264,
- "Amy": 40797,
- "An": 2025,
- "Analy": 37702,
- "Analysis": 32750,
- "Ancient": 44974,
- "And": 1870,
- "Anderson": 42991,
- "Andre": 31258,
- "Andrew": 20508,
- "Android": 25934,
- "Andy": 35314,
- "Ang": 13450,
- "Angel": 33246,
- "Angelo": 45585,
- "Anim": 35320,
- "Animal": 40002,
- "Animation": 39520,
- "Ann": 18858,
- "Anna": 31160,
- "Anne": 43227,
- "Anonymous": 20660,
- "Another": 6610,
- "Answer": 33706,
- "Ant": 13217,
- "Anth": 30327,
- "Anthony": 32697,
- "Anti": 28795,
- "Any": 7149,
- "Anyone": 21129,
- "Anything": 40028,
- "Anyway": 23795,
- "Ap": 25189,
- "Apart": 39182,
- "App": 4677,
- "AppData": 22322,
- "Apparently": 30402,
- "Appearance": 48231,
- "Appearances": 47569,
- "Apple": 16108,
- "Applic": 33583,
- "Application": 23416,
- "Applications": 41995,
- "Apply": 44836,
- "Apps": 48433,
- "Apr": 13680,
- "April": 16784,
- "Ar": 3163,
- "Arab": 31602,
- "Arc": 24021,
- "Arcade": 43763,
- "Arch": 19895,
- "Are": 8491,
- "Area": 30547,
- "Aren": 43199,
- "Arg": 28100,
- "Args": 42035,
- "Ari": 26529,
- "Arizona": 40732,
- "Ark": 42007,
- "Arm": 26560,
- "Armor": 31512,
- "Army": 45272,
- "Around": 24472,
- "Array": 19182,
- "Arsenal": 46230,
- "Art": 8001,
- "Arthur": 29874,
- "Article": 14906,
- "Artist": 43020,
- "As": 1722,
- "Ash": 26754,
- "Asia": 38555,
- "Asian": 43224,
- "Aside": 32602,
- "Ask": 25214,
- "Asked": 18932,
- "Ass": 8021,
- "Assad": 23622,
- "Assembly": 49670,
- "Asset": 45869,
- "Assistant": 48902,
- "Associated": 29014,
- "Assuming": 48142,
- "Ast": 33751,
- "Async": 42367,
- "At": 2953,
- "Atl": 25255,
- "Atlanta": 43482,
- "Atlantic": 41120,
- "Att": 8086,
- "Attach": 33296,
- "Attack": 27732,
- "Attempt": 37177,
- "Attempts": 48452,
- "Attorney": 46319,
- "Attribute": 33682,
- "Attributes": 29021,
- "Aud": 16353,
- "Audio": 21206,
- "Aug": 12512,
- "August": 17908,
- "Aust": 15160,
- "Austin": 40245,
- "Austral": 19763,
- "Australia": 27429,
- "Australian": 38036,
- "Aut": 16541,
- "Auth": 30515,
- "Authent": 47649,
- "Author": 13838,
- "Authorities": 28705,
- "Auto": 27722,
- "Autom": 38062,
- "Av": 7355,
- "Availability": 29841,
- "Available": 10493,
- "Average": 26287,
- "Avg": 48997,
- "Avoid": 38618,
- "Aw": 23155,
- "Awesome": 49061,
- "Ax": 31554,
- "Ay": 42012,
- "Az": 26903,
- "B": 33,
- "BA": 4339,
- "BACK": 31098,
- "BALL": 45463,
- "BAT": 47379,
- "BB": 15199,
- "BBC": 33833,
- "BC": 2749,
- "BD": 14529,
- "BE": 12473,
- "BER": 13246,
- "BF": 29499,
- "BG": 40469,
- "BI": 3483,
- "BIL": 19676,
- "BILITIES": 49516,
- "BILITY": 25382,
- "BILL": 39888,
- "BIP": 47772,
- "BIT": 26094,
- "BL": 9148,
- "BLE": 19146,
- "BLIC": 32936,
- "BM": 12261,
- "BN": 15766,
- "BO": 8202,
- "BOOK": 39453,
- "BOX": 39758,
- "BP": 20866,
- "BR": 11473,
- "BRE": 40438,
- "BS": 4462,
- "BSD": 21800,
- "BT": 19313,
- "BTC": 35964,
- "BU": 19499,
- "BUG": 12953,
- "BUR": 38926,
- "BUS": 45346,
- "BUT": 47526,
- "BW": 48802,
- "BY": 17513,
- "Ba": 34458,
- "Baby": 36534,
- "Back": 7282,
- "Background": 21756,
- "Bad": 22069,
- "Bah": 47514,
- "Bal": 24597,
- "Balance": 45866,
- "Ball": 23410,
- "Balt": 41312,
- "Baltimore": 46139,
- "Ban": 30457,
- "Band": 31407,
- "Bang": 43984,
- "Bank": 28650,
- "Bar": 10374,
- "Barn": 47359,
- "Bas": 15522,
- "Base": 14881,
- "Based": 15001,
- "Basic": 26416,
- "Basically": 31524,
- "Bat": 24541,
- "Batman": 37039,
- "Battery": 47006,
- "Battle": 24064,
- "Bay": 15262,
- "Be": 3856,
- "Bear": 36352,
- "Beast": 41490,
- "Beat": 34979,
- "Beaut": 38413,
- "Bec": 39649,
- "Because": 8128,
- "Beck": 43454,
- "Bed": 45896,
- "Bee": 49512,
- "Beer": 49802,
- "Before": 8421,
- "Beg": 24586,
- "Begin": 44140,
- "Beginning": 45198,
- "Beh": 25267,
- "Behind": 34163,
- "Being": 18357,
- "Bel": 12193,
- "Bell": 36488,
- "Below": 21106,
- "Ben": 11696,
- "Bench": 44199,
- "Benef": 42166,
- "Benz": 42484,
- "Ber": 24814,
- "Bern": 23927,
- "Bernie": 33433,
- "Berry": 25215,
- "Besides": 23937,
- "Best": 13014,
- "Bet": 13056,
- "Beta": 43303,
- "Better": 28971,
- "Between": 25262,
- "Bey": 21993,
- "Beyond": 24102,
- "Bi": 23286,
- "Big": 12804,
- "Bill": 17798,
- "Billy": 49640,
- "Bind": 36180,
- "Bio": 42787,
- "Bir": 50091,
- "Bird": 42562,
- "Birth": 38480,
- "Bit": 13128,
- "Bitcoin": 22614,
- "Bl": 3629,
- "Black": 9915,
- "Blade": 47520,
- "Blake": 37849,
- "Ble": 43413,
- "Block": 12235,
- "Blocks": 45356,
- "Blog": 42383,
- "Blood": 21659,
- "Bloom": 38941,
- "Bloomberg": 47696,
- "Blu": 38676,
- "Blue": 14573,
- "Bo": 16635,
- "Board": 29828,
- "Bob": 18861,
- "Body": 25842,
- "Bomb": 48478,
- "Bon": 20682,
- "Bone": 49580,
- "Bonus": 29435,
- "Boo": 46120,
- "Book": 10482,
- "Books": 30650,
- "Boost": 45686,
- "Boot": 36476,
- "Border": 34189,
- "Born": 28524,
- "Boss": 37310,
- "Boston": 31710,
- "Bot": 20630,
- "Both": 10265,
- "Bott": 28653,
- "Bottom": 34104,
- "Bound": 49646,
- "Bow": 39961,
- "Box": 14253,
- "Boy": 26554,
- "Br": 9414,
- "Bra": 42333,
- "Brad": 30805,
- "Brain": 44687,
- "Brand": 38416,
- "Brandon": 45467,
- "Brave": 39787,
- "Brazil": 39190,
- "Bre": 12679,
- "Break": 31737,
- "Breaking": 29449,
- "Brend": 48015,
- "Brew": 44029,
- "Brexit": 40730,
- "Brian": 24761,
- "Bride": 47148,
- "Bridge": 37385,
- "Brien": 20118,
- "Bright": 41267,
- "Bring": 31416,
- "Brit": 17959,
- "Britain": 37114,
- "British": 25631,
- "Bro": 15783,
- "Broad": 30507,
- "Bron": 18760,
- "Brook": 45534,
- "Brother": 39461,
- "Brow": 32635,
- "Brown": 20644,
- "Browser": 46532,
- "Bruce": 38509,
- "Bs": 37000,
- "Bu": 38374,
- "Buff": 36474,
- "Buffer": 28632,
- "Bug": 25624,
- "Build": 15580,
- "Builder": 32875,
- "Building": 25954,
- "Built": 39582,
- "Bul": 33481,
- "Bull": 39549,
- "Bur": 22991,
- "Burn": 29053,
- "Bus": 16286,
- "Bush": 36113,
- "Business": 24749,
- "But": 1537,
- "Button": 21864,
- "Buy": 14518,
- "Buyable": 39693,
- "BuyableInstoreAndOnline": 40242,
- "Buzz": 48230,
- "By": 3886,
- "ById": 48364,
- "Byte": 40778,
- "Bytes": 45992,
- "C": 34,
- "CA": 8141,
- "CAN": 44565,
- "CAP": 33177,
- "CAR": 20034,
- "CAST": 44647,
- "CB": 23199,
- "CBC": 29208,
- "CBS": 22923,
- "CC": 4093,
- "CCC": 46361,
- "CD": 8610,
- "CDC": 47667,
- "CE": 5222,
- "CENT": 43960,
- "CEO": 46691,
- "CEPT": 42006,
- "CF": 22495,
- "CG": 39816,
- "CH": 3398,
- "CHA": 49285,
- "CHAPTER": 41481,
- "CHAR": 38019,
- "CHAT": 31542,
- "CHECK": 50084,
- "CHO": 44899,
- "CHQ": 47831,
- "CHR": 37846,
- "CI": 25690,
- "CIA": 49732,
- "CL": 5097,
- "CLA": 16827,
- "CLAIM": 48778,
- "CLASS": 31631,
- "CLASSIFIED": 45449,
- "CLE": 29931,
- "CLOSE": 32737,
- "CLUD": 39149,
- "CLUS": 28332,
- "CM": 24187,
- "CN": 44175,
- "CNN": 18474,
- "CO": 8220,
- "COL": 25154,
- "COLOR": 46786,
- "COM": 9858,
- "COMPLE": 41335,
- "CON": 10943,
- "CONCLUS": 47542,
- "CONT": 37815,
- "COR": 44879,
- "CP": 8697,
- "CPU": 36037,
- "CR": 9419,
- "CRE": 43387,
- "CRIP": 36584,
- "CRIPTION": 40165,
- "CS": 7902,
- "CSS": 49155,
- "CT": 4177,
- "CTV": 30428,
- "CU": 43633,
- "CV": 33538,
- "CVE": 31436,
- "CW": 43538,
- "Ca": 24334,
- "Cache": 30562,
- "Cal": 9771,
- "Calif": 19619,
- "California": 25284,
- "Call": 14134,
- "Callback": 47258,
- "Calling": 48593,
- "Cam": 21701,
- "Camera": 35632,
- "Camp": 21111,
- "Campaign": 46102,
- "Can": 6090,
- "Canada": 17940,
- "Canadian": 28203,
- "Cand": 41572,
- "Cap": 15610,
- "Capital": 39315,
- "Capt": 19209,
- "Captain": 27898,
- "Capture": 49630,
- "Car": 9914,
- "Card": 16962,
- "Care": 17784,
- "Carl": 26886,
- "Cart": 43476,
- "Carter": 49958,
- "Cas": 35155,
- "Case": 20448,
- "Cash": 35361,
- "Cass": 43529,
- "Cast": 19248,
- "Cat": 21979,
- "Catal": 39075,
- "Catalog": 49015,
- "Category": 27313,
- "Cath": 39581,
- "Catholic": 48919,
- "Cause": 42323,
- "Cele": 42741,
- "Cell": 28780,
- "Cent": 19085,
- "Center": 23656,
- "Central": 30645,
- "Cert": 37608,
- "Certain": 26469,
- "Certainly": 36001,
- "Ch": 1925,
- "Chain": 35491,
- "Chair": 43189,
- "Chall": 41812,
- "Champ": 48507,
- "Chan": 48407,
- "Chance": 43606,
- "Change": 19400,
- "Changed": 31813,
- "Changes": 29238,
- "Changing": 48333,
- "Channel": 29239,
- "Chapter": 14126,
- "Char": 12441,
- "Character": 27275,
- "Characters": 48393,
- "Charg": 28316,
- "Charge": 50044,
- "Charges": 36970,
- "Charl": 24453,
- "Charles": 28711,
- "Charlie": 37136,
- "Chart": 45488,
- "Chat": 30820,
- "Che": 7376,
- "Check": 9787,
- "Chel": 38292,
- "Chelsea": 41053,
- "Chem": 41829,
- "Chest": 45170,
- "Chicago": 25705,
- "Chicken": 45565,
- "Chief": 23675,
- "Child": 16424,
- "Children": 26829,
- "China": 14581,
- "Chinese": 23604,
- "Chip": 49985,
- "Cho": 22164,
- "Choice": 46770,
- "Choose": 31851,
- "Chris": 15645,
- "Christ": 10684,
- "Christian": 20298,
- "Christmas": 44614,
- "Christopher": 38025,
- "Chuck": 44324,
- "Church": 46686,
- "Circ": 31560,
- "City": 14941,
- "Civil": 32610,
- "Cl": 2601,
- "Cla": 47404,
- "Claim": 44819,
- "Clar": 48035,
- "Clark": 43250,
- "Class": 9487,
- "Classic": 39914,
- "Cle": 34349,
- "Clean": 32657,
- "Clear": 19856,
- "Clearly": 30638,
- "Click": 8164,
- "Client": 11792,
- "Climate": 37649,
- "Clinton": 16549,
- "Clock": 44758,
- "Close": 26125,
- "Closure": 45398,
- "Cloud": 18839,
- "Club": 42350,
- "Cmd": 40109,
- "Co": 7222,
- "Coach": 40677,
- "Cod": 43806,
- "Code": 10669,
- "Coin": 24387,
- "Col": 5216,
- "Cola": 28635,
- "Cold": 34312,
- "Cole": 46509,
- "Coll": 22667,
- "Collect": 31337,
- "Collection": 36307,
- "College": 38951,
- "Collins": 49645,
- "Color": 10258,
- "Colorado": 41330,
- "Columb": 36063,
- "Column": 39470,
- "Com": 5377,
- "Comb": 20575,
- "Combat": 38667,
- "Come": 16773,
- "Coming": 30804,
- "Comm": 6935,
- "Command": 21575,
- "Comment": 21357,
- "Comments": 23903,
- "Commerce": 47662,
- "Commercial": 48401,
- "Commission": 50246,
- "Common": 17227,
- "Commun": 30813,
- "Community": 20012,
- "Comp": 7293,
- "Compan": 41309,
- "Companies": 49111,
- "Company": 39154,
- "Compar": 50249,
- "Compare": 41488,
- "Compared": 44669,
- "Compat": 40073,
- "Compl": 38143,
- "Complete": 20988,
- "Completed": 43768,
- "Component": 21950,
- "Computer": 34556,
- "Con": 3103,
- "Conclusion": 21481,
- "Cond": 25559,
- "Condition": 48362,
- "Conf": 18546,
- "Config": 16934,
- "Configuration": 38149,
- "Cong": 18649,
- "Congratulations": 45048,
- "Congress": 25916,
- "Conn": 37321,
- "Connect": 13313,
- "Connection": 32048,
- "Connector": 34525,
- "Connell": 15559,
- "Connor": 27136,
- "Cons": 9444,
- "Conservative": 42039,
- "Consider": 19626,
- "Considering": 40475,
- "Console": 47581,
- "Const": 34184,
- "Construct": 42316,
- "Constructed": 25207,
- "Construction": 36687,
- "Consumer": 49106,
- "Cont": 4264,
- "Contact": 17829,
- "Container": 29869,
- "Content": 19746,
- "Contents": 15842,
- "Context": 21947,
- "Contin": 17875,
- "Continue": 29453,
- "Contract": 45845,
- "Contribut": 37146,
- "Control": 15988,
- "Controller": 22130,
- "Cook": 28937,
- "Cool": 34530,
- "Cooldown": 45953,
- "Cop": 13379,
- "Copy": 29881,
- "Copyright": 15269,
- "Cor": 10606,
- "Core": 14055,
- "Corn": 41389,
- "Corp": 45680,
- "Correct": 42779,
- "Correction": 43267,
- "Cos": 36734,
- "Cost": 13729,
- "Could": 23722,
- "Coun": 31053,
- "Council": 40940,
- "Count": 12332,
- "Counter": 31694,
- "Country": 33921,
- "Cour": 25877,
- "Course": 49046,
- "Court": 36699,
- "Courtesy": 31825,
- "Cover": 27245,
- "Cow": 40147,
- "Cr": 13916,
- "Cra": 33800,
- "Craft": 14467,
- "Craig": 40441,
- "Crash": 47598,
- "Cre": 12443,
- "Creat": 16719,
- "Create": 16447,
- "Created": 41972,
- "Creating": 32071,
- "Credit": 23690,
- "Credits": 42855,
- "Crew": 46724,
- "Crime": 45580,
- "Crit": 18559,
- "Critical": 41000,
- "Critics": 36623,
- "Cro": 35403,
- "Cross": 21544,
- "Cru": 27535,
- "Crunch": 49384,
- "Cruz": 41811,
- "Cry": 26677,
- "Crypt": 23919,
- "Crystal": 43752,
- "Cs": 32274,
- "Ct": 33707,
- "Ctrl": 40069,
- "Cu": 46141,
- "Cub": 43632,
- "Cube": 29071,
- "Cur": 26628,
- "Current": 11297,
- "Currently": 21327,
- "Custom": 15022,
- "Customer": 44939,
- "Cut": 26254,
- "Cy": 20418,
- "D": 35,
- "DA": 5631,
- "DAQ": 46640,
- "DATA": 26947,
- "DAY": 26442,
- "DB": 11012,
- "DC": 9697,
- "DCS": 49513,
- "DD": 16458,
- "DE": 7206,
- "DEBUG": 30531,
- "DEC": 41374,
- "DEF": 32988,
- "DEM": 39429,
- "DEN": 41819,
- "DEP": 46162,
- "DER": 14418,
- "DERR": 49643,
- "DES": 30910,
- "DEV": 39345,
- "DF": 8068,
- "DH": 41473,
- "DI": 17931,
- "DIR": 34720,
- "DIS": 26288,
- "DIT": 49828,
- "DIV": 33569,
- "DJ": 35028,
- "DK": 48510,
- "DL": 19260,
- "DM": 23127,
- "DN": 35504,
- "DNA": 28886,
- "DO": 18227,
- "DOC": 38715,
- "DOM": 39170,
- "DON": 41173,
- "DOS": 35178,
- "DOWN": 41925,
- "DP": 6322,
- "DR": 7707,
- "DS": 5258,
- "DT": 24544,
- "DVD": 39218,
- "DW": 42955,
- "DX": 36227,
- "Da": 26531,
- "Dad": 46270,
- "Daddy": 48280,
- "Daily": 28545,
- "Dallas": 40540,
- "Dam": 14550,
- "Damage": 22022,
- "Damn": 43343,
- "Dan": 21174,
- "Daniel": 19962,
- "Danny": 45478,
- "Dar": 32708,
- "Dark": 17367,
- "Dash": 43041,
- "Dat": 27354,
- "Data": 6601,
- "Database": 38105,
- "Date": 10430,
- "Dave": 27984,
- "David": 11006,
- "Davis": 36462,
- "Day": 12393,
- "Days": 38770,
- "Db": 43832,
- "De": 5005,
- "Dead": 20489,
- "Deal": 45776,
- "Dean": 36372,
- "Dear": 20266,
- "Death": 20148,
- "Deb": 16587,
- "Debug": 27509,
- "Dec": 10707,
- "December": 20588,
- "Decl": 37835,
- "Decre": 43198,
- "Deep": 29744,
- "Def": 7469,
- "Default": 19463,
- "Defense": 27300,
- "Definition": 36621,
- "Del": 13856,
- "Delete": 38727,
- "Delivery": 33129,
- "DeliveryDate": 39749,
- "Delta": 42430,
- "Dem": 11522,
- "Demand": 42782,
- "Democratic": 33939,
- "Democrats": 29969,
- "Demon": 35477,
- "Den": 21306,
- "Denver": 49818,
- "Dep": 12156,
- "Department": 36261,
- "Depending": 41156,
- "Deploy": 49322,
- "Depth": 48791,
- "Depths": 42382,
- "Der": 28532,
- "Des": 5960,
- "Desc": 24564,
- "Description": 11828,
- "Design": 23067,
- "Desk": 42523,
- "Desktop": 36881,
- "Despite": 8332,
- "Dest": 24159,
- "Destroy": 49174,
- "Det": 11242,
- "Detailed": 32080,
- "Details": 24259,
- "Detect": 47504,
- "Detroit": 40404,
- "Dev": 13603,
- "Develop": 19246,
- "Developer": 45351,
- "Development": 41206,
- "Device": 24728,
- "Dex": 48875,
- "Di": 18683,
- "Dial": 24400,
- "Dialog": 44204,
- "Dialogue": 41099,
- "Diamond": 47710,
- "Dick": 38743,
- "Did": 11633,
- "Die": 32423,
- "Diff": 28813,
- "Different": 40341,
- "Dig": 19511,
- "Digital": 27640,
- "Dim": 29271,
- "Dir": 35277,
- "Direct": 13470,
- "Director": 28702,
- "Directory": 43055,
- "Dis": 7279,
- "Disable": 48893,
- "Disc": 15642,
- "Disclaimer": 19618,
- "Discover": 44596,
- "Discuss": 48873,
- "Discussion": 34255,
- "Disk": 40961,
- "Disney": 37338,
- "Dispatch": 49354,
- "Display": 23114,
- "Dist": 20344,
- "Distance": 45767,
- "District": 44857,
- "Div": 24095,
- "Do": 5211,
- "DoS": 46498,
- "Doc": 23579,
- "Doctor": 37564,
- "Doctors": 47087,
- "Document": 24941,
- "Documents": 38354,
- "Does": 13921,
- "Dog": 32942,
- "Dom": 24510,
- "Domain": 43961,
- "Domin": 43417,
- "Don": 3987,
- "Donald": 7371,
- "DonaldTrump": 27674,
- "Done": 45677,
- "Donnell": 24853,
- "Dou": 40287,
- "Double": 25628,
- "Doug": 42297,
- "Down": 8048,
- "Download": 10002,
- "Downloadha": 41551,
- "Dr": 6187,
- "Draft": 37741,
- "Drag": 46022,
- "Dragon": 17808,
- "DragonMagazine": 42424,
- "Draw": 25302,
- "Dream": 30571,
- "Dri": 20564,
- "Drive": 24825,
- "Driver": 32103,
- "Dro": 35442,
- "Drop": 26932,
- "Drug": 37943,
- "Ds": 30832,
- "Du": 35660,
- "Dual": 36248,
- "Dub": 37590,
- "Due": 22229,
- "Dun": 30128,
- "Dur": 36927,
- "Duration": 26054,
- "During": 7191,
- "Dust": 43767,
- "Dutch": 49717,
- "Dynamic": 44090,
- "E": 36,
- "EA": 16412,
- "EAR": 17133,
- "EB": 30195,
- "EC": 2943,
- "ECA": 36600,
- "ECD": 27295,
- "ECH": 25994,
- "ECK": 25171,
- "ECT": 9782,
- "ECTION": 24565,
- "ED": 1961,
- "EDIT": 24706,
- "EE": 6500,
- "EED": 41841,
- "EEE": 31909,
- "EEEE": 35039,
- "EEK": 33823,
- "EEP": 35238,
- "EF": 25425,
- "EFF": 37267,
- "EG": 7156,
- "EGA": 33146,
- "EGIN": 43312,
- "EH": 42413,
- "EL": 3698,
- "ELD": 24639,
- "ELF": 37738,
- "ELL": 23304,
- "ELS": 37142,
- "ELY": 30943,
- "EM": 3620,
- "EMA": 27630,
- "EMBER": 28952,
- "EMENT": 12529,
- "EMOTE": 36862,
- "EMP": 39494,
- "EMS": 39201,
- "EN": 1677,
- "ENA": 45510,
- "ENC": 24181,
- "ENCE": 18310,
- "ENCY": 45155,
- "END": 10619,
- "ENDED": 49361,
- "ENE": 39267,
- "ENG": 26808,
- "ENGTH": 49494,
- "ENN": 34571,
- "ENS": 16938,
- "ENSE": 24290,
- "ENT": 3525,
- "ENTION": 45589,
- "ENTS": 15365,
- "EO": 4720,
- "EP": 8905,
- "EPA": 40906,
- "ER": 1137,
- "ERA": 46461,
- "ERAL": 27130,
- "ERC": 47691,
- "ERE": 9338,
- "ERG": 49837,
- "ERN": 28778,
- "ERO": 34812,
- "ERROR": 24908,
- "ERS": 4877,
- "ERSON": 29086,
- "ERT": 17395,
- "ERY": 19664,
- "ES": 1546,
- "ESA": 43279,
- "ESCO": 43311,
- "ESE": 33635,
- "ESH": 44011,
- "ESPN": 31730,
- "ESS": 7597,
- "ESSION": 47621,
- "EST": 6465,
- "EStream": 39906,
- "EStreamFrame": 43177,
- "ET": 2767,
- "ETA": 20892,
- "ETF": 22274,
- "ETH": 20702,
- "ETHOD": 36252,
- "ETS": 32716,
- "EU": 19684,
- "EV": 20114,
- "EVA": 27881,
- "EW": 6217,
- "EX": 6369,
- "EXP": 49864,
- "EXT": 13918,
- "EY": 22348,
- "Each": 10871,
- "Ear": 8419,
- "Earlier": 13689,
- "Early": 20457,
- "Earn": 49725,
- "Earth": 22840,
- "East": 25234,
- "Eastern": 46109,
- "Easy": 28406,
- "Eat": 47659,
- "Ec": 49136,
- "Econom": 28489,
- "Economic": 48307,
- "Ed": 7407,
- "Edge": 37021,
- "Edit": 18378,
- "Edited": 45882,
- "Editor": 17171,
- "Educ": 33380,
- "Education": 41183,
- "Edward": 43982,
- "Effect": 18610,
- "Effective": 44831,
- "Effects": 47738,
- "Egypt": 39299,
- "Eh": 43894,
- "Eight": 29571,
- "Either": 32478,
- "El": 9527,
- "Ele": 28827,
- "Elect": 19453,
- "Electric": 44132,
- "Element": 20180,
- "Elf": 46995,
- "Elizabeth": 43568,
- "Ell": 30639,
- "Els": 45507,
- "Elsa": 49050,
- "Else": 40674,
- "Elsewhere": 49374,
- "Em": 10161,
- "Email": 15333,
- "Emb": 31567,
- "Emer": 32779,
- "Emergency": 48979,
- "Emily": 48640,
- "Employ": 29733,
- "Empty": 40613,
- "En": 4834,
- "Enable": 36695,
- "Enabled": 20491,
- "Enc": 27195,
- "End": 12915,
- "Energy": 28925,
- "Eng": 7936,
- "Engine": 13798,
- "EngineDebug": 49781,
- "Engineers": 28620,
- "England": 39163,
- "English": 15823,
- "Enh": 35476,
- "Enhanced": 49026,
- "Enjoy": 27467,
- "Enlarge": 30952,
- "Enough": 47323,
- "Ent": 14539,
- "Enter": 17469,
- "Entity": 32398,
- "Entry": 30150,
- "Environment": 31441,
- "Environmental": 47213,
- "Ep": 13807,
- "Episode": 23758,
- "Equ": 23588,
- "Er": 9139,
- "Eric": 25004,
- "Error": 12331,
- "Es": 23041,
- "Esc": 47051,
- "Especially": 48464,
- "Ess": 29508,
- "Est": 22362,
- "Eth": 40226,
- "Euro": 14398,
- "Europe": 16112,
- "European": 22030,
- "Ev": 15200,
- "Eva": 44239,
- "Even": 6104,
- "Event": 9237,
- "Events": 37103,
- "Eventually": 28724,
- "Ever": 23921,
- "Every": 6109,
- "Everybody": 28172,
- "Everyone": 16190,
- "Everything": 19693,
- "Evidence": 46785,
- "Evil": 48477,
- "Ex": 3109,
- "Exactly": 47173,
- "Example": 16281,
- "Examples": 27730,
- "Exc": 40127,
- "Excellent": 45675,
- "Except": 30313,
- "Exception": 16922,
- "Exec": 23002,
- "Executive": 43885,
- "Exit": 30337,
- "Exp": 16870,
- "Exper": 20468,
- "Experience": 44901,
- "Experts": 38897,
- "Expl": 18438,
- "Explore": 35433,
- "Export": 43834,
- "Express": 38839,
- "Ext": 11627,
- "External": 41506,
- "Extra": 27726,
- "Extreme": 36716,
- "Ey": 36287,
- "Eye": 24876,
- "F": 37,
- "FA": 7708,
- "FACE": 49836,
- "FAQ": 42680,
- "FAULT": 38865,
- "FB": 26001,
- "FBI": 39379,
- "FC": 4851,
- "FD": 26009,
- "FE": 15112,
- "FER": 24302,
- "FF": 5777,
- "FFER": 45746,
- "FFFF": 29312,
- "FG": 30386,
- "FH": 44602,
- "FI": 11674,
- "FIELD": 44603,
- "FIG": 16254,
- "FIL": 46700,
- "FILE": 25664,
- "FIN": 20032,
- "FINE": 29940,
- "FINEST": 40236,
- "FIR": 39776,
- "FIX": 47084,
- "FK": 26236,
- "FL": 3697,
- "FLAG": 38948,
- "FM": 23264,
- "FML": 34708,
- "FN": 43221,
- "FO": 6080,
- "FOR": 13775,
- "FORE": 30818,
- "FORM": 21389,
- "FORMATION": 35036,
- "FOX": 47853,
- "FP": 5837,
- "FR": 10913,
- "FREE": 39274,
- "FS": 10652,
- "FT": 9792,
- "FTWARE": 37485,
- "FU": 38989,
- "FUL": 46476,
- "FUN": 42296,
- "FW": 24160,
- "FX": 17213,
- "FY": 43833,
- "Fa": 50110,
- "Fab": 43957,
- "Fac": 47522,
- "Face": 32388,
- "Facebook": 12025,
- "Fact": 29054,
- "Factor": 41384,
- "Factory": 22810,
- "FactoryReloaded": 37631,
- "Fail": 39044,
- "Failure": 50015,
- "Fair": 30099,
- "Faith": 45536,
- "Fake": 49233,
- "Fal": 41129,
- "Fall": 24750,
- "False": 25101,
- "Family": 24094,
- "Fan": 22480,
- "Fans": 36570,
- "Far": 21428,
- "Farm": 48412,
- "Fast": 22968,
- "Fat": 33804,
- "Father": 34823,
- "Favorite": 49434,
- "Fax": 46512,
- "Fe": 14304,
- "Fear": 37798,
- "Feature": 38816,
- "Featured": 37948,
- "Features": 23595,
- "Feb": 15146,
- "February": 21816,
- "Fed": 42268,
- "Federal": 24099,
- "Feed": 18332,
- "Feel": 35114,
- "Fel": 42493,
- "Female": 27273,
- "Fer": 43362,
- "Fest": 45139,
- "Few": 32351,
- "Fi": 10547,
- "Field": 15878,
- "Fif": 44403,
- "Fig": 14989,
- "Fight": 27365,
- "Fighting": 46375,
- "Figure": 11337,
- "Fil": 11928,
- "File": 8979,
- "Filename": 35063,
- "Files": 25876,
- "Fill": 33762,
- "Film": 39750,
- "Filter": 22417,
- "Fin": 18467,
- "Final": 19006,
- "Finally": 11158,
- "Financial": 43621,
- "Find": 16742,
- "Finding": 36276,
- "Fine": 34389,
- "Finish": 48658,
- "Fire": 13543,
- "First": 5962,
- "Firstly": 49709,
- "Fish": 39428,
- "Fit": 31805,
- "Five": 20029,
- "Fix": 22743,
- "Fixed": 13715,
- "Fl": 7414,
- "Fla": 47487,
- "Flag": 34227,
- "Flags": 40053,
- "Flash": 30670,
- "Fle": 47669,
- "Flickr": 47250,
- "Flight": 43069,
- "Flo": 33574,
- "Float": 43879,
- "Flor": 26953,
- "Florida": 31135,
- "Flow": 37535,
- "Fly": 33771,
- "Flying": 49095,
- "Focus": 34888,
- "Folder": 41092,
- "Follow": 7155,
- "Following": 14291,
- "Font": 23252,
- "FontSize": 38160,
- "Food": 24602,
- "Foot": 17574,
- "Football": 37316,
- "Footnote": 33795,
- "For": 1890,
- "Force": 10292,
- "Ford": 37308,
- "Fore": 16351,
- "Foreign": 33616,
- "Forest": 34605,
- "Forge": 19857,
- "ForgeModLoader": 24934,
- "Form": 8479,
- "Format": 26227,
- "Former": 14282,
- "Fort": 21926,
- "Fortunately": 31276,
- "Forward": 39746,
- "Found": 21077,
- "Four": 15137,
- "Fourth": 45530,
- "Fox": 19399,
- "Fr": 6732,
- "Fra": 49562,
- "Frag": 42974,
- "Fram": 21055,
- "Frame": 19778,
- "Frames": 35439,
- "Frameworks": 42026,
- "Fran": 38848,
- "Franc": 42885,
- "France": 28572,
- "Frank": 17439,
- "Fre": 20366,
- "Fred": 30847,
- "Free": 11146,
- "Freedom": 38885,
- "French": 24111,
- "Fresh": 35857,
- "Fri": 30214,
- "Friday": 20610,
- "Friend": 23331,
- "Friends": 36705,
- "From": 4863,
- "Front": 25886,
- "Fs": 42388,
- "Fu": 41133,
- "Fuck": 34094,
- "Fuel": 42663,
- "Full": 13295,
- "Fun": 24629,
- "Function": 22203,
- "Fund": 24553,
- "Further": 13518,
- "Furthermore": 24951,
- "Future": 29783,
- "G": 38,
- "GA": 9273,
- "GAME": 47109,
- "GAN": 45028,
- "GB": 4579,
- "GBT": 9146,
- "GC": 15916,
- "GD": 45113,
- "GE": 8264,
- "GEN": 35353,
- "GER": 30373,
- "GES": 48075,
- "GET": 18851,
- "GF": 21713,
- "GG": 11190,
- "GGGG": 25611,
- "GGGGGGGG": 40415,
- "GH": 17511,
- "GHz": 23741,
- "GI": 18878,
- "GL": 8763,
- "GM": 15548,
- "GMT": 49424,
- "GN": 16630,
- "GO": 11230,
- "GOP": 44962,
- "GP": 16960,
- "GPU": 33346,
- "GR": 10761,
- "GRE": 28934,
- "GREEN": 43016,
- "GROUND": 46025,
- "GROUP": 46846,
- "GS": 14313,
- "GT": 19555,
- "GU": 38022,
- "GUI": 40156,
- "GV": 37094,
- "GW": 33191,
- "GY": 31212,
- "Ga": 35389,
- "Gab": 46079,
- "Gal": 26552,
- "Gall": 37122,
- "Gallery": 29352,
- "Gam": 34777,
- "Game": 8777,
- "Gameplay": 43241,
- "Gamer": 33648,
- "Games": 24474,
- "Gaming": 45509,
- "Gar": 27676,
- "Gary": 33820,
- "Gas": 39699,
- "Gate": 22628,
- "Gay": 41787,
- "Gaza": 48790,
- "Gb": 49017,
- "Ge": 10082,
- "Gear": 38141,
- "Gen": 13746,
- "Gender": 41394,
- "Gene": 39358,
- "Gener": 8645,
- "General": 12218,
- "Generally": 37058,
- "Generic": 46189,
- "Georg": 33428,
- "George": 20191,
- "Georgia": 41072,
- "Ger": 38069,
- "German": 16010,
- "Germany": 27079,
- "Get": 3855,
- "Getting": 20570,
- "Getty": 6633,
- "Gh": 41126,
- "Ghost": 32001,
- "Gi": 33704,
- "Gil": 40747,
- "Girl": 24151,
- "Girls": 41044,
- "Give": 23318,
- "Given": 15056,
- "Giving": 49701,
- "Gl": 9861,
- "Glass": 47698,
- "Global": 22289,
- "Go": 5247,
- "Goal": 49045,
- "God": 13482,
- "Going": 27404,
- "Gold": 13306,
- "GoldMagikarp": 42202,
- "Golden": 32378,
- "Good": 10248,
- "Google": 11708,
- "Gordon": 47073,
- "Got": 30074,
- "Gov": 23774,
- "Govern": 29168,
- "Government": 28848,
- "Gr": 8642,
- "Gra": 46971,
- "Grab": 48400,
- "Grad": 42731,
- "Grade": 42233,
- "Graham": 45821,
- "Grand": 23581,
- "Grant": 45431,
- "Graph": 37065,
- "Graphics": 18172,
- "Gray": 46130,
- "Gre": 43887,
- "Great": 13681,
- "Greek": 44059,
- "Green": 13719,
- "Greg": 25025,
- "Grey": 49141,
- "Grid": 41339,
- "Gro": 42921,
- "Ground": 35539,
- "Group": 13247,
- "Growing": 43964,
- "Gs": 33884,
- "Gu": 8205,
- "Guard": 24502,
- "Guest": 42481,
- "Guide": 47889,
- "Gun": 22993,
- "Guy": 31080,
- "Gy": 44802,
- "H": 39,
- "HA": 7801,
- "HAEL": 47452,
- "HAHA": 21271,
- "HAHAHAHA": 39021,
- "HAM": 33363,
- "HB": 32886,
- "HC": 16045,
- "HCR": 43230,
- "HD": 10227,
- "HE": 13909,
- "HEAD": 37682,
- "HER": 16879,
- "HF": 29567,
- "HH": 16768,
- "HHHH": 41100,
- "HI": 25374,
- "HK": 38730,
- "HL": 6581,
- "HM": 36905,
- "HO": 32298,
- "HOME": 39069,
- "HOU": 46685,
- "HOW": 37181,
- "HP": 14082,
- "HQ": 41275,
- "HR": 17184,
- "HS": 7998,
- "HT": 6535,
- "HTML": 28656,
- "HTTP": 40717,
- "HUD": 28410,
- "HY": 42598,
- "Ha": 23303,
- "Hack": 32833,
- "Had": 25383,
- "Hal": 40202,
- "Half": 31305,
- "Hall": 34194,
- "Ham": 21281,
- "Hamilton": 45405,
- "Han": 29919,
- "Hand": 12885,
- "Handle": 37508,
- "Handler": 25060,
- "Happy": 25082,
- "Har": 13587,
- "Hard": 17309,
- "Hardware": 49865,
- "Harris": 41589,
- "Harry": 18308,
- "Hart": 44719,
- "Has": 19242,
- "Hash": 26257,
- "Hat": 40483,
- "Haunted": 46979,
- "Have": 11980,
- "Having": 14698,
- "Haw": 33055,
- "Hay": 31306,
- "He": 1544,
- "Head": 13847,
- "Header": 39681,
- "Health": 18081,
- "Heart": 28541,
- "Heat": 39596,
- "Heavy": 33210,
- "Height": 23106,
- "Hel": 12621,
- "Hell": 28254,
- "Hello": 15496,
- "Help": 22087,
- "Helper": 47429,
- "Hen": 26055,
- "Henry": 32476,
- "Her": 9360,
- "Here": 4342,
- "Herm": 48523,
- "Hero": 30411,
- "Hey": 10814,
- "Hi": 17250,
- "Hidden": 41691,
- "Hide": 38518,
- "Hig": 36124,
- "High": 11922,
- "Higher": 48708,
- "Hill": 36369,
- "Hillary": 20397,
- "His": 6653,
- "Hispanic": 43830,
- "Hist": 13749,
- "History": 18122,
- "Hit": 17889,
- "Hmm": 44217,
- "Ho": 28900,
- "Hol": 28115,
- "Hold": 26807,
- "Holy": 33336,
- "Hom": 28718,
- "Home": 16060,
- "Hon": 29478,
- "Honest": 37411,
- "Honestly": 40817,
- "Hong": 48559,
- "Hop": 23483,
- "Hope": 34456,
- "Hopefully": 32365,
- "Hor": 27991,
- "Host": 17932,
- "Hot": 21352,
- "Hour": 43223,
- "Hours": 39792,
- "House": 18102,
- "Houston": 33387,
- "How": 2437,
- "Howard": 32434,
- "However": 4864,
- "Http": 43481,
- "Hu": 38202,
- "Hub": 16066,
- "Hug": 48098,
- "Huh": 46010,
- "Hum": 32661,
- "Human": 20490,
- "Hun": 25117,
- "Hundreds": 38150,
- "Hung": 39505,
- "Hunt": 47663,
- "Hunter": 38803,
- "Hur": 42633,
- "Hy": 21217,
- "Hyd": 40436,
- "Hyp": 49926,
- "Hyper": 38197,
- "Hz": 7399,
- "I": 40,
- "IA": 3539,
- "IAL": 12576,
- "IAN": 16868,
- "IAS": 43429,
- "IB": 9865,
- "IBLE": 34563,
- "IC": 2149,
- "ICA": 25241,
- "ICAL": 20151,
- "ICAN": 42710,
- "ICE": 8476,
- "ICES": 34444,
- "ICH": 20739,
- "ICK": 11860,
- "ICLE": 31419,
- "ICO": 22707,
- "ICS": 19505,
- "ICT": 18379,
- "ID": 2389,
- "IDA": 41957,
- "IDE": 14114,
- "IDENT": 25256,
- "IDER": 41237,
- "IDES": 42538,
- "IDS": 14255,
- "IDs": 47954,
- "IE": 10008,
- "IED": 19767,
- "IELD": 49979,
- "IENCE": 42589,
- "IENT": 28495,
- "IER": 38311,
- "IES": 11015,
- "IF": 5064,
- "IFA": 19071,
- "IFE": 29150,
- "IFF": 29267,
- "IFIC": 30643,
- "IFIED": 28343,
- "IFT": 32297,
- "IG": 3528,
- "IGH": 18060,
- "IGHT": 9947,
- "IGHTS": 34874,
- "IGN": 16284,
- "II": 3978,
- "III": 10855,
- "IJ": 23852,
- "IK": 18694,
- "IL": 4146,
- "ILA": 47164,
- "ILD": 26761,
- "ILE": 41119,
- "ILL": 8267,
- "ILLE": 33844,
- "ILS": 45484,
- "ILY": 33340,
- "IM": 3955,
- "IME": 12789,
- "IN": 1268,
- "INA": 28893,
- "INAL": 17961,
- "INC": 30158,
- "IND": 12115,
- "INE": 8881,
- "INESS": 44180,
- "INFO": 10778,
- "ING": 2751,
- "INGS": 20754,
- "INGTON": 17480,
- "INK": 17248,
- "INO": 46016,
- "INS": 20913,
- "INST": 38604,
- "INT": 12394,
- "INTER": 41358,
- "INTON": 46812,
- "IO": 9399,
- "ION": 2849,
- "IONS": 11053,
- "IOR": 41254,
- "IP": 4061,
- "IPP": 31444,
- "IPS": 47643,
- "IQ": 33866,
- "IR": 4663,
- "IRC": 49060,
- "IRD": 46833,
- "IRE": 41736,
- "IRED": 37819,
- "IRO": 43708,
- "IRT": 48771,
- "IS": 1797,
- "ISA": 22312,
- "ISC": 37719,
- "ISE": 24352,
- "ISH": 18422,
- "ISION": 42446,
- "ISIS": 29322,
- "ISM": 31125,
- "ISO": 40734,
- "ISON": 39960,
- "ISS": 16744,
- "ISSION": 40373,
- "IST": 8808,
- "ISTER": 41517,
- "ISTORY": 42480,
- "IT": 2043,
- "ITAL": 40579,
- "ITCH": 31949,
- "ITE": 12709,
- "ITED": 22061,
- "ITH": 10554,
- "ITIES": 30383,
- "ITION": 17941,
- "ITNESS": 46144,
- "ITS": 29722,
- "ITT": 22470,
- "ITY": 9050,
- "IU": 44958,
- "IUM": 41796,
- "IV": 3824,
- "IVE": 9306,
- "IVER": 38757,
- "IVERS": 30194,
- "IVES": 42472,
- "IX": 10426,
- "IZ": 14887,
- "IZE": 35400,
- "Ian": 37776,
- "Ice": 23709,
- "Icon": 19578,
- "Id": 7390,
- "Ide": 41452,
- "Ident": 33234,
- "If": 1532,
- "Ign": 32916,
- "Il": 33666,
- "Ill": 21478,
- "Im": 3546,
- "Image": 5159,
- "Images": 29398,
- "Imagine": 25153,
- "Imm": 24675,
- "Imp": 26950,
- "Impl": 29710,
- "Import": 20939,
- "Important": 33796,
- "Impro": 23028,
- "Improve": 47531,
- "Improved": 35453,
- "In": 818,
- "Inc": 25517,
- "Includes": 42986,
- "Incre": 15562,
- "Increase": 46890,
- "Increased": 40281,
- "Increases": 28544,
- "Ind": 5497,
- "Indeed": 17854,
- "Independent": 40566,
- "Index": 15732,
- "India": 21569,
- "Indian": 30821,
- "Indiana": 49153,
- "Individual": 35392,
- "Indust": 35848,
- "Inf": 18943,
- "Info": 12360,
- "Information": 21918,
- "Ing": 27682,
- "Ingredients": 41222,
- "Init": 31768,
- "Initial": 24243,
- "Initialized": 28500,
- "Initially": 40443,
- "Input": 20560,
- "Ins": 20376,
- "Insert": 44402,
- "Inside": 24441,
- "Insp": 41502,
- "Inst": 6310,
- "Install": 15798,
- "Installation": 30838,
- "Instance": 33384,
- "Instant": 49933,
- "Instead": 13193,
- "InstoreAndOnline": 40241,
- "Instruct": 43993,
- "Int": 5317,
- "Integ": 34500,
- "Integer": 46541,
- "Intel": 24123,
- "Inter": 9492,
- "Interest": 19302,
- "Interested": 48860,
- "Interestingly": 33092,
- "Interface": 39317,
- "Intern": 15865,
- "Internal": 37693,
- "International": 24274,
- "Internet": 28566,
- "Interstitial": 29447,
- "Interview": 39945,
- "Introdu": 15005,
- "Introduced": 37143,
- "Introduction": 21906,
- "Inv": 19904,
- "Invalid": 44651,
- "Invest": 19070,
- "Investigators": 33528,
- "Iowa": 45186,
- "Ir": 23820,
- "Iran": 23798,
- "Iraq": 31206,
- "Ire": 48505,
- "Ireland": 49752,
- "Irish": 43293,
- "Iron": 22797,
- "Ironically": 44850,
- "Is": 3792,
- "Isa": 39443,
- "Islam": 16991,
- "Islamic": 26723,
- "Isn": 41451,
- "Israel": 14040,
- "Israeli": 29818,
- "Iss": 27738,
- "Issue": 45147,
- "It": 1026,
- "Italian": 45696,
- "Italy": 45001,
- "Item": 7449,
- "ItemImage": 25502,
- "ItemThumbnailImage": 39177,
- "ItemTracker": 47198,
- "Items": 23022,
- "Iter": 29993,
- "Iterator": 37787,
- "Its": 20459,
- "Iv": 45766,
- "J": 41,
- "JA": 37048,
- "JB": 47858,
- "JC": 34382,
- "JD": 37882,
- "JECT": 23680,
- "JJ": 32178,
- "JM": 50229,
- "JO": 45006,
- "JOHN": 47118,
- "JP": 12889,
- "JR": 44817,
- "JS": 20120,
- "JSON": 40386,
- "JUST": 25008,
- "JV": 41697,
- "Ja": 33186,
- "Jac": 28821,
- "Jack": 14295,
- "Jackson": 31270,
- "Jacob": 46751,
- "Jake": 43930,
- "Jam": 30380,
- "James": 14731,
- "Jamie": 48337,
- "Jan": 12128,
- "Jane": 41083,
- "January": 21339,
- "Japan": 16504,
- "Japanese": 25324,
- "Jar": 47511,
- "Jason": 26497,
- "Java": 29584,
- "Jay": 30568,
- "Je": 40932,
- "Jean": 38248,
- "Jeff": 19139,
- "Jen": 44875,
- "Jenn": 35994,
- "Jennifer": 43187,
- "Jer": 36134,
- "Jere": 31579,
- "Jeremy": 35623,
- "Jerry": 43462,
- "Jes": 22290,
- "Jess": 34648,
- "Jessica": 45572,
- "Jesus": 28219,
- "Jet": 42273,
- "Jew": 23119,
- "Jewish": 28240,
- "Jews": 47415,
- "Jim": 18050,
- "Jimmy": 40335,
- "Jo": 9908,
- "Job": 33308,
- "Joe": 19585,
- "John": 7554,
- "Johnny": 44960,
- "Johnson": 25378,
- "Join": 18234,
- "Joined": 24363,
- "Jon": 18219,
- "Jonathan": 30365,
- "Jones": 25784,
- "Jordan": 34522,
- "Jose": 23409,
- "Joseph": 29458,
- "Josh": 23808,
- "Joshua": 47740,
- "Journal": 25296,
- "Joy": 41338,
- "Jr": 50123,
- "Js": 49044,
- "Ju": 33018,
- "Jud": 26141,
- "Judge": 29511,
- "Jul": 16980,
- "July": 16157,
- "Jump": 36046,
- "Jun": 22396,
- "June": 15749,
- "Just": 5703,
- "Justice": 28447,
- "Justin": 33229,
- "K": 42,
- "KA": 25123,
- "KB": 22764,
- "KC": 36222,
- "KE": 7336,
- "KEN": 43959,
- "KER": 42839,
- "KEY": 20373,
- "KI": 37845,
- "KING": 37286,
- "KK": 16601,
- "KN": 29132,
- "KNOWN": 44706,
- "KO": 22328,
- "KR": 30758,
- "KS": 27015,
- "KT": 42176,
- "KY": 31159,
- "Ka": 37281,
- "Kal": 41428,
- "Kansas": 43451,
- "Kar": 37753,
- "Karl": 46063,
- "Kat": 25881,
- "Kate": 45087,
- "Kay": 37247,
- "Ke": 8896,
- "Keefe": 48122,
- "Keep": 15597,
- "Keeping": 44815,
- "Keith": 46868,
- "Kelly": 34831,
- "Ken": 27827,
- "Kenn": 39324,
- "Kent": 42265,
- "Kevin": 23865,
- "Key": 9218,
- "Keys": 40729,
- "Kh": 33155,
- "Kick": 45390,
- "Kid": 48374,
- "Kids": 40229,
- "Kill": 27100,
- "Kim": 26374,
- "Kin": 49681,
- "Kind": 35854,
- "King": 15708,
- "Kings": 42912,
- "Kit": 20827,
- "Kn": 25095,
- "Knight": 44242,
- "Know": 23812,
- "Knowing": 45648,
- "Known": 29870,
- "Ko": 48735,
- "Krist": 40756,
- "Ku": 41733,
- "Ky": 30630,
- "Kyle": 42516,
- "L": 43,
- "LA": 13534,
- "LAB": 48780,
- "LAN": 25697,
- "LAND": 28182,
- "LB": 30501,
- "LC": 5639,
- "LCS": 29814,
- "LD": 11163,
- "LE": 2538,
- "LEASE": 22781,
- "LECT": 16779,
- "LED": 30465,
- "LER": 39878,
- "LES": 28378,
- "LESS": 48481,
- "LET": 28882,
- "LEY": 25173,
- "LG": 41257,
- "LGBT": 37701,
- "LI": 31271,
- "LIB": 40347,
- "LIN": 34509,
- "LINE": 24027,
- "LIST": 45849,
- "LL": 3069,
- "LLOW": 44765,
- "LM": 31288,
- "LO": 21982,
- "LOAD": 35613,
- "LOC": 29701,
- "LOCK": 36840,
- "LOD": 38543,
- "LOG": 25294,
- "LOS": 45376,
- "LP": 19930,
- "LR": 35972,
- "LS": 6561,
- "LT": 27734,
- "LU": 41596,
- "LV": 30976,
- "LY": 11319,
- "La": 14772,
- "Lab": 17822,
- "Label": 33986,
- "Labor": 42230,
- "Labour": 32475,
- "Lady": 38887,
- "Lago": 48694,
- "Lair": 40041,
- "Lake": 43035,
- "Land": 22342,
- "Language": 32065,
- "Large": 21968,
- "Larry": 42918,
- "Las": 46898,
- "Last": 5956,
- "Lastly": 37511,
- "Lat": 24220,
- "Late": 26302,
- "Later": 18602,
- "Latest": 39478,
- "Latin": 49022,
- "Laughs": 34610,
- "Laun": 46182,
- "Launch": 38296,
- "Laura": 43687,
- "Law": 16966,
- "Lay": 23763,
- "Layer": 49925,
- "Layout": 32517,
- "Le": 3123,
- "Lead": 20451,
- "Leader": 45009,
- "League": 24623,
- "Leaks": 17874,
- "Lean": 35806,
- "Lear": 14961,
- "Learn": 20238,
- "Learning": 41730,
- "Leary": 48487,
- "Leave": 35087,
- "Led": 42416,
- "Lee": 24338,
- "Left": 18819,
- "Leg": 11484,
- "Legal": 38263,
- "Legend": 21351,
- "Legendary": 24524,
- "Len": 30659,
- "Length": 24539,
- "Lenin": 49036,
- "Lens": 49479,
- "Leod": 44559,
- "Leon": 36185,
- "Les": 35882,
- "Less": 22058,
- "Let": 5756,
- "Letter": 45708,
- "Lev": 32163,
- "Level": 4971,
- "Lew": 33450,
- "Lewis": 40330,
- "Lex": 45117,
- "Li": 32304,
- "Lib": 25835,
- "Liber": 31199,
- "Liberal": 46650,
- "Library": 23377,
- "Lic": 26656,
- "License": 34156,
- "Lie": 47918,
- "Life": 14662,
- "Light": 15047,
- "Like": 7594,
- "Likewise": 45872,
- "Lim": 19352,
- "Limit": 39184,
- "Limited": 37214,
- "Lin": 14993,
- "Lind": 43410,
- "Line": 13949,
- "Link": 11280,
- "LinkedIn": 40574,
- "Links": 31815,
- "Linux": 19314,
- "Liquid": 41966,
- "Lisa": 44203,
- "List": 8053,
- "Listen": 23061,
- "Listener": 33252,
- "Liter": 43460,
- "Little": 22253,
- "Live": 18947,
- "Liverpool": 44232,
- "Living": 36376,
- "Lo": 27654,
- "Load": 8912,
- "Loader": 17401,
- "Loading": 19031,
- "Loc": 33711,
- "Local": 14565,
- "Located": 43525,
- "Location": 14749,
- "Lock": 25392,
- "Log": 11187,
- "Login": 47790,
- "London": 23421,
- "Long": 14617,
- "Look": 8567,
- "Looking": 15784,
- "Looks": 41102,
- "Loop": 39516,
- "Lord": 22438,
- "Los": 28903,
- "Lost": 31042,
- "Lot": 48601,
- "Lots": 43643,
- "Lou": 24016,
- "Louis": 32117,
- "Love": 18565,
- "Low": 20535,
- "Lower": 31426,
- "Lt": 49578,
- "Lu": 25596,
- "Lua": 36127,
- "Luc": 22946,
- "Luck": 35498,
- "Luckily": 42332,
- "Luke": 30730,
- "Lv": 29507,
- "Ly": 31633,
- "Lyn": 37207,
- "M": 44,
- "MA": 5673,
- "MAC": 44721,
- "MAG": 45820,
- "MAL": 42126,
- "MAN": 10725,
- "MAP": 33767,
- "MAR": 40569,
- "MAS": 31180,
- "MAT": 41636,
- "MAX": 22921,
- "MB": 10744,
- "MC": 9655,
- "MD": 12740,
- "ME": 11682,
- "MED": 30733,
- "MEN": 49275,
- "MENT": 10979,
- "MENTS": 28957,
- "MER": 29296,
- "MET": 47123,
- "METHOD": 49273,
- "MF": 49800,
- "MG": 20474,
- "MH": 36208,
- "MHz": 25983,
- "MI": 8895,
- "MIC": 49884,
- "MIN": 23678,
- "MIT": 36393,
- "MJ": 43421,
- "MK": 33907,
- "ML": 5805,
- "MM": 12038,
- "MN": 39764,
- "MO": 11770,
- "MOD": 33365,
- "MODE": 49058,
- "MON": 27857,
- "MORE": 23346,
- "MP": 7378,
- "MQ": 49215,
- "MR": 13599,
- "MRI": 40952,
- "MS": 5653,
- "MSN": 30295,
- "MT": 13752,
- "MU": 42422,
- "MW": 14326,
- "MX": 43243,
- "MY": 26708,
- "Ma": 21467,
- "Mac": 14155,
- "Mach": 49999,
- "Machine": 37573,
- "Mad": 18454,
- "Made": 24616,
- "Madison": 46845,
- "Mag": 13436,
- "Magazine": 36028,
- "Magic": 22975,
- "Magikarp": 41538,
- "Magn": 48017,
- "Mah": 40936,
- "Mail": 25804,
- "Main": 13383,
- "Major": 24206,
- "Make": 12050,
- "Maker": 48890,
- "Making": 23874,
- "Mal": 15029,
- "Male": 25486,
- "Malley": 33776,
- "Man": 5124,
- "Management": 48032,
- "Manager": 13511,
- "Manchester": 40744,
- "Mand": 49846,
- "Mania": 45844,
- "Manufact": 44445,
- "Many": 7085,
- "Map": 13912,
- "Maps": 47010,
- "Mar": 7676,
- "Marc": 22697,
- "March": 16192,
- "Marco": 37179,
- "Marcus": 35110,
- "Marg": 24428,
- "Marginal": 36003,
- "Maria": 46827,
- "Marie": 44507,
- "Mario": 42315,
- "Mark": 9704,
- "Market": 27470,
- "Mars": 43725,
- "Marsh": 41984,
- "Mart": 13143,
- "Martin": 24778,
- "Marvel": 38864,
- "Marx": 45258,
- "Mary": 24119,
- "Mas": 38224,
- "Mask": 45195,
- "Mass": 20273,
- "Master": 18254,
- "Mat": 19044,
- "Match": 23850,
- "Material": 17518,
- "Materials": 41657,
- "Math": 37372,
- "Matrix": 46912,
- "Matt": 13448,
- "Matthew": 25372,
- "Max": 11518,
- "Maximum": 40541,
- "May": 6747,
- "Maybe": 13300,
- "Mayor": 37396,
- "Mbps": 47842,
- "Mc": 9742,
- "McC": 30464,
- "Me": 5308,
- "Meanwhile": 10294,
- "Measure": 47384,
- "Meat": 35620,
- "Mech": 28452,
- "Med": 9921,
- "Media": 13152,
- "Medic": 39112,
- "Medical": 37158,
- "Medium": 31205,
- "Meet": 29318,
- "Meg": 42672,
- "Mega": 43471,
- "Mel": 21102,
- "Mem": 13579,
- "Member": 27608,
- "Members": 25341,
- "Memory": 30871,
- "Men": 10418,
- "Menu": 23381,
- "Mer": 13102,
- "Merc": 42981,
- "Merit": 21583,
- "Mesh": 37031,
- "Mess": 36479,
- "Message": 12837,
- "Met": 9171,
- "Meta": 48526,
- "Metal": 36790,
- "Method": 17410,
- "Methods": 46202,
- "Metro": 45141,
- "Mex": 24670,
- "Mexico": 33006,
- "Mi": 41541,
- "Miami": 41191,
- "Mic": 25437,
- "Mich": 11180,
- "Michael": 13256,
- "Michelle": 48736,
- "Michigan": 40610,
- "Micro": 13031,
- "Microsoft": 15905,
- "Mid": 22622,
- "Middle": 34621,
- "Mike": 16073,
- "Mil": 24857,
- "Military": 37837,
- "Mill": 22603,
- "Miller": 33253,
- "Min": 9452,
- "Mind": 28478,
- "Mine": 24461,
- "Minecraft": 39194,
- "Mini": 39234,
- "Minimum": 44046,
- "Minnesota": 45670,
- "Minor": 39825,
- "Mir": 27453,
- "Mis": 31281,
- "Miss": 17140,
- "Missing": 43730,
- "Mission": 37057,
- "Mist": 49370,
- "Mit": 43339,
- "Mix": 35608,
- "Mo": 16632,
- "Mob": 44702,
- "Mobil": 47100,
- "Mobile": 17066,
- "Mod": 5841,
- "ModLoader": 24847,
- "Mode": 19076,
- "Model": 17633,
- "Modern": 31439,
- "Mods": 24239,
- "Module": 26796,
- "Moh": 38443,
- "Mom": 29252,
- "Mon": 9069,
- "Monday": 23810,
- "Money": 26788,
- "Monitor": 35479,
- "Monster": 40872,
- "Mont": 26031,
- "Month": 31948,
- "Moon": 31640,
- "Moore": 40049,
- "Mor": 20044,
- "More": 5167,
- "Moreover": 24606,
- "Morgan": 47184,
- "Morning": 42997,
- "Mos": 32668,
- "Moscow": 49757,
- "Most": 6943,
- "Mot": 47733,
- "Mother": 31398,
- "Motion": 45740,
- "Motor": 34919,
- "Mount": 35452,
- "Mouse": 39643,
- "Move": 21774,
- "Movie": 25097,
- "Moving": 33622,
- "Mp": 28861,
- "MpServer": 31765,
- "Mr": 5246,
- "Mrs": 27034,
- "Ms": 10128,
- "Msg": 50108,
- "Mu": 33239,
- "Much": 20045,
- "Mult": 15205,
- "Multi": 29800,
- "Multiple": 31217,
- "Mur": 23830,
- "Murray": 49998,
- "Mus": 10694,
- "Music": 22648,
- "Muslim": 17067,
- "Muslims": 36452,
- "Must": 34320,
- "Mut": 41603,
- "My": 3666,
- "Myth": 41444,
- "N": 45,
- "NA": 4535,
- "NAME": 20608,
- "NAS": 18293,
- "NASA": 29998,
- "NAT": 34259,
- "NB": 32819,
- "NBA": 32470,
- "NBC": 13175,
- "NC": 7792,
- "ND": 8575,
- "NE": 12161,
- "NECT": 48842,
- "NER": 21479,
- "NES": 37379,
- "NESS": 31097,
- "NET": 12884,
- "NEW": 13965,
- "NEWS": 49597,
- "NEY": 36231,
- "NF": 21870,
- "NFL": 32078,
- "NG": 10503,
- "NH": 33863,
- "NI": 22125,
- "NING": 15871,
- "NJ": 41074,
- "NK": 46888,
- "NL": 32572,
- "NM": 32755,
- "NN": 6144,
- "NO": 15285,
- "NOR": 35510,
- "NOT": 11929,
- "NOTE": 16580,
- "NOW": 45669,
- "NP": 22182,
- "NPR": 38588,
- "NR": 24723,
- "NRS": 41256,
- "NS": 8035,
- "NSA": 47549,
- "NT": 11251,
- "NULL": 33991,
- "NUM": 41359,
- "NV": 27159,
- "NVIDIA": 38021,
- "NW": 27605,
- "NY": 12805,
- "NYSE": 49430,
- "NZ": 37371,
- "Na": 26705,
- "Name": 5376,
- "Names": 36690,
- "Nap": 49799,
- "Nar": 40059,
- "Narr": 45750,
- "Nat": 47849,
- "Nation": 46108,
- "National": 16186,
- "Native": 31272,
- "Natural": 35364,
- "Naturally": 44213,
- "Nature": 46934,
- "Nav": 30575,
- "Naz": 37235,
- "Nazi": 31343,
- "Nazis": 44527,
- "Ne": 8199,
- "Neal": 40581,
- "Near": 40640,
- "Nearly": 27927,
- "Need": 23037,
- "Neg": 32863,
- "Neigh": 46445,
- "Neil": 29354,
- "Neill": 26538,
- "Neither": 27270,
- "Net": 7934,
- "NetMessage": 25193,
- "Netflix": 42826,
- "Network": 26245,
- "Nev": 43555,
- "Never": 12295,
- "Nevertheless": 29011,
- "New": 3791,
- "News": 9980,
- "Newsletter": 33031,
- "Next": 10019,
- "Ni": 34153,
- "Nic": 30403,
- "Nice": 35284,
- "Nich": 46489,
- "Nick": 23609,
- "Night": 24732,
- "Nik": 40979,
- "Nin": 36091,
- "Nine": 37603,
- "Nintendo": 32348,
- "Nit": 33772,
- "Nitrome": 42066,
- "No": 2949,
- "Nob": 21191,
- "Nobody": 24795,
- "Node": 19667,
- "Non": 15419,
- "None": 14202,
- "Nonetheless": 43258,
- "Nor": 21991,
- "Norm": 35393,
- "Normal": 26447,
- "Normally": 43625,
- "North": 14157,
- "Northern": 40495,
- "Not": 3673,
- "Notable": 45533,
- "Note": 6425,
- "Notes": 16130,
- "Nothing": 18465,
- "Notice": 26396,
- "Nov": 20795,
- "November": 21159,
- "Now": 3844,
- "Ns": 47503,
- "Null": 35067,
- "Num": 33111,
- "Number": 15057,
- "Numbers": 49601,
- "Nusra": 39294,
- "Nut": 49004,
- "O": 46,
- "OA": 23621,
- "OAD": 41048,
- "OB": 9864,
- "OC": 4503,
- "OCK": 11290,
- "OD": 3727,
- "ODE": 16820,
- "ODUCT": 28644,
- "ODY": 33076,
- "OE": 27799,
- "OF": 19238,
- "OFF": 27977,
- "OG": 7730,
- "OGR": 49656,
- "OH": 12096,
- "OHN": 27600,
- "OIL": 49713,
- "OK": 11380,
- "OL": 3535,
- "OLD": 15173,
- "OLOG": 33462,
- "OLOGY": 43781,
- "OM": 2662,
- "OME": 13649,
- "ON": 1340,
- "OND": 18672,
- "ONDON": 47383,
- "ONE": 11651,
- "ONES": 39677,
- "ONEY": 48399,
- "ONG": 18494,
- "ONS": 19213,
- "ONSORED": 36406,
- "ONT": 35830,
- "ONY": 40508,
- "OO": 6684,
- "OOD": 22808,
- "OOK": 15308,
- "OOL": 31559,
- "OOOO": 23803,
- "OOOOOOOO": 47732,
- "OP": 3185,
- "OPA": 43345,
- "OPE": 32135,
- "OPER": 31054,
- "OPLE": 34354,
- "OPS": 30737,
- "OR": 1581,
- "ORD": 12532,
- "ORE": 6965,
- "ORED": 32023,
- "ORGE": 49697,
- "ORK": 14670,
- "ORN": 30649,
- "ORPG": 49665,
- "ORS": 20673,
- "ORT": 9863,
- "ORTS": 33002,
- "ORY": 15513,
- "OS": 2640,
- "OSE": 14058,
- "OSED": 48751,
- "OSH": 45704,
- "OSP": 47053,
- "OSS": 18420,
- "OST": 10892,
- "OT": 2394,
- "OTA": 29009,
- "OTAL": 27510,
- "OTE": 23051,
- "OTH": 26946,
- "OTHER": 31858,
- "OTO": 26631,
- "OTOS": 33291,
- "OTS": 33472,
- "OTT": 29089,
- "OTUS": 39205,
- "OU": 2606,
- "OUGH": 32632,
- "OULD": 24010,
- "OUN": 19385,
- "OUND": 15919,
- "OUNT": 28270,
- "OUP": 27755,
- "OUR": 11698,
- "OURCE": 31033,
- "OUS": 20958,
- "OUT": 12425,
- "OV": 8874,
- "OVA": 41576,
- "OVER": 41983,
- "OW": 3913,
- "OWER": 36048,
- "OWN": 14165,
- "OWS": 22845,
- "OX": 48632,
- "OY": 21414,
- "Oak": 42426,
- "Ob": 5944,
- "Obama": 15948,
- "Obj": 49201,
- "Object": 10267,
- "Obs": 31310,
- "Obviously": 20670,
- "Occ": 29223,
- "Occup": 47658,
- "Ocean": 46607,
- "Oct": 12349,
- "October": 18517,
- "Of": 5189,
- "Off": 9362,
- "Offic": 12710,
- "Office": 27743,
- "Officers": 34059,
- "Official": 28529,
- "Officials": 25883,
- "Offline": 28657,
- "Offset": 34519,
- "Often": 37288,
- "Oh": 5812,
- "Ohio": 31274,
- "Oil": 44142,
- "Ok": 18690,
- "Okay": 16454,
- "Ol": 30098,
- "Old": 19620,
- "On": 2202,
- "Once": 7454,
- "One": 3198,
- "Online": 14439,
- "Only": 10049,
- "Ont": 45984,
- "Op": 18257,
- "Open": 11505,
- "Opening": 43093,
- "Oper": 18843,
- "Operation": 32180,
- "Opp": 27524,
- "Ops": 41472,
- "Opt": 27871,
- "Option": 19722,
- "Optional": 30719,
- "Options": 29046,
- "Or": 5574,
- "Oracle": 48625,
- "Orange": 40141,
- "Ord": 35422,
- "Order": 18743,
- "Orderable": 39655,
- "Ore": 41543,
- "Oregon": 41243,
- "Org": 46808,
- "Organ": 26121,
- "Orig": 11610,
- "Origin": 39688,
- "Original": 20556,
- "Originally": 22731,
- "Os": 16748,
- "Other": 6395,
- "Others": 25599,
- "Otherwise": 48059,
- "Ott": 49092,
- "Our": 5122,
- "Out": 7975,
- "Output": 26410,
- "Outside": 30815,
- "Over": 5886,
- "Overall": 16350,
- "Override": 37961,
- "Overview": 29064,
- "Own": 23858,
- "Owner": 42419,
- "Ox": 38208,
- "P": 47,
- "PA": 4537,
- "PAC": 44938,
- "PAR": 27082,
- "PART": 30709,
- "PASS": 47924,
- "PATH": 34219,
- "PB": 49079,
- "PC": 5662,
- "PD": 5760,
- "PDATE": 14341,
- "PDATED": 49316,
- "PDF": 20456,
- "PE": 11401,
- "PER": 18973,
- "PET": 47731,
- "PF": 42668,
- "PG": 6968,
- "PH": 11909,
- "PHOTOS": 42709,
- "PI": 11901,
- "PIN": 44032,
- "PK": 40492,
- "PL": 6489,
- "PLA": 45710,
- "PLAY": 31519,
- "PLE": 16437,
- "PLIC": 31484,
- "PLIED": 49094,
- "PM": 5868,
- "PN": 13137,
- "PO": 16402,
- "POL": 45472,
- "POR": 44680,
- "PORT": 15490,
- "POS": 37997,
- "POSE": 48933,
- "POST": 32782,
- "PP": 10246,
- "PR": 4805,
- "PRE": 46437,
- "PRES": 48296,
- "PRESS": 32761,
- "PRO": 31190,
- "PROV": 41283,
- "PS": 3705,
- "PT": 11571,
- "PU": 5105,
- "PUT": 30076,
- "Pa": 28875,
- "Pac": 18844,
- "Pacific": 22933,
- "Pack": 11869,
- "Package": 27813,
- "Pad": 26114,
- "Page": 9876,
- "Pages": 47798,
- "Pain": 38490,
- "Pak": 29675,
- "Pakistan": 38485,
- "Pal": 11531,
- "Palest": 32570,
- "Palestinian": 35969,
- "Pan": 15730,
- "Pand": 47206,
- "Panel": 26639,
- "Paper": 42950,
- "Par": 10044,
- "Param": 22973,
- "Parameter": 36301,
- "Parameters": 48944,
- "Parent": 24546,
- "Parents": 42969,
- "Paris": 40313,
- "Park": 25478,
- "Parser": 46677,
- "Part": 7841,
- "Particip": 34363,
- "Parts": 42670,
- "Party": 33553,
- "Pass": 14478,
- "Password": 35215,
- "Past": 34533,
- "Pat": 12130,
- "Patch": 33952,
- "Path": 15235,
- "Patrick": 32718,
- "Pattern": 47546,
- "Paul": 12041,
- "Pause": 49991,
- "Pay": 19197,
- "Pe": 6435,
- "Peace": 43445,
- "Pear": 46262,
- "Ped": 43468,
- "Pen": 25553,
- "Penn": 39899,
- "People": 8061,
- "Per": 5990,
- "Percent": 31905,
- "Perfect": 36635,
- "Performance": 32273,
- "Perhaps": 13710,
- "Pers": 30946,
- "Person": 15439,
- "Personal": 30228,
- "Personally": 42322,
- "Pet": 25803,
- "Peter": 19727,
- "Pg": 31743,
- "Ph": 2725,
- "Phase": 35645,
- "Phil": 18673,
- "Philadelphia": 42349,
- "Philipp": 49680,
- "Phill": 41970,
- "Phoenix": 36422,
- "Phone": 6132,
- "Phones": 32212,
- "Phot": 27248,
- "Photo": 6191,
- "Photos": 21197,
- "Phys": 43215,
- "Physical": 31611,
- "Pi": 38729,
- "Pic": 39507,
- "Pick": 31686,
- "Pict": 21300,
- "Picture": 28070,
- "Pie": 48223,
- "Pierre": 36910,
- "Pin": 28348,
- "Ping": 49806,
- "Pink": 41912,
- "Pinterest": 35767,
- "Pir": 46772,
- "Pitt": 47627,
- "Pixel": 40809,
- "Pl": 3646,
- "Place": 27271,
- "Plan": 20854,
- "Planet": 41801,
- "Platform": 37148,
- "Play": 11002,
- "Player": 14140,
- "Players": 24860,
- "Playing": 36530,
- "Please": 5492,
- "Plex": 46383,
- "Plot": 43328,
- "Plug": 23257,
- "Plugin": 37233,
- "Plus": 17860,
- "Po": 18833,
- "Pocket": 45454,
- "Pod": 41565,
- "Point": 12727,
- "Points": 40710,
- "Pokemon": 48034,
- "Poké": 41386,
- "Pokémon": 46602,
- "Pol": 8017,
- "Police": 9039,
- "Policy": 36727,
- "Polit": 39866,
- "Political": 35443,
- "Politics": 43921,
- "Poll": 39176,
- "Poly": 34220,
- "Pont": 48039,
- "Pool": 27201,
- "Poor": 43920,
- "Pop": 16979,
- "Pope": 46172,
- "Population": 45251,
- "Port": 13924,
- "Portland": 45330,
- "Pos": 21604,
- "Position": 26545,
- "Post": 6307,
- "Posted": 14231,
- "Posts": 21496,
- "Pot": 25396,
- "Power": 13434,
- "Pr": 6836,
- "Pract": 49515,
- "Pre": 6719,
- "Pred": 39156,
- "Pref": 36698,
- "Prem": 24914,
- "Premium": 36787,
- "Prep": 37534,
- "Pres": 25460,
- "Present": 34695,
- "President": 10364,
- "Press": 13800,
- "Pretty": 35700,
- "Prev": 36854,
- "Preview": 48835,
- "Previous": 21448,
- "Previously": 36837,
- "Pri": 34487,
- "Price": 18124,
- "Prim": 23828,
- "Primary": 35170,
- "Prime": 26405,
- "Prin": 47231,
- "Princ": 42904,
- "Prince": 35784,
- "Print": 18557,
- "Prior": 22442,
- "Priv": 20184,
- "Privacy": 48948,
- "Private": 29067,
- "Pro": 2964,
- "Probably": 34784,
- "Problem": 40781,
- "Process": 18709,
- "Produ": 11547,
- "Product": 15667,
- "Production": 35027,
- "Products": 48650,
- "Prof": 15404,
- "Professional": 49138,
- "Professor": 25031,
- "Profile": 37046,
- "Program": 15167,
- "Progress": 32577,
- "Project": 16775,
- "Prom": 24129,
- "Proof": 44683,
- "Prop": 24331,
- "Property": 21746,
- "Pros": 35726,
- "Prosecut": 34301,
- "Prosecutors": 39401,
- "Prot": 19703,
- "Protect": 41426,
- "Prov": 15946,
- "Provider": 29495,
- "Proxy": 44148,
- "Ps": 12016,
- "Psy": 25918,
- "PsyNetMessage": 28666,
- "Psych": 31923,
- "Ptr": 46745,
- "Pub": 14876,
- "Public": 15202,
- "Published": 24492,
- "Publisher": 46471,
- "Pull": 42940,
- "Pur": 30026,
- "Purchase": 47651,
- "Pure": 49548,
- "Push": 49222,
- "Put": 11588,
- "Putin": 17060,
- "Putting": 46399,
- "Py": 20519,
- "Python": 37906,
- "Q": 48,
- "QB": 40291,
- "QL": 9711,
- "QU": 10917,
- "QUEST": 35780,
- "QUI": 43702,
- "QUIRE": 49128,
- "Qaeda": 19058,
- "Qaida": 41225,
- "Qu": 4507,
- "Qual": 46181,
- "Quality": 35013,
- "Quant": 24915,
- "Quantity": 31208,
- "Que": 15681,
- "Queen": 32466,
- "Query": 20746,
- "Quest": 12166,
- "Question": 24361,
- "Questions": 35741,
- "Queue": 34991,
- "Quick": 21063,
- "Quite": 44959,
- "Quote": 25178,
- "Quotes": 23138,
- "R": 49,
- "RA": 3861,
- "RAFT": 44700,
- "RAG": 33202,
- "RAL": 35296,
- "RAM": 24115,
- "RANT": 32506,
- "RAW": 20530,
- "RAY": 30631,
- "RB": 27912,
- "RC": 7397,
- "RD": 35257,
- "RE": 2200,
- "READ": 15675,
- "REAM": 32235,
- "REC": 38827,
- "RECT": 23988,
- "RED": 22083,
- "REDACTED": 45999,
- "REE": 11587,
- "REF": 31688,
- "REG": 31553,
- "REL": 16448,
- "RELATED": 20112,
- "REM": 40726,
- "REP": 35316,
- "RES": 19535,
- "RESULTS": 46274,
- "RET": 26087,
- "RF": 32754,
- "RFC": 41150,
- "RG": 48192,
- "RGB": 36982,
- "RH": 48587,
- "RI": 7112,
- "RIC": 41132,
- "RIP": 32618,
- "RIPT": 46023,
- "RL": 7836,
- "RM": 29138,
- "RN": 42336,
- "RNA": 27204,
- "RO": 13252,
- "ROM": 33676,
- "RON": 45806,
- "ROR": 16411,
- "RP": 20031,
- "RPG": 46954,
- "RR": 21095,
- "RS": 6998,
- "RT": 14181,
- "RW": 46747,
- "RY": 18276,
- "Ra": 21762,
- "Race": 35157,
- "Rachel": 44045,
- "Rad": 15546,
- "Radio": 26093,
- "Rah": 47135,
- "Raid": 49043,
- "Rail": 44631,
- "Rain": 31443,
- "Ram": 33754,
- "Rand": 38918,
- "Random": 29531,
- "Range": 17257,
- "Rank": 27520,
- "Ranked": 36713,
- "Rap": 35230,
- "Rare": 26737,
- "Rat": 29665,
- "Rate": 32184,
- "Rated": 15322,
- "Rather": 27202,
- "Rating": 29321,
- "Raven": 49098,
- "Raw": 27369,
- "Ray": 19591,
- "Re": 3041,
- "Read": 5569,
- "Reader": 33634,
- "Reading": 36120,
- "Ready": 35474,
- "Real": 15633,
- "Really": 26392,
- "Reason": 45008,
- "Reb": 28951,
- "Rec": 6690,
- "Recent": 26446,
- "Recently": 24661,
- "Recipe": 37523,
- "Recomm": 24898,
- "Recommend": 41248,
- "Recommended": 36171,
- "Record": 23739,
- "Rect": 45474,
- "Red": 7738,
- "Redd": 32259,
- "Reddit": 22367,
- "Redditor": 34832,
- "Ref": 8134,
- "Refer": 46238,
- "Reference": 26687,
- "References": 19927,
- "Reg": 8081,
- "Regarding": 39424,
- "Regardless": 27894,
- "Region": 47371,
- "Register": 38804,
- "Registered": 47473,
- "Registration": 47133,
- "Regular": 40164,
- "Reilly": 25819,
- "Rel": 6892,
- "Related": 9819,
- "Relations": 47117,
- "Release": 26362,
- "Released": 45037,
- "Reloaded": 36726,
- "Rem": 8413,
- "Remember": 16676,
- "Remote": 36510,
- "Remove": 27914,
- "Removed": 45975,
- "Ren": 26764,
- "Render": 45819,
- "Rep": 6207,
- "Repe": 47541,
- "Repeat": 40322,
- "Repl": 39232,
- "Reply": 36875,
- "Report": 19100,
- "Reporting": 42159,
- "Reports": 37844,
- "Represent": 40171,
- "Republic": 15431,
- "Republican": 25777,
- "Republicans": 28455,
- "Requ": 16844,
- "Request": 18453,
- "Required": 37374,
- "Requirements": 42249,
- "Requires": 39618,
- "Res": 4965,
- "Research": 25104,
- "Researchers": 25606,
- "Residents": 42347,
- "Resource": 26198,
- "Resources": 33236,
- "Resp": 19309,
- "Response": 31077,
- "Rest": 19452,
- "Result": 23004,
- "Results": 25468,
- "Ret": 9781,
- "Return": 13615,
- "Returns": 35561,
- "Reuters": 12637,
- "Rev": 18009,
- "Review": 14832,
- "Reviewed": 40266,
- "Reviewer": 35407,
- "Revolution": 50237,
- "Rew": 30003,
- "Reward": 48123,
- "Rex": 47389,
- "Rh": 38576,
- "Rich": 14868,
- "Richard": 22245,
- "Rick": 33048,
- "Right": 11028,
- "Ring": 39687,
- "River": 42204,
- "Ro": 15450,
- "Road": 29197,
- "Roaming": 27352,
- "Rob": 14350,
- "Rober": 15924,
- "Robert": 19156,
- "Roberts": 45487,
- "Robin": 40656,
- "Rock": 19665,
- "Rocket": 50218,
- "Rod": 27917,
- "Rog": 30417,
- "Roger": 43719,
- "Rogue": 48163,
- "Role": 47445,
- "Roll": 26869,
- "Rom": 22834,
- "Roman": 32454,
- "Romney": 42184,
- "Ron": 23672,
- "Room": 41178,
- "Root": 30016,
- "Ros": 35740,
- "Rose": 31087,
- "Ross": 38328,
- "Rot": 24864,
- "Round": 22685,
- "Route": 43401,
- "Row": 25166,
- "Roy": 32027,
- "Royal": 41861,
- "Rs": 31273,
- "Ru": 40464,
- "Rub": 21312,
- "Ruby": 32101,
- "Rule": 31929,
- "Rules": 37766,
- "Rum": 47127,
- "Run": 10987,
- "Runner": 49493,
- "Running": 28768,
- "Runtime": 41006,
- "Rus": 35313,
- "Rush": 49942,
- "Russ": 10020,
- "Russell": 46325,
- "Russia": 16347,
- "Russian": 16220,
- "Rust": 49444,
- "Ry": 46987,
- "Ryan": 21868,
- "S": 50,
- "SA": 4090,
- "SAM": 49302,
- "SAN": 36753,
- "SAY": 27358,
- "SB": 16811,
- "SC": 6173,
- "SCP": 48956,
- "SD": 10305,
- "SE": 5188,
- "SEA": 46887,
- "SEC": 23683,
- "SEE": 36078,
- "SELECT": 46506,
- "SER": 35009,
- "SET": 28480,
- "SF": 20802,
- "SG": 38475,
- "SH": 9693,
- "SHA": 37596,
- "SHARE": 42597,
- "SHIP": 49423,
- "SI": 11584,
- "SIGN": 46224,
- "SIM": 48913,
- "SIZE": 33489,
- "SK": 18831,
- "SL": 8634,
- "SM": 12310,
- "SN": 15571,
- "SO": 15821,
- "SON": 11782,
- "SOURCE": 47690,
- "SP": 4303,
- "SPA": 50087,
- "SPEC": 48451,
- "SPONSORED": 37190,
- "SQL": 17861,
- "SR": 12562,
- "SS": 5432,
- "SSL": 31127,
- "ST": 2257,
- "STAR": 46678,
- "STAT": 35744,
- "STATE": 44724,
- "STD": 32147,
- "STDOUT": 36886,
- "STE": 30516,
- "STEM": 25361,
- "STEP": 42135,
- "STER": 41809,
- "STON": 41924,
- "STR": 18601,
- "STRUCT": 46126,
- "SU": 12564,
- "SUP": 40331,
- "SW": 17887,
- "SY": 23060,
- "Sa": 33890,
- "Sab": 39646,
- "Sac": 38318,
- "Sad": 26699,
- "Sadly": 36725,
- "Safe": 31511,
- "Safety": 45372,
- "Sah": 32194,
- "Saharan": 40461,
- "Said": 47638,
- "Saint": 48615,
- "Sal": 19221,
- "Sales": 44490,
- "Salt": 43061,
- "Sam": 16305,
- "Same": 30556,
- "Sample": 36674,
- "Samsung": 32334,
- "San": 15017,
- "Sand": 18471,
- "Sanders": 26747,
- "Santa": 42694,
- "Sarah": 29284,
- "Sat": 20245,
- "Saturday": 19844,
- "Saudi": 36939,
- "Sav": 47362,
- "Save": 16928,
- "Sax": 41152,
- "Say": 25515,
- "Sc": 3351,
- "Scale": 29990,
- "Scan": 33351,
- "Scar": 44433,
- "Scene": 36542,
- "Sch": 14874,
- "Sche": 27054,
- "School": 26130,
- "Science": 26959,
- "Scient": 23010,
- "Scientists": 29193,
- "Scope": 43642,
- "Score": 26595,
- "Scot": 37559,
- "Scotland": 47230,
- "Scott": 19040,
- "Screen": 23901,
- "Screenshot": 34204,
- "Script": 7391,
- "Scroll": 29261,
- "Se": 4653,
- "Sea": 37567,
- "Sean": 26408,
- "Search": 18243,
- "Season": 18960,
- "Seattle": 34007,
- "Sec": 6558,
- "Second": 12211,
- "Secondly": 44276,
- "Secret": 23725,
- "Secretary": 38541,
- "Section": 16375,
- "Secure": 49793,
- "Security": 24074,
- "See": 6214,
- "Seeing": 36314,
- "Seg": 41030,
- "Sel": 48767,
- "Select": 17563,
- "Self": 24704,
- "Sem": 13900,
- "Semitic": 28753,
- "Semitism": 25406,
- "Sen": 10445,
- "Senate": 32998,
- "Senator": 29774,
- "Send": 25206,
- "Senior": 31224,
- "Sense": 41166,
- "Sensor": 47864,
- "Sent": 31837,
- "Sep": 19117,
- "Sept": 14635,
- "September": 17543,
- "Sequ": 44015,
- "Ser": 7089,
- "Serial": 32634,
- "Series": 27996,
- "Seriously": 42338,
- "Serv": 11838,
- "Server": 10697,
- "Service": 16177,
- "Services": 31007,
- "Session": 36044,
- "Set": 7248,
- "Setting": 34149,
- "Settings": 26232,
- "Setup": 40786,
- "Seven": 31334,
- "Several": 14945,
- "Sex": 23398,
- "Sexual": 49161,
- "Sh": 2484,
- "Shadow": 27447,
- "Sham": 43478,
- "Shape": 33383,
- "Shar": 40201,
- "Share": 11649,
- "Shares": 43566,
- "Sharp": 44336,
- "She": 3347,
- "Shell": 23248,
- "Sher": 28782,
- "Shield": 33651,
- "Shift": 33377,
- "Shin": 44592,
- "Ship": 25586,
- "Shipping": 45169,
- "Shock": 31646,
- "Shop": 29917,
- "Short": 16438,
- "Shortly": 30513,
- "Shot": 28512,
- "Should": 19926,
- "Show": 15307,
- "Shut": 39079,
- "Si": 42801,
- "Side": 24819,
- "Sign": 11712,
- "Sil": 15086,
- "Silver": 26766,
- "Sim": 8890,
- "Similar": 18925,
- "Similarly": 28039,
- "Simon": 35475,
- "Simple": 26437,
- "Simply": 35596,
- "Sin": 46200,
- "Since": 6385,
- "Sing": 29974,
- "Single": 28008,
- "Sir": 22788,
- "Sit": 46655,
- "Site": 29123,
- "Six": 21447,
- "Size": 10699,
- "Sk": 15739,
- "Skill": 35040,
- "Skin": 42455,
- "Skip": 50232,
- "Sky": 22308,
- "Sl": 11122,
- "Sleep": 40555,
- "Slot": 38963,
- "Slow": 36423,
- "Sm": 7556,
- "Small": 18712,
- "Smart": 25610,
- "Smith": 17919,
- "Sn": 16501,
- "Snake": 49795,
- "Snap": 43826,
- "Snow": 28974,
- "So": 2396,
- "Soc": 37949,
- "Social": 20636,
- "Socket": 39105,
- "Soft": 18380,
- "Software": 25423,
- "Sol": 36949,
- "Solar": 38825,
- "Sold": 33873,
- "Solid": 46933,
- "Solution": 46344,
- "Some": 4366,
- "Someone": 28211,
- "Something": 22210,
- "Sometimes": 15468,
- "Son": 31056,
- "Song": 44241,
- "Sony": 32895,
- "Soon": 28093,
- "Sorry": 14385,
- "Sort": 42758,
- "Soul": 36315,
- "Sound": 21369,
- "Sounds": 40825,
- "Source": 7416,
- "SourceFile": 37226,
- "Sources": 21188,
- "South": 14942,
- "Southern": 44993,
- "Sov": 38574,
- "Soviet": 40408,
- "Sp": 4561,
- "Space": 14106,
- "SpaceEngineers": 31032,
- "Spain": 45355,
- "Spanish": 43584,
- "Spawn": 49855,
- "Spe": 5248,
- "Speaking": 13887,
- "Spec": 22882,
- "Special": 13409,
- "Specific": 32419,
- "Specifically": 48379,
- "Spect": 49738,
- "Speed": 22785,
- "Spell": 31221,
- "Sphere": 38882,
- "Spider": 41294,
- "Spirit": 41910,
- "Spl": 26568,
- "Split": 41205,
- "Spoiler": 31895,
- "Spons": 43522,
- "Sport": 42576,
- "Sports": 18153,
- "Spot": 32565,
- "Spr": 38454,
- "Spread": 44458,
- "Spring": 30387,
- "Squ": 22266,
- "Square": 48011,
- "St": 1273,
- "Stack": 25896,
- "Staff": 31449,
- "Stage": 29391,
- "Stan": 32140,
- "Stand": 15480,
- "Standard": 23615,
- "Standing": 44196,
- "Star": 8248,
- "Stars": 29366,
- "Start": 10434,
- "Starting": 22851,
- "Stat": 17126,
- "State": 9012,
- "Statement": 48682,
- "States": 42237,
- "Static": 45442,
- "Station": 12367,
- "Statistics": 48346,
- "Stats": 29668,
- "Status": 19580,
- "Stay": 25681,
- "Ste": 7447,
- "Steam": 19109,
- "Steel": 39807,
- "Step": 8600,
- "Stephen": 24920,
- "Steve": 19206,
- "Steven": 28292,
- "Stew": 49328,
- "Still": 9590,
- "Stock": 26207,
- "Stone": 34346,
- "Stop": 19485,
- "Storage": 31425,
- "Store": 22658,
- "Storm": 32173,
- "Story": 11605,
- "Str": 13290,
- "Stra": 41347,
- "Strange": 38114,
- "Stre": 30611,
- "Stream": 12124,
- "Streamer": 28696,
- "StreamerBot": 37574,
- "Street": 34356,
- "Strength": 45027,
- "Stretch": 39181,
- "Strike": 31584,
- "String": 10100,
- "Strong": 33004,
- "Struct": 44909,
- "Stud": 13007,
- "Student": 38778,
- "Students": 28239,
- "Studies": 45833,
- "Studio": 41501,
- "Study": 39841,
- "Sty": 18716,
- "Style": 21466,
- "Su": 5606,
- "Sub": 7004,
- "Subject": 19776,
- "Submit": 45135,
- "Subscribe": 27125,
- "Success": 33244,
- "Such": 16678,
- "Suddenly": 38582,
- "Suggest": 43857,
- "Sullivan": 47572,
- "Sum": 13065,
- "Summary": 22093,
- "Summer": 33560,
- "Sun": 16012,
- "Sund": 20602,
- "Sunday": 21934,
- "Sup": 40784,
- "Super": 12442,
- "Supp": 15979,
- "Supplement": 42615,
- "Support": 15514,
- "Supported": 48181,
- "Supporters": 49422,
- "Sur": 14214,
- "Sure": 19457,
- "Surv": 34652,
- "Sus": 30746,
- "Susan": 45842,
- "Sw": 10462,
- "Swe": 40783,
- "Sweet": 36087,
- "Switch": 38978,
- "Sword": 43117,
- "Sy": 13940,
- "Sym": 43094,
- "Syn": 29934,
- "Sync": 28985,
- "Synopsis": 49771,
- "Syria": 40029,
- "Syrian": 42747,
- "Sys": 44387,
- "System": 11964,
- "T": 51,
- "TA": 5603,
- "TABLE": 38148,
- "TAG": 42197,
- "TAIN": 30339,
- "TB": 22737,
- "TC": 4825,
- "TD": 21016,
- "TE": 9328,
- "TED": 36493,
- "TER": 5781,
- "TERN": 31800,
- "TEXT": 32541,
- "TEXTURE": 47648,
- "TF": 10234,
- "TG": 35990,
- "TH": 4221,
- "THE": 10970,
- "THER": 21250,
- "THING": 39356,
- "THIS": 43559,
- "TI": 25621,
- "TIME": 34694,
- "TING": 48996,
- "TION": 24131,
- "TIT": 49560,
- "TL": 14990,
- "TM": 15972,
- "TN": 46559,
- "TO": 10468,
- "TON": 11357,
- "TOP": 35222,
- "TOR": 32961,
- "TP": 7250,
- "TPP": 31435,
- "TPPStreamerBot": 37579,
- "TPS": 28820,
- "TR": 5446,
- "TRUMP": 42473,
- "TRY": 40405,
- "TS": 4694,
- "TT": 15751,
- "TV": 6849,
- "TW": 34551,
- "TX": 29551,
- "TY": 9936,
- "TYPE": 25216,
- "Ta": 38586,
- "Tab": 33349,
- "Table": 10962,
- "Tact": 45803,
- "Tag": 24835,
- "Tags": 36142,
- "Tai": 47976,
- "Take": 12322,
- "Taking": 26556,
- "Tal": 31466,
- "Talk": 25685,
- "Talking": 45904,
- "Tam": 42061,
- "Tan": 45557,
- "Tang": 43909,
- "Tank": 32978,
- "Tap": 45081,
- "Tar": 47079,
- "Target": 21745,
- "Task": 25714,
- "Tax": 27017,
- "Taylor": 29907,
- "Te": 6767,
- "TeX": 49568,
- "Tea": 49770,
- "Team": 15592,
- "Tech": 17760,
- "Techn": 25574,
- "Technical": 45638,
- "Technology": 44893,
- "Ted": 38972,
- "Teen": 45639,
- "Tel": 33317,
- "Tele": 31709,
- "Tell": 24446,
- "Tem": 12966,
- "Temp": 30782,
- "Temperature": 42492,
- "Template": 30800,
- "Ten": 24893,
- "Tenn": 43139,
- "Ter": 15156,
- "Term": 40596,
- "Termin": 44798,
- "Terror": 40194,
- "Terry": 50241,
- "Tes": 36504,
- "Tesla": 41351,
- "Test": 14402,
- "Testing": 44154,
- "Tex": 17005,
- "Texas": 21607,
- "Text": 8206,
- "TextColor": 42470,
- "Texture": 32742,
- "Textures": 39860,
- "Th": 817,
- "Thank": 10449,
- "Thankfully": 48387,
- "Thanks": 9690,
- "That": 2504,
- "The": 464,
- "Their": 14574,
- "Theme": 47863,
- "Then": 6423,
- "Ther": 35048,
- "There": 1858,
- "Therefore": 26583,
- "These": 4711,
- "They": 2990,
- "Things": 22248,
- "Think": 22073,
- "Third": 22747,
- "Thirty": 38856,
- "This": 1212,
- "Thom": 37582,
- "Thomas": 22405,
- "Thompson": 48942,
- "Thor": 46765,
- "Those": 9627,
- "Though": 10915,
- "Thousands": 37482,
- "Thread": 16818,
- "Three": 12510,
- "Through": 15046,
- "Throughout": 26797,
- "Throw": 39431,
- "Thu": 39902,
- "Thumbnail": 35523,
- "ThumbnailImage": 39142,
- "Thunder": 45713,
- "Thursday": 25381,
- "Thus": 19093,
- "Ti": 40533,
- "Tickets": 43254,
- "Tier": 35252,
- "Tile": 35103,
- "Tim": 14967,
- "Time": 7575,
- "Timeout": 48031,
- "Timer": 48801,
- "Times": 28595,
- "Tip": 28434,
- "Tips": 43368,
- "Title": 19160,
- "To": 2514,
- "Today": 8888,
- "Todd": 42817,
- "Together": 41631,
- "Tok": 19042,
- "Token": 30642,
- "Tokens": 22906,
- "Tom": 13787,
- "Tomorrow": 49488,
- "Ton": 35416,
- "Tonight": 43783,
- "Tony": 29387,
- "Too": 23307,
- "Tool": 25391,
- "Tools": 33637,
- "Top": 9126,
- "Topic": 33221,
- "Topics": 25902,
- "Tor": 15884,
- "Toronto": 31359,
- "Torrent": 39286,
- "Total": 14957,
- "Touch": 35211,
- "Tour": 39152,
- "Town": 38097,
- "Toy": 48236,
- "Tr": 2898,
- "Tra": 15721,
- "Track": 24802,
- "Tracker": 35694,
- "Trade": 35965,
- "Traditional": 48485,
- "Train": 44077,
- "Training": 44357,
- "Trans": 8291,
- "Transaction": 48720,
- "Transfer": 43260,
- "Transform": 41762,
- "Translation": 48313,
- "Travel": 33074,
- "Tre": 31055,
- "Tree": 27660,
- "Trend": 45461,
- "Tri": 14824,
- "Trigger": 48344,
- "Trivia": 23854,
- "Tro": 44095,
- "True": 17821,
- "Trump": 6170,
- "Trust": 33814,
- "Truth": 38782,
- "Try": 23433,
- "Ts": 33758,
- "Tu": 47247,
- "Tube": 6876,
- "Tue": 41392,
- "Tuesday": 26133,
- "Tumblr": 39415,
- "Tur": 17483,
- "Turkey": 31632,
- "Turkish": 42872,
- "Turn": 17278,
- "Tw": 5080,
- "Twe": 32665,
- "Tweet": 47845,
- "Twenty": 34096,
- "Twitter": 14254,
- "Two": 7571,
- "Tx": 46047,
- "Ty": 25492,
- "Tyler": 46807,
- "Typ": 31467,
- "Type": 6030,
- "Types": 31431,
- "Typically": 49321,
- "U": 52,
- "UA": 34970,
- "UAL": 25620,
- "UB": 10526,
- "UC": 9598,
- "UCK": 16696,
- "UCT": 18415,
- "UD": 8322,
- "UE": 8924,
- "UES": 35409,
- "UF": 36820,
- "UFC": 44534,
- "UFF": 47588,
- "UG": 7340,
- "UGC": 31179,
- "UGE": 41251,
- "UGH": 44004,
- "UI": 10080,
- "UID": 27586,
- "UK": 15039,
- "UL": 6239,
- "ULAR": 37232,
- "ULE": 24212,
- "ULL": 9994,
- "ULT": 16724,
- "ULTS": 35342,
- "UM": 5883,
- "UME": 38340,
- "UMP": 20476,
- "UN": 4944,
- "UNCH": 47461,
- "UNE": 41884,
- "UP": 8577,
- "UPDATE": 16977,
- "UR": 4261,
- "URA": 45570,
- "URE": 11335,
- "URES": 29514,
- "URI": 47269,
- "URL": 21886,
- "URN": 27064,
- "URR": 31302,
- "URRENT": 39237,
- "US": 2937,
- "USA": 14053,
- "USB": 27155,
- "USD": 29072,
- "USE": 19108,
- "USER": 29904,
- "USH": 27143,
- "USS": 32835,
- "UST": 7759,
- "UT": 3843,
- "UTC": 17429,
- "UTE": 37780,
- "UTERS": 14974,
- "UTF": 48504,
- "UTH": 24318,
- "UTION": 35354,
- "UU": 30100,
- "UV": 31667,
- "UX": 31235,
- "Ub": 36609,
- "Uber": 39018,
- "Uh": 34653,
- "Uk": 28425,
- "Ukraine": 44814,
- "Ul": 47920,
- "Ult": 16301,
- "Ultimate": 47892,
- "Ultimately": 27212,
- "Ultra": 36122,
- "Um": 37280,
- "Un": 3118,
- "Uncommon": 43023,
- "Und": 31319,
- "Under": 9203,
- "Understanding": 43467,
- "Unfortunately": 13898,
- "Union": 38176,
- "Unique": 40257,
- "Unit": 26453,
- "United": 17013,
- "Unity": 35955,
- "Universal": 38747,
- "University": 21009,
- "Unix": 47000,
- "Unknown": 20035,
- "Unless": 28042,
- "Unlike": 18521,
- "Unt": 35792,
- "Until": 18273,
- "Untitled": 46332,
- "Up": 4933,
- "Update": 10260,
- "Updated": 17354,
- "Upgrade": 44948,
- "Upload": 41592,
- "Upon": 23792,
- "Ur": 16692,
- "Urban": 46667,
- "Url": 28165,
- "Us": 5842,
- "Usage": 28350,
- "Use": 11041,
- "Used": 38052,
- "User": 12982,
- "Users": 14490,
- "Using": 12814,
- "Usually": 37887,
- "Ut": 18274,
- "Utah": 44350,
- "V": 53,
- "VA": 11731,
- "VAL": 23428,
- "VALUE": 39488,
- "VB": 44526,
- "VC": 15922,
- "VD": 8898,
- "VE": 6089,
- "VEL": 18697,
- "VEN": 28290,
- "VER": 5959,
- "VERS": 28884,
- "VERSION": 43717,
- "VERT": 15858,
- "VERTIS": 18000,
- "VERTISEMENT": 18679,
- "VG": 43490,
- "VI": 12861,
- "VICE": 27389,
- "VID": 11008,
- "VIDEO": 42937,
- "VIDIA": 13171,
- "VIEW": 28206,
- "VII": 45529,
- "VILLE": 38526,
- "VIS": 29817,
- "VK": 47191,
- "VL": 47468,
- "VM": 15996,
- "VO": 29516,
- "VOL": 44558,
- "VP": 8859,
- "VPN": 33883,
- "VR": 13024,
- "VS": 20304,
- "VT": 36392,
- "VW": 30133,
- "Va": 33906,
- "Val": 7762,
- "Valid": 47139,
- "Value": 11395,
- "Values": 40161,
- "Van": 25298,
- "Var": 19852,
- "Vari": 23907,
- "Variable": 43015,
- "Various": 40009,
- "Vaults": 33937,
- "Ve": 26979,
- "Vector": 38469,
- "Veh": 37870,
- "Vel": 46261,
- "Ven": 37522,
- "Ver": 13414,
- "Vers": 34947,
- "Version": 14815,
- "Versions": 45150,
- "Vert": 42369,
- "Very": 16371,
- "Veter": 45182,
- "Vi": 38432,
- "Via": 30754,
- "Vice": 47910,
- "Vict": 21944,
- "Victoria": 49898,
- "Video": 10798,
- "View": 7680,
- "Vill": 42074,
- "Viol": 33894,
- "Virgin": 34674,
- "Virginia": 41017,
- "Virtual": 37725,
- "Vis": 15854,
- "Vision": 44206,
- "Visit": 31141,
- "Visual": 36259,
- "Vo": 42144,
- "Voice": 35708,
- "Vol": 16598,
- "Volume": 31715,
- "Vote": 37394,
- "Vs": 23266,
- "W": 54,
- "WA": 15543,
- "WAR": 16279,
- "WARD": 39743,
- "WARE": 33746,
- "WARN": 37771,
- "WARNING": 31502,
- "WASHINGTON": 21793,
- "WATCH": 35192,
- "WAY": 27285,
- "WAYS": 42451,
- "WB": 45607,
- "WC": 27353,
- "WD": 22332,
- "WE": 8845,
- "WER": 45532,
- "WF": 48397,
- "WH": 12418,
- "WHAT": 32971,
- "WHERE": 47357,
- "WHO": 41856,
- "WI": 36326,
- "WIN": 37620,
- "WIND": 28929,
- "WINDOWS": 33207,
- "WM": 22117,
- "WN": 29767,
- "WOOD": 49466,
- "WOR": 45359,
- "WORK": 33249,
- "WP": 25527,
- "WR": 18564,
- "WS": 19416,
- "WT": 39386,
- "WW": 17947,
- "Wa": 33484,
- "Wait": 21321,
- "Wal": 21902,
- "Walk": 35963,
- "Walker": 39950,
- "Wall": 22401,
- "Wallet": 47152,
- "Wan": 45681,
- "Want": 19633,
- "War": 13195,
- "Ward": 49021,
- "Ware": 38824,
- "Warning": 20361,
- "Warren": 43464,
- "Wars": 41508,
- "Was": 16973,
- "Washington": 17402,
- "Watch": 10723,
- "Water": 19184,
- "Wave": 39709,
- "Way": 25309,
- "We": 1135,
- "Weak": 44898,
- "Weapon": 27632,
- "Weapons": 41818,
- "Weather": 41865,
- "Web": 13908,
- "Website": 33420,
- "Wed": 19864,
- "Wednesday": 27150,
- "Week": 20916,
- "Weight": 25844,
- "Weiss": 48760,
- "Welcome": 14618,
- "Well": 5779,
- "Were": 35653,
- "West": 15045,
- "Western": 24227,
- "Wh": 1199,
- "What": 2061,
- "Whatever": 21875,
- "Whe": 10842,
- "Wheel": 45307,
- "When": 2215,
- "Whenever": 28877,
- "Where": 8496,
- "Whereas": 48494,
- "Whether": 15354,
- "Which": 13828,
- "While": 3633,
- "Whit": 43617,
- "White": 12256,
- "Who": 8241,
- "Whoever": 47896,
- "Why": 5195,
- "Wi": 31294,
- "Wide": 42559,
- "Widget": 38300,
- "Width": 30916,
- "Wik": 33010,
- "Wiki": 32603,
- "Wikipedia": 48845,
- "Wil": 22327,
- "Wild": 25946,
- "Will": 8743,
- "William": 17121,
- "Williams": 27869,
- "Wilson": 37349,
- "Win": 16643,
- "Wind": 8731,
- "Window": 27703,
- "Windows": 11209,
- "Wing": 35612,
- "Winged": 47418,
- "Winner": 48056,
- "Winter": 35376,
- "Wire": 29451,
- "Wisconsin": 49097,
- "With": 3152,
- "WithNo": 35992,
- "Within": 22005,
- "Without": 16249,
- "Witness": 38670,
- "Wo": 49450,
- "Wolf": 32069,
- "Woman": 48081,
- "Women": 18495,
- "Wonder": 42337,
- "Wood": 22911,
- "Word": 26449,
- "Words": 37117,
- "Work": 12468,
- "Working": 28516,
- "Works": 23044,
- "World": 10603,
- "Would": 17353,
- "Wow": 22017,
- "Wr": 39213,
- "Wra": 36918,
- "Writ": 20257,
- "Write": 16594,
- "Writer": 34379,
- "Writing": 33874,
- "Written": 25354,
- "Ws": 46456,
- "X": 55,
- "XL": 32457,
- "XM": 37643,
- "XP": 27481,
- "XT": 25010,
- "XX": 8051,
- "XXX": 43145,
- "XXXX": 24376,
- "XY": 34278,
- "Xbox": 43377,
- "Xi": 42528,
- "Y": 56,
- "YA": 44947,
- "YC": 44816,
- "YD": 35755,
- "YE": 48743,
- "YES": 43335,
- "YING": 45761,
- "YL": 45448,
- "YN": 40760,
- "YOU": 36981,
- "YP": 48232,
- "YR": 38162,
- "YS": 16309,
- "YY": 26314,
- "Yan": 49664,
- "Yang": 38663,
- "Ye": 35543,
- "Yeah": 10995,
- "Year": 17688,
- "Years": 40630,
- "Yellow": 39499,
- "Yep": 47834,
- "Yes": 5297,
- "Yesterday": 28065,
- "Yet": 11486,
- "Yo": 38101,
- "York": 49278,
- "You": 1639,
- "YouTube": 33869,
- "Young": 20917,
- "Your": 7120,
- "Yu": 40728,
- "Z": 57,
- "ZA": 34892,
- "ZE": 21211,
- "ZI": 48926,
- "ZX": 40692,
- "ZZ": 30148,
- "Ze": 36056,
- "Zen": 47573,
- "Zero": 28667,
- "Zip": 41729,
- "Zone": 26961,
- "[": 58,
- "[\"": 14692,
- "['": 17816,
- "[/": 13412,
- "[[": 30109,
- "[]": 21737,
- "[_": 29795,
- "\\": 59,
- "\\\"": 7879,
- "\\\",": 34607,
- "\\\":": 30478,
- "\\\">": 38214,
- "\\'": 43054,
- "\\)": 22725,
- "\\-": 41441,
- "\\.": 17405,
- "\\/": 11139,
- "\\/\\/": 45422,
- "\\<": 49778,
- "\\\\": 6852,
- "\\\\\\\\": 13426,
- "\\\\\\\\\\\\\\\\": 21807,
- "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\": 34604,
- "]": 60,
- "]\"": 30866,
- "]'": 49946,
- "](": 16151,
- "])": 12962,
- "]),": 46570,
- "]).": 35944,
- "]);": 36563,
- "]+": 48688,
- "],": 4357,
- "],\"": 17241,
- "],[": 38430,
- "]-": 45297,
- "].": 4083,
- "].\"": 29225,
- "]:": 5974,
- "];": 11208,
- "]=": 22241,
- "][": 7131,
- "][/": 44926,
- "]]": 11907,
- "]}": 48999,
- "^": 61,
- "^^": 18237,
- "^^^^": 39397,
- "^{": 36796,
- "_": 62,
- "_(": 41052,
- "_-": 22955,
- "_-_": 31386,
- "_.": 44807,
- "_>": 49029,
- "__": 834,
- "___": 17569,
- "____": 1427,
- "_____": 29343,
- "______": 25947,
- "_______": 37405,
- "________": 2602,
- "________________": 4841,
- "________________________": 32941,
- "________________________________": 10221,
- "________________________________________________________________": 27193,
- "_{": 23330,
- "`": 63,
- "`,": 47671,
- "`.": 44646,
- "``": 15506,
- "````": 33153,
- "a": 64,
- "aa": 7252,
- "aaa": 46071,
- "aaaa": 24794,
- "aah": 37500,
- "aan": 28340,
- "ab": 397,
- "aba": 15498,
- "abad": 17325,
- "abal": 44349,
- "abama": 8809,
- "aban": 45094,
- "aband": 49248,
- "abase": 5754,
- "abases": 18826,
- "abb": 6485,
- "abba": 48910,
- "abbage": 32061,
- "abbit": 14229,
- "abbling": 47883,
- "abby": 42457,
- "abc": 39305,
- "abe": 11231,
- "abee": 32580,
- "abel": 9608,
- "abella": 43653,
- "aber": 27359,
- "abet": 8380,
- "abetes": 11064,
- "abeth": 9407,
- "abetic": 33312,
- "abi": 17914,
- "abiding": 43056,
- "abies": 43256,
- "abil": 14991,
- "abilia": 48249,
- "abilities": 5738,
- "ability": 1799,
- "abin": 6014,
- "abis": 8102,
- "abit": 29968,
- "abl": 23117,
- "able": 540,
- "abled": 4510,
- "ables": 2977,
- "abling": 11716,
- "ablish": 17148,
- "ablished": 22555,
- "ablishment": 25380,
- "ablo": 18817,
- "ably": 1346,
- "abo": 34748,
- "abol": 28426,
- "abolic": 29304,
- "abor": 4820,
- "abortion": 32396,
- "about": 10755,
- "abouts": 27880,
- "above": 29370,
- "abre": 46241,
- "abs": 8937,
- "absolute": 48546,
- "absolutely": 42994,
- "absor": 46303,
- "abul": 16665,
- "abulary": 22528,
- "abus": 46844,
- "abuse": 47158,
- "abwe": 27050,
- "aby": 3930,
- "abyte": 37828,
- "abytes": 38346,
- "ac": 330,
- "aca": 22260,
- "acan": 50195,
- "acas": 40263,
- "acc": 4134,
- "acca": 43552,
- "accept": 13635,
- "acceptable": 16037,
- "access": 15526,
- "accessible": 33780,
- "acci": 44456,
- "acco": 8679,
- "accompan": 41974,
- "accompanied": 42588,
- "according": 38169,
- "account": 23317,
- "ace": 558,
- "acea": 44977,
- "aceae": 48319,
- "acebook": 2887,
- "aced": 2286,
- "acement": 5592,
- "acements": 28613,
- "acent": 12643,
- "aceous": 37797,
- "acer": 11736,
- "acerb": 22428,
- "acers": 49908,
- "aces": 2114,
- "acet": 23253,
- "aceutical": 14642,
- "acey": 25415,
- "ach": 620,
- "acha": 34518,
- "achable": 34446,
- "ache": 4891,
- "ached": 2317,
- "achel": 9636,
- "achelor": 19335,
- "acher": 3493,
- "achers": 17892,
- "aches": 3694,
- "achev": 42961,
- "achi": 14299,
- "achine": 20480,
- "aching": 8103,
- "achment": 15520,
- "acho": 43703,
- "acht": 19725,
- "achu": 32323,
- "achus": 9523,
- "achusetts": 9770,
- "achy": 35586,
- "aci": 32009,
- "acia": 47431,
- "acial": 18150,
- "acid": 46309,
- "acies": 13433,
- "acing": 4092,
- "acio": 48711,
- "acion": 49443,
- "acious": 14209,
- "aciously": 45289,
- "acist": 20279,
- "acists": 33194,
- "acity": 4355,
- "ack": 441,
- "acked": 6021,
- "acker": 10735,
- "ackers": 28874,
- "acket": 8317,
- "ackets": 25180,
- "acking": 5430,
- "ackle": 20523,
- "acks": 4595,
- "acky": 36053,
- "acl": 37779,
- "acle": 6008,
- "acles": 9928,
- "acly": 39691,
- "aclysm": 40605,
- "aco": 10602,
- "acon": 7807,
- "acons": 37256,
- "acqu": 43561,
- "acre": 12345,
- "acs": 16436,
- "act": 529,
- "acted": 23800,
- "acter": 7321,
- "acteria": 10634,
- "acterial": 44965,
- "acters": 19858,
- "actic": 12009,
- "acting": 27362,
- "action": 2673,
- "actionDate": 31538,
- "actions": 4658,
- "activ": 15791,
- "activate": 39022,
- "activated": 33106,
- "activation": 48545,
- "active": 5275,
- "actively": 33329,
- "activity": 21797,
- "actly": 24342,
- "actor": 11218,
- "actory": 9548,
- "acts": 8656,
- "actual": 50039,
- "actually": 37739,
- "actus": 34144,
- "acular": 12754,
- "acus": 48628,
- "acy": 1590,
- "ad": 324,
- "ada": 4763,
- "adal": 31682,
- "adan": 29157,
- "adapt": 42552,
- "adas": 38768,
- "adata": 14706,
- "aday": 43593,
- "adays": 20544,
- "add": 2860,
- "addafi": 32113,
- "added": 29373,
- "adden": 38014,
- "adder": 26676,
- "adders": 45940,
- "addin": 46782,
- "adding": 26872,
- "addle": 37382,
- "addock": 35509,
- "addon": 48078,
- "addons": 39996,
- "addr": 29851,
- "address": 21975,
- "addy": 13218,
- "ade": 671,
- "aded": 5286,
- "adel": 6959,
- "adelphia": 8273,
- "adem": 36920,
- "ademic": 49113,
- "aden": 40780,
- "adena": 38047,
- "adeon": 12424,
- "adequ": 16515,
- "ader": 5067,
- "aders": 9972,
- "ades": 2367,
- "adesh": 13410,
- "adh": 24411,
- "adi": 9189,
- "adia": 29523,
- "adian": 18425,
- "adiator": 33716,
- "adic": 23876,
- "adier": 38868,
- "adies": 50192,
- "adin": 17072,
- "ading": 4980,
- "adiq": 48687,
- "adish": 48563,
- "aditional": 27013,
- "adium": 6271,
- "adj": 41255,
- "adjust": 23032,
- "adjusted": 29117,
- "adle": 35166,
- "admin": 28482,
- "administ": 39081,
- "ado": 4533,
- "adobe": 36752,
- "adoes": 46368,
- "ador": 7079,
- "ados": 22484,
- "adow": 4584,
- "adows": 9797,
- "adr": 41909,
- "adra": 49456,
- "ads": 5643,
- "adult": 49922,
- "adv": 32225,
- "advant": 13461,
- "advert": 17904,
- "advertisement": 45876,
- "advertising": 34442,
- "ady": 4597,
- "ae": 3609,
- "aea": 44705,
- "aed": 8432,
- "aeda": 11641,
- "ael": 3010,
- "aeper": 28235,
- "aepernick": 28333,
- "aer": 25534,
- "aeus": 46052,
- "aez": 47246,
- "af": 1878,
- "afa": 28485,
- "afe": 8635,
- "afer": 34659,
- "afety": 27925,
- "aff": 2001,
- "affe": 21223,
- "affected": 43958,
- "affer": 31183,
- "affiliated": 46818,
- "affle": 30697,
- "affles": 48501,
- "afi": 19910,
- "afia": 22214,
- "afort": 24515,
- "aft": 14940,
- "after": 8499,
- "ag": 363,
- "aga": 8126,
- "again": 17776,
- "against": 32826,
- "agall": 44906,
- "agame": 46746,
- "agan": 7329,
- "aganda": 43589,
- "agar": 32452,
- "agara": 38415,
- "agascar": 44309,
- "agate": 37861,
- "age": 496,
- "aged": 1886,
- "ageddon": 33054,
- "agement": 5082,
- "agements": 38113,
- "agen": 11286,
- "agency": 40955,
- "agent": 25781,
- "agents": 49638,
- "ager": 3536,
- "agers": 10321,
- "ages": 1095,
- "agg": 9460,
- "agged": 14655,
- "agger": 7928,
- "agging": 16406,
- "aggressive": 49639,
- "agh": 10471,
- "aghan": 45109,
- "aghd": 16650,
- "agher": 30450,
- "aghetti": 35812,
- "agi": 18013,
- "agic": 9083,
- "agically": 39066,
- "agin": 23183,
- "agine": 12756,
- "aging": 3039,
- "agle": 19345,
- "agles": 37803,
- "agn": 4660,
- "agna": 48669,
- "agnar": 30475,
- "agne": 21080,
- "agnetic": 25145,
- "ago": 3839,
- "agog": 37300,
- "agogue": 32238,
- "agon": 1840,
- "agonal": 27923,
- "agonist": 15239,
- "agonists": 36764,
- "agons": 34765,
- "agos": 48215,
- "agra": 45429,
- "agram": 6713,
- "agraph": 6111,
- "agree": 49221,
- "ags": 3775,
- "agu": 11433,
- "ague": 2064,
- "agues": 6120,
- "agus": 31111,
- "agy": 46671,
- "ah": 993,
- "aha": 12236,
- "ahah": 36225,
- "ahan": 19210,
- "ahar": 37325,
- "ahead": 38204,
- "ahi": 32810,
- "ahime": 49997,
- "ahl": 15668,
- "ahn": 15386,
- "aho": 17108,
- "ahon": 30491,
- "ahoo": 12992,
- "ahs": 39095,
- "ahu": 12196,
- "ai": 1872,
- "aic": 18452,
- "aid": 1698,
- "aida": 30546,
- "aiden": 17538,
- "aido": 44354,
- "aign": 1784,
- "aii": 42648,
- "ail": 603,
- "aila": 39460,
- "ailability": 8994,
- "ailable": 1508,
- "ailand": 16188,
- "ailed": 6255,
- "ailing": 11608,
- "ails": 1768,
- "aily": 3079,
- "aim": 1385,
- "aiman": 47840,
- "aimon": 49438,
- "ain": 391,
- "aina": 42183,
- "aine": 5718,
- "ained": 1328,
- "ainer": 10613,
- "ainers": 50221,
- "aining": 1397,
- "ainment": 37091,
- "ains": 1299,
- "aint": 2913,
- "aintain": 32725,
- "ainted": 14215,
- "aints": 6003,
- "air": 958,
- "aird": 41620,
- "aire": 7626,
- "aired": 9820,
- "aires": 17693,
- "airo": 18131,
- "airs": 3468,
- "airy": 13021,
- "ais": 15152,
- "ait": 4548,
- "aith": 3921,
- "aito": 38995,
- "aj": 1228,
- "aja": 27792,
- "aji": 26436,
- "ajo": 34944,
- "ajor": 1518,
- "ak": 461,
- "aka": 8130,
- "akable": 29033,
- "ake": 539,
- "aked": 4335,
- "akedown": 25817,
- "aken": 1685,
- "akening": 18800,
- "akens": 31627,
- "aker": 3110,
- "akers": 3979,
- "akeru": 43246,
- "akery": 33684,
- "akes": 1124,
- "akespe": 20621,
- "akespeare": 20946,
- "akh": 11322,
- "aki": 8182,
- "akia": 21897,
- "akin": 27048,
- "aking": 868,
- "akings": 45665,
- "akis": 27321,
- "ako": 25496,
- "akov": 44715,
- "akra": 38004,
- "aks": 4730,
- "aku": 8719,
- "akura": 47754,
- "akuya": 29863,
- "aky": 15492,
- "al": 282,
- "ala": 6081,
- "alach": 33786,
- "alam": 44949,
- "alan": 25786,
- "albeit": 45781,
- "album": 40916,
- "alcohol": 42142,
- "ald": 1940,
- "alde": 35885,
- "aldehyde": 44895,
- "aldi": 37566,
- "aldo": 41476,
- "ale": 1000,
- "aleb": 32100,
- "aled": 3021,
- "aleigh": 30729,
- "aler": 36213,
- "alert": 44598,
- "ales": 2040,
- "aley": 16730,
- "alez": 22149,
- "alf": 1604,
- "alg": 14016,
- "algia": 47111,
- "ali": 7344,
- "alia": 9752,
- "alian": 7199,
- "alias": 26011,
- "aliation": 22885,
- "alid": 10751,
- "alien": 42690,
- "align": 31494,
- "aligned": 41634,
- "alin": 14414,
- "aline": 20663,
- "aling": 4272,
- "alion": 19275,
- "alions": 50022,
- "alis": 27315,
- "alist": 49845,
- "alities": 27969,
- "ality": 1483,
- "alk": 971,
- "alker": 20949,
- "alking": 18998,
- "alks": 23833,
- "alky": 18354,
- "alkyrie": 21316,
- "all": 439,
- "alla": 30315,
- "allah": 31840,
- "allas": 7826,
- "alle": 6765,
- "alled": 4262,
- "allel": 29363,
- "allery": 17022,
- "alli": 36546,
- "allic": 18196,
- "alling": 9221,
- "allion": 48332,
- "allo": 49457,
- "alloc": 32332,
- "allow": 12154,
- "allowed": 40845,
- "alloween": 50107,
- "allows": 47205,
- "alls": 5691,
- "ally": 453,
- "alm": 38182,
- "almost": 28177,
- "alo": 7335,
- "alog": 11794,
- "alogue": 30326,
- "alogy": 48909,
- "alon": 40755,
- "alone": 17749,
- "along": 24176,
- "alore": 40612,
- "alos": 41823,
- "alph": 17307,
- "alpha": 26591,
- "als": 874,
- "alsa": 32058,
- "alse": 2820,
- "alsh": 22114,
- "also": 14508,
- "alt": 2501,
- "alted": 29590,
- "alter": 47653,
- "altern": 33645,
- "alth": 1094,
- "although": 16670,
- "alties": 10355,
- "alty": 6017,
- "alus": 46781,
- "always": 33770,
- "aly": 3400,
- "alys": 26266,
- "alysed": 47557,
- "alyses": 43710,
- "alysis": 8767,
- "alyst": 21470,
- "am": 321,
- "ama": 1689,
- "amac": 11494,
- "amacare": 11724,
- "aman": 10546,
- "amar": 39236,
- "amara": 47848,
- "amaru": 46893,
- "amas": 17485,
- "amate": 36754,
- "amation": 14755,
- "amaz": 45983,
- "amazon": 33103,
- "amb": 4131,
- "amba": 31842,
- "amber": 7789,
- "ambers": 16368,
- "ambling": 15366,
- "ambo": 22651,
- "amboo": 27708,
- "amd": 28745,
- "ame": 480,
- "amed": 2434,
- "ameda": 49637,
- "amel": 17983,
- "ameless": 39942,
- "amen": 41763,
- "ament": 3263,
- "amental": 6860,
- "aments": 12604,
- "amer": 2382,
- "amera": 18144,
- "ameron": 41639,
- "ames": 1047,
- "ami": 6277,
- "amia": 49442,
- "amic": 18127,
- "amide": 37905,
- "amiliar": 19968,
- "amily": 5993,
- "amin": 5669,
- "amina": 18891,
- "amination": 24979,
- "amine": 9862,
- "aminer": 32086,
- "amines": 41047,
- "aming": 3723,
- "amins": 24937,
- "amiya": 38241,
- "aml": 43695,
- "amm": 6475,
- "ammad": 26035,
- "ammed": 10573,
- "ammers": 36846,
- "ammu": 49487,
- "ammy": 46736,
- "amn": 34684,
- "amo": 18811,
- "amon": 16487,
- "among": 35131,
- "amorph": 37670,
- "amoto": 25384,
- "amount": 17287,
- "amous": 10877,
- "amp": 696,
- "ampa": 13299,
- "amped": 13322,
- "amph": 28474,
- "amphetamine": 31262,
- "amping": 37843,
- "ampion": 6734,
- "ampions": 4350,
- "ampire": 13577,
- "ampires": 27933,
- "ample": 1403,
- "amples": 12629,
- "ampoo": 40239,
- "amps": 9430,
- "ampton": 23427,
- "ampunk": 46183,
- "ams": 4105,
- "amsung": 30136,
- "amura": 37324,
- "amus": 25509,
- "amy": 14814,
- "an": 272,
- "ana": 2271,
- "analy": 38200,
- "analysis": 20930,
- "anamo": 33524,
- "anan": 27870,
- "anas": 15991,
- "anasia": 45551,
- "anc": 1192,
- "anca": 42124,
- "ance": 590,
- "anced": 2903,
- "ancel": 21130,
- "ancer": 8250,
- "ancers": 20811,
- "ances": 1817,
- "anch": 3702,
- "anche": 6362,
- "anches": 12140,
- "anchester": 8911,
- "anchez": 20364,
- "ancial": 2783,
- "ancies": 16183,
- "ancing": 5077,
- "anco": 47699,
- "ancock": 37077,
- "ancouver": 10264,
- "ancy": 3883,
- "and": 392,
- "anda": 5282,
- "andal": 7642,
- "andals": 23819,
- "andan": 42509,
- "ande": 40004,
- "anded": 12249,
- "andel": 33134,
- "andem": 30025,
- "ander": 4066,
- "andering": 42454,
- "anders": 45070,
- "andestine": 35887,
- "andi": 26800,
- "anding": 27225,
- "andise": 18888,
- "ando": 25440,
- "andom": 3749,
- "andon": 5063,
- "andowski": 44391,
- "andr": 46273,
- "andra": 15918,
- "andre": 49078,
- "andro": 28092,
- "android": 19411,
- "ands": 1746,
- "andum": 25933,
- "andy": 10757,
- "ane": 1531,
- "aned": 22739,
- "aneers": 33547,
- "aneous": 11655,
- "aneously": 27683,
- "anes": 7305,
- "aney": 22297,
- "ang": 648,
- "anga": 16484,
- "angan": 37089,
- "ange": 858,
- "anged": 5102,
- "angel": 8368,
- "angelo": 46525,
- "anger": 2564,
- "angered": 19041,
- "angering": 49470,
- "angers": 6606,
- "anges": 6231,
- "angible": 39639,
- "anging": 4924,
- "angle": 9248,
- "angled": 22393,
- "angler": 49910,
- "angles": 27787,
- "angling": 27499,
- "ango": 14208,
- "angs": 27725,
- "angu": 2303,
- "anguage": 9000,
- "anguages": 33213,
- "anguard": 23521,
- "angular": 21413,
- "ani": 3216,
- "ania": 5411,
- "anian": 38336,
- "anic": 26277,
- "anical": 36684,
- "anie": 34166,
- "aniel": 6321,
- "anim": 11227,
- "animal": 41607,
- "animate": 45685,
- "animous": 45873,
- "aning": 7574,
- "anish": 7115,
- "anism": 48162,
- "anity": 19689,
- "anium": 15776,
- "ank": 962,
- "anka": 15927,
- "anke": 49200,
- "anked": 14076,
- "ankind": 28066,
- "anking": 15230,
- "anks": 2283,
- "anky": 39556,
- "anmar": 21708,
- "ann": 1236,
- "anna": 7697,
- "annabin": 43655,
- "annah": 25761,
- "anne": 21952,
- "anned": 3577,
- "annel": 4276,
- "annels": 8961,
- "anners": 15672,
- "anni": 31296,
- "annie": 42883,
- "annis": 45017,
- "annon": 8825,
- "annot": 34574,
- "announced": 43499,
- "anny": 7737,
- "ano": 5733,
- "anoia": 30661,
- "anol": 22012,
- "anon": 36902,
- "anooga": 42165,
- "anos": 40015,
- "another": 29214,
- "anova": 40993,
- "anqu": 26184,
- "ans": 504,
- "ansas": 6618,
- "anse": 40054,
- "ansen": 33807,
- "anship": 47086,
- "ansion": 5487,
- "ansk": 34738,
- "anski": 44978,
- "ansky": 49792,
- "ansom": 22011,
- "anson": 23103,
- "ansson": 44038,
- "answer": 41484,
- "answered": 31966,
- "ant": 415,
- "anta": 4910,
- "antage": 36403,
- "antam": 49653,
- "antasy": 34921,
- "ante": 12427,
- "anted": 4126,
- "antes": 39781,
- "anth": 29313,
- "antha": 32589,
- "anthrop": 22178,
- "anti": 17096,
- "antic": 5109,
- "antically": 31589,
- "anticipated": 45178,
- "antics": 29320,
- "antine": 29003,
- "anting": 20482,
- "antis": 20836,
- "antle": 16941,
- "antly": 3875,
- "anto": 14723,
- "antom": 11456,
- "anton": 23026,
- "antry": 21238,
- "ants": 1187,
- "anty": 46098,
- "antz": 46269,
- "anu": 42357,
- "anus": 41141,
- "anut": 20651,
- "anuts": 37555,
- "anwhile": 6710,
- "any": 1092,
- "anya": 34183,
- "anyahu": 15966,
- "anye": 23495,
- "anyl": 34816,
- "anyon": 21330,
- "anything": 49459,
- "anz": 35410,
- "anza": 35819,
- "ao": 5488,
- "aos": 7495,
- "ap": 499,
- "apa": 32678,
- "apache": 43073,
- "apan": 2674,
- "ape": 1758,
- "apeake": 49528,
- "aped": 5813,
- "apego": 40561,
- "aper": 2136,
- "apers": 5656,
- "apes": 7916,
- "apesh": 25490,
- "apeshifter": 29554,
- "apest": 35746,
- "aph": 6570,
- "aphael": 34889,
- "api": 15042,
- "aping": 9269,
- "apist": 41690,
- "apixel": 48633,
- "aple": 24052,
- "aples": 28624,
- "apo": 41817,
- "apolis": 11174,
- "apolog": 46407,
- "apon": 9184,
- "apons": 13486,
- "apor": 12687,
- "apore": 11656,
- "app": 1324,
- "appa": 20975,
- "apped": 6320,
- "append": 33295,
- "apper": 11463,
- "appers": 46629,
- "appiness": 42661,
- "apping": 5912,
- "appings": 39242,
- "apple": 18040,
- "application": 31438,
- "apply": 39014,
- "appointed": 32924,
- "appro": 21064,
- "appropri": 11488,
- "appropriate": 13335,
- "appropriately": 45175,
- "approved": 29137,
- "approximately": 47498,
- "apps": 18211,
- "appy": 7774,
- "aps": 1686,
- "apse": 7512,
- "apsed": 28361,
- "apses": 45903,
- "apt": 2373,
- "apter": 3429,
- "apters": 12126,
- "aptic": 32963,
- "aptop": 45007,
- "apult": 41387,
- "apy": 12826,
- "aq": 30188,
- "aqu": 36129,
- "aque": 18251,
- "aques": 46806,
- "aquin": 48734,
- "ar": 283,
- "ara": 3301,
- "arag": 29967,
- "arah": 23066,
- "arak": 30447,
- "aram": 41158,
- "aran": 19173,
- "arant": 4741,
- "arantine": 37996,
- "araoh": 33766,
- "arat": 34174,
- "arate": 30748,
- "aration": 10186,
- "arations": 24355,
- "arb": 38039,
- "arbon": 42084,
- "arc": 5605,
- "arcer": 17649,
- "arch": 998,
- "arching": 38270,
- "archive": 17474,
- "archives": 48814,
- "archment": 36767,
- "archs": 34592,
- "archy": 9282,
- "arcity": 32689,
- "ard": 446,
- "arde": 45093,
- "arded": 10676,
- "arden": 5872,
- "ardi": 22490,
- "arding": 13493,
- "ardless": 14694,
- "ardo": 13109,
- "ardon": 19917,
- "ards": 1371,
- "ardy": 39124,
- "are": 533,
- "area": 20337,
- "ared": 1144,
- "aredevil": 38281,
- "arel": 20318,
- "arella": 45494,
- "aren": 5757,
- "arent": 1580,
- "arenthood": 17117,
- "arently": 13773,
- "arer": 11258,
- "arers": 34231,
- "ares": 3565,
- "arest": 12423,
- "aret": 8984,
- "areth": 26659,
- "arette": 14758,
- "arettes": 13890,
- "aretz": 48338,
- "arez": 19655,
- "arf": 37595,
- "arg": 853,
- "arge": 1376,
- "arger": 32270,
- "arget": 7641,
- "argo": 9448,
- "argon": 37920,
- "args": 22046,
- "argument": 49140,
- "ari": 2743,
- "aria": 10312,
- "arial": 36098,
- "arian": 3699,
- "arians": 13517,
- "ariat": 21621,
- "arie": 49173,
- "aries": 3166,
- "arij": 39010,
- "arijuana": 42834,
- "arily": 3093,
- "arin": 17714,
- "arine": 34569,
- "aring": 1723,
- "ario": 4982,
- "arios": 13010,
- "arious": 27129,
- "aris": 20066,
- "arist": 34566,
- "arity": 6806,
- "arium": 17756,
- "arius": 19897,
- "ark": 668,
- "arkable": 45543,
- "arkin": 39027,
- "arks": 5558,
- "arl": 7063,
- "arlane": 49344,
- "arling": 30045,
- "arm": 1670,
- "arma": 10961,
- "armac": 32813,
- "armed": 12026,
- "arming": 18052,
- "armor": 40456,
- "arms": 8357,
- "arn": 1501,
- "arna": 28610,
- "arnaev": 42311,
- "arning": 4228,
- "aro": 12022,
- "aron": 8045,
- "aroo": 38049,
- "around": 14145,
- "arov": 42737,
- "arp": 5117,
- "arr": 3258,
- "arranted": 47940,
- "arrass": 9187,
- "array": 18747,
- "arre": 9624,
- "arrell": 47769,
- "arrett": 34878,
- "arrison": 22472,
- "arro": 34852,
- "arrow": 6018,
- "arry": 6532,
- "ars": 945,
- "arse": 17208,
- "arser": 28198,
- "arsh": 5406,
- "arsity": 45826,
- "arson": 12613,
- "art": 433,
- "arta": 34202,
- "arte": 32074,
- "arted": 19112,
- "arten": 23996,
- "arter": 2571,
- "arters": 6137,
- "arth": 11999,
- "arthed": 36370,
- "arthy": 18270,
- "article": 20205,
- "articles": 26845,
- "artifacts": 50179,
- "artisan": 19714,
- "artist": 49016,
- "artment": 1823,
- "artments": 32514,
- "artney": 41709,
- "arton": 41328,
- "arts": 5889,
- "arty": 25494,
- "artz": 13636,
- "aru": 11493,
- "arus": 20272,
- "ary": 560,
- "arya": 43898,
- "aryl": 36822,
- "aryn": 38621,
- "as": 292,
- "asa": 15462,
- "asaki": 33846,
- "asant": 8775,
- "asar": 42391,
- "asc": 3372,
- "asca": 42688,
- "ascade": 28966,
- "ascal": 27747,
- "ascar": 37740,
- "ascist": 31968,
- "ascript": 15961,
- "ascular": 14767,
- "ascus": 20275,
- "ase": 589,
- "ased": 839,
- "asel": 48038,
- "aser": 6005,
- "asers": 19865,
- "ases": 1386,
- "ash": 1077,
- "asha": 14715,
- "ashed": 5263,
- "asher": 31218,
- "ashes": 7465,
- "ashi": 12144,
- "ashing": 2140,
- "ashington": 2542,
- "ashion": 5880,
- "ashtra": 38535,
- "asi": 17053,
- "asia": 23218,
- "asin": 47337,
- "asing": 2313,
- "asio": 29831,
- "asion": 4247,
- "asionally": 31775,
- "asions": 39327,
- "asis": 17765,
- "asive": 17443,
- "ask": 2093,
- "aska": 8480,
- "asket": 11715,
- "asketball": 14575,
- "asking": 30463,
- "asks": 6791,
- "asley": 30705,
- "asm": 8597,
- "asma": 11797,
- "asms": 34432,
- "ason": 888,
- "asonable": 17994,
- "asonic": 30189,
- "asonry": 38950,
- "asons": 2812,
- "asp": 5126,
- "aspberry": 17653,
- "asper": 32981,
- "aspers": 49412,
- "aspx": 31740,
- "ass": 562,
- "assad": 30178,
- "assador": 10623,
- "assadors": 33429,
- "assault": 46635,
- "asse": 21612,
- "assed": 21390,
- "assemb": 34455,
- "assembled": 46826,
- "assembly": 41873,
- "asser": 24929,
- "assert": 30493,
- "asses": 13978,
- "assets": 19668,
- "assetsadobe": 41383,
- "assi": 46527,
- "assian": 46091,
- "assic": 31635,
- "assies": 46257,
- "assin": 44961,
- "assing": 19696,
- "assion": 11857,
- "assis": 20297,
- "assisted": 42191,
- "assium": 26663,
- "assment": 45312,
- "asso": 28372,
- "associated": 32852,
- "assuming": 32935,
- "assy": 11720,
- "ast": 459,
- "asta": 40197,
- "aste": 4594,
- "asted": 8992,
- "aster": 1603,
- "astered": 14054,
- "astern": 6470,
- "asters": 7060,
- "astery": 29310,
- "astic": 3477,
- "astical": 32044,
- "astically": 16607,
- "astics": 24232,
- "asting": 9222,
- "aston": 45966,
- "astrous": 20168,
- "asts": 5773,
- "asty": 7833,
- "asu": 27345,
- "asure": 5015,
- "asured": 34006,
- "asures": 13846,
- "asuring": 45925,
- "asury": 11579,
- "asus": 40895,
- "asy": 4107,
- "at": 265,
- "ata": 1045,
- "atable": 21156,
- "ataka": 48088,
- "atal": 10254,
- "atalie": 30951,
- "atan": 39036,
- "atana": 43777,
- "atar": 9459,
- "atari": 35554,
- "atars": 40193,
- "atch": 963,
- "atche": 24809,
- "atched": 14265,
- "atcher": 34734,
- "atches": 20981,
- "atchewan": 29736,
- "atching": 19775,
- "ate": 378,
- "atech": 40340,
- "ated": 515,
- "ateful": 11850,
- "ateg": 2397,
- "ategic": 47917,
- "ategor": 47467,
- "ategories": 26129,
- "ategory": 11606,
- "ategy": 4338,
- "atel": 25791,
- "atell": 7528,
- "atellite": 26493,
- "ately": 1286,
- "atem": 23900,
- "aten": 36686,
- "ater": 729,
- "ateral": 10534,
- "aterasu": 45335,
- "atered": 34190,
- "aterial": 2273,
- "atern": 9205,
- "aternal": 14744,
- "aternity": 17094,
- "aters": 8605,
- "ates": 689,
- "ateur": 15093,
- "ateurs": 45211,
- "atever": 3587,
- "atform": 3390,
- "ath": 776,
- "atha": 30921,
- "atham": 37520,
- "athan": 6696,
- "athe": 26221,
- "athed": 35932,
- "ather": 1032,
- "athered": 8638,
- "atherine": 15289,
- "athering": 25545,
- "athetic": 18874,
- "athi": 44202,
- "athing": 26927,
- "athlon": 50236,
- "athom": 32910,
- "athon": 12938,
- "aths": 33148,
- "athy": 10036,
- "ati": 7246,
- "atial": 34961,
- "atibility": 25901,
- "atible": 16873,
- "atic": 1512,
- "atical": 39056,
- "atically": 4142,
- "atican": 18245,
- "atics": 23372,
- "atile": 12610,
- "atility": 18486,
- "atin": 10680,
- "ating": 803,
- "atinum": 16881,
- "atio": 39485,
- "ation": 341,
- "ational": 864,
- "ationally": 15208,
- "ations": 602,
- "atis": 37749,
- "atisf": 17403,
- "atism": 26185,
- "ative": 876,
- "atively": 9404,
- "atives": 2929,
- "ativity": 22055,
- "atl": 25864,
- "atlantic": 43342,
- "atmeal": 45280,
- "ato": 5549,
- "atoes": 15048,
- "atography": 45501,
- "atom": 37696,
- "atomic": 47116,
- "aton": 13951,
- "atonin": 44248,
- "atoon": 23122,
- "ator": 1352,
- "atorial": 21592,
- "atories": 19854,
- "atorium": 30732,
- "ators": 2024,
- "atory": 2870,
- "atos": 35492,
- "atown": 41079,
- "atra": 26066,
- "atre": 10562,
- "atri": 26646,
- "atro": 47756,
- "atron": 23484,
- "ats": 1381,
- "atson": 13506,
- "atsu": 19231,
- "atsuki": 40063,
- "att": 1078,
- "atta": 25014,
- "attach": 47348,
- "attack": 20358,
- "attacks": 38458,
- "atted": 16898,
- "atten": 41769,
- "atter": 1436,
- "attered": 10228,
- "attering": 16475,
- "atters": 34387,
- "attery": 16296,
- "atti": 34891,
- "attle": 1999,
- "attled": 43535,
- "atto": 45807,
- "atton": 38680,
- "attr": 35226,
- "attribute": 42348,
- "atts": 30353,
- "atu": 33419,
- "atum": 21307,
- "atur": 2541,
- "atural": 2660,
- "aturally": 7436,
- "aturated": 30192,
- "aturation": 36921,
- "aturday": 3658,
- "aturdays": 39724,
- "ature": 1300,
- "atures": 6691,
- "atus": 7240,
- "atz": 27906,
- "au": 559,
- "auc": 14272,
- "aucas": 25205,
- "aucus": 16710,
- "aucuses": 38271,
- "aud": 3885,
- "auder": 29233,
- "audi": 31330,
- "audio": 24051,
- "auer": 16261,
- "aug": 7493,
- "auga": 44718,
- "augh": 1567,
- "aughed": 13726,
- "aughlin": 42730,
- "aughs": 19256,
- "aught": 3413,
- "aughter": 3637,
- "aughtered": 32734,
- "aughters": 13441,
- "aughty": 28496,
- "aukee": 15263,
- "aul": 2518,
- "auld": 30406,
- "auldron": 45637,
- "ault": 1721,
- "aults": 13185,
- "aum": 26043,
- "aun": 1942,
- "auna": 32837,
- "aunch": 11429,
- "aund": 14677,
- "aunder": 21118,
- "aundering": 23496,
- "aunders": 32818,
- "aunt": 12968,
- "aunted": 20227,
- "aunting": 20706,
- "auntlet": 32633,
- "auntlets": 39695,
- "aunts": 43981,
- "aur": 2899,
- "aura": 33830,
- "auri": 35190,
- "aurus": 22302,
- "aus": 8717,
- "ause": 682,
- "ausible": 17178,
- "aut": 2306,
- "auth": 18439,
- "authent": 41299,
- "author": 9800,
- "authored": 39351,
- "authorized": 19721,
- "authors": 41617,
- "autical": 37073,
- "aution": 32917,
- "autions": 28766,
- "auto": 23736,
- "automatic": 37800,
- "auts": 17712,
- "aux": 14644,
- "av": 615,
- "ava": 4170,
- "avage": 33757,
- "availability": 47274,
- "available": 15182,
- "aval": 9226,
- "avan": 12421,
- "avanaugh": 19872,
- "avascript": 16098,
- "ave": 1015,
- "aved": 9586,
- "avement": 44034,
- "aven": 4005,
- "aver": 8770,
- "average": 23913,
- "avering": 42610,
- "avers": 30400,
- "avery": 12447,
- "aves": 3080,
- "avez": 28851,
- "avi": 15820,
- "avia": 40543,
- "avid": 8490,
- "avier": 19492,
- "avin": 20637,
- "aving": 2703,
- "avior": 15759,
- "aviour": 37716,
- "avis": 23401,
- "avoid": 27080,
- "avor": 5570,
- "avorable": 32006,
- "avored": 48275,
- "avorite": 19227,
- "avour": 29023,
- "avy": 2830,
- "aw": 707,
- "awa": 6909,
- "awaited": 41742,
- "awan": 43004,
- "awar": 48841,
- "aware": 9685,
- "awareness": 47812,
- "awaru": 39008,
- "awatts": 46684,
- "away": 8272,
- "aways": 23949,
- "awed": 36825,
- "awei": 38247,
- "awi": 23368,
- "awk": 19301,
- "awks": 11890,
- "awn": 3832,
- "aws": 8356,
- "ax": 897,
- "axe": 38231,
- "axies": 25472,
- "axis": 22704,
- "axter": 40864,
- "axy": 6969,
- "ay": 323,
- "aya": 11729,
- "ayan": 22931,
- "aye": 48822,
- "ayed": 16548,
- "ayer": 2794,
- "ayers": 6962,
- "ayette": 27067,
- "aying": 8369,
- "aylor": 7167,
- "ayn": 49987,
- "ayne": 43906,
- "ays": 592,
- "ayson": 34907,
- "az": 1031,
- "aza": 7056,
- "azaar": 34485,
- "azaki": 32276,
- "azar": 29413,
- "azard": 26267,
- "aze": 6201,
- "azed": 13865,
- "azeera": 28535,
- "azel": 41319,
- "azer": 19178,
- "azes": 36096,
- "azi": 7761,
- "azine": 4994,
- "azines": 15742,
- "azing": 4070,
- "azo": 44299,
- "azon": 5168,
- "azor": 17725,
- "azy": 12582,
- "azz": 8101,
- "b": 65,
- "ba": 7012,
- "bable": 33460,
- "bably": 11921,
- "baby": 40252,
- "bach": 19496,
- "back": 1891,
- "backed": 17078,
- "backer": 49978,
- "background": 25249,
- "backs": 10146,
- "bad": 14774,
- "bag": 21454,
- "bage": 13866,
- "bags": 34005,
- "bah": 47041,
- "bal": 6893,
- "balance": 20427,
- "balanced": 27753,
- "ball": 1894,
- "balls": 21591,
- "ban": 3820,
- "band": 3903,
- "bands": 21397,
- "bane": 20235,
- "bang": 36668,
- "bank": 17796,
- "banks": 43558,
- "bar": 5657,
- "bara": 39389,
- "bard": 23024,
- "bare": 49382,
- "bars": 34046,
- "bart": 16575,
- "bas": 12093,
- "base": 8692,
- "based": 3106,
- "bash": 41757,
- "basic": 35487,
- "basketball": 21265,
- "bass": 42933,
- "bat": 8664,
- "batch": 43501,
- "bath": 37648,
- "bats": 50199,
- "battle": 38471,
- "baugh": 23768,
- "baum": 24738,
- "bay": 24406,
- "bb": 11848,
- "bc": 15630,
- "bd": 17457,
- "bda": 43444,
- "be": 1350,
- "beam": 40045,
- "bean": 14289,
- "beans": 44749,
- "bear": 33227,
- "beard": 39433,
- "bearing": 28655,
- "beat": 12945,
- "beaut": 40544,
- "bec": 9423,
- "because": 13893,
- "becca": 20627,
- "beck": 27343,
- "becue": 31927,
- "bed": 3077,
- "bedroom": 36269,
- "bee": 20963,
- "been": 47436,
- "beer": 42428,
- "bees": 41712,
- "before": 19052,
- "begin": 27471,
- "beh": 20709,
- "behavior": 46571,
- "behind": 42200,
- "being": 11873,
- "beit": 15357,
- "bek": 47083,
- "bel": 6667,
- "bell": 7923,
- "below": 35993,
- "belt": 37976,
- "ben": 11722,
- "bench": 26968,
- "bender": 45666,
- "bending": 49667,
- "benef": 36934,
- "benefit": 48649,
- "bent": 46119,
- "ber": 527,
- "bered": 9451,
- "berg": 3900,
- "berger": 21041,
- "berman": 34591,
- "bern": 33900,
- "bernatorial": 43660,
- "berra": 31358,
- "berries": 20853,
- "berry": 8396,
- "bers": 1213,
- "bert": 4835,
- "berto": 32371,
- "berus": 39192,
- "bery": 13001,
- "bes": 12636,
- "best": 13466,
- "bestos": 40651,
- "bet": 11181,
- "beta": 31361,
- "bett": 48138,
- "better": 27903,
- "between": 23395,
- "bey": 23454,
- "bf": 19881,
- "bg": 35904,
- "bh": 34369,
- "bi": 8482,
- "bia": 23339,
- "bial": 25200,
- "bian": 12210,
- "bians": 30071,
- "biased": 38002,
- "bid": 14065,
- "bidden": 37978,
- "bie": 12590,
- "bies": 29846,
- "big": 14261,
- "bike": 32256,
- "bil": 33473,
- "bill": 35546,
- "billion": 24540,
- "bilt": 34508,
- "bin": 8800,
- "binary": 39491,
- "bind": 21653,
- "binding": 30786,
- "bing": 4623,
- "biology": 43592,
- "bird": 16944,
- "birds": 32002,
- "birth": 24280,
- "bis": 41907,
- "bish": 31795,
- "bishop": 27832,
- "bit": 2545,
- "bitcoin": 35395,
- "bite": 37018,
- "bitious": 14228,
- "bits": 9895,
- "biz": 42189,
- "bj": 50007,
- "bl": 2436,
- "black": 13424,
- "blade": 22500,
- "blance": 42757,
- "blank": 27190,
- "blast": 39806,
- "ble": 903,
- "bleacher": 47975,
- "bled": 9342,
- "bledon": 49258,
- "blem": 11253,
- "blems": 22143,
- "bler": 43400,
- "blers": 43022,
- "bles": 7689,
- "bley": 43263,
- "blind": 27461,
- "bling": 11108,
- "block": 9967,
- "blocking": 41938,
- "blocks": 27372,
- "blog": 14036,
- "blogs": 49096,
- "blogspot": 35217,
- "blood": 18041,
- "blooded": 50132,
- "blow": 48619,
- "blown": 31290,
- "blue": 17585,
- "bly": 36874,
- "bm": 20475,
- "bn": 9374,
- "bnb": 31971,
- "bo": 2127,
- "boa": 48614,
- "board": 3526,
- "boarding": 27794,
- "boards": 12821,
- "boat": 24482,
- "boats": 46058,
- "bodied": 45190,
- "body": 2618,
- "bol": 28984,
- "bold": 36575,
- "bole": 45693,
- "bolt": 25593,
- "bomb": 27657,
- "bon": 4189,
- "bone": 15992,
- "bones": 35095,
- "bons": 23461,
- "book": 2070,
- "books": 12106,
- "bool": 30388,
- "boost": 39521,
- "boot": 18769,
- "bor": 2865,
- "border": 20192,
- "borg": 23297,
- "borgh": 49870,
- "born": 6286,
- "borne": 13555,
- "boro": 21513,
- "borough": 17913,
- "bors": 32289,
- "bos": 39565,
- "boss": 42820,
- "bot": 13645,
- "both": 16885,
- "bots": 42478,
- "bott": 10985,
- "bottom": 22487,
- "bound": 7784,
- "bour": 6084,
- "bourg": 24256,
- "bourne": 12544,
- "bow": 8176,
- "bowl": 36859,
- "bows": 25435,
- "box": 3524,
- "boxes": 29305,
- "boxing": 45471,
- "boy": 7081,
- "boys": 13202,
- "bp": 46583,
- "bps": 18799,
- "br": 1671,
- "bra": 16057,
- "brace": 46565,
- "brain": 27825,
- "brainer": 49334,
- "bral": 24427,
- "brance": 28031,
- "brand": 17938,
- "branded": 35559,
- "braska": 17088,
- "brate": 40804,
- "brates": 44835,
- "bre": 4679,
- "bread": 29573,
- "break": 9032,
- "breaker": 25766,
- "breakers": 49295,
- "breaking": 13395,
- "breaks": 30058,
- "bred": 36074,
- "breeding": 49705,
- "brew": 11269,
- "brid": 10236,
- "bridge": 9458,
- "brids": 40637,
- "bright": 29199,
- "bring": 48580,
- "bringer": 48046,
- "bringing": 35749,
- "bris": 15311,
- "bro": 7957,
- "broad": 36654,
- "broken": 25826,
- "brook": 19094,
- "brother": 37343,
- "brow": 25367,
- "brown": 33282,
- "browser": 40259,
- "brush": 32680,
- "bryce": 32524,
- "bs": 1443,
- "bsite": 12485,
- "bsp": 24145,
- "bt": 18347,
- "btn": 46118,
- "bu": 11110,
- "bub": 46176,
- "buck": 27041,
- "bucks": 18999,
- "budget": 37315,
- "buf": 29325,
- "buff": 36873,
- "buffer": 22252,
- "bug": 25456,
- "bugs": 32965,
- "build": 11249,
- "builder": 38272,
- "builders": 50034,
- "building": 16894,
- "built": 18780,
- "bul": 15065,
- "bull": 16308,
- "bum": 4435,
- "buquerque": 36461,
- "bur": 6236,
- "burg": 7423,
- "burgh": 9228,
- "burn": 10899,
- "burning": 44313,
- "burse": 21780,
- "burst": 31961,
- "bury": 10711,
- "bus": 10885,
- "bush": 50231,
- "business": 22680,
- "buster": 24899,
- "busters": 30181,
- "but": 4360,
- "butt": 43059,
- "button": 16539,
- "buy": 17846,
- "by": 1525,
- "bye": 16390,
- "byn": 14929,
- "bys": 48209,
- "byss": 15040,
- "byte": 26327,
- "byter": 36204,
- "bytes": 33661,
- "c": 66,
- "ca": 6888,
- "cache": 23870,
- "cade": 46395,
- "cair": 37155,
- "cake": 30560,
- "cakes": 37263,
- "cal": 9948,
- "cale": 38765,
- "caliber": 43288,
- "call": 13345,
- "callback": 47423,
- "called": 7174,
- "calling": 44714,
- "cam": 20991,
- "camera": 25695,
- "camp": 16544,
- "campaign": 35012,
- "campus": 43842,
- "can": 5171,
- "cancer": 48870,
- "cand": 46188,
- "cano": 35490,
- "canon": 49883,
- "cap": 11128,
- "capacity": 42404,
- "cape": 36435,
- "capital": 27544,
- "capitalist": 49970,
- "caps": 27979,
- "capt": 27144,
- "car": 7718,
- "carb": 35684,
- "carbon": 29255,
- "card": 9517,
- "cards": 27761,
- "care": 6651,
- "carry": 34993,
- "cars": 37993,
- "cart": 26674,
- "cas": 34004,
- "case": 7442,
- "cases": 33964,
- "cash": 30350,
- "cast": 2701,
- "caster": 17970,
- "casters": 26248,
- "casting": 19913,
- "castle": 18676,
- "casts": 40924,
- "cat": 9246,
- "catch": 40198,
- "catching": 50106,
- "category": 22872,
- "catentry": 39165,
- "cation": 30907,
- "cats": 24619,
- "cause": 25587,
- "cb": 21101,
- "cc": 535,
- "cca": 13227,
- "ccess": 1591,
- "cci": 35764,
- "ccoli": 34544,
- "ccording": 2941,
- "cd": 10210,
- "cdn": 32341,
- "ce": 344,
- "cean": 5829,
- "ceans": 19961,
- "ced": 771,
- "cedented": 12292,
- "cedes": 19285,
- "ceed": 2707,
- "ceivable": 48054,
- "ceive": 15164,
- "ceived": 6471,
- "ceiver": 39729,
- "cel": 5276,
- "cele": 49840,
- "celer": 7015,
- "cell": 3846,
- "cellaneous": 25673,
- "cellence": 19801,
- "cellent": 5666,
- "cells": 46342,
- "celona": 14308,
- "cember": 3273,
- "cemic": 40478,
- "cence": 43696,
- "cend": 15695,
- "cens": 42595,
- "cent": 1087,
- "center": 16159,
- "centered": 38050,
- "central": 31463,
- "centric": 28577,
- "century": 14792,
- "cephal": 43996,
- "cept": 984,
- "ception": 4516,
- "ceptions": 11755,
- "ceptive": 25867,
- "ceptor": 49492,
- "cer": 2189,
- "cern": 30903,
- "cerned": 49990,
- "cerning": 41981,
- "cerpt": 17040,
- "cers": 7999,
- "cert": 22583,
- "certain": 39239,
- "cery": 12757,
- "ces": 728,
- "cess": 919,
- "cession": 43914,
- "cessive": 45428,
- "cest": 9165,
- "cester": 33187,
- "cf": 12993,
- "cffff": 31727,
- "cffffcc": 31957,
- "cfg": 37581,
- "cgi": 37157,
- "ch": 354,
- "cha": 11693,
- "chain": 7983,
- "chains": 38861,
- "chair": 16337,
- "chairs": 49655,
- "chal": 38009,
- "chall": 36747,
- "cham": 49869,
- "chan": 3147,
- "chance": 39486,
- "change": 3803,
- "changed": 40985,
- "changes": 36653,
- "changing": 22954,
- "channel": 17620,
- "channelAvailability": 39757,
- "chant": 8907,
- "chanted": 28923,
- "chapter": 43582,
- "char": 10641,
- "character": 22769,
- "chard": 30215,
- "charg": 11121,
- "charge": 10136,
- "charged": 17200,
- "charges": 34948,
- "charging": 31498,
- "chart": 40926,
- "chat": 17006,
- "che": 2395,
- "cheat": 46799,
- "check": 9122,
- "checked": 26752,
- "checking": 41004,
- "checks": 42116,
- "ched": 1740,
- "chedel": 24015,
- "chel": 29232,
- "chell": 12398,
- "chem": 15245,
- "chemical": 31379,
- "chemist": 28899,
- "chemy": 26599,
- "chen": 6607,
- "chenko": 45059,
- "chens": 29937,
- "cheon": 40556,
- "cher": 2044,
- "chers": 3533,
- "chery": 31132,
- "ches": 2052,
- "chest": 46713,
- "chester": 35983,
- "chet": 20043,
- "chev": 49916,
- "chi": 11072,
- "chid": 28402,
- "chie": 3043,
- "chief": 17351,
- "chieve": 24957,
- "child": 9410,
- "children": 17197,
- "chin": 24658,
- "ching": 10813,
- "chini": 45045,
- "chio": 40900,
- "chip": 35902,
- "chlor": 36813,
- "chn": 1349,
- "chnology": 19587,
- "cho": 6679,
- "choes": 23001,
- "choice": 25541,
- "chool": 1251,
- "christ": 43533,
- "chrom": 28663,
- "chrome": 46659,
- "chron": 11413,
- "cht": 21474,
- "chu": 46417,
- "chuk": 46019,
- "church": 36964,
- "chwitz": 36297,
- "chy": 29658,
- "ci": 979,
- "cia": 33743,
- "cial": 2413,
- "cially": 2131,
- "ciating": 46136,
- "ciation": 17269,
- "cible": 37369,
- "cience": 4234,
- "cient": 3456,
- "cientious": 43037,
- "cients": 35611,
- "cies": 3171,
- "cific": 7790,
- "cig": 22683,
- "cigarette": 46040,
- "cigarettes": 32529,
- "cil": 2856,
- "cill": 20346,
- "cin": 17879,
- "cing": 2259,
- "cious": 4680,
- "cipl": 6671,
- "cipled": 41296,
- "ciples": 6418,
- "ciplinary": 29386,
- "cipline": 34647,
- "circ": 21170,
- "circle": 45597,
- "cise": 37561,
- "cised": 37168,
- "cision": 16005,
- "cit": 47992,
- "citizens": 46801,
- "city": 19205,
- "cium": 16910,
- "cius": 28599,
- "civil": 37636,
- "ck": 694,
- "cker": 15280,
- "cki": 49108,
- "cking": 44377,
- "cknow": 5319,
- "cknowled": 33165,
- "cko": 37549,
- "cks": 4657,
- "cl": 565,
- "clad": 29853,
- "claim": 6604,
- "claimed": 12795,
- "claimer": 17111,
- "clair": 27659,
- "clamation": 20931,
- "class": 4871,
- "classes": 37724,
- "classic": 49421,
- "classified": 31691,
- "clave": 44281,
- "claw": 43143,
- "cle": 2375,
- "clean": 27773,
- "clear": 20063,
- "cled": 20095,
- "cler": 22902,
- "clerosis": 31399,
- "cles": 5427,
- "cli": 44506,
- "click": 12976,
- "client": 16366,
- "cliffe": 33783,
- "climate": 42570,
- "cling": 8493,
- "clinical": 47367,
- "clinton": 37821,
- "clip": 15036,
- "clips": 31945,
- "clipse": 17043,
- "clock": 15750,
- "clone": 21018,
- "cloneembedreportprint": 30899,
- "close": 19836,
- "closed": 20225,
- "closure": 17966,
- "cloth": 44905,
- "cloud": 17721,
- "club": 18664,
- "clud": 758,
- "clude": 9152,
- "cluded": 10341,
- "cludes": 13955,
- "cluding": 6360,
- "clus": 2527,
- "clusion": 4717,
- "clusions": 11539,
- "clusive": 5731,
- "clusively": 44307,
- "cm": 11215,
- "cmd": 28758,
- "cmp": 48991,
- "cms": 46406,
- "cn": 31522,
- "co": 1073,
- "coal": 25140,
- "coat": 31434,
- "cock": 21517,
- "cod": 19815,
- "code": 8189,
- "coded": 40976,
- "codes": 40148,
- "coe": 49270,
- "cohol": 4857,
- "coin": 3630,
- "coins": 14624,
- "col": 4033,
- "cold": 36673,
- "coll": 26000,
- "collar": 37676,
- "collect": 33327,
- "collection": 43681,
- "college": 44107,
- "colm": 18414,
- "colo": 45745,
- "colonial": 49787,
- "color": 8043,
- "colored": 25717,
- "colour": 49903,
- "column": 28665,
- "com": 785,
- "comb": 24011,
- "combat": 39969,
- "combe": 49325,
- "come": 2958,
- "comed": 15128,
- "comes": 8988,
- "comfort": 21598,
- "coming": 4976,
- "comings": 30715,
- "comm": 9503,
- "command": 21812,
- "comment": 23893,
- "comments": 15944,
- "commerce": 27061,
- "commercial": 36313,
- "commit": 41509,
- "committee": 26799,
- "common": 11321,
- "commun": 10709,
- "communication": 32560,
- "communications": 20860,
- "community": 28158,
- "comp": 5589,
- "compan": 34390,
- "company": 39722,
- "compatible": 38532,
- "competitive": 46131,
- "compl": 23855,
- "complete": 20751,
- "completely": 46699,
- "complex": 41887,
- "compliance": 47587,
- "component": 42895,
- "computer": 33215,
- "con": 1102,
- "concept": 43169,
- "concert": 48415,
- "cond": 17561,
- "condition": 31448,
- "conduct": 36495,
- "cone": 49180,
- "conf": 10414,
- "conference": 41124,
- "confidence": 39745,
- "config": 11250,
- "confirmed": 36349,
- "cong": 36801,
- "coni": 45774,
- "conn": 37043,
- "connect": 8443,
- "connected": 15236,
- "connection": 38659,
- "conom": 1519,
- "cons": 5936,
- "conscious": 16796,
- "conserv": 38925,
- "conservancy": 41215,
- "conservative": 43218,
- "consider": 44353,
- "console": 41947,
- "const": 9979,
- "constitutional": 18789,
- "construct": 41571,
- "consumer": 49827,
- "consuming": 35873,
- "cont": 3642,
- "contact": 32057,
- "contained": 45964,
- "container": 34924,
- "containing": 38301,
- "content": 11299,
- "context": 22866,
- "contin": 18487,
- "continental": 35415,
- "continue": 43043,
- "contract": 28484,
- "control": 13716,
- "controlled": 14401,
- "controller": 36500,
- "conv": 42946,
- "cook": 27916,
- "cooked": 46591,
- "cookie": 44453,
- "cool": 24494,
- "coon": 20912,
- "coord": 37652,
- "cop": 22163,
- "copy": 30073,
- "cor": 10215,
- "core": 7295,
- "corn": 20772,
- "correct": 30283,
- "corruption": 46260,
- "cos": 6966,
- "cost": 15805,
- "cosystem": 12541,
- "cot": 25557,
- "cott": 14612,
- "could": 24089,
- "count": 9127,
- "counter": 24588,
- "country": 19315,
- "cour": 43220,
- "course": 17319,
- "court": 22230,
- "cover": 9631,
- "covered": 32111,
- "cow": 8232,
- "cox": 40359,
- "cp": 13155,
- "cpp": 20322,
- "cpu": 36166,
- "cr": 6098,
- "craft": 3323,
- "crafted": 39160,
- "crazy": 50112,
- "cre": 7513,
- "cream": 36277,
- "creat": 20123,
- "create": 17953,
- "created": 25598,
- "creation": 38793,
- "creator": 45382,
- "credit": 43082,
- "creen": 32060,
- "crete": 38669,
- "crew": 42276,
- "cribed": 32968,
- "crim": 50086,
- "crime": 28126,
- "criminal": 45955,
- "cript": 6519,
- "cription": 6820,
- "criptions": 24370,
- "crit": 22213,
- "critical": 34666,
- "cro": 19915,
- "croft": 36714,
- "crop": 31476,
- "cross": 19692,
- "crow": 47114,
- "cru": 32838,
- "cry": 20470,
- "crypt": 29609,
- "cs": 6359,
- "css": 25471,
- "csv": 40664,
- "ct": 310,
- "ctic": 11048,
- "ctica": 28914,
- "ction": 596,
- "ctions": 2733,
- "ctive": 14070,
- "ctl": 34168,
- "ctor": 2715,
- "ctors": 5217,
- "ctory": 25977,
- "ctr": 24087,
- "ctrl": 44755,
- "ctuary": 15258,
- "cture": 48715,
- "ctx": 49464,
- "cu": 27399,
- "cube": 40296,
- "cue": 15509,
- "cul": 3129,
- "cular": 10440,
- "culated": 49262,
- "culation": 14902,
- "cule": 23172,
- "cules": 13930,
- "culosis": 38767,
- "cult": 40820,
- "cultural": 30844,
- "culture": 25584,
- "culus": 17576,
- "cum": 36340,
- "cup": 25244,
- "cur": 22019,
- "currency": 34415,
- "current": 14421,
- "currently": 41745,
- "cus": 9042,
- "cussion": 36262,
- "custom": 23144,
- "cut": 8968,
- "cuts": 23779,
- "cutting": 29753,
- "cv": 33967,
- "cy": 948,
- "cycl": 15539,
- "cycle": 13696,
- "cycles": 32503,
- "cyclop": 22873,
- "cyclopedia": 25497,
- "cyl": 38801,
- "cz": 26691,
- "cé": 32682,
- "d": 67,
- "dB": 36077,
- "dL": 45582,
- "da": 6814,
- "dad": 47984,
- "daily": 29468,
- "dain": 27162,
- "dal": 31748,
- "dale": 14597,
- "dam": 11043,
- "damage": 28735,
- "dan": 25604,
- "danger": 38537,
- "daq": 48539,
- "dar": 27455,
- "dark": 21953,
- "dash": 42460,
- "dat": 19608,
- "data": 7890,
- "database": 48806,
- "date": 4475,
- "dated": 8715,
- "dates": 19581,
- "dating": 38734,
- "daughter": 29642,
- "day": 820,
- "dayName": 45392,
- "days": 12545,
- "db": 9945,
- "dc": 17896,
- "dd": 1860,
- "dden": 4742,
- "dding": 33403,
- "dds": 33714,
- "de": 2934,
- "dead": 25124,
- "deal": 31769,
- "deals": 14302,
- "death": 22595,
- "deb": 11275,
- "debian": 24689,
- "debug": 24442,
- "dec": 12501,
- "deck": 35875,
- "decl": 32446,
- "ded": 9395,
- "deen": 39060,
- "deep": 22089,
- "def": 4299,
- "default": 12286,
- "defense": 19774,
- "define": 13086,
- "defined": 23211,
- "definition": 46758,
- "deg": 13500,
- "degree": 16863,
- "del": 12381,
- "delay": 40850,
- "delete": 33678,
- "dem": 9536,
- "demand": 28550,
- "democracy": 42017,
- "democratic": 41232,
- "demon": 26567,
- "den": 6559,
- "density": 43337,
- "dep": 10378,
- "depend": 45841,
- "dependent": 21186,
- "depending": 44023,
- "depth": 18053,
- "der": 1082,
- "derived": 34631,
- "des": 8906,
- "desc": 20147,
- "described": 34869,
- "description": 11213,
- "design": 26124,
- "designed": 30473,
- "desktop": 41375,
- "despite": 41081,
- "dest": 16520,
- "destroy": 41659,
- "destruct": 35678,
- "det": 15255,
- "detail": 49170,
- "details": 36604,
- "determination": 40869,
- "dev": 7959,
- "develop": 16244,
- "developed": 33082,
- "development": 31267,
- "device": 25202,
- "devices": 42034,
- "df": 7568,
- "dfx": 48753,
- "dh": 34985,
- "di": 10989,
- "diagn": 47356,
- "dial": 38969,
- "dict": 11600,
- "did": 20839,
- "didn": 45168,
- "die": 11979,
- "dies": 25990,
- "diff": 26069,
- "different": 39799,
- "dig": 12894,
- "digit": 27003,
- "digital": 34725,
- "digy": 41923,
- "dim": 27740,
- "dimension": 46156,
- "dimensional": 19577,
- "din": 25194,
- "dinand": 41993,
- "ding": 12083,
- "dir": 15908,
- "direct": 12942,
- "directed": 34762,
- "direction": 37295,
- "director": 35248,
- "directory": 34945,
- "dirty": 49075,
- "dis": 6381,
- "disable": 40223,
- "disabled": 47730,
- "disc": 15410,
- "disciplinary": 40625,
- "discrimination": 42723,
- "disk": 39531,
- "display": 13812,
- "displayText": 31536,
- "dist": 17080,
- "distance": 30246,
- "dit": 5266,
- "div": 7146,
- "division": 21426,
- "dj": 28241,
- "dk": 34388,
- "dl": 25404,
- "dll": 12736,
- "dm": 36020,
- "dn": 32656,
- "do": 4598,
- "doc": 15390,
- "docker": 45986,
- "docs": 31628,
- "doctor": 35580,
- "doctoral": 44064,
- "document": 22897,
- "documented": 47045,
- "does": 22437,
- "doesn": 45084,
- "dog": 9703,
- "dogs": 22242,
- "doi": 34023,
- "doing": 19631,
- "dollar": 22569,
- "dom": 3438,
- "domain": 27830,
- "dominated": 34475,
- "doms": 23686,
- "don": 9099,
- "donald": 40915,
- "done": 28060,
- "door": 9424,
- "doors": 19559,
- "dor": 40180,
- "dos": 37427,
- "dose": 34436,
- "dot": 26518,
- "double": 23352,
- "down": 2902,
- "download": 15002,
- "downs": 30371,
- "dozen": 44932,
- "dp": 26059,
- "dq": 49506,
- "dr": 7109,
- "dra": 32491,
- "draft": 35679,
- "dragon": 14844,
- "draw": 19334,
- "drawn": 41549,
- "dream": 25966,
- "dress": 49380,
- "dri": 7553,
- "drive": 19472,
- "driven": 15808,
- "driver": 26230,
- "drivers": 36702,
- "driving": 24255,
- "drm": 49007,
- "dro": 22285,
- "drop": 14781,
- "dropping": 37554,
- "drops": 49253,
- "drug": 30349,
- "dry": 39140,
- "ds": 9310,
- "dt": 28664,
- "du": 646,
- "duc": 6077,
- "ducers": 41213,
- "duct": 2359,
- "duction": 11124,
- "due": 23301,
- "duino": 24493,
- "dule": 5950,
- "dullah": 23969,
- "dump": 39455,
- "duration": 32257,
- "during": 42122,
- "dust": 48859,
- "duty": 26278,
- "dx": 34350,
- "dy": 9892,
- "dyl": 30360,
- "dylib": 31739,
- "e": 68,
- "ea": 18213,
- "each": 27379,
- "ead": 1329,
- "eah": 4617,
- "eal": 2287,
- "ealing": 26919,
- "ealous": 15746,
- "eals": 10621,
- "ean": 11025,
- "eanor": 17663,
- "ear": 451,
- "earable": 40816,
- "earance": 23435,
- "earances": 35630,
- "earch": 3679,
- "earcher": 50194,
- "earchers": 16604,
- "eared": 3380,
- "earing": 6648,
- "early": 11458,
- "earned": 39123,
- "ears": 4127,
- "earth": 16442,
- "eas": 30412,
- "east": 23316,
- "easy": 38171,
- "eat": 4098,
- "eating": 30041,
- "eatured": 20980,
- "eatures": 11585,
- "eaturing": 31347,
- "eb": 1765,
- "ebin": 23497,
- "ebook": 16497,
- "ebra": 37052,
- "ebted": 35895,
- "ebus": 33209,
- "ec": 721,
- "eca": 31047,
- "ecake": 46557,
- "ecast": 43299,
- "ecause": 3156,
- "ecd": 21142,
- "ech": 3055,
- "eches": 16672,
- "echo": 30328,
- "ecided": 35503,
- "eco": 47704,
- "econom": 13926,
- "economic": 17079,
- "ect": 478,
- "ectar": 44504,
- "ected": 11197,
- "ection": 3213,
- "ective": 13967,
- "ectomy": 42505,
- "ector": 9250,
- "ecycle": 47510,
- "ed": 276,
- "edIn": 20801,
- "eda": 18082,
- "edar": 44226,
- "eday": 23712,
- "edd": 6048,
- "edded": 47238,
- "eddy": 21874,
- "ede": 18654,
- "eded": 15395,
- "eden": 31829,
- "eder": 5702,
- "ederal": 2110,
- "ederation": 9748,
- "edes": 37507,
- "edge": 14907,
- "edged": 48916,
- "edi": 13740,
- "edia": 5507,
- "edience": 20826,
- "edient": 35279,
- "edin": 27152,
- "eding": 8228,
- "edit": 19312,
- "edited": 42131,
- "edition": 28736,
- "editor": 35352,
- "edly": 49288,
- "edo": 24757,
- "edom": 3836,
- "eds": 5379,
- "edu": 15532,
- "educ": 18123,
- "educated": 27317,
- "education": 40796,
- "edy": 4716,
- "ee": 1453,
- "eed": 2308,
- "eeds": 39642,
- "eeee": 41591,
- "eeks": 32201,
- "eele": 26213,
- "eely": 45269,
- "eem": 13761,
- "een": 6429,
- "eenth": 28117,
- "eeper": 41278,
- "eer": 28153,
- "eering": 48066,
- "eers": 47619,
- "ees": 2841,
- "eez": 33105,
- "ef": 891,
- "efe": 22521,
- "efeated": 36807,
- "efer": 41027,
- "eff": 14822,
- "effect": 10760,
- "effective": 16803,
- "effects": 34435,
- "effic": 24531,
- "efficiency": 45888,
- "efficient": 16814,
- "efficients": 41945,
- "efined": 18156,
- "eful": 13839,
- "efully": 7549,
- "eg": 1533,
- "ega": 26470,
- "egal": 39839,
- "eger": 11893,
- "egg": 33856,
- "egu": 15703,
- "eh": 17231,
- "ei": 20295,
- "eight": 26022,
- "either": 31336,
- "ek": 988,
- "eka": 38001,
- "eker": 28233,
- "eki": 39548,
- "eking": 18754,
- "eks": 2573,
- "el": 417,
- "ela": 10304,
- "elaide": 25078,
- "eland": 8822,
- "elcome": 9571,
- "ele": 11129,
- "elect": 9509,
- "elected": 28604,
- "election": 14300,
- "electric": 31067,
- "eled": 18449,
- "element": 30854,
- "eless": 5321,
- "elf": 7046,
- "elfare": 27122,
- "elfth": 44659,
- "eli": 43733,
- "elia": 25418,
- "elight": 49984,
- "eligible": 31595,
- "elin": 27176,
- "eline": 4470,
- "elines": 20655,
- "eling": 10809,
- "elist": 46331,
- "ell": 695,
- "ella": 12627,
- "ellar": 14203,
- "ellation": 28828,
- "elle": 13485,
- "ellect": 6879,
- "ellectual": 29706,
- "elled": 11978,
- "ellen": 40635,
- "eller": 12368,
- "ellery": 41800,
- "elli": 23225,
- "ellig": 2976,
- "elligence": 3480,
- "elligent": 32940,
- "elling": 9417,
- "ello": 11109,
- "ellow": 5037,
- "ells": 19187,
- "elly": 6148,
- "elman": 32370,
- "eln": 45542,
- "elo": 22126,
- "elong": 21537,
- "elope": 47329,
- "els": 1424,
- "else": 17772,
- "elsen": 25328,
- "elsh": 21564,
- "elsius": 32495,
- "elson": 10151,
- "elt": 2120,
- "elta": 12514,
- "elve": 9954,
- "elvet": 32667,
- "em": 368,
- "ema": 19687,
- "emade": 21398,
- "email": 12888,
- "emaker": 32174,
- "emale": 10144,
- "eman": 8463,
- "emark": 47626,
- "emate": 47686,
- "emb": 24419,
- "embed": 20521,
- "embedreportprint": 30898,
- "ember": 1491,
- "eme": 34755,
- "emed": 9006,
- "emen": 8952,
- "ement": 972,
- "ements": 3196,
- "emer": 24677,
- "emet": 19261,
- "emetery": 19785,
- "emi": 43967,
- "emia": 22859,
- "emic": 5314,
- "emies": 5090,
- "emin": 14857,
- "eming": 46564,
- "emis": 30561,
- "emn": 37705,
- "emo": 41903,
- "emon": 7966,
- "emonic": 50016,
- "emonium": 33044,
- "emort": 24466,
- "emouth": 46880,
- "emp": 45787,
- "emphasis": 36663,
- "empl": 18856,
- "employ": 7033,
- "employed": 36266,
- "employment": 28812,
- "emporary": 33080,
- "empt": 1791,
- "emption": 11221,
- "empty": 28920,
- "ems": 5232,
- "emy": 3065,
- "en": 268,
- "ena": 8107,
- "enable": 21633,
- "enabled": 25616,
- "ename": 12453,
- "enance": 36368,
- "enaries": 30216,
- "enario": 39055,
- "enary": 21629,
- "enberg": 23140,
- "enburg": 37036,
- "enc": 12685,
- "ence": 594,
- "enced": 5864,
- "encer": 12137,
- "encers": 42288,
- "ences": 3007,
- "ench": 24421,
- "encia": 29634,
- "encies": 3976,
- "encing": 9532,
- "encrypted": 43628,
- "ency": 1387,
- "end": 437,
- "enda": 7438,
- "endale": 41147,
- "endant": 23048,
- "endants": 30841,
- "endar": 9239,
- "endars": 44942,
- "endas": 35624,
- "ende": 38396,
- "ended": 1631,
- "ender": 2194,
- "endered": 30398,
- "enders": 7338,
- "endez": 41913,
- "endi": 43109,
- "endiary": 43034,
- "endif": 32088,
- "ending": 1571,
- "endish": 48442,
- "endium": 49811,
- "endix": 19573,
- "endment": 5904,
- "endo": 31110,
- "endon": 43153,
- "endor": 18738,
- "endra": 48286,
- "ends": 2412,
- "endum": 43755,
- "ene": 1734,
- "ened": 2945,
- "eneg": 46495,
- "enegger": 44028,
- "enei": 46009,
- "enemy": 46970,
- "ener": 877,
- "energy": 22554,
- "eners": 36014,
- "enery": 24156,
- "enes": 18719,
- "eness": 9449,
- "enez": 11437,
- "enezuel": 12596,
- "enf": 33701,
- "enforcement": 44976,
- "enfranch": 39827,
- "eng": 1516,
- "enge": 3540,
- "engeance": 21364,
- "enged": 47422,
- "enger": 6540,
- "engers": 9302,
- "enges": 34120,
- "engine": 18392,
- "engineering": 40321,
- "english": 39126,
- "ength": 3286,
- "engu": 13561,
- "enh": 16550,
- "enhagen": 30347,
- "eni": 43850,
- "enic": 35866,
- "ening": 3101,
- "enium": 47477,
- "enko": 32720,
- "enment": 23242,
- "enn": 1697,
- "enna": 13713,
- "enne": 29727,
- "ennes": 42573,
- "ennett": 48151,
- "ennial": 27779,
- "ennis": 10679,
- "enny": 11870,
- "eno": 23397,
- "enos": 28380,
- "enough": 48229,
- "ens": 641,
- "ensable": 33447,
- "ensation": 25742,
- "ense": 1072,
- "ensed": 15385,
- "ensen": 18756,
- "enser": 45268,
- "enses": 4541,
- "ensible": 27339,
- "ensibly": 28508,
- "ensical": 46165,
- "ensing": 26426,
- "ension": 3004,
- "ensional": 37176,
- "ensions": 5736,
- "ensis": 37834,
- "ensitive": 18464,
- "ensitivity": 40545,
- "ensity": 6377,
- "ensive": 2021,
- "enson": 19069,
- "ensor": 22854,
- "enstein": 37975,
- "ensual": 31406,
- "ensus": 7314,
- "ent": 298,
- "enta": 29188,
- "ental": 2470,
- "entanyl": 41455,
- "entary": 48648,
- "ente": 21872,
- "ented": 4714,
- "enter": 9255,
- "enth": 7944,
- "enthal": 34728,
- "ential": 1843,
- "entially": 3746,
- "entials": 14817,
- "entimes": 43598,
- "entin": 31371,
- "enting": 36589,
- "ention": 1463,
- "entious": 43787,
- "entity": 26858,
- "entle": 8651,
- "ently": 1473,
- "ento": 50217,
- "enton": 26673,
- "entric": 22317,
- "entry": 13000,
- "ents": 658,
- "enture": 36697,
- "enty": 3787,
- "enum": 44709,
- "env": 24330,
- "environment": 38986,
- "eny": 28558,
- "enz": 19471,
- "enza": 23674,
- "enzie": 26389,
- "eon": 23277,
- "eor": 13492,
- "eous": 15303,
- "ep": 538,
- "epad": 47852,
- "epend": 2690,
- "ependence": 15091,
- "ependent": 8682,
- "eper": 5723,
- "eph": 27446,
- "eping": 7213,
- "episode": 38668,
- "eport": 45813,
- "eps": 25386,
- "ept": 19598,
- "eq": 27363,
- "equ": 4853,
- "equal": 40496,
- "equality": 48203,
- "equipped": 40617,
- "er": 263,
- "era": 8607,
- "eral": 1691,
- "erala": 33314,
- "erald": 12573,
- "erate": 21620,
- "erb": 23552,
- "erc": 2798,
- "ercise": 23697,
- "erd": 45744,
- "ere": 567,
- "ered": 1068,
- "eredith": 36897,
- "eree": 45316,
- "erek": 18238,
- "erella": 36648,
- "eren": 14226,
- "erence": 1945,
- "erences": 4972,
- "erenn": 31915,
- "erent": 9100,
- "erential": 33369,
- "ereo": 32934,
- "erer": 11882,
- "erers": 19288,
- "erest": 1260,
- "eret": 31229,
- "erey": 48023,
- "erg": 6422,
- "ergic": 19793,
- "ergus": 13607,
- "erguson": 14168,
- "ergy": 26079,
- "eri": 33442,
- "eria": 5142,
- "erial": 48499,
- "eric": 35626,
- "erick": 41556,
- "erie": 18287,
- "eries": 10640,
- "ering": 1586,
- "erion": 28019,
- "erity": 32821,
- "erk": 9587,
- "erker": 35779,
- "erm": 7780,
- "erman": 2224,
- "ermanent": 30312,
- "ermott": 46187,
- "ern": 1142,
- "ernal": 35220,
- "ername": 13292,
- "ernand": 13023,
- "ernandez": 18092,
- "ernaut": 37879,
- "ernel": 7948,
- "ernels": 44930,
- "erness": 17447,
- "erning": 8917,
- "erno": 24100,
- "ero": 3529,
- "eros": 27498,
- "erous": 48411,
- "err": 8056,
- "erred": 17436,
- "errilla": 31859,
- "error": 18224,
- "errors": 48277,
- "erry": 6996,
- "ers": 364,
- "ersed": 20204,
- "ersen": 46516,
- "ership": 49437,
- "ersion": 6900,
- "ersive": 24469,
- "erson": 882,
- "ert": 861,
- "ertain": 1425,
- "ertation": 42245,
- "ertility": 27651,
- "erto": 13806,
- "ertodd": 36481,
- "erton": 29111,
- "erv": 712,
- "erva": 32775,
- "ervation": 13208,
- "ervative": 22003,
- "ervatives": 35291,
- "erve": 3760,
- "erved": 8520,
- "erver": 18497,
- "erves": 11184,
- "erville": 33487,
- "erving": 14344,
- "ery": 1924,
- "eryl": 44886,
- "es": 274,
- "esa": 49183,
- "esame": 34038,
- "esan": 42890,
- "esar": 18964,
- "esc": 3798,
- "escal": 47647,
- "escap": 50141,
- "escape": 41915,
- "escent": 45470,
- "escription": 7260,
- "ese": 2771,
- "esh": 5069,
- "esi": 46551,
- "esian": 35610,
- "esides": 11788,
- "esis": 9339,
- "esity": 11924,
- "esley": 49048,
- "esm": 45798,
- "esome": 5927,
- "eson": 42038,
- "esp": 9774,
- "especially": 16480,
- "espie": 42120,
- "esque": 28939,
- "ess": 408,
- "essa": 21411,
- "essage": 7589,
- "esse": 35270,
- "essed": 6676,
- "essee": 10702,
- "essel": 7878,
- "essen": 44483,
- "essential": 31195,
- "essert": 20335,
- "esses": 44667,
- "essim": 30265,
- "essing": 27289,
- "ession": 2521,
- "essional": 12743,
- "essions": 6202,
- "essler": 33730,
- "essment": 21687,
- "esson": 39670,
- "essor": 5987,
- "essors": 23295,
- "est": 395,
- "esta": 18059,
- "establish": 40037,
- "established": 27718,
- "establishment": 44390,
- "estamp": 27823,
- "estate": 44146,
- "estation": 27364,
- "este": 29872,
- "estead": 37897,
- "ested": 7287,
- "esteem": 31869,
- "ester": 7834,
- "estern": 3330,
- "esters": 8586,
- "esthes": 29678,
- "esthesia": 34811,
- "esthetic": 37531,
- "estial": 21711,
- "estic": 4699,
- "estinal": 34284,
- "estine": 27374,
- "esting": 37761,
- "estival": 6743,
- "eston": 19115,
- "estone": 13631,
- "estones": 30637,
- "estro": 47692,
- "ests": 3558,
- "esty": 9673,
- "estyle": 10992,
- "estyles": 42530,
- "esville": 19641,
- "esy": 9259,
- "et": 316,
- "eta": 17167,
- "etary": 8527,
- "etc": 14784,
- "etch": 7569,
- "etchup": 47132,
- "ete": 14471,
- "eteen": 34026,
- "eteenth": 26425,
- "eter": 2357,
- "eteria": 39622,
- "etermin": 13221,
- "etermination": 29610,
- "etermined": 23444,
- "eters": 7307,
- "eth": 2788,
- "ethe": 10567,
- "etheless": 12845,
- "ether": 6750,
- "etheus": 36916,
- "ethical": 32949,
- "ethnic": 38546,
- "ethy": 33077,
- "ethyl": 21610,
- "ethyst": 44166,
- "etic": 5139,
- "etically": 16877,
- "etics": 14596,
- "eties": 31638,
- "etime": 8079,
- "etimes": 46874,
- "eting": 13629,
- "etition": 15620,
- "etitive": 17295,
- "eto": 27206,
- "eton": 18483,
- "etooth": 16271,
- "etr": 21879,
- "etric": 19482,
- "etrical": 34546,
- "etry": 11973,
- "ets": 1039,
- "etsk": 29515,
- "etsu": 30470,
- "etsy": 34877,
- "ett": 3087,
- "etta": 15253,
- "ette": 5857,
- "ettel": 47417,
- "etter": 40088,
- "ettes": 23014,
- "etti": 24851,
- "etting": 35463,
- "ettings": 12374,
- "ettle": 23570,
- "ettlement": 27331,
- "etts": 9357,
- "etus": 29158,
- "ety": 2963,
- "etz": 23773,
- "eu": 12496,
- "eur": 23365,
- "euro": 44252,
- "eus": 27650,
- "ev": 1990,
- "eva": 48855,
- "eval": 18206,
- "evaluate": 49786,
- "eve": 44655,
- "even": 10197,
- "event": 15596,
- "events": 31534,
- "ever": 964,
- "everal": 8438,
- "every": 16833,
- "everyone": 47057,
- "everything": 37814,
- "evidence": 46817,
- "evil": 23542,
- "evin": 6830,
- "ew": 413,
- "eware": 29725,
- "ewater": 21422,
- "eway": 16172,
- "eways": 43613,
- "ewitness": 28588,
- "ework": 6433,
- "eworks": 19653,
- "eworld": 38136,
- "eworthy": 25969,
- "ews": 15515,
- "ewski": 46151,
- "ex": 1069,
- "examination": 47779,
- "example": 20688,
- "exc": 41194,
- "except": 16341,
- "excluding": 42218,
- "exclusive": 41195,
- "exe": 13499,
- "exec": 18558,
- "execute": 41049,
- "exempt": 42679,
- "exist": 38476,
- "existence": 41084,
- "existent": 32786,
- "existing": 25687,
- "exit": 37023,
- "exp": 11201,
- "expected": 40319,
- "expensive": 22031,
- "exper": 23100,
- "expl": 20676,
- "export": 39344,
- "expr": 31937,
- "express": 42712,
- "expression": 38011,
- "ext": 2302,
- "external": 22615,
- "externalActionCode": 31576,
- "extra": 26086,
- "extreme": 29896,
- "extremely": 41073,
- "ey": 2959,
- "eye": 25379,
- "eyed": 18834,
- "eyes": 48418,
- "ez": 8471,
- "ezvous": 50063,
- "f": 69,
- "fa": 13331,
- "fab": 36434,
- "fac": 38942,
- "face": 2550,
- "facebook": 19024,
- "faced": 24903,
- "faces": 32186,
- "facing": 29532,
- "fact": 22584,
- "factor": 31412,
- "facts": 37473,
- "fail": 32165,
- "failed": 47904,
- "fair": 22043,
- "faith": 41751,
- "fake": 30706,
- "fal": 42932,
- "fall": 7207,
- "falls": 23348,
- "false": 9562,
- "fam": 44769,
- "family": 17989,
- "famous": 45143,
- "fan": 24408,
- "far": 16370,
- "fare": 9496,
- "farious": 41504,
- "farm": 43323,
- "fascist": 46928,
- "fashion": 25265,
- "fashioned": 28776,
- "fast": 7217,
- "fat": 17359,
- "father": 11358,
- "favorite": 35200,
- "fax": 23560,
- "fb": 21855,
- "fc": 16072,
- "fd": 16344,
- "fe": 5036,
- "feat": 27594,
- "feature": 30053,
- "features": 40890,
- "fect": 2309,
- "fecture": 36637,
- "fed": 19082,
- "fee": 39071,
- "feed": 12363,
- "feeding": 22824,
- "feel": 36410,
- "feet": 39690,
- "feld": 16265,
- "fell": 23299,
- "felt": 31985,
- "female": 24724,
- "femin": 33594,
- "fen": 41037,
- "fer": 2232,
- "ference": 4288,
- "ferred": 18186,
- "fest": 23411,
- "fet": 34045,
- "fetched": 50012,
- "few": 32146,
- "ff": 487,
- "ffe": 16658,
- "ffect": 4812,
- "ffee": 5853,
- "ffen": 46985,
- "ffer": 36761,
- "fff": 20972,
- "ffff": 12927,
- "ffic": 2108,
- "fficiency": 35590,
- "fficient": 5632,
- "ffield": 31374,
- "ffiti": 25198,
- "fg": 40616,
- "fi": 12463,
- "fiction": 24046,
- "field": 3245,
- "fields": 25747,
- "fif": 32041,
- "fifth": 43556,
- "fig": 5647,
- "fight": 15481,
- "fighter": 24733,
- "fighters": 17114,
- "fighting": 26594,
- "fights": 50121,
- "figure": 26875,
- "figured": 46296,
- "fil": 10379,
- "file": 7753,
- "filename": 34345,
- "files": 16624,
- "fill": 20797,
- "filled": 20286,
- "film": 26240,
- "filter": 24455,
- "fin": 15643,
- "final": 20311,
- "finals": 32089,
- "financial": 46921,
- "find": 19796,
- "finder": 22805,
- "finding": 41070,
- "fine": 38125,
- "fing": 28825,
- "finger": 35461,
- "finished": 43952,
- "fire": 6495,
- "fired": 26803,
- "fires": 27312,
- "first": 11085,
- "fish": 11084,
- "fit": 11147,
- "fits": 21013,
- "fitted": 38631,
- "fitting": 32232,
- "five": 13261,
- "fix": 13049,
- "fixed": 34021,
- "fixes": 42624,
- "fl": 2704,
- "flag": 32109,
- "flags": 33152,
- "flake": 47597,
- "flame": 49621,
- "flash": 34167,
- "flat": 38568,
- "flation": 33521,
- "fle": 27919,
- "fledged": 45223,
- "fleet": 33559,
- "flex": 32880,
- "flies": 27959,
- "flight": 22560,
- "flix": 10046,
- "flo": 48679,
- "float": 22468,
- "floor": 28300,
- "flow": 11125,
- "flower": 25547,
- "flows": 44041,
- "flu": 35522,
- "flush": 25925,
- "fly": 12254,
- "flying": 45928,
- "fm": 38353,
- "fman": 35826,
- "fml": 38122,
- "fn": 22184,
- "fo": 6513,
- "focus": 37635,
- "focused": 18143,
- "fol": 9062,
- "fold": 11379,
- "folder": 43551,
- "folio": 13652,
- "folios": 45242,
- "folk": 19956,
- "follow": 27780,
- "font": 10331,
- "foo": 21943,
- "food": 19425,
- "foot": 5898,
- "football": 15914,
- "footed": 43127,
- "for": 1640,
- "force": 3174,
- "forced": 12072,
- "forcement": 13442,
- "forcer": 45515,
- "forces": 27087,
- "forcing": 18766,
- "ford": 3841,
- "fore": 754,
- "foreign": 38823,
- "foreseen": 44952,
- "forest": 29623,
- "forestation": 41570,
- "forge": 30293,
- "fork": 32523,
- "form": 687,
- "formance": 10367,
- "format": 18982,
- "formation": 1161,
- "formed": 12214,
- "former": 16354,
- "formerly": 36234,
- "forming": 15464,
- "forms": 23914,
- "fort": 3319,
- "fortable": 12065,
- "forth": 25718,
- "forts": 47378,
- "fortunately": 6668,
- "fortune": 37359,
- "forum": 27302,
- "forums": 37141,
- "forward": 11813,
- "found": 9275,
- "foundation": 42526,
- "founded": 27060,
- "founder": 15454,
- "foundland": 42030,
- "four": 14337,
- "fourth": 49393,
- "fox": 12792,
- "fp": 46428,
- "fps": 29647,
- "fr": 8310,
- "frac": 31944,
- "fram": 19298,
- "frame": 14535,
- "frames": 37805,
- "framework": 30604,
- "fre": 19503,
- "fred": 39193,
- "free": 5787,
- "freedom": 41295,
- "frequency": 35324,
- "fresh": 48797,
- "frey": 37425,
- "fried": 25520,
- "friend": 6726,
- "friendly": 13120,
- "friends": 36154,
- "frog": 49956,
- "from": 6738,
- "front": 8534,
- "fruit": 34711,
- "fs": 9501,
- "ft": 701,
- "ften": 14785,
- "fter": 637,
- "fters": 47131,
- "ftime": 31387,
- "fts": 35594,
- "fty": 19628,
- "fu": 20942,
- "fuck": 31699,
- "fuel": 25802,
- "ful": 913,
- "full": 12853,
- "fully": 2759,
- "fulness": 15538,
- "fun": 12543,
- "func": 20786,
- "function": 8818,
- "functional": 45124,
- "fund": 10990,
- "funded": 18246,
- "funding": 25032,
- "fur": 38916,
- "furt": 29205,
- "fusc": 37695,
- "future": 37443,
- "fw": 44482,
- "fx": 21373,
- "fy": 24928,
- "g": 70,
- "ga": 4908,
- "gaard": 36232,
- "gado": 50054,
- "gae": 25002,
- "gage": 10502,
- "gain": 48544,
- "gal": 13528,
- "galitarian": 39907,
- "gall": 39580,
- "gallery": 24460,
- "gam": 28483,
- "game": 6057,
- "gamer": 36515,
- "games": 19966,
- "gaming": 48616,
- "gan": 1030,
- "gang": 28284,
- "gans": 39352,
- "gap": 43554,
- "gar": 4563,
- "gard": 19977,
- "gars": 25821,
- "gart": 41651,
- "gary": 14849,
- "gas": 22649,
- "gat": 41268,
- "gate": 10494,
- "gay": 22744,
- "gb": 22296,
- "gc": 36484,
- "gd": 21287,
- "gdala": 40420,
- "ge": 469,
- "geant": 30205,
- "gear": 31763,
- "gebra": 29230,
- "ged": 2004,
- "gee": 29622,
- "geist": 49782,
- "gel": 25280,
- "gem": 24090,
- "gement": 16025,
- "gements": 43547,
- "gemony": 38953,
- "gen": 5235,
- "gence": 12745,
- "gencies": 33333,
- "gency": 4949,
- "gender": 8388,
- "gener": 8612,
- "general": 24622,
- "generated": 27568,
- "generation": 20158,
- "generic": 41357,
- "genic": 38516,
- "genre": 35850,
- "gent": 6783,
- "gently": 34727,
- "geon": 6281,
- "geoning": 31614,
- "geons": 16297,
- "ger": 1362,
- "gerald": 26941,
- "gered": 10446,
- "geries": 30230,
- "gers": 5355,
- "gery": 7076,
- "ges": 3212,
- "gest": 3495,
- "get": 1136,
- "getic": 24321,
- "gets": 11407,
- "gettable": 42182,
- "getting": 37210,
- "gew": 39909,
- "gewater": 40843,
- "gex": 25636,
- "gey": 39608,
- "gg": 1130,
- "gged": 11178,
- "gger": 26679,
- "ggie": 23571,
- "ggies": 33049,
- "gging": 18792,
- "ggle": 16444,
- "ggles": 32723,
- "ggy": 19970,
- "gh": 456,
- "gha": 46090,
- "ghai": 20380,
- "ghan": 6064,
- "ghazi": 21775,
- "ghost": 38933,
- "gi": 12397,
- "gian": 18299,
- "gie": 22699,
- "giene": 28363,
- "gif": 27908,
- "gil": 37718,
- "gin": 1655,
- "ging": 2667,
- "gins": 29878,
- "ginx": 42822,
- "gio": 27769,
- "girl": 15219,
- "girlfriend": 45189,
- "girls": 36960,
- "git": 18300,
- "github": 12567,
- "give": 26535,
- "given": 35569,
- "giving": 13992,
- "gl": 4743,
- "glas": 14391,
- "glass": 20721,
- "glers": 33641,
- "gling": 40799,
- "global": 20541,
- "glomer": 37757,
- "gly": 10853,
- "gm": 39870,
- "gmail": 14816,
- "gment": 5154,
- "gments": 11726,
- "gn": 4593,
- "gnu": 41791,
- "go": 2188,
- "goal": 35231,
- "gob": 44270,
- "god": 25344,
- "goers": 31006,
- "going": 5146,
- "gold": 24267,
- "gom": 19120,
- "gomery": 20142,
- "gon": 14520,
- "gone": 21260,
- "goo": 42469,
- "good": 11274,
- "google": 13297,
- "gor": 7053,
- "gorith": 7727,
- "gorithm": 42289,
- "got": 23442,
- "gotten": 21646,
- "gov": 9567,
- "govern": 47866,
- "government": 14480,
- "governmental": 31353,
- "govtrack": 41230,
- "gow": 21175,
- "gp": 31197,
- "gpu": 46999,
- "gr": 2164,
- "gra": 46784,
- "grab": 32393,
- "grad": 9744,
- "gradation": 26317,
- "grade": 9526,
- "graded": 21791,
- "grades": 31177,
- "gradient": 49607,
- "grading": 29247,
- "graduate": 17680,
- "grain": 48270,
- "gram": 4546,
- "gran": 46324,
- "grand": 23936,
- "graph": 34960,
- "grass": 29815,
- "grave": 41711,
- "gravity": 46453,
- "gray": 44605,
- "gre": 16694,
- "greSQL": 47701,
- "great": 18223,
- "green": 14809,
- "greg": 9903,
- "gregation": 17097,
- "gren": 32762,
- "gres": 34239,
- "gress": 5914,
- "gression": 32383,
- "gressive": 19741,
- "grey": 49502,
- "grid": 25928,
- "grim": 33563,
- "gro": 27333,
- "gross": 47181,
- "ground": 2833,
- "grounds": 40520,
- "group": 8094,
- "groupon": 14531,
- "groups": 24432,
- "grow": 45921,
- "growing": 25167,
- "grown": 22377,
- "growth": 27922,
- "gru": 48929,
- "gs": 14542,
- "gt": 13655,
- "gu": 5162,
- "guard": 14864,
- "guards": 33427,
- "gue": 18701,
- "gui": 48317,
- "guide": 41311,
- "guided": 23657,
- "gun": 7145,
- "guns": 44265,
- "gur": 45073,
- "guy": 22932,
- "guyen": 39922,
- "gy": 1360,
- "gyn": 40183,
- "gypt": 6022,
- "gz": 34586,
- "h": 71,
- "ha": 3099,
- "haar": 42948,
- "hab": 5976,
- "habi": 37362,
- "hack": 31153,
- "had": 18108,
- "hai": 44488,
- "hair": 27108,
- "haired": 29972,
- "hak": 43573,
- "hal": 14201,
- "half": 13959,
- "hall": 18323,
- "halla": 41911,
- "ham": 2763,
- "hammad": 14875,
- "hammer": 17980,
- "han": 7637,
- "hand": 4993,
- "handed": 13638,
- "handedly": 43919,
- "hander": 44510,
- "handle": 28144,
- "handled": 38788,
- "handler": 30281,
- "hands": 43365,
- "hang": 33255,
- "hani": 29839,
- "hao": 23778,
- "hap": 45897,
- "happy": 34191,
- "haps": 2772,
- "har": 9869,
- "hard": 10424,
- "hardt": 28375,
- "hare": 43466,
- "hari": 49573,
- "harm": 29155,
- "hart": 18647,
- "has": 10134,
- "hash": 17831,
- "hat": 5183,
- "hate": 37035,
- "hatt": 11653,
- "hattan": 12904,
- "haul": 15194,
- "haus": 30404,
- "haust": 42456,
- "have": 14150,
- "haven": 39487,
- "having": 40965,
- "haw": 26615,
- "hawk": 40624,
- "hawks": 27221,
- "hazard": 37598,
- "hd": 31298,
- "he": 258,
- "hea": 21632,
- "head": 2256,
- "headed": 15353,
- "header": 25677,
- "headers": 50145,
- "heading": 33878,
- "heads": 16600,
- "health": 13948,
- "healthy": 22796,
- "heard": 23636,
- "heart": 11499,
- "hearted": 20122,
- "heartedly": 44407,
- "heast": 9522,
- "heastern": 18160,
- "heat": 25080,
- "heavy": 23701,
- "hed": 704,
- "heddar": 44937,
- "hedon": 46086,
- "hedral": 21962,
- "hee": 21067,
- "heed": 23616,
- "heet": 25473,
- "hei": 27392,
- "heid": 28420,
- "height": 17015,
- "heim": 9096,
- "heimer": 16288,
- "heit": 29361,
- "hel": 2978,
- "held": 10217,
- "helial": 35566,
- "hell": 12758,
- "helle": 34454,
- "hello": 31373,
- "helm": 33485,
- "help": 16794,
- "helps": 35194,
- "hem": 4411,
- "hemat": 10024,
- "hematic": 23380,
- "hematically": 46558,
- "hement": 35347,
- "hemer": 39557,
- "hemoth": 34394,
- "hemy": 36598,
- "hen": 831,
- "hend": 15631,
- "hene": 29473,
- "heng": 31753,
- "henko": 30161,
- "hens": 5135,
- "hent": 6925,
- "heny": 47413,
- "heon": 37060,
- "her": 372,
- "here": 1456,
- "hered": 6083,
- "herence": 23545,
- "herent": 8334,
- "herer": 48386,
- "heres": 19079,
- "heric": 15011,
- "herical": 37910,
- "hern": 2881,
- "hero": 11718,
- "herry": 13372,
- "hers": 7084,
- "herty": 29029,
- "hes": 956,
- "hesda": 30049,
- "heses": 39815,
- "hesion": 32582,
- "hesis": 8497,
- "hesive": 25938,
- "hess": 33979,
- "hest": 3634,
- "hester": 19593,
- "het": 3202,
- "hetamine": 25385,
- "heter": 43332,
- "hetic": 6587,
- "hetical": 21485,
- "hetically": 31786,
- "hetics": 24965,
- "hett": 17442,
- "hetti": 33392,
- "hetto": 35619,
- "hew": 6391,
- "hews": 40645,
- "hex": 33095,
- "hey": 20342,
- "hh": 12337,
- "hhh": 49126,
- "hhhh": 36607,
- "hi": 5303,
- "hib": 3145,
- "hiba": 49224,
- "hibit": 26964,
- "hibited": 44139,
- "hibition": 24108,
- "hid": 49675,
- "hidden": 30342,
- "hide": 24717,
- "hift": 29323,
- "hig": 25196,
- "high": 8929,
- "higher": 46503,
- "highest": 35323,
- "highly": 47444,
- "hill": 12639,
- "hillary": 47826,
- "him": 38400,
- "hin": 20079,
- "hing": 722,
- "hip": 1056,
- "hips": 5748,
- "hire": 10695,
- "hiro": 49907,
- "hirt": 49756,
- "his": 14363,
- "hist": 10034,
- "historic": 31304,
- "history": 23569,
- "hit": 17945,
- "hitting": 48320,
- "hl": 18519,
- "hler": 49737,
- "hm": 23940,
- "hma": 21720,
- "hn": 21116,
- "hner": 22277,
- "ho": 8873,
- "hod": 2065,
- "hoe": 38979,
- "hof": 39891,
- "hoff": 36092,
- "hog": 31897,
- "hol": 3937,
- "hold": 2946,
- "holder": 13829,
- "holders": 10476,
- "holding": 19216,
- "hole": 13207,
- "holes": 28439,
- "holiday": 37689,
- "holm": 22981,
- "holy": 44287,
- "hom": 26452,
- "home": 11195,
- "hon": 24130,
- "hood": 2894,
- "hook": 25480,
- "hooting": 35486,
- "hop": 8548,
- "hops": 21936,
- "hor": 17899,
- "horn": 25311,
- "horse": 30527,
- "hospital": 49257,
- "host": 4774,
- "hot": 8940,
- "hots": 17398,
- "hou": 15710,
- "houn": 47714,
- "hound": 39047,
- "hour": 9769,
- "hours": 24425,
- "house": 4803,
- "houses": 20089,
- "housing": 50028,
- "hov": 28026,
- "hovah": 33023,
- "hover": 43753,
- "how": 4919,
- "hower": 33539,
- "hp": 24831,
- "hr": 11840,
- "hra": 45056,
- "hran": 16848,
- "href": 33257,
- "hs": 11994,
- "ht": 4352,
- "htaking": 34148,
- "htar": 38672,
- "htm": 19211,
- "html": 6494,
- "htt": 2804,
- "http": 4023,
- "https": 5450,
- "hu": 13415,
- "hua": 33061,
- "hub": 40140,
- "huge": 40878,
- "hum": 17047,
- "human": 10734,
- "humane": 44766,
- "humans": 40205,
- "hun": 20088,
- "hung": 43274,
- "hunt": 35060,
- "hunter": 37488,
- "hur": 48349,
- "hurst": 33500,
- "hus": 7537,
- "husband": 48912,
- "hw": 36599,
- "hy": 12114,
- "hya": 48812,
- "hyd": 15511,
- "hyde": 39175,
- "hyp": 36362,
- "hyper": 49229,
- "hz": 32179,
- "i": 72,
- "iHUD": 38370,
- "iOS": 35742,
- "iPhone": 37032,
- "ia": 544,
- "iability": 12455,
- "iable": 3379,
- "iably": 18745,
- "iac": 9607,
- "iae": 33100,
- "iage": 42360,
- "iago": 29601,
- "iah": 9520,
- "iak": 32994,
- "ial": 498,
- "ially": 1927,
- "ials": 8231,
- "iam": 1789,
- "iameter": 13173,
- "iami": 7871,
- "iamond": 8446,
- "ian": 666,
- "iana": 7484,
- "iance": 3610,
- "iances": 16097,
- "iane": 46470,
- "iang": 15483,
- "iani": 25111,
- "iann": 28627,
- "iannopoulos": 36408,
- "iano": 10115,
- "ians": 1547,
- "iant": 3014,
- "iants": 17883,
- "iao": 13481,
- "iar": 12571,
- "iard": 42425,
- "iaries": 18361,
- "iary": 8042,
- "ias": 4448,
- "iasco": 40025,
- "iasis": 48455,
- "iasm": 16401,
- "iat": 5375,
- "iate": 9386,
- "iated": 12931,
- "iates": 32820,
- "iating": 26336,
- "iation": 3920,
- "iations": 40356,
- "iator": 38585,
- "iatric": 11439,
- "iatrics": 36549,
- "iatures": 42711,
- "iatus": 34704,
- "iaz": 17890,
- "iazep": 48826,
- "ib": 571,
- "iba": 23718,
- "ibaba": 37541,
- "ibal": 21342,
- "iban": 14278,
- "iband": 35967,
- "ibble": 43992,
- "ibe": 32438,
- "ibel": 43837,
- "iber": 1856,
- "iberal": 16813,
- "ibi": 27567,
- "ibia": 41145,
- "ibilities": 7992,
- "ibility": 2247,
- "ibl": 10506,
- "ible": 856,
- "ibles": 18764,
- "ibli": 29142,
- "iblical": 16897,
- "ibling": 27448,
- "iblings": 19389,
- "ibliography": 45689,
- "ibly": 3193,
- "ibo": 26762,
- "ibr": 2889,
- "ibrarian": 35808,
- "ibraries": 11127,
- "ibrary": 4115,
- "ibu": 33828,
- "ibur": 38616,
- "ibus": 26333,
- "ic": 291,
- "ica": 3970,
- "icable": 18424,
- "icably": 41685,
- "icago": 4549,
- "ical": 605,
- "ically": 1146,
- "icals": 20155,
- "ican": 7490,
- "icans": 22398,
- "icas": 44645,
- "icate": 5344,
- "icated": 3474,
- "icates": 16856,
- "icating": 12364,
- "ication": 3299,
- "ications": 3736,
- "icative": 43058,
- "icator": 26407,
- "icators": 44549,
- "icc": 44240,
- "ice": 501,
- "iced": 3711,
- "icent": 36712,
- "iceps": 41663,
- "icer": 16647,
- "ices": 1063,
- "icester": 26382,
- "ich": 488,
- "ichael": 40302,
- "iche": 14234,
- "ichen": 41437,
- "ichever": 22617,
- "ichi": 16590,
- "ichick": 38448,
- "ichita": 41940,
- "icho": 38720,
- "icht": 30830,
- "ici": 44070,
- "icia": 33577,
- "icial": 6652,
- "ician": 6749,
- "icians": 5106,
- "iciary": 13556,
- "icidal": 21488,
- "icide": 5285,
- "icides": 16751,
- "iciency": 19777,
- "icient": 11373,
- "icing": 6345,
- "icio": 46441,
- "icion": 47430,
- "icious": 6243,
- "icip": 4311,
- "icipated": 40988,
- "icism": 11965,
- "icist": 48187,
- "icit": 3628,
- "icity": 8467,
- "ick": 624,
- "icka": 29873,
- "icked": 9484,
- "icken": 5973,
- "icker": 15799,
- "ickers": 21630,
- "icket": 9715,
- "ickets": 15970,
- "ickey": 40389,
- "icking": 7958,
- "ickle": 39423,
- "ickr": 18994,
- "icks": 3378,
- "ickson": 46381,
- "icky": 17479,
- "icle": 1548,
- "icles": 2983,
- "ico": 3713,
- "icol": 27045,
- "icon": 4749,
- "icone": 27981,
- "icons": 34280,
- "icro": 2500,
- "icrobial": 48518,
- "ics": 873,
- "ict": 713,
- "icted": 5722,
- "icter": 36278,
- "iction": 2867,
- "ictional": 47273,
- "ictionary": 14188,
- "ictions": 9278,
- "ictive": 45279,
- "icts": 14137,
- "icular": 13174,
- "icularly": 22585,
- "icult": 2249,
- "icultural": 26823,
- "iculture": 47428,
- "iculty": 22402,
- "icum": 39901,
- "icus": 24552,
- "icut": 13554,
- "icy": 4611,
- "icycle": 35298,
- "icz": 28051,
- "id": 312,
- "ida": 3755,
- "idable": 23321,
- "idad": 32482,
- "idae": 31718,
- "idal": 11624,
- "idan": 27610,
- "idas": 24496,
- "idate": 20540,
- "idated": 41475,
- "idates": 37051,
- "idation": 24765,
- "idav": 20331,
- "iday": 2567,
- "idays": 13842,
- "idd": 1638,
- "idden": 4651,
- "idding": 13494,
- "iddle": 2509,
- "iddled": 34897,
- "iddler": 26458,
- "iddles": 29319,
- "iddling": 41367,
- "iddy": 34208,
- "ide": 485,
- "ided": 1384,
- "idel": 5943,
- "idelines": 7984,
- "idelity": 23091,
- "idem": 28913,
- "iden": 14029,
- "idence": 1704,
- "idences": 44845,
- "idency": 9147,
- "ident": 738,
- "idental": 35182,
- "identally": 23961,
- "idential": 35599,
- "identified": 19107,
- "idently": 46046,
- "idents": 3231,
- "ideo": 1651,
- "ideon": 42381,
- "ideos": 4921,
- "idepress": 25895,
- "ider": 1304,
- "idered": 3089,
- "iders": 4157,
- "ides": 1460,
- "ideshow": 42286,
- "idespread": 9790,
- "idge": 3130,
- "idges": 15969,
- "idget": 17484,
- "idi": 19830,
- "idia": 38513,
- "idian": 19825,
- "idine": 39422,
- "iding": 2530,
- "idious": 33243,
- "idis": 29207,
- "idity": 17995,
- "idium": 43523,
- "ido": 17305,
- "idon": 47287,
- "ids": 2340,
- "idth": 5649,
- "idy": 19325,
- "ie": 494,
- "iece": 8535,
- "ied": 798,
- "ief": 2086,
- "ieft": 49868,
- "ieg": 15702,
- "iege": 14566,
- "iegel": 28210,
- "iel": 8207,
- "ield": 1164,
- "ielding": 30449,
- "iem": 26597,
- "ien": 2013,
- "ience": 1240,
- "ienced": 26343,
- "iences": 10035,
- "iencies": 22139,
- "iency": 6160,
- "ienne": 37938,
- "iens": 10465,
- "ient": 1153,
- "ients": 2334,
- "ier": 959,
- "iera": 41976,
- "ierce": 9798,
- "iere": 13235,
- "ieri": 29864,
- "ierra": 16367,
- "ierre": 31058,
- "ierrez": 44448,
- "iers": 3183,
- "iership": 36689,
- "iery": 23012,
- "ies": 444,
- "iesel": 29893,
- "iest": 6386,
- "iesta": 36283,
- "iet": 1155,
- "ietal": 21587,
- "ieth": 19235,
- "ieties": 9545,
- "iets": 27955,
- "iety": 1905,
- "ieu": 22304,
- "iev": 11203,
- "ieval": 14671,
- "ieve": 12311,
- "ieved": 39591,
- "iever": 47818,
- "ievers": 30296,
- "ieves": 17974,
- "ieving": 30749,
- "iew": 769,
- "iewicz": 48596,
- "if": 361,
- "ifa": 19215,
- "ifact": 29660,
- "ifacts": 37199,
- "ifax": 26590,
- "ife": 901,
- "ifer": 7087,
- "iferation": 49801,
- "ifest": 8409,
- "ifestyle": 42004,
- "iff": 733,
- "iffe": 22391,
- "ifference": 33012,
- "ifferent": 17125,
- "iffin": 42022,
- "iffs": 10203,
- "ifi": 22238,
- "ifiable": 16823,
- "ific": 811,
- "ificant": 17294,
- "ificantly": 42491,
- "ificate": 22460,
- "ification": 2649,
- "ifications": 6637,
- "ifice": 9680,
- "ificent": 21559,
- "ificial": 9542,
- "ified": 1431,
- "ifier": 7483,
- "ifiers": 13350,
- "ifies": 6945,
- "ifix": 42169,
- "ifle": 8316,
- "ifled": 47157,
- "ifles": 16063,
- "ifling": 38966,
- "iflower": 42642,
- "iform": 6933,
- "iframe": 39621,
- "ift": 2135,
- "ifted": 21715,
- "ifter": 18171,
- "ifting": 13309,
- "ifts": 19265,
- "ifty": 24905,
- "iful": 4135,
- "ifully": 17049,
- "ify": 1958,
- "ifying": 4035,
- "ig": 328,
- "iga": 13827,
- "igan": 5516,
- "igans": 34090,
- "igate": 10055,
- "igated": 26963,
- "igating": 29129,
- "igation": 7065,
- "igator": 23823,
- "igators": 25975,
- "ige": 10045,
- "igel": 47709,
- "igen": 9324,
- "igenous": 12357,
- "igent": 47096,
- "iger": 8254,
- "igers": 34984,
- "igg": 6950,
- "igger": 15249,
- "iggins": 23567,
- "iggle": 24082,
- "iggs": 20340,
- "iggurat": 44557,
- "igh": 394,
- "igham": 34000,
- "ighed": 12570,
- "ight": 432,
- "ighter": 4799,
- "ighters": 6261,
- "ighth": 10887,
- "ighthouse": 32303,
- "ighting": 47610,
- "ighton": 42993,
- "ights": 2337,
- "ighty": 14400,
- "igi": 25754,
- "igible": 26032,
- "igil": 27187,
- "igion": 17035,
- "igious": 10956,
- "igl": 38686,
- "igm": 17225,
- "igma": 13495,
- "igmat": 32441,
- "igmatic": 38860,
- "ign": 570,
- "ignant": 25114,
- "igne": 48946,
- "igned": 3916,
- "igning": 38944,
- "ignment": 16747,
- "ignore": 46430,
- "ignt": 16891,
- "ignty": 17224,
- "igo": 14031,
- "igon": 37107,
- "igor": 36274,
- "igr": 3692,
- "igrant": 9893,
- "igrants": 5663,
- "igraph": 45920,
- "igrate": 42175,
- "igrated": 38769,
- "igration": 4254,
- "igree": 41233,
- "igroup": 47875,
- "igs": 9235,
- "igsaw": 45636,
- "igslist": 40704,
- "igue": 15212,
- "igun": 50118,
- "iguous": 29709,
- "igure": 7047,
- "ih": 4449,
- "ihad": 11166,
- "ihadi": 42449,
- "ihar": 38405,
- "ihara": 45902,
- "ihil": 20898,
- "ihilation": 33299,
- "ihu": 48406,
- "ii": 4178,
- "iii": 15479,
- "ij": 2926,
- "ija": 34655,
- "ijah": 32778,
- "iji": 20770,
- "ijing": 11030,
- "ijk": 45961,
- "ijn": 48848,
- "ijuana": 5343,
- "ik": 1134,
- "ika": 9232,
- "ikan": 49894,
- "ikarp": 36850,
- "ikawa": 40398,
- "ike": 522,
- "iked": 17951,
- "iken": 29943,
- "iker": 18320,
- "ikers": 24913,
- "ikes": 7938,
- "ikh": 13848,
- "ikhail": 39065,
- "iki": 5580,
- "iking": 14132,
- "ikini": 35542,
- "ikk": 36073,
- "iko": 12125,
- "iku": 28643,
- "ikuman": 42889,
- "iky": 47536,
- "il": 346,
- "ila": 10102,
- "ilage": 50006,
- "ilan": 38239,
- "iland": 40855,
- "ilant": 37794,
- "ilantro": 48311,
- "ilar": 1794,
- "ilated": 40080,
- "ilater": 38601,
- "ilateral": 14796,
- "ilaterally": 39707,
- "ilation": 10520,
- "ild": 688,
- "ilda": 27281,
- "ilde": 44725,
- "ilded": 46158,
- "ildo": 39583,
- "ile": 576,
- "ileaks": 27983,
- "iled": 3902,
- "ilee": 40626,
- "ileen": 42236,
- "ilege": 41866,
- "ileged": 48446,
- "iler": 5329,
- "ilers": 34393,
- "iles": 2915,
- "iless": 30608,
- "ilet": 41550,
- "iley": 9618,
- "ili": 2403,
- "ilia": 17517,
- "ilial": 43475,
- "ilian": 35824,
- "iliar": 4797,
- "iliary": 28129,
- "iliate": 49826,
- "iliated": 31705,
- "iliation": 15547,
- "ilib": 22282,
- "ilibrium": 24741,
- "ilic": 41896,
- "ilies": 3922,
- "ilight": 15512,
- "iling": 4386,
- "ilings": 43271,
- "ilingual": 34900,
- "ilion": 29935,
- "ilipp": 8908,
- "ilit": 6392,
- "ilitarian": 43900,
- "ilitary": 18748,
- "ilitating": 34871,
- "ilitation": 18194,
- "ilities": 2410,
- "ility": 879,
- "ilk": 43545,
- "ill": 359,
- "illa": 5049,
- "illac": 40607,
- "illance": 7682,
- "illard": 32681,
- "illary": 15856,
- "illas": 25314,
- "illation": 40903,
- "ille": 8270,
- "illed": 2967,
- "illegal": 47749,
- "iller": 4665,
- "illery": 14920,
- "illes": 21718,
- "illet": 32512,
- "illi": 50173,
- "illian": 37896,
- "illin": 32672,
- "illing": 4509,
- "illion": 1131,
- "illions": 40083,
- "illo": 16111,
- "illon": 23027,
- "ills": 2171,
- "illus": 44342,
- "illusion": 35760,
- "illy": 6548,
- "ilo": 18526,
- "ilogy": 19202,
- "ilon": 33576,
- "ilot": 23439,
- "ils": 4487,
- "ilst": 11750,
- "ilt": 2326,
- "ilton": 9044,
- "iltr": 19438,
- "iltration": 36055,
- "ilts": 50076,
- "ilty": 6267,
- "ilus": 35815,
- "ilver": 46978,
- "ily": 813,
- "ilyn": 38020,
- "im": 320,
- "ima": 8083,
- "imag": 48466,
- "image": 9060,
- "images": 17566,
- "imal": 4402,
- "iman": 24086,
- "imar": 49399,
- "imaru": 49551,
- "imate": 1920,
- "imated": 15655,
- "imately": 3358,
- "imates": 26748,
- "imating": 39204,
- "imation": 18991,
- "imb": 14107,
- "imbabwe": 27175,
- "imble": 34477,
- "ime": 524,
- "imedia": 20626,
- "imei": 45519,
- "imen": 19027,
- "imens": 12117,
- "imensional": 16198,
- "iment": 3681,
- "imental": 9134,
- "imentary": 39051,
- "iments": 6800,
- "imeo": 47776,
- "imer": 22723,
- "imes": 999,
- "imester": 47484,
- "imet": 38813,
- "imeter": 16912,
- "imeters": 31551,
- "img": 9600,
- "imgur": 19791,
- "imi": 25236,
- "imil": 26641,
- "imilar": 49941,
- "imilation": 42963,
- "iminary": 38429,
- "imir": 13057,
- "imity": 18853,
- "imize": 48439,
- "imm": 8608,
- "immer": 10957,
- "immers": 36904,
- "immigrant": 39835,
- "immigration": 47620,
- "imming": 27428,
- "immune": 38345,
- "imo": 25147,
- "imon": 20473,
- "imony": 33969,
- "imore": 9401,
- "imoto": 43354,
- "imov": 44273,
- "imp": 11011,
- "impact": 48240,
- "impl": 23928,
- "import": 11748,
- "important": 18049,
- "imposed": 36457,
- "impro": 32077,
- "improve": 49453,
- "ims": 12078,
- "imsy": 48295,
- "imum": 2847,
- "imura": 43817,
- "imus": 20704,
- "in": 259,
- "ina": 1437,
- "inal": 1292,
- "inally": 3289,
- "inals": 6897,
- "inance": 14149,
- "inances": 34999,
- "inant": 42483,
- "inar": 22050,
- "inarily": 21565,
- "inary": 3219,
- "inas": 24252,
- "inate": 4559,
- "inated": 3898,
- "inately": 48618,
- "inates": 17540,
- "inating": 6010,
- "ination": 1883,
- "inational": 26201,
- "inations": 7352,
- "inator": 20900,
- "inators": 47721,
- "inatory": 23132,
- "inav": 26802,
- "inburgh": 22222,
- "inc": 1939,
- "incarn": 13211,
- "ince": 924,
- "incent": 42816,
- "incerity": 40310,
- "inces": 17386,
- "inch": 8589,
- "inches": 45457,
- "incial": 13744,
- "incible": 33494,
- "incinn": 15020,
- "incinnati": 15130,
- "include": 17256,
- "includes": 42813,
- "including": 8201,
- "incoln": 11690,
- "income": 12519,
- "incre": 24988,
- "increasing": 42647,
- "inct": 4612,
- "inction": 9438,
- "inctions": 31253,
- "ind": 521,
- "inda": 22261,
- "indal": 44644,
- "independence": 39894,
- "independent": 34750,
- "inder": 5540,
- "inders": 29700,
- "index": 9630,
- "inding": 6020,
- "individual": 43129,
- "indle": 42343,
- "indu": 10259,
- "induced": 17223,
- "inducing": 48016,
- "indust": 23213,
- "industrial": 31130,
- "ine": 500,
- "inea": 18343,
- "ined": 1389,
- "inel": 20538,
- "inelli": 44076,
- "inem": 7749,
- "inement": 21828,
- "inen": 42326,
- "inence": 18386,
- "inent": 7233,
- "inently": 26528,
- "iner": 7274,
- "ineries": 48858,
- "iners": 21257,
- "inery": 15451,
- "ines": 1127,
- "inese": 3762,
- "iness": 1272,
- "inet": 42504,
- "inez": 18885,
- "inf": 10745,
- "infect": 27816,
- "infeld": 47187,
- "inflamm": 29639,
- "inflammatory": 32272,
- "info": 10951,
- "information": 17018,
- "informed": 35698,
- "ing": 278,
- "inge": 11912,
- "inged": 24431,
- "ingen": 36795,
- "inger": 3889,
- "ingers": 40923,
- "inges": 26792,
- "ingham": 25875,
- "inging": 14146,
- "ingle": 17697,
- "ingly": 4420,
- "ingo": 32735,
- "ings": 654,
- "ington": 9557,
- "ingu": 6680,
- "inguishable": 41726,
- "inguished": 46709,
- "inho": 20327,
- "ini": 5362,
- "inia": 43168,
- "inian": 24605,
- "inic": 47277,
- "inical": 32352,
- "ining": 3191,
- "inion": 23971,
- "inis": 16661,
- "inished": 30603,
- "init": 15003,
- "inite": 9504,
- "initely": 12998,
- "initial": 36733,
- "initialized": 17532,
- "inition": 17750,
- "initions": 50101,
- "inity": 6269,
- "ink": 676,
- "inka": 48955,
- "inker": 24275,
- "inki": 38799,
- "inking": 8040,
- "inkle": 19894,
- "inks": 2973,
- "inky": 29246,
- "inline": 45145,
- "inn": 3732,
- "innacle": 37087,
- "innamon": 21920,
- "inner": 5083,
- "inness": 32990,
- "innie": 49708,
- "inning": 23062,
- "innon": 45067,
- "ino": 2879,
- "inoa": 40564,
- "inois": 8981,
- "inos": 11996,
- "inosaur": 21317,
- "inous": 29823,
- "input": 15414,
- "inqu": 18934,
- "ins": 1040,
- "inse": 38521,
- "insert": 28463,
- "inside": 48787,
- "insk": 35803,
- "inski": 21141,
- "insky": 19870,
- "inson": 7899,
- "inspired": 24194,
- "inst": 8625,
- "install": 17350,
- "installed": 37050,
- "instance": 39098,
- "instead": 38070,
- "instein": 11962,
- "insula": 16495,
- "insured": 28409,
- "int": 600,
- "intage": 14630,
- "integ": 18908,
- "integer": 41433,
- "intel": 48779,
- "intelligence": 32683,
- "intend": 7315,
- "intendent": 21075,
- "intendo": 8773,
- "intensity": 47799,
- "intensive": 38096,
- "intent": 48536,
- "intention": 40867,
- "inter": 3849,
- "interest": 9446,
- "interested": 34339,
- "interesting": 47914,
- "interface": 39994,
- "intern": 23124,
- "internal": 32538,
- "international": 45609,
- "internet": 37675,
- "interpret": 27381,
- "interrupted": 46037,
- "inters": 20193,
- "interstitial": 29446,
- "intestinal": 36387,
- "inth": 9304,
- "into": 20424,
- "inton": 2371,
- "intosh": 37638,
- "introdu": 27427,
- "ints": 29503,
- "intuitive": 42105,
- "inus": 35237,
- "inv": 16340,
- "inventory": 24807,
- "inventoryQuantity": 39756,
- "invest": 24859,
- "invoke": 37669,
- "involved": 44697,
- "inx": 28413,
- "iny": 3541,
- "inyl": 19754,
- "io": 952,
- "ioch": 41097,
- "iod": 2101,
- "iol": 1669,
- "iola": 30292,
- "iolet": 19194,
- "iological": 15071,
- "iologist": 31599,
- "iology": 12371,
- "iom": 29005,
- "ion": 295,
- "iona": 32792,
- "ionage": 24919,
- "ional": 1538,
- "ione": 7935,
- "ioned": 14994,
- "ionic": 26523,
- "ionics": 49900,
- "ions": 507,
- "iop": 14922,
- "ior": 1504,
- "iors": 12706,
- "ios": 4267,
- "iosis": 42960,
- "iosity": 15023,
- "iosyn": 48448,
- "iosyncr": 48702,
- "iot": 5151,
- "iotic": 16357,
- "iotics": 18296,
- "iots": 16228,
- "iott": 20773,
- "iour": 49439,
- "ious": 699,
- "iously": 6819,
- "iov": 16664,
- "iovascular": 19381,
- "iox": 12190,
- "ioxid": 26294,
- "ioxide": 16671,
- "ip": 541,
- "ipal": 8521,
- "ipation": 25857,
- "ipe": 3757,
- "iped": 46647,
- "ipedia": 11151,
- "ipeg": 21700,
- "ipel": 40634,
- "iper": 9346,
- "ipers": 29288,
- "ipes": 18636,
- "iph": 13323,
- "iphany": 49915,
- "iphate": 34981,
- "ipher": 10803,
- "ipient": 48137,
- "iping": 34690,
- "ipl": 24705,
- "iple": 2480,
- "ipment": 4667,
- "ipolar": 49133,
- "ipop": 42800,
- "ipp": 3974,
- "ipped": 3949,
- "ipper": 14710,
- "ippers": 16415,
- "ippery": 29530,
- "ippi": 12715,
- "ipping": 4501,
- "ipple": 18793,
- "ipples": 27844,
- "ippy": 41214,
- "ips": 2419,
- "ipt": 10257,
- "iq": 25011,
- "iqu": 1557,
- "ique": 2350,
- "iqueness": 46764,
- "iques": 6368,
- "iquette": 40387,
- "iquid": 6394,
- "ir": 343,
- "ira": 8704,
- "irable": 13194,
- "iral": 21093,
- "iration": 15297,
- "irc": 1980,
- "ircraft": 27002,
- "ird": 1447,
- "irds": 11049,
- "ire": 557,
- "irect": 1060,
- "irection": 4154,
- "ired": 1202,
- "irement": 24615,
- "irements": 18883,
- "iren": 24080,
- "irens": 42917,
- "ires": 2387,
- "irez": 31762,
- "irgin": 4672,
- "iri": 14783,
- "irie": 28191,
- "iries": 18561,
- "irin": 47388,
- "iring": 3428,
- "iris": 29616,
- "irit": 3276,
- "irk": 14232,
- "irl": 1901,
- "irled": 49376,
- "irlf": 9841,
- "irlfriend": 9872,
- "irling": 24297,
- "irlwind": 32785,
- "irm": 2533,
- "irmation": 36241,
- "irmed": 15491,
- "irming": 29808,
- "irms": 8789,
- "iro": 7058,
- "iron": 1934,
- "irrel": 22793,
- "irs": 17062,
- "irsch": 47108,
- "irst": 667,
- "irt": 2265,
- "irted": 48357,
- "irteen": 22530,
- "irth": 3333,
- "irting": 35355,
- "irts": 9682,
- "irtual": 22341,
- "irty": 5893,
- "iru": 35406,
- "irus": 19397,
- "iry": 9045,
- "is": 271,
- "isSpecial": 39714,
- "isSpecialOrderable": 39755,
- "isa": 9160,
- "isable": 43942,
- "isal": 28456,
- "isan": 9057,
- "isance": 31872,
- "isans": 26100,
- "isation": 5612,
- "isations": 38189,
- "isbury": 47967,
- "isc": 2304,
- "iscal": 7860,
- "isch": 25308,
- "ische": 46097,
- "ischer": 24645,
- "isco": 4861,
- "iscons": 8795,
- "isconsin": 8816,
- "iscopal": 42522,
- "iscover": 29392,
- "iscovered": 41168,
- "iscovery": 40821,
- "isd": 9409,
- "isdom": 9350,
- "ise": 786,
- "isec": 27866,
- "ised": 1417,
- "isel": 36811,
- "isen": 13254,
- "iser": 5847,
- "isers": 21572,
- "ises": 2696,
- "iseum": 38277,
- "isexual": 20863,
- "isf": 4468,
- "ish": 680,
- "isha": 19388,
- "ishable": 31785,
- "ished": 1348,
- "isher": 4828,
- "ishers": 39116,
- "ishes": 5614,
- "ishi": 21644,
- "ishing": 3929,
- "ishly": 29735,
- "ishment": 17862,
- "ishop": 10124,
- "ishops": 21863,
- "ishy": 49785,
- "isi": 23267,
- "isible": 12843,
- "isin": 45763,
- "isine": 27480,
- "ising": 1710,
- "ision": 1166,
- "isions": 3279,
- "isite": 16107,
- "isites": 31327,
- "isition": 10027,
- "isitions": 29593,
- "isive": 13911,
- "isively": 42042,
- "isk": 1984,
- "isks": 36730,
- "isky": 34041,
- "isl": 3044,
- "isle": 20919,
- "ism": 1042,
- "isma": 38017,
- "isman": 23845,
- "ismo": 44126,
- "isms": 6583,
- "isner": 49861,
- "iso": 26786,
- "isode": 3282,
- "isodes": 8052,
- "isoft": 29719,
- "isol": 30152,
- "ison": 1653,
- "isons": 9886,
- "isp": 8802,
- "ispers": 27148,
- "isphere": 22833,
- "iss": 747,
- "issa": 13808,
- "issan": 24112,
- "issance": 16419,
- "isse": 20782,
- "ission": 1480,
- "issions": 7717,
- "isson": 30927,
- "issors": 32555,
- "issue": 21949,
- "issued": 39361,
- "issues": 37165,
- "issy": 36419,
- "ist": 396,
- "ista": 12523,
- "istan": 4103,
- "istance": 9311,
- "istani": 16688,
- "istant": 10167,
- "istar": 47229,
- "istas": 37503,
- "iste": 40833,
- "isted": 6347,
- "istence": 13274,
- "istent": 7609,
- "ister": 1694,
- "istered": 23187,
- "isters": 6223,
- "istic": 2569,
- "istical": 19929,
- "istically": 16772,
- "istics": 3969,
- "istine": 32248,
- "isting": 9665,
- "istle": 12535,
- "iston": 36363,
- "istor": 32380,
- "istors": 46334,
- "istrate": 28534,
- "istrates": 37909,
- "istration": 33397,
- "istries": 32995,
- "istry": 4592,
- "ists": 1023,
- "isu": 46313,
- "isure": 20609,
- "isy": 13560,
- "it": 270,
- "ita": 5350,
- "itability": 34147,
- "itable": 4674,
- "itably": 14829,
- "itage": 10208,
- "itaire": 26627,
- "ital": 1287,
- "itals": 8321,
- "itamin": 40746,
- "itan": 18642,
- "itance": 42942,
- "itans": 43716,
- "itant": 23737,
- "itar": 7940,
- "itarian": 8353,
- "itars": 27745,
- "itary": 9331,
- "itas": 21416,
- "itate": 12027,
- "itated": 13939,
- "itates": 38654,
- "itating": 21712,
- "itation": 3780,
- "itational": 22181,
- "itations": 20597,
- "itative": 12464,
- "itatively": 48668,
- "itbart": 17868,
- "itch": 2007,
- "itched": 10981,
- "itcher": 23640,
- "itches": 9249,
- "itchie": 48423,
- "itching": 19811,
- "ite": 578,
- "itech": 45396,
- "itect": 5712,
- "ited": 863,
- "itely": 3973,
- "item": 9186,
- "itement": 12559,
- "items": 23814,
- "itent": 48324,
- "iter": 2676,
- "iterator": 48727,
- "iterranean": 19012,
- "ites": 2737,
- "ith": 342,
- "ithe": 31470,
- "ither": 1555,
- "ithering": 40861,
- "ithing": 44556,
- "ithmetic": 29848,
- "iths": 47252,
- "ithub": 10060,
- "iti": 8846,
- "itia": 36723,
- "itial": 6847,
- "itialized": 13562,
- "itially": 22640,
- "itic": 16233,
- "ities": 871,
- "itimate": 30233,
- "itime": 22552,
- "iting": 1780,
- "ition": 653,
- "itional": 1859,
- "itionally": 8736,
- "itions": 1756,
- "itious": 25253,
- "itis": 11815,
- "itism": 18937,
- "itive": 1800,
- "itiveness": 31366,
- "itives": 20288,
- "itivity": 11365,
- "itiz": 3029,
- "itized": 36951,
- "itizen": 36958,
- "itizens": 34100,
- "itle": 2578,
- "itled": 7803,
- "itles": 30540,
- "itness": 3659,
- "ito": 10094,
- "itol": 11650,
- "iton": 37752,
- "itone": 49644,
- "itor": 2072,
- "itored": 20026,
- "itors": 6742,
- "itory": 37765,
- "itous": 22109,
- "itri": 49510,
- "its": 896,
- "itsch": 48279,
- "itsu": 19831,
- "itt": 715,
- "itta": 48519,
- "ittal": 39979,
- "ittance": 47912,
- "itte": 2654,
- "itted": 2175,
- "ittee": 2979,
- "ittees": 13263,
- "itten": 2621,
- "ittens": 34978,
- "itter": 1967,
- "ittered": 36613,
- "itters": 45512,
- "itting": 2535,
- "ittle": 1206,
- "itto": 37606,
- "itton": 47304,
- "itty": 9760,
- "itu": 34272,
- "itual": 10587,
- "itud": 26331,
- "itude": 3984,
- "itudes": 10455,
- "itudinal": 29121,
- "iture": 8089,
- "itures": 20686,
- "itus": 17506,
- "itute": 3678,
- "itutes": 16845,
- "itution": 2738,
- "itutional": 5677,
- "ity": 414,
- "itz": 4224,
- "itzer": 10557,
- "itzerland": 13947,
- "ité": 43816,
- "iu": 16115,
- "ium": 1505,
- "ius": 3754,
- "iuses": 44666,
- "iv": 452,
- "iva": 12151,
- "ivable": 21911,
- "ivably": 47994,
- "ival": 2473,
- "ivalent": 29540,
- "ivalry": 47310,
- "ivals": 10336,
- "ivan": 13809,
- "ivari": 35460,
- "ivariate": 42524,
- "ivas": 38630,
- "ivated": 30829,
- "ivating": 39438,
- "ivation": 26939,
- "ive": 425,
- "ived": 1572,
- "ively": 2280,
- "iven": 1469,
- "iveness": 6517,
- "iver": 1428,
- "ivered": 6396,
- "ivering": 35598,
- "iverpool": 10864,
- "ivers": 1191,
- "iversal": 11480,
- "iversary": 9023,
- "iverse": 3997,
- "iversity": 1608,
- "ivery": 6315,
- "ives": 1083,
- "ivia": 20817,
- "ivic": 16482,
- "ivid": 1699,
- "ividual": 1896,
- "ividually": 16335,
- "ivil": 2464,
- "iving": 1412,
- "ivism": 25085,
- "ivist": 30944,
- "ivities": 28720,
- "ivity": 3458,
- "ivo": 23593,
- "ivot": 45785,
- "iw": 14246,
- "ix": 844,
- "ixed": 2966,
- "ixel": 7168,
- "ixels": 14810,
- "ixie": 39291,
- "ixir": 32345,
- "ixon": 12305,
- "ixt": 6346,
- "ixtape": 43938,
- "ixties": 46550,
- "ixture": 9602,
- "ixtures": 25506,
- "ixty": 19404,
- "iy": 7745,
- "iya": 21008,
- "iyah": 46398,
- "iz": 528,
- "iza": 23638,
- "izabeth": 9924,
- "izable": 13821,
- "izard": 8669,
- "izards": 14124,
- "izarre": 12474,
- "ization": 1634,
- "izational": 22684,
- "izations": 4582,
- "ize": 1096,
- "ized": 1143,
- "izen": 33977,
- "izens": 44908,
- "izer": 7509,
- "izers": 11341,
- "izes": 4340,
- "izing": 2890,
- "izo": 41282,
- "izon": 8637,
- "izons": 29457,
- "izont": 12071,
- "izontal": 38342,
- "izoph": 18115,
- "izophren": 18337,
- "izu": 47775,
- "izz": 6457,
- "izza": 9990,
- "izzard": 16191,
- "izzle": 44461,
- "izzy": 40593,
- "j": 73,
- "ja": 6592,
- "jab": 27935,
- "jac": 30482,
- "jack": 19650,
- "jad": 38442,
- "jah": 31558,
- "jam": 39159,
- "jamin": 13337,
- "jan": 13881,
- "jandro": 47983,
- "jar": 9491,
- "jas": 28121,
- "java": 12355,
- "javascript": 37495,
- "jay": 33708,
- "jc": 48055,
- "je": 18015,
- "ject": 752,
- "jected": 35408,
- "jection": 29192,
- "jee": 34589,
- "jen": 48796,
- "jer": 44009,
- "jet": 31173,
- "jew": 47483,
- "ji": 7285,
- "jiang": 39598,
- "jin": 18594,
- "jing": 49940,
- "jit": 45051,
- "jj": 41098,
- "jl": 20362,
- "jo": 7639,
- "job": 21858,
- "jobs": 43863,
- "john": 30686,
- "joice": 41026,
- "join": 22179,
- "joined": 46416,
- "joining": 40044,
- "jon": 46286,
- "jong": 32428,
- "journal": 24891,
- "joy": 2633,
- "jp": 34523,
- "jpg": 9479,
- "jri": 38790,
- "jriwal": 39890,
- "js": 8457,
- "json": 17752,
- "ju": 14396,
- "jud": 10456,
- "judicial": 46769,
- "jug": 31761,
- "jump": 43327,
- "jun": 29741,
- "jured": 38608,
- "juries": 47496,
- "jury": 21871,
- "just": 3137,
- "justice": 31012,
- "juven": 39427,
- "k": 74,
- "kB": 38841,
- "kHz": 44191,
- "ka": 4914,
- "kai": 32765,
- "kamp": 40899,
- "kan": 27541,
- "kar": 21070,
- "kas": 42749,
- "kat": 41826,
- "kay": 5568,
- "kaya": 35372,
- "kb": 32812,
- "ke": 365,
- "ked": 9091,
- "kee": 11035,
- "keep": 14894,
- "keeper": 13884,
- "keepers": 24952,
- "keeping": 19934,
- "kees": 16683,
- "kef": 30728,
- "kefeller": 31424,
- "kel": 7750,
- "keleton": 38800,
- "keley": 13490,
- "kell": 17164,
- "ken": 3464,
- "kens": 14972,
- "kept": 45089,
- "ker": 6122,
- "kered": 28970,
- "kernel": 33885,
- "kers": 15949,
- "kes": 5209,
- "ket": 7126,
- "key": 2539,
- "keye": 34929,
- "keyes": 43174,
- "keys": 13083,
- "kg": 10025,
- "kh": 14636,
- "ki": 4106,
- "kick": 24585,
- "kid": 38439,
- "kids": 45235,
- "kie": 49375,
- "kies": 43724,
- "kil": 34553,
- "kill": 12728,
- "killed": 42130,
- "killer": 32156,
- "killers": 43492,
- "killing": 43764,
- "kin": 5116,
- "kind": 11031,
- "king": 3364,
- "kins": 5331,
- "kinson": 26030,
- "kish": 31501,
- "kiss": 41304,
- "kit": 15813,
- "kj": 42421,
- "kk": 28747,
- "kl": 41582,
- "km": 13276,
- "kn": 15418,
- "knife": 48810,
- "knit": 47095,
- "know": 16275,
- "knowledge": 45066,
- "known": 4002,
- "ko": 7204,
- "kok": 32004,
- "kos": 46150,
- "kov": 21862,
- "kowski": 26216,
- "kr": 38584,
- "krit": 44531,
- "ks": 591,
- "ksh": 50133,
- "kson": 46505,
- "kt": 21841,
- "ktop": 16201,
- "ku": 23063,
- "kun": 28374,
- "kus": 45614,
- "kw": 46265,
- "kward": 12378,
- "ky": 2584,
- "l": 75,
- "la": 5031,
- "lab": 23912,
- "label": 18242,
- "lace": 27077,
- "lad": 9435,
- "laden": 35668,
- "lag": 30909,
- "lah": 9271,
- "lahoma": 9802,
- "laim": 20438,
- "lain": 34277,
- "lake": 27180,
- "lam": 2543,
- "lambda": 50033,
- "lamm": 11199,
- "lan": 9620,
- "lance": 23215,
- "land": 1044,
- "lander": 16235,
- "landers": 32358,
- "landish": 45626,
- "lando": 11993,
- "lands": 4447,
- "lane": 33533,
- "lang": 17204,
- "language": 16129,
- "lap": 37796,
- "lar": 21681,
- "larg": 15521,
- "large": 11664,
- "largest": 28209,
- "las": 21921,
- "lash": 17055,
- "lass": 31172,
- "lasses": 28958,
- "last": 12957,
- "lasting": 24810,
- "lat": 15460,
- "latable": 49009,
- "late": 17660,
- "lated": 17249,
- "later": 36760,
- "latest": 42861,
- "lation": 7592,
- "lations": 49905,
- "lator": 41880,
- "laugh": 44944,
- "laughs": 28124,
- "laughter": 27815,
- "laun": 38722,
- "launch": 35681,
- "laus": 38024,
- "lav": 18809,
- "law": 6270,
- "laws": 29317,
- "lay": 10724,
- "layer": 29289,
- "layout": 39786,
- "lb": 23160,
- "lbs": 32133,
- "lc": 44601,
- "ld": 335,
- "lda": 18986,
- "lde": 35209,
- "lder": 6499,
- "ldom": 23826,
- "ldon": 25900,
- "le": 293,
- "lead": 28230,
- "leader": 27940,
- "leaders": 37553,
- "leading": 12294,
- "leaf": 33201,
- "league": 19316,
- "lean": 13087,
- "leaning": 25909,
- "leanor": 41807,
- "leans": 11861,
- "lear": 3238,
- "learn": 35720,
- "learning": 40684,
- "lease": 1274,
- "leased": 14684,
- "leases": 29329,
- "leasing": 48764,
- "leave": 47408,
- "leck": 40667,
- "lect": 801,
- "lected": 12609,
- "lectic": 42009,
- "lection": 1564,
- "lections": 26448,
- "led": 992,
- "ledge": 2965,
- "ledged": 37436,
- "lee": 7197,
- "leen": 20042,
- "leep": 8892,
- "lees": 49410,
- "leeve": 49189,
- "left": 9464,
- "leg": 1455,
- "legal": 18011,
- "legate": 34637,
- "legates": 37061,
- "lege": 2765,
- "legged": 40898,
- "legram": 30536,
- "legraph": 16606,
- "leground": 28272,
- "lehem": 44797,
- "leigh": 42342,
- "lein": 33663,
- "lem": 10671,
- "lement": 1732,
- "lements": 3639,
- "lems": 46367,
- "len": 11925,
- "lene": 29466,
- "leneck": 43163,
- "leness": 48795,
- "length": 13664,
- "leon": 38970,
- "ler": 1754,
- "lers": 8116,
- "les": 829,
- "lesh": 29730,
- "lesi": 36027,
- "lesiastical": 46360,
- "less": 1203,
- "lessly": 8613,
- "lessness": 17587,
- "lest": 32712,
- "let": 1616,
- "letal": 47293,
- "letcher": 29257,
- "lete": 5807,
- "leted": 33342,
- "letes": 40676,
- "lethal": 46480,
- "letico": 47286,
- "leton": 10565,
- "lets": 5289,
- "lett": 15503,
- "lette": 21348,
- "letter": 9291,
- "letters": 15653,
- "lev": 2768,
- "levant": 14938,
- "levard": 22123,
- "level": 5715,
- "levels": 46170,
- "levision": 5024,
- "lex": 2588,
- "ley": 1636,
- "leys": 21325,
- "lez": 36858,
- "lf": 1652,
- "li": 4528,
- "lia": 24660,
- "liam": 5058,
- "liament": 5130,
- "lib": 8019,
- "liber": 33203,
- "liberal": 35739,
- "library": 32016,
- "lic": 677,
- "lication": 10142,
- "license": 43085,
- "licensed": 36612,
- "lich": 33467,
- "licks": 49191,
- "lict": 13758,
- "licted": 17823,
- "liction": 41101,
- "licts": 42267,
- "lie": 14485,
- "lied": 18511,
- "lier": 2505,
- "lies": 13508,
- "liest": 11318,
- "lif": 36195,
- "life": 6042,
- "lift": 26282,
- "lifting": 30510,
- "lig": 4604,
- "liga": 38910,
- "light": 2971,
- "lighting": 43351,
- "lightly": 30945,
- "lights": 8091,
- "lihood": 11935,
- "lik": 46965,
- "like": 2339,
- "likely": 40798,
- "lim": 2475,
- "lime": 27299,
- "limit": 32374,
- "limited": 10698,
- "limits": 49196,
- "lin": 2815,
- "line": 1370,
- "linear": 29127,
- "lined": 10837,
- "liner": 24683,
- "liners": 34380,
- "lines": 6615,
- "liness": 26061,
- "ling": 1359,
- "linger": 33550,
- "lings": 17783,
- "lington": 17299,
- "lining": 21310,
- "link": 8726,
- "linked": 25614,
- "links": 28751,
- "lins": 21602,
- "linux": 23289,
- "lio": 48590,
- "lip": 40712,
- "lique": 41522,
- "liquid": 39250,
- "lis": 27999,
- "lish": 1836,
- "lished": 2115,
- "lisher": 8191,
- "lishes": 19724,
- "lishing": 20020,
- "list": 4868,
- "listed": 17935,
- "lists": 20713,
- "lit": 18250,
- "lite": 36890,
- "liter": 17201,
- "literally": 43819,
- "little": 31629,
- "liv": 16017,
- "live": 12583,
- "lived": 24489,
- "living": 19950,
- "livion": 26018,
- "livious": 35260,
- "ll": 297,
- "lla": 8466,
- "llah": 22734,
- "llan": 47993,
- "lled": 3353,
- "ller": 6051,
- "llers": 13802,
- "lli": 15516,
- "lling": 2680,
- "llo": 18798,
- "llor": 14127,
- "llular": 32771,
- "lly": 12810,
- "ln": 18755,
- "lo": 5439,
- "load": 2220,
- "loaded": 14578,
- "loader": 29356,
- "loading": 25138,
- "loads": 46030,
- "loc": 17946,
- "local": 12001,
- "localhost": 36750,
- "location": 24886,
- "lock": 5354,
- "locked": 24162,
- "locking": 48331,
- "locks": 28860,
- "loe": 24617,
- "log": 6404,
- "login": 38235,
- "lol": 47288,
- "lon": 14995,
- "long": 6511,
- "loo": 29680,
- "look": 5460,
- "looking": 11534,
- "loop": 26268,
- "lopp": 39590,
- "lor": 4685,
- "lord": 10572,
- "lords": 19673,
- "lore": 31131,
- "los": 33280,
- "loss": 22462,
- "lost": 33224,
- "lot": 26487,
- "lov": 27086,
- "love": 23205,
- "loving": 33983,
- "low": 9319,
- "lower": 21037,
- "lp": 34431,
- "lr": 14050,
- "ls": 7278,
- "lt": 2528,
- "lu": 2290,
- "lua": 40211,
- "luaj": 36473,
- "lucent": 35600,
- "luck": 46708,
- "lude": 38792,
- "luence": 23079,
- "luent": 28216,
- "lund": 37525,
- "lus": 41790,
- "lust": 38878,
- "luster": 48375,
- "lux": 22564,
- "lv": 6780,
- "lves": 31018,
- "lvl": 47147,
- "ly": 306,
- "lyak": 43782,
- "lycer": 38577,
- "lying": 3157,
- "lymp": 6760,
- "lyn": 6213,
- "lynn": 12935,
- "lys": 27385,
- "lyss": 35670,
- "lé": 45031,
- "m": 76,
- "mA": 42646,
- "mAh": 28142,
- "mL": 32087,
- "ma": 2611,
- "mable": 44102,
- "mac": 20285,
- "machine": 30243,
- "mad": 9937,
- "made": 9727,
- "mag": 19726,
- "mage": 25561,
- "magic": 32707,
- "maid": 23151,
- "mail": 4529,
- "mails": 26165,
- "main": 12417,
- "major": 22478,
- "majority": 35839,
- "make": 15883,
- "maker": 10297,
- "makers": 6620,
- "makes": 49123,
- "making": 8601,
- "mal": 7617,
- "male": 22606,
- "malink": 31000,
- "mallow": 42725,
- "man": 805,
- "manac": 46870,
- "managed": 39935,
- "management": 27604,
- "manager": 37153,
- "mand": 22249,
- "manent": 44172,
- "mania": 45733,
- "mann": 9038,
- "mans": 16221,
- "manship": 25428,
- "manuel": 18713,
- "manufact": 48119,
- "many": 21834,
- "map": 8899,
- "maps": 31803,
- "mar": 3876,
- "mare": 11449,
- "mares": 23745,
- "marg": 30887,
- "margin": 36153,
- "marine": 42380,
- "mark": 4102,
- "marked": 23505,
- "market": 10728,
- "markets": 34162,
- "marks": 14306,
- "marriage": 45394,
- "married": 30526,
- "mart": 13822,
- "mary": 6874,
- "mas": 5356,
- "mask": 27932,
- "mass": 22208,
- "massive": 49777,
- "mast": 47616,
- "master": 9866,
- "masters": 40706,
- "mat": 6759,
- "match": 15699,
- "matched": 31409,
- "mate": 9830,
- "material": 33665,
- "mates": 7300,
- "math": 11018,
- "matic": 13849,
- "matical": 44935,
- "matically": 49454,
- "matter": 47635,
- "max": 9806,
- "maximum": 47033,
- "maxwell": 29047,
- "may": 11261,
- "maybe": 25991,
- "mb": 2022,
- "mber": 1916,
- "mberg": 47369,
- "mble": 11306,
- "mbol": 23650,
- "mbuds": 45664,
- "mbudsman": 47012,
- "mc": 23209,
- "md": 9132,
- "me": 1326,
- "meal": 28208,
- "mean": 32604,
- "meaning": 24815,
- "measures": 47336,
- "meat": 41495,
- "med": 1150,
- "medi": 2379,
- "media": 11431,
- "mediate": 13857,
- "mediated": 38363,
- "mediately": 23802,
- "medical": 41693,
- "medium": 24132,
- "meet": 47745,
- "meg": 28917,
- "mega": 13731,
- "meier": 49468,
- "mel": 17694,
- "melon": 45690,
- "mem": 11883,
- "member": 19522,
- "members": 30814,
- "memory": 31673,
- "men": 3653,
- "mens": 45535,
- "ment": 434,
- "mental": 37098,
- "mentation": 14374,
- "mented": 12061,
- "mentioned": 17181,
- "ments": 902,
- "menu": 26272,
- "mer": 647,
- "merce": 11647,
- "mercial": 15790,
- "mere": 34671,
- "merga": 44739,
- "meric": 946,
- "mers": 11056,
- "mes": 6880,
- "mess": 37348,
- "message": 20500,
- "met": 4164,
- "meta": 28961,
- "metadata": 38993,
- "metal": 28469,
- "meter": 27231,
- "method": 24396,
- "methyl": 43654,
- "metic": 15103,
- "metics": 27757,
- "metry": 41935,
- "meyer": 48794,
- "mg": 11296,
- "mi": 11632,
- "mia": 20730,
- "miah": 35029,
- "mic": 9383,
- "micro": 24055,
- "microsoft": 40485,
- "mid": 13602,
- "middle": 27171,
- "midt": 21184,
- "mie": 44871,
- "might": 44092,
- "mil": 25433,
- "mile": 18085,
- "military": 33631,
- "mill": 17805,
- "million": 14100,
- "milo": 48995,
- "min": 1084,
- "mination": 17928,
- "mind": 10155,
- "minded": 14543,
- "mine": 3810,
- "minecraft": 17761,
- "minent": 19669,
- "ming": 2229,
- "mingham": 17737,
- "mington": 39773,
- "mini": 45313,
- "minimum": 39504,
- "mining": 45374,
- "minist": 2201,
- "ministic": 49228,
- "mins": 42951,
- "minster": 18462,
- "mint": 34289,
- "minus": 40191,
- "minute": 11374,
- "mir": 10793,
- "mire": 47004,
- "mis": 25413,
- "misc": 44374,
- "miss": 3927,
- "missible": 21597,
- "missing": 45688,
- "mission": 3411,
- "missions": 8481,
- "missive": 33532,
- "mist": 37980,
- "mit": 2781,
- "mite": 32937,
- "mith": 22947,
- "mits": 24883,
- "mitt": 20124,
- "mitted": 3291,
- "mittedly": 43011,
- "mitter": 37974,
- "mitting": 16138,
- "mix": 19816,
- "mk": 28015,
- "ml": 4029,
- "mm": 3020,
- "mma": 21672,
- "mmm": 27532,
- "mmmm": 40133,
- "mn": 10295,
- "mo": 5908,
- "mob": 39949,
- "mobi": 43549,
- "mobile": 24896,
- "mod": 4666,
- "mode": 14171,
- "model": 19849,
- "models": 27530,
- "moderate": 47189,
- "modern": 23922,
- "modified": 41771,
- "mods": 24122,
- "module": 21412,
- "modules": 18170,
- "moil": 25538,
- "mol": 43132,
- "mology": 29126,
- "mom": 32542,
- "mon": 2144,
- "monary": 36639,
- "mond": 6327,
- "monds": 42620,
- "mone": 47122,
- "money": 26316,
- "mong": 31059,
- "monitor": 41143,
- "monkey": 49572,
- "mons": 11567,
- "monster": 39050,
- "mont": 8691,
- "month": 8424,
- "months": 41537,
- "monton": 19729,
- "mony": 9926,
- "moon": 22977,
- "mop": 35244,
- "mopolitan": 44331,
- "mor": 4491,
- "moral": 41996,
- "more": 3549,
- "morning": 43911,
- "morph": 24503,
- "morrow": 9201,
- "mort": 30171,
- "mortem": 46515,
- "mos": 16785,
- "mosp": 6384,
- "most": 1712,
- "mostly": 29471,
- "mot": 27926,
- "mother": 13552,
- "motion": 38714,
- "mount": 14948,
- "mounted": 29728,
- "mouse": 35888,
- "mouth": 14775,
- "move": 21084,
- "movie": 41364,
- "moving": 31462,
- "mp": 3149,
- "mpeg": 43913,
- "mph": 23335,
- "mpire": 35386,
- "mr": 43395,
- "ms": 907,
- "msg": 19662,
- "mson": 24996,
- "mt": 16762,
- "mu": 30300,
- "much": 29482,
- "mud": 41650,
- "mult": 16680,
- "multi": 41684,
- "multipl": 47945,
- "multiple": 48101,
- "mun": 6199,
- "mund": 20125,
- "munition": 12640,
- "mur": 28582,
- "mus": 14664,
- "music": 28965,
- "must": 27238,
- "mut": 21973,
- "mx": 36802,
- "my": 1820,
- "myra": 49216,
- "mys": 28744,
- "n": 77,
- "na": 2616,
- "nah": 40909,
- "nai": 38600,
- "naire": 24042,
- "naires": 43317,
- "naissance": 47090,
- "nam": 7402,
- "name": 3672,
- "named": 13190,
- "names": 14933,
- "namese": 22678,
- "nan": 12647,
- "nance": 41601,
- "nant": 22057,
- "nants": 26501,
- "nar": 23955,
- "nard": 40542,
- "nas": 24716,
- "nat": 32353,
- "natal": 33150,
- "nation": 25729,
- "national": 14648,
- "native": 30191,
- "natural": 11802,
- "nature": 21353,
- "natureconservancy": 41380,
- "nav": 28341,
- "nb": 46803,
- "nc": 10782,
- "nce": 1198,
- "nces": 3179,
- "nd": 358,
- "nda": 45658,
- "nder": 681,
- "ndra": 24631,
- "ndum": 11021,
- "ne": 710,
- "nea": 39718,
- "neapolis": 19359,
- "near": 40093,
- "neath": 13725,
- "neau": 46533,
- "nec": 32984,
- "necess": 10789,
- "necessary": 49986,
- "neck": 27235,
- "nect": 1606,
- "ned": 2817,
- "nee": 21381,
- "need": 31227,
- "needed": 27938,
- "needs": 50032,
- "neg": 12480,
- "negative": 31591,
- "negie": 32360,
- "nel": 4954,
- "nell": 10076,
- "nels": 19423,
- "nen": 38572,
- "ner": 1008,
- "nered": 15826,
- "nerg": 25649,
- "nergy": 5877,
- "ners": 2741,
- "nery": 35865,
- "nes": 2516,
- "nesday": 3462,
- "nesia": 31401,
- "nesium": 27619,
- "nesota": 8360,
- "ness": 1108,
- "nesses": 47556,
- "nesty": 18718,
- "net": 3262,
- "netflix": 36977,
- "netic": 9833,
- "nets": 45938,
- "nette": 48115,
- "network": 27349,
- "neum": 25668,
- "neutral": 29797,
- "never": 12081,
- "new": 3605,
- "news": 10827,
- "nex": 12413,
- "nexpected": 42072,
- "next": 19545,
- "nexus": 44520,
- "ney": 1681,
- "neys": 20141,
- "ng": 782,
- "ngth": 11910,
- "ni": 8461,
- "nia": 18142,
- "nian": 44516,
- "nic": 6988,
- "nice": 44460,
- "nick": 17172,
- "nie": 11952,
- "night": 3847,
- "nih": 37373,
- "nik": 17187,
- "nikov": 45451,
- "nil": 45991,
- "nin": 35073,
- "nine": 30888,
- "ning": 768,
- "nings": 23400,
- "nington": 48405,
- "niper": 45554,
- "nir": 32986,
- "nis": 21361,
- "nit": 48825,
- "nl": 21283,
- "nm": 21533,
- "nn": 20471,
- "no": 3919,
- "nob": 34952,
- "node": 17440,
- "nom": 26601,
- "non": 13159,
- "none": 23108,
- "noon": 6357,
- "nor": 13099,
- "norm": 27237,
- "normal": 11265,
- "north": 43588,
- "nos": 39369,
- "nosis": 31707,
- "nostic": 43758,
- "not": 1662,
- "notation": 38983,
- "notations": 30078,
- "note": 11295,
- "notes": 17815,
- "nothing": 22366,
- "notice": 42138,
- "noticed": 31696,
- "nov": 37302,
- "nova": 38438,
- "now": 2197,
- "nown": 3408,
- "nox": 35420,
- "noxious": 40591,
- "np": 37659,
- "nr": 48624,
- "ns": 5907,
- "nsic": 19364,
- "nsics": 49242,
- "nt": 429,
- "ntax": 41641,
- "ntil": 10125,
- "nton": 28936,
- "nu": 28803,
- "nuclear": 43131,
- "null": 8423,
- "num": 22510,
- "number": 17618,
- "numbered": 35565,
- "nut": 14930,
- "nutrition": 40482,
- "nuts": 31381,
- "nv": 48005,
- "nw": 47516,
- "ny": 3281,
- "nyder": 21053,
- "nz": 27305,
- "o": 78,
- "oS": 34049,
- "oa": 12162,
- "oad": 1170,
- "oaded": 22273,
- "oak": 15877,
- "oan": 24611,
- "oard": 11953,
- "oat": 15073,
- "ob": 672,
- "oba": 19981,
- "obal": 2572,
- "obar": 30973,
- "obb": 21963,
- "obbies": 41372,
- "obby": 11369,
- "obe": 5910,
- "ober": 2023,
- "obi": 13411,
- "obia": 30665,
- "obic": 20803,
- "obil": 25898,
- "obile": 3579,
- "obiles": 36329,
- "obin": 38954,
- "obj": 26801,
- "object": 15252,
- "objects": 48205,
- "obl": 45292,
- "obo": 20391,
- "obook": 49776,
- "obos": 49878,
- "obs": 8158,
- "oby": 26730,
- "obyl": 46666,
- "oc": 420,
- "oca": 11216,
- "ocado": 33441,
- "ocal": 4374,
- "ocally": 44190,
- "ocaly": 12063,
- "ocalypse": 15145,
- "ocalyptic": 28339,
- "ocamp": 26047,
- "ocard": 44412,
- "ocate": 13369,
- "ocated": 10533,
- "ocating": 27123,
- "ocation": 5040,
- "ocations": 20968,
- "ocative": 23466,
- "ocaust": 16377,
- "occ": 13966,
- "occup": 19596,
- "occupied": 28756,
- "ocene": 34973,
- "ocent": 29421,
- "ocese": 31292,
- "och": 5374,
- "oche": 30848,
- "ochem": 18958,
- "ochemical": 32864,
- "ochemistry": 37074,
- "ochet": 49579,
- "ochond": 22400,
- "oci": 1733,
- "ocial": 9402,
- "ociate": 47615,
- "ociated": 19293,
- "ociation": 41003,
- "ocide": 16207,
- "ocious": 32346,
- "ocity": 11683,
- "ock": 735,
- "ocked": 3543,
- "ocker": 12721,
- "ocket": 5459,
- "ockets": 11603,
- "ockey": 8337,
- "ocking": 8629,
- "ocks": 3320,
- "ocl": 38679,
- "oco": 25634,
- "ocobo": 37642,
- "ococ": 34403,
- "ocol": 4668,
- "ocolate": 9140,
- "ocom": 42829,
- "ocon": 36221,
- "ocr": 1696,
- "ocracy": 17818,
- "ocrat": 35128,
- "ocrates": 34095,
- "ocratic": 15405,
- "ocrats": 21988,
- "ocre": 27945,
- "ocrin": 39913,
- "ocrine": 38658,
- "ocry": 48103,
- "oct": 38441,
- "ocular": 37320,
- "ocument": 7990,
- "ocumented": 17664,
- "ocus": 10901,
- "ocused": 13073,
- "ocusing": 45743,
- "ocy": 13733,
- "ocyte": 43320,
- "ocytes": 30309,
- "od": 375,
- "oda": 11329,
- "odan": 45561,
- "oday": 4348,
- "odcast": 7107,
- "odd": 5088,
- "odder": 35346,
- "oddy": 38553,
- "ode": 1098,
- "oded": 9043,
- "oder": 12342,
- "odes": 4147,
- "odge": 9728,
- "odi": 23130,
- "odiac": 40096,
- "odic": 29512,
- "odied": 32255,
- "odies": 5042,
- "oding": 7656,
- "odium": 12664,
- "odka": 28601,
- "odo": 24313,
- "odon": 46457,
- "odor": 30530,
- "odore": 25102,
- "odox": 11430,
- "ods": 12978,
- "odus": 21878,
- "ody": 1118,
- "odynam": 24319,
- "odynamic": 34743,
- "odynamics": 44124,
- "oe": 2577,
- "oen": 6571,
- "oenix": 8538,
- "oes": 3028,
- "oeuv": 37600,
- "of": 1659,
- "ofer": 30288,
- "off": 2364,
- "offensive": 45055,
- "offer": 47895,
- "offic": 14406,
- "office": 31810,
- "official": 16841,
- "offs": 8210,
- "offset": 28968,
- "ofi": 39542,
- "oft": 11205,
- "often": 28950,
- "og": 519,
- "oga": 10949,
- "ogan": 9632,
- "ogen": 6644,
- "ogene": 20878,
- "ogeneity": 37477,
- "ogeneous": 32269,
- "ogenesis": 25908,
- "ogenic": 15147,
- "ogenous": 27897,
- "ogens": 26612,
- "ogether": 8236,
- "ogg": 10332,
- "ogged": 42545,
- "ogging": 30853,
- "oggle": 20258,
- "oggles": 48549,
- "ogh": 46664,
- "ogi": 44381,
- "ogical": 30766,
- "ogie": 37987,
- "ogl": 28678,
- "ogle": 2467,
- "oglobin": 49835,
- "oglu": 49006,
- "ogly": 34619,
- "ogn": 2360,
- "ognitive": 46610,
- "ogo": 24076,
- "ogram": 21857,
- "ograms": 26836,
- "ograp": 7113,
- "ograph": 2384,
- "ographed": 39620,
- "ographer": 18539,
- "ographers": 34063,
- "ographic": 6826,
- "ographical": 17046,
- "ographically": 33145,
- "ographics": 24188,
- "ographies": 41480,
- "ographs": 33492,
- "ography": 4867,
- "ogs": 18463,
- "ogue": 5119,
- "ogun": 39918,
- "ogy": 9868,
- "ogyn": 20593,
- "oh": 1219,
- "oha": 28083,
- "ohan": 22436,
- "ohl": 48988,
- "ohm": 34028,
- "ohn": 1562,
- "oho": 40950,
- "ohyd": 15782,
- "ohydrate": 46358,
- "oi": 23013,
- "oice": 2942,
- "oid": 1868,
- "oidal": 47502,
- "oided": 13780,
- "oids": 10994,
- "oil": 9437,
- "oiler": 20837,
- "oin": 36743,
- "oine": 42722,
- "oing": 40519,
- "oint": 1563,
- "ointed": 20909,
- "ointment": 49805,
- "oir": 10840,
- "oire": 32177,
- "ois": 10924,
- "oise": 25678,
- "oit": 30711,
- "oj": 13210,
- "oji": 31370,
- "ojure": 32511,
- "ok": 482,
- "oka": 17411,
- "okane": 41776,
- "oke": 2088,
- "oked": 6545,
- "okemon": 12717,
- "oken": 4233,
- "oker": 11020,
- "okers": 18698,
- "okes": 3369,
- "oki": 18228,
- "okia": 22903,
- "okin": 36749,
- "oking": 5730,
- "okingly": 48343,
- "oko": 16044,
- "oks": 28194,
- "oku": 11601,
- "oky": 31375,
- "oké": 35861,
- "ol": 349,
- "ola": 5708,
- "olan": 16617,
- "oland": 23573,
- "olar": 6192,
- "olars": 7828,
- "olas": 12456,
- "olate": 27976,
- "olated": 50027,
- "olation": 21417,
- "old": 727,
- "olded": 48959,
- "oldemort": 24710,
- "older": 19892,
- "olding": 33266,
- "oldown": 15041,
- "olds": 10119,
- "ole": 2305,
- "olean": 21052,
- "oled": 45342,
- "olen": 8622,
- "oleon": 25637,
- "oler": 13625,
- "olerance": 37668,
- "oles": 4316,
- "olesc": 16850,
- "olescent": 23406,
- "olester": 15764,
- "olesterol": 16743,
- "oley": 48437,
- "olf": 4024,
- "oli": 11106,
- "olia": 22703,
- "oliath": 43009,
- "oliberal": 28525,
- "olic": 4160,
- "olicited": 47607,
- "olics": 19615,
- "olicy": 21424,
- "olid": 10180,
- "olin": 24910,
- "olina": 47196,
- "oline": 14453,
- "oling": 40949,
- "olini": 43232,
- "olis": 8506,
- "olit": 6212,
- "olitan": 12977,
- "olith": 21446,
- "olithic": 30764,
- "olitical": 13781,
- "olitics": 21704,
- "olition": 50014,
- "olk": 13597,
- "olkien": 31052,
- "oll": 692,
- "olla": 33011,
- "ollah": 17999,
- "ollar": 13228,
- "ollen": 29952,
- "oller": 49252,
- "ollo": 15578,
- "ollow": 950,
- "ollower": 47030,
- "olls": 33421,
- "olly": 5098,
- "ollywood": 31777,
- "oln": 10875,
- "olo": 14057,
- "olog": 928,
- "ologic": 20781,
- "ological": 2770,
- "ologically": 13437,
- "ologies": 5823,
- "ologist": 7451,
- "ologists": 9251,
- "ologne": 30520,
- "ologue": 39795,
- "ology": 1435,
- "olon": 43645,
- "olor": 45621,
- "olph": 10196,
- "olphin": 27161,
- "olphins": 16547,
- "ols": 10220,
- "olson": 32836,
- "olt": 5978,
- "olulu": 39814,
- "olute": 3552,
- "olutely": 16780,
- "olution": 2122,
- "olutions": 14191,
- "olve": 6442,
- "olved": 5634,
- "olver": 14375,
- "olves": 9010,
- "olving": 10890,
- "oly": 3366,
- "olyn": 34742,
- "om": 296,
- "oma": 6086,
- "omach": 10806,
- "omal": 18048,
- "omaly": 24335,
- "oman": 5185,
- "omas": 16911,
- "omatic": 13730,
- "omb": 2381,
- "ombat": 41628,
- "ombie": 9081,
- "ombies": 12676,
- "ombo": 47265,
- "ombs": 33273,
- "ome": 462,
- "omed": 12657,
- "omedical": 35914,
- "omen": 3674,
- "omer": 12057,
- "omers": 21499,
- "omes": 2586,
- "omet": 908,
- "ometer": 15635,
- "ometers": 40077,
- "omething": 8370,
- "ometime": 47056,
- "ometimes": 6533,
- "ometown": 19191,
- "ometric": 16996,
- "ometry": 15748,
- "omever": 49784,
- "omew": 28030,
- "omez": 30010,
- "omi": 12753,
- "omial": 49070,
- "omic": 10179,
- "omical": 22545,
- "omics": 31994,
- "omin": 6351,
- "ominated": 50251,
- "omination": 27744,
- "oming": 3383,
- "ominium": 46134,
- "omm": 2002,
- "ommel": 48990,
- "ommod": 8641,
- "omnia": 37340,
- "omo": 17902,
- "omon": 16698,
- "omore": 22113,
- "omorph": 25831,
- "omorphic": 46374,
- "omp": 3361,
- "ompl": 6316,
- "oms": 3150,
- "omsday": 33415,
- "omsky": 37093,
- "omy": 9145,
- "on": 261,
- "ona": 4450,
- "onal": 20996,
- "once": 27078,
- "ond": 623,
- "onda": 13533,
- "onday": 3204,
- "onde": 14378,
- "onder": 8623,
- "onding": 42703,
- "ondo": 22311,
- "ondon": 3391,
- "onds": 24764,
- "onduct": 12920,
- "onductor": 40990,
- "one": 505,
- "oned": 12004,
- "onel": 26261,
- "oneliness": 34634,
- "onement": 49844,
- "onen": 34481,
- "onent": 3471,
- "onential": 35470,
- "onents": 3906,
- "oner": 14491,
- "ones": 1952,
- "onest": 19129,
- "onet": 36823,
- "onew": 44181,
- "oney": 1419,
- "ong": 506,
- "onga": 44294,
- "onge": 14220,
- "ongevity": 25454,
- "ongh": 19757,
- "ongo": 25162,
- "ongs": 28079,
- "ongyang": 20659,
- "oni": 14651,
- "onia": 11339,
- "onial": 30752,
- "onian": 27625,
- "onic": 9229,
- "onica": 32752,
- "onics": 38530,
- "onies": 17300,
- "oning": 12484,
- "onis": 43524,
- "onite": 46285,
- "online": 25119,
- "only": 8807,
- "onna": 6415,
- "onnaissance": 31539,
- "onne": 47476,
- "ono": 29941,
- "onom": 6326,
- "onomic": 40036,
- "onomous": 38175,
- "onomy": 30565,
- "ons": 684,
- "onse": 2591,
- "onsense": 46563,
- "onsequ": 40819,
- "onso": 26666,
- "onson": 36742,
- "ont": 756,
- "onte": 38599,
- "ontent": 38564,
- "onto": 5957,
- "onut": 16478,
- "ony": 1647,
- "onym": 5177,
- "onymous": 6704,
- "onyms": 43612,
- "onz": 13569,
- "oo": 2238,
- "ood": 702,
- "oodle": 27106,
- "oodoo": 36038,
- "oof": 37711,
- "ook": 566,
- "ooked": 46288,
- "ookie": 18055,
- "ooks": 31085,
- "ooky": 29655,
- "ool": 970,
- "oola": 10513,
- "ools": 10141,
- "oom": 4207,
- "ooming": 30602,
- "oon": 2049,
- "oons": 13022,
- "ooo": 34160,
- "oooo": 13321,
- "oooooooo": 26759,
- "oooooooooooooooo": 49135,
- "oop": 11224,
- "oops": 44860,
- "oor": 2675,
- "oos": 16426,
- "oot": 1025,
- "ooter": 25141,
- "ooters": 48316,
- "ooth": 5226,
- "oother": 31724,
- "ooting": 12494,
- "oots": 13880,
- "op": 404,
- "opa": 26186,
- "opal": 33067,
- "opard": 15478,
- "opath": 18569,
- "opathic": 44650,
- "opathy": 27189,
- "opausal": 47637,
- "ope": 3008,
- "oped": 19458,
- "open": 9654,
- "opened": 26350,
- "opening": 29443,
- "opens": 44813,
- "oper": 3575,
- "operated": 42767,
- "operation": 27184,
- "operative": 27173,
- "operator": 46616,
- "opers": 20618,
- "opes": 13920,
- "opez": 20808,
- "oph": 2522,
- "ophe": 43852,
- "ophen": 47806,
- "opher": 8803,
- "ophers": 20856,
- "ophical": 49256,
- "ophile": 37161,
- "ophob": 13253,
- "ophobia": 19851,
- "ophobic": 23468,
- "ophon": 48982,
- "ophone": 38656,
- "ophy": 16982,
- "ophys": 39665,
- "ophysical": 41789,
- "opia": 24464,
- "opian": 37548,
- "opic": 16603,
- "oping": 15816,
- "opl": 20106,
- "oplan": 46853,
- "ople": 643,
- "oples": 12614,
- "opol": 39704,
- "opolis": 47575,
- "opoly": 35894,
- "opot": 43372,
- "opoulos": 20338,
- "opp": 10365,
- "oppable": 35628,
- "opped": 38333,
- "oppers": 37186,
- "opping": 33307,
- "oppy": 26696,
- "ops": 2840,
- "opsis": 24608,
- "opsy": 44522,
- "opt": 8738,
- "opted": 45256,
- "opter": 32563,
- "optim": 40085,
- "option": 18076,
- "optional": 25968,
- "options": 25811,
- "opus": 25790,
- "opy": 11081,
- "oqu": 22696,
- "or": 273,
- "ora": 5799,
- "orable": 10475,
- "orage": 4945,
- "orah": 40844,
- "oral": 6864,
- "orama": 36161,
- "oran": 31884,
- "orange": 43745,
- "oras": 41043,
- "orate": 16262,
- "oration": 6944,
- "orative": 36478,
- "orb": 27688,
- "orbit": 42594,
- "orc": 24449,
- "orce": 8387,
- "ord": 585,
- "ordable": 16819,
- "ordan": 7350,
- "orde": 17531,
- "order": 2875,
- "ordered": 24071,
- "ordering": 34555,
- "orders": 6361,
- "ordes": 35770,
- "ordial": 31223,
- "ordinary": 35947,
- "ordinate": 45480,
- "ording": 1284,
- "ordon": 9999,
- "ords": 3669,
- "ore": 382,
- "oreAnd": 40219,
- "oreAndOnline": 40240,
- "orea": 46215,
- "oreal": 39396,
- "orean": 29456,
- "ored": 1850,
- "orem": 29625,
- "oren": 29578,
- "orer": 11934,
- "orers": 28089,
- "ores": 2850,
- "oresc": 45166,
- "orescence": 48699,
- "orescent": 35414,
- "orest": 26522,
- "oret": 9997,
- "orf": 24263,
- "org": 2398,
- "organ": 9971,
- "organic": 36617,
- "organisms": 45165,
- "organized": 30280,
- "orge": 3643,
- "orget": 28122,
- "orgetown": 29085,
- "ori": 10145,
- "oria": 7661,
- "orial": 5132,
- "orian": 22618,
- "orians": 45825,
- "oric": 8146,
- "orical": 12409,
- "orically": 26847,
- "orie": 19257,
- "oried": 42058,
- "orient": 13989,
- "oriented": 17107,
- "ories": 1749,
- "orig": 11612,
- "origin": 47103,
- "original": 14986,
- "oring": 3255,
- "orio": 40974,
- "orious": 9982,
- "oris": 37279,
- "ority": 29134,
- "orius": 48759,
- "ork": 967,
- "orks": 3647,
- "orkshire": 29918,
- "orld": 1764,
- "orm": 579,
- "ormal": 6636,
- "orman": 26183,
- "ormon": 10615,
- "ormonal": 33792,
- "ormons": 29966,
- "orn": 1211,
- "orne": 8553,
- "orned": 26994,
- "orney": 4195,
- "orneys": 13060,
- "ornia": 3317,
- "ornings": 28863,
- "orno": 46447,
- "orns": 19942,
- "oro": 16522,
- "oros": 40877,
- "orough": 7985,
- "orous": 9610,
- "orously": 24882,
- "orp": 16300,
- "orph": 13425,
- "orpor": 31150,
- "orporated": 40132,
- "orr": 38890,
- "orrect": 47315,
- "orrow": 6254,
- "orry": 5152,
- "ors": 669,
- "orsche": 26164,
- "orse": 7615,
- "orses": 11836,
- "orset": 49590,
- "orship": 11094,
- "orsi": 35255,
- "orst": 29422,
- "ort": 419,
- "ortal": 16906,
- "ortality": 28337,
- "orted": 9741,
- "orter": 4337,
- "orters": 3816,
- "ortex": 26158,
- "orth": 1506,
- "orthern": 4824,
- "orthodox": 42539,
- "orthy": 18906,
- "orting": 24707,
- "ortion": 5817,
- "ortium": 25182,
- "ortment": 33920,
- "ortmund": 34876,
- "orts": 2096,
- "ortun": 1922,
- "ortunate": 13651,
- "ortunately": 4690,
- "oru": 27786,
- "orum": 19220,
- "orus": 15125,
- "ory": 652,
- "os": 418,
- "osa": 8546,
- "osal": 40725,
- "osate": 33931,
- "osaurs": 22344,
- "osaurus": 47650,
- "osc": 17500,
- "oscope": 40326,
- "oscopic": 48228,
- "ose": 577,
- "osed": 1335,
- "osen": 5233,
- "oser": 13416,
- "oses": 4629,
- "osexual": 8542,
- "osh": 3768,
- "oshenko": 43934,
- "osher": 38321,
- "oshi": 13704,
- "oshop": 25444,
- "osi": 21707,
- "osing": 2752,
- "osion": 18442,
- "osis": 5958,
- "osit": 7434,
- "osite": 5971,
- "osition": 3507,
- "ositories": 35061,
- "ository": 13264,
- "osity": 16579,
- "oslav": 26388,
- "oslov": 50005,
- "oso": 28213,
- "osp": 2117,
- "ospace": 24912,
- "ospel": 13994,
- "ospels": 41908,
- "osph": 14222,
- "osphere": 22829,
- "ospital": 3531,
- "ospons": 35952,
- "osponsors": 39500,
- "oss": 793,
- "ossal": 33582,
- "ossession": 49809,
- "ossibility": 43691,
- "ossible": 4733,
- "ossibly": 20846,
- "ossier": 30087,
- "ossip": 26710,
- "ossom": 25548,
- "ossus": 36533,
- "ost": 455,
- "osta": 39818,
- "oster": 6197,
- "osterone": 16372,
- "ostic": 15132,
- "ostics": 34558,
- "oston": 5744,
- "osuke": 45914,
- "osure": 4567,
- "osures": 16091,
- "ot": 313,
- "ota": 4265,
- "otal": 4997,
- "otally": 38908,
- "otation": 14221,
- "otaur": 35269,
- "ote": 1258,
- "otech": 32469,
- "otechnology": 31201,
- "oted": 5191,
- "otent": 33715,
- "oter": 19543,
- "oteric": 38571,
- "oters": 26008,
- "otes": 6421,
- "oth": 849,
- "othal": 42376,
- "othe": 20388,
- "other": 847,
- "otherapy": 18952,
- "othermal": 49723,
- "othes": 31690,
- "othing": 24834,
- "oths": 27118,
- "othy": 14863,
- "oti": 5092,
- "otiation": 21236,
- "otic": 6210,
- "otics": 23891,
- "otide": 45608,
- "otin": 41403,
- "otine": 16174,
- "oting": 10720,
- "otion": 9650,
- "otional": 25453,
- "otions": 36083,
- "otive": 19138,
- "otle": 23556,
- "oto": 2069,
- "otom": 43125,
- "otomy": 38385,
- "oton": 18970,
- "otonin": 29613,
- "otor": 20965,
- "otos": 14163,
- "otrop": 34248,
- "otropic": 46084,
- "ots": 1747,
- "ott": 1252,
- "otta": 12375,
- "ottage": 29480,
- "otte": 11404,
- "otted": 8426,
- "otten": 4728,
- "ottenham": 21889,
- "ottest": 24879,
- "ottesville": 23806,
- "otti": 26380,
- "otto": 17631,
- "otton": 11324,
- "otyp": 17183,
- "otype": 8690,
- "otypes": 13567,
- "ou": 280,
- "oub": 12944,
- "ouble": 15270,
- "oubt": 47675,
- "oubted": 15973,
- "oubtedly": 16423,
- "ouch": 7673,
- "ouched": 30075,
- "oud": 2778,
- "ouf": 37116,
- "oufl": 28012,
- "oug": 20805,
- "ough": 619,
- "ought": 2917,
- "ouk": 38960,
- "oul": 2852,
- "ould": 426,
- "oulder": 17601,
- "oulos": 19537,
- "ouls": 42033,
- "oult": 25955,
- "oultry": 30056,
- "oun": 977,
- "ounce": 8652,
- "ounced": 8918,
- "ounces": 45982,
- "ouncing": 18155,
- "ound": 633,
- "ounded": 6302,
- "ounding": 9969,
- "ounds": 3733,
- "ounge": 20891,
- "ount": 608,
- "ountain": 18635,
- "ounter": 6828,
- "ounters": 15044,
- "ounty": 17705,
- "oup": 10486,
- "ouple": 43846,
- "our": 454,
- "ourage": 32885,
- "ource": 1668,
- "ourced": 30555,
- "ources": 2203,
- "ourcing": 29985,
- "oured": 8167,
- "ourge": 14501,
- "ourgeois": 18924,
- "ouri": 10300,
- "ouring": 21823,
- "ourke": 49003,
- "ourmet": 39094,
- "ourn": 1798,
- "ournal": 2549,
- "ournals": 18408,
- "ournament": 5138,
- "ournaments": 16950,
- "ourney": 5604,
- "ourning": 31626,
- "ours": 4662,
- "ourse": 9047,
- "ourses": 39975,
- "ourt": 15666,
- "ous": 516,
- "ousand": 29910,
- "ousands": 19983,
- "ouse": 1076,
- "oused": 29997,
- "ousel": 48355,
- "ouses": 11370,
- "ousing": 12752,
- "ously": 3481,
- "ousse": 28396,
- "oust": 23968,
- "oustic": 21618,
- "ouston": 6526,
- "ousy": 41808,
- "out": 448,
- "oute": 13192,
- "outed": 18534,
- "outer": 39605,
- "outh": 1536,
- "outheast": 14474,
- "outheastern": 27873,
- "outher": 44262,
- "outhern": 4927,
- "outine": 28399,
- "outing": 13660,
- "output": 22915,
- "outs": 5269,
- "outside": 43435,
- "outube": 9762,
- "ouver": 10166,
- "oux": 22193,
- "ov": 709,
- "ova": 10071,
- "ovable": 21985,
- "oval": 8325,
- "ovan": 22590,
- "ovation": 17882,
- "ove": 659,
- "oved": 2668,
- "ovember": 3239,
- "oven": 16206,
- "over": 2502,
- "overe": 33518,
- "overed": 2557,
- "overs": 13801,
- "overty": 24085,
- "overy": 6560,
- "oves": 5241,
- "ovi": 47297,
- "ovic": 17215,
- "ovich": 18198,
- "ovie": 10739,
- "ovies": 20526,
- "oving": 5165,
- "ovo": 18768,
- "ovsky": 29716,
- "ovy": 27796,
- "ovych": 40822,
- "ow": 322,
- "owa": 8455,
- "owan": 45197,
- "oward": 46138,
- "oway": 41788,
- "owder": 34656,
- "owe": 47097,
- "owed": 6972,
- "owell": 32829,
- "ower": 789,
- "owered": 10387,
- "owers": 3618,
- "owicz": 47982,
- "owing": 7855,
- "owitz": 20951,
- "owl": 4883,
- "owler": 30014,
- "owment": 36569,
- "own": 593,
- "owned": 11990,
- "owner": 18403,
- "owners": 15605,
- "ownt": 6887,
- "owntown": 22748,
- "ows": 1666,
- "owship": 23473,
- "owski": 12079,
- "owsky": 47223,
- "ox": 1140,
- "oxic": 18047,
- "oxicity": 44086,
- "oxide": 28885,
- "oxin": 39366,
- "oxy": 23536,
- "oy": 726,
- "oya": 23790,
- "oyal": 4815,
- "oyd": 12192,
- "oyer": 35301,
- "oyle": 19802,
- "oys": 19417,
- "oz": 8590,
- "ozo": 45149,
- "ozy": 31060,
- "ozyg": 49834,
- "oÄŁ": 45492,
- "oÄŁan": 48030,
- "p": 79,
- "pa": 8957,
- "pac": 33587,
- "pace": 10223,
- "paced": 32416,
- "paces": 43076,
- "pack": 8002,
- "package": 26495,
- "packages": 43789,
- "packed": 34860,
- "packing": 41291,
- "packs": 32377,
- "pad": 15636,
- "padding": 39231,
- "page": 7700,
- "pages": 31126,
- "pai": 49712,
- "paid": 20333,
- "pain": 35436,
- "painted": 47351,
- "paio": 43491,
- "pair": 24874,
- "pak": 41091,
- "pal": 18596,
- "pan": 6839,
- "panel": 35330,
- "panic": 35843,
- "pants": 38895,
- "paper": 20189,
- "papers": 40491,
- "par": 1845,
- "parable": 37064,
- "paragraph": 20360,
- "paralle": 37083,
- "paralleled": 37859,
- "param": 17143,
- "params": 37266,
- "pard": 26037,
- "pared": 29190,
- "paren": 11730,
- "parency": 11944,
- "parent": 8000,
- "parents": 23743,
- "park": 20928,
- "parse": 29572,
- "parser": 48610,
- "part": 3911,
- "partial": 47172,
- "particip": 48013,
- "particularly": 31722,
- "partisan": 28226,
- "parts": 42632,
- "party": 10608,
- "pas": 44429,
- "pass": 6603,
- "password": 28712,
- "past": 30119,
- "paste": 34274,
- "pat": 8071,
- "patch": 17147,
- "path": 6978,
- "pathic": 38829,
- "pathy": 16786,
- "patient": 26029,
- "patrick": 29615,
- "pattern": 33279,
- "pause": 32125,
- "pay": 15577,
- "payer": 34987,
- "payers": 45773,
- "paying": 32629,
- "payment": 37301,
- "pb": 40842,
- "pc": 14751,
- "pd": 30094,
- "pdf": 12315,
- "pe": 431,
- "peace": 22988,
- "peak": 36729,
- "peat": 18267,
- "pec": 43106,
- "pecially": 2333,
- "pect": 806,
- "pected": 7254,
- "pecting": 35570,
- "pection": 14978,
- "pects": 38046,
- "ped": 9124,
- "pedia": 50235,
- "pee": 39463,
- "peed": 39492,
- "peer": 33350,
- "pees": 42623,
- "peg": 22071,
- "pei": 33265,
- "pel": 30242,
- "pell": 23506,
- "pelled": 15803,
- "pelling": 35025,
- "pen": 3617,
- "pend": 37038,
- "pent": 16923,
- "penter": 26419,
- "people": 15332,
- "per": 525,
- "perate": 30052,
- "perature": 21069,
- "percent": 25067,
- "pered": 13653,
- "perfect": 25833,
- "performance": 26585,
- "performing": 37440,
- "perhaps": 28998,
- "peria": 38032,
- "perial": 7629,
- "pering": 21255,
- "period": 41007,
- "perm": 16321,
- "peror": 8723,
- "perors": 49406,
- "pers": 19276,
- "perse": 38696,
- "person": 6259,
- "personal": 22682,
- "pert": 11766,
- "perties": 18200,
- "perture": 27286,
- "perty": 9287,
- "pes": 12272,
- "pet": 6449,
- "pex": 24900,
- "pez": 46057,
- "pg": 6024,
- "ph": 746,
- "pha": 7566,
- "phabet": 19557,
- "phal": 27451,
- "phalt": 41942,
- "phan": 19080,
- "phans": 44464,
- "phant": 33959,
- "phas": 5902,
- "phase": 40715,
- "phasis": 28432,
- "phe": 36335,
- "phen": 31024,
- "pher": 17042,
- "pherd": 23111,
- "pheus": 46421,
- "phi": 34846,
- "phia": 8193,
- "phies": 19380,
- "phil": 28864,
- "philis": 49613,
- "phis": 18691,
- "phone": 4862,
- "phones": 9708,
- "phony": 23021,
- "phot": 38611,
- "photo": 23074,
- "photos": 24729,
- "php": 10121,
- "phrase": 34675,
- "phrine": 47723,
- "phthal": 48118,
- "phy": 6883,
- "phys": 34411,
- "physical": 42854,
- "pi": 14415,
- "pic": 16564,
- "pick": 27729,
- "picked": 41891,
- "picking": 48864,
- "pict": 18847,
- "picture": 34053,
- "pictured": 28852,
- "pid": 35317,
- "pie": 21749,
- "piece": 12239,
- "pieces": 34154,
- "pill": 27215,
- "pillar": 41643,
- "pin": 11635,
- "pine": 23908,
- "ping": 13886,
- "pins": 49556,
- "pipe": 34360,
- "pir": 4063,
- "piracy": 8703,
- "piration": 10514,
- "pire": 5111,
- "pired": 6474,
- "pires": 17833,
- "piring": 12987,
- "pit": 15544,
- "pite": 2595,
- "pixel": 32515,
- "pkg": 35339,
- "pl": 489,
- "place": 5372,
- "placed": 21820,
- "places": 23625,
- "plain": 25638,
- "plan": 11578,
- "plane": 14382,
- "planes": 22587,
- "planet": 47427,
- "planned": 36800,
- "plant": 15060,
- "plate": 6816,
- "plates": 17041,
- "platform": 24254,
- "play": 1759,
- "played": 21542,
- "player": 7829,
- "players": 32399,
- "playing": 17916,
- "plays": 26024,
- "ple": 1154,
- "pleasant": 21109,
- "please": 29688,
- "pled": 10137,
- "plement": 26908,
- "plementation": 32851,
- "pler": 20053,
- "ples": 2374,
- "pless": 14570,
- "plet": 37069,
- "plete": 6677,
- "pleted": 16838,
- "pleting": 47130,
- "pletion": 24547,
- "plets": 46916,
- "plex": 11141,
- "plin": 46982,
- "pling": 11347,
- "plings": 47093,
- "pload": 7304,
- "plom": 7302,
- "ploma": 35728,
- "plot": 29487,
- "ploy": 1420,
- "plug": 16875,
- "plugin": 33803,
- "plugins": 37390,
- "plus": 9541,
- "ply": 2145,
- "pm": 4426,
- "pmwiki": 45321,
- "pn": 21999,
- "png": 11134,
- "po": 7501,
- "pocket": 31991,
- "pod": 33320,
- "podcast": 46032,
- "point": 4122,
- "pointer": 29536,
- "pointers": 47809,
- "points": 13033,
- "poke": 35924,
- "pol": 16104,
- "pole": 36869,
- "police": 38191,
- "policy": 30586,
- "polit": 34470,
- "political": 23149,
- "politics": 34127,
- "poll": 30393,
- "poly": 35428,
- "pool": 7742,
- "poon": 26743,
- "poons": 27575,
- "poor": 36672,
- "pop": 12924,
- "popular": 47568,
- "population": 39748,
- "por": 1819,
- "pora": 38851,
- "poral": 35738,
- "porary": 5551,
- "porate": 38133,
- "port": 634,
- "portation": 10189,
- "ported": 9213,
- "porter": 26634,
- "porting": 26527,
- "portion": 16864,
- "ports": 3742,
- "pos": 1930,
- "posal": 40007,
- "pose": 3455,
- "posed": 29813,
- "poses": 4832,
- "posing": 32927,
- "position": 9150,
- "positive": 24561,
- "posium": 35864,
- "possibly": 39363,
- "post": 7353,
- "posted": 40578,
- "posts": 24875,
- "posure": 26205,
- "pot": 13059,
- "potion": 49324,
- "pots": 40793,
- "pound": 19568,
- "pour": 48681,
- "powder": 45855,
- "power": 6477,
- "powered": 12293,
- "powerful": 44548,
- "powers": 30132,
- "pox": 42557,
- "pp": 381,
- "ppa": 44989,
- "ppard": 43988,
- "ppe": 27768,
- "pped": 1496,
- "ppel": 46357,
- "ppelin": 48425,
- "pper": 2848,
- "pperc": 39921,
- "ppers": 11799,
- "pping": 2105,
- "ppings": 37840,
- "ppo": 16634,
- "pport": 4926,
- "pps": 41799,
- "ppy": 14097,
- "pr": 1050,
- "pract": 29152,
- "practice": 39541,
- "pre": 3866,
- "pread": 9681,
- "pred": 28764,
- "prefix": 40290,
- "prem": 31605,
- "prep": 46012,
- "pres": 18302,
- "present": 25579,
- "president": 22540,
- "press": 8439,
- "pressed": 45477,
- "pressure": 36151,
- "pret": 5310,
- "pretty": 37784,
- "prev": 47050,
- "pri": 3448,
- "price": 20888,
- "priced": 30883,
- "prim": 19795,
- "primary": 39754,
- "prime": 35505,
- "pring": 12667,
- "print": 4798,
- "printed": 49695,
- "printf": 37435,
- "println": 35235,
- "prints": 17190,
- "priority": 49336,
- "prise": 7919,
- "prises": 18166,
- "prising": 14619,
- "prisingly": 20859,
- "prison": 35156,
- "priv": 13776,
- "private": 19734,
- "pro": 1676,
- "probably": 26949,
- "problem": 45573,
- "proc": 36942,
- "process": 14681,
- "processing": 36948,
- "processor": 41341,
- "proclaimed": 39865,
- "produ": 18230,
- "produced": 32783,
- "producing": 36866,
- "product": 11167,
- "production": 25493,
- "productive": 27781,
- "products": 29498,
- "prof": 5577,
- "professional": 33163,
- "profile": 13317,
- "profit": 9183,
- "profits": 31504,
- "program": 23065,
- "progress": 33723,
- "project": 16302,
- "projects": 42068,
- "prom": 16963,
- "pron": 31186,
- "prone": 46330,
- "proof": 13288,
- "prop": 22930,
- "properties": 48310,
- "property": 26745,
- "prot": 11235,
- "protect": 35499,
- "protected": 24326,
- "protection": 42846,
- "protein": 48693,
- "prototype": 38124,
- "prov": 15234,
- "proven": 42874,
- "provided": 41279,
- "proxy": 36436,
- "prus": 26440,
- "ps": 862,
- "psc": 27566,
- "pse": 7752,
- "psey": 39070,
- "pson": 8430,
- "psons": 31410,
- "psy": 13764,
- "psych": 23947,
- "pt": 457,
- "pta": 32283,
- "pter": 42104,
- "ptic": 17459,
- "ptin": 43217,
- "ption": 1159,
- "ptions": 8544,
- "ptive": 21665,
- "ptives": 43903,
- "ptoms": 35533,
- "pton": 10972,
- "ptr": 20692,
- "ptroller": 44913,
- "pty": 5835,
- "pu": 19944,
- "pub": 12984,
- "public": 11377,
- "published": 30271,
- "puff": 49357,
- "pull": 31216,
- "pun": 35512,
- "punk": 30354,
- "pur": 14225,
- "pure": 37424,
- "purpose": 29983,
- "push": 14689,
- "put": 1996,
- "putable": 48840,
- "puted": 17128,
- "puter": 10549,
- "puters": 41510,
- "puting": 48074,
- "px": 8416,
- "py": 9078,
- "python": 29412,
- "q": 80,
- "qa": 20402,
- "qi": 40603,
- "ql": 13976,
- "qq": 38227,
- "qqa": 28794,
- "qs": 48382,
- "qt": 39568,
- "qu": 421,
- "qua": 39566,
- "quad": 47003,
- "qual": 13255,
- "qualified": 22557,
- "quality": 13237,
- "quant": 40972,
- "quart": 36008,
- "quarter": 24385,
- "quartered": 42508,
- "quarters": 8230,
- "que": 4188,
- "quel": 31735,
- "quer": 10819,
- "querade": 33357,
- "querque": 36119,
- "query": 22766,
- "ques": 13281,
- "quest": 6138,
- "question": 25652,
- "quet": 21108,
- "queue": 36560,
- "quez": 22281,
- "quick": 24209,
- "quickShip": 39752,
- "quickShipAvailable": 39753,
- "quiet": 39624,
- "quila": 43652,
- "quin": 21915,
- "quire": 29782,
- "quished": 39737,
- "quist": 30062,
- "quit": 47391,
- "quite": 37121,
- "quote": 22708,
- "qus": 45260,
- "qv": 44179,
- "r": 81,
- "ra": 430,
- "rab": 25619,
- "rac": 11510,
- "race": 16740,
- "racial": 33001,
- "racist": 41131,
- "rack": 39638,
- "ract": 974,
- "racted": 20216,
- "ractical": 36112,
- "raction": 7861,
- "ractions": 37810,
- "ractive": 35587,
- "ractor": 40450,
- "racuse": 28268,
- "rad": 6335,
- "rade": 27585,
- "radical": 42325,
- "radio": 37004,
- "radius": 42172,
- "rador": 40368,
- "rael": 2510,
- "raf": 32188,
- "raft": 1617,
- "rafted": 30235,
- "rag": 22562,
- "rage": 8394,
- "raged": 18312,
- "ragon": 25753,
- "rah": 11392,
- "raham": 13220,
- "rahim": 26922,
- "raid": 7086,
- "rail": 30224,
- "rain": 3201,
- "raine": 23440,
- "rained": 13363,
- "raining": 24674,
- "raint": 16947,
- "raints": 15517,
- "raise": 40225,
- "raised": 49309,
- "raising": 32741,
- "rait": 12907,
- "raits": 27554,
- "rak": 17716,
- "rake": 33788,
- "ral": 1373,
- "rals": 30691,
- "raltar": 45662,
- "ram": 859,
- "rama": 20058,
- "rame": 28073,
- "rament": 15141,
- "ramer": 29172,
- "ramid": 20255,
- "ramids": 43591,
- "rams": 9474,
- "ran": 2596,
- "rance": 8132,
- "ranch": 25642,
- "rand": 25192,
- "random": 25120,
- "rane": 14579,
- "ranean": 16474,
- "rang": 36985,
- "range": 9521,
- "ranged": 34457,
- "ranging": 32319,
- "rank": 43027,
- "ranked": 28282,
- "ranking": 28405,
- "rano": 35823,
- "rans": 26084,
- "rant": 5250,
- "rants": 15087,
- "rap": 2416,
- "rape": 13484,
- "raped": 31951,
- "raper": 38545,
- "raph": 1470,
- "raphic": 22262,
- "raphics": 11549,
- "rapnel": 48766,
- "raq": 3766,
- "rar": 20040,
- "rared": 25122,
- "rarily": 39000,
- "rary": 11619,
- "ras": 8847,
- "rase": 22789,
- "rast": 5685,
- "rastructure": 6410,
- "rat": 10366,
- "ratch": 36722,
- "rate": 4873,
- "rated": 4111,
- "rates": 9700,
- "rather": 34330,
- "rating": 8821,
- "ration": 1358,
- "rational": 20310,
- "rations": 9143,
- "rative": 13260,
- "ratom": 44616,
- "rator": 12392,
- "rators": 18942,
- "rats": 46714,
- "ratulations": 30167,
- "raud": 22863,
- "raught": 44451,
- "rav": 4108,
- "rave": 5758,
- "raved": 28366,
- "ravel": 25843,
- "ravings": 42335,
- "raviolet": 44223,
- "ravis": 16956,
- "ravity": 16995,
- "raw": 1831,
- "rawdownload": 30905,
- "rawdownloadcloneembedreportprint": 30906,
- "rawl": 13132,
- "rawled": 49263,
- "rawler": 39464,
- "rawling": 18771,
- "rawn": 5791,
- "rax": 32040,
- "ray": 2433,
- "rays": 20477,
- "raz": 3247,
- "razen": 36409,
- "razil": 7098,
- "razy": 5918,
- "rb": 26145,
- "rc": 6015,
- "rd": 4372,
- "re": 260,
- "rea": 21468,
- "reach": 16250,
- "reaching": 30771,
- "react": 45018,
- "read": 961,
- "readable": 46155,
- "reader": 46862,
- "reading": 25782,
- "reads": 40779,
- "ready": 1493,
- "real": 5305,
- "realDonaldTrump": 28024,
- "reality": 46290,
- "really": 27485,
- "ream": 1476,
- "reason": 41181,
- "reasonable": 42275,
- "reat": 630,
- "reated": 15978,
- "reath": 19367,
- "reating": 34567,
- "reatment": 21731,
- "reau": 43611,
- "reb": 34806,
- "rec": 8344,
- "recated": 31023,
- "received": 47844,
- "recent": 49921,
- "reci": 29102,
- "reciation": 33950,
- "reck": 11402,
- "recogn": 26243,
- "recomm": 47335,
- "record": 22105,
- "recorded": 47398,
- "rect": 2554,
- "rection": 8243,
- "recy": 20568,
- "red": 445,
- "redd": 26504,
- "reddit": 10748,
- "reddits": 36581,
- "redible": 26260,
- "redibly": 45779,
- "redict": 17407,
- "redients": 23320,
- "redit": 7470,
- "reditary": 47333,
- "reditation": 42845,
- "redited": 19465,
- "redits": 20696,
- "redo": 48454,
- "ree": 631,
- "reed": 15977,
- "reek": 10316,
- "reement": 10237,
- "reements": 28919,
- "reen": 1361,
- "reens": 5681,
- "reenshot": 26892,
- "reenshots": 39551,
- "rees": 6037,
- "reet": 2871,
- "reetings": 46648,
- "ref": 5420,
- "reference": 35790,
- "reflect": 35051,
- "reg": 2301,
- "regate": 49373,
- "regation": 43068,
- "region": 36996,
- "register": 30238,
- "registered": 33736,
- "regn": 28321,
- "regnancy": 39982,
- "regon": 8285,
- "regor": 32288,
- "regular": 16338,
- "regulated": 27739,
- "regulation": 27727,
- "rehend": 23979,
- "rehens": 7345,
- "rehensible": 34718,
- "rehensive": 36321,
- "rek": 37818,
- "rel": 2411,
- "related": 5363,
- "relation": 49501,
- "relations": 39468,
- "relative": 43762,
- "release": 20979,
- "released": 30147,
- "relevant": 49659,
- "religious": 27626,
- "rell": 11252,
- "rella": 20481,
- "rely": 38015,
- "rem": 2787,
- "reme": 2182,
- "remember": 38947,
- "remlin": 17244,
- "remote": 47960,
- "remove": 28956,
- "ren": 918,
- "rence": 6784,
- "rences": 34303,
- "rench": 3532,
- "renched": 23437,
- "renches": 33650,
- "rencies": 14038,
- "rency": 5227,
- "rend": 10920,
- "render": 13287,
- "rendered": 26238,
- "rene": 25924,
- "renheit": 34032,
- "rent": 1156,
- "rentice": 20098,
- "rentices": 34368,
- "reon": 21833,
- "rep": 7856,
- "repair": 49932,
- "repe": 45956,
- "repeat": 44754,
- "repl": 35666,
- "replace": 33491,
- "reply": 47768,
- "report": 13116,
- "reported": 26263,
- "reporting": 49914,
- "reportprint": 30897,
- "reports": 48922,
- "repre": 10353,
- "reprene": 10406,
- "represent": 15603,
- "represented": 33469,
- "req": 42180,
- "requ": 8897,
- "requency": 28707,
- "requent": 46018,
- "requently": 37971,
- "request": 25927,
- "require": 46115,
- "required": 35827,
- "requires": 47911,
- "requisite": 27614,
- "requisites": 34075,
- "rer": 11751,
- "rera": 24420,
- "rero": 34785,
- "rers": 27736,
- "res": 411,
- "resa": 14625,
- "rescent": 26505,
- "research": 34033,
- "resent": 2028,
- "resents": 6629,
- "reset": 42503,
- "resh": 3447,
- "reshold": 10126,
- "resident": 8154,
- "resist": 35119,
- "resistant": 26128,
- "resolution": 29268,
- "resource": 31092,
- "resources": 37540,
- "resp": 4363,
- "respect": 15008,
- "respected": 48268,
- "respective": 36990,
- "respond": 5546,
- "respons": 16733,
- "response": 26209,
- "responsible": 24358,
- "responsive": 39772,
- "ress": 601,
- "ressed": 2790,
- "resses": 16746,
- "ressing": 11697,
- "ression": 2234,
- "ressive": 3314,
- "resso": 33852,
- "ressor": 44292,
- "rest": 2118,
- "restling": 48839,
- "restrial": 23522,
- "restricted": 49343,
- "result": 20274,
- "results": 43420,
- "resy": 33000,
- "ret": 1186,
- "retch": 22592,
- "retched": 27528,
- "rete": 8374,
- "reth": 40978,
- "retion": 12307,
- "rets": 8004,
- "rett": 11489,
- "rette": 42908,
- "retty": 16100,
- "return": 7783,
- "rev": 18218,
- "reve": 36955,
- "reverse": 50188,
- "review": 19023,
- "reviewed": 32974,
- "revolution": 32243,
- "rew": 1809,
- "rex": 21510,
- "rey": 4364,
- "reys": 46703,
- "rez": 21107,
- "rf": 41871,
- "rg": 41345,
- "rh": 17179,
- "rha": 30268,
- "ri": 380,
- "ria": 7496,
- "riad": 21244,
- "riage": 4087,
- "riages": 16451,
- "rial": 4454,
- "rian": 4484,
- "rians": 19151,
- "rib": 822,
- "ribe": 4892,
- "ribed": 8725,
- "riber": 24735,
- "ribes": 22090,
- "ribing": 23098,
- "rible": 5547,
- "ribly": 16358,
- "ribune": 44130,
- "ribut": 2455,
- "ribute": 4163,
- "ributed": 6169,
- "ributes": 7657,
- "ribution": 3890,
- "ric": 1173,
- "rica": 30997,
- "rical": 8143,
- "rican": 37189,
- "ricane": 11551,
- "ricanes": 24881,
- "rice": 20970,
- "rices": 45977,
- "rich": 7527,
- "riched": 30486,
- "ricia": 26654,
- "rick": 5557,
- "ricks": 23706,
- "rics": 10466,
- "rict": 2012,
- "ricted": 20941,
- "ricting": 42870,
- "riction": 46214,
- "ricular": 41001,
- "rid": 6058,
- "ridden": 40372,
- "ride": 13154,
- "rider": 49449,
- "ridge": 12818,
- "ridges": 32124,
- "ridor": 44425,
- "rie": 5034,
- "ried": 2228,
- "rief": 3796,
- "rieg": 48429,
- "riel": 11719,
- "rien": 15355,
- "riend": 1289,
- "rient": 8289,
- "rients": 18491,
- "rier": 5277,
- "riers": 8910,
- "ries": 1678,
- "riet": 42098,
- "rieve": 30227,
- "rieved": 28130,
- "rieving": 37418,
- "rification": 38763,
- "rifice": 31932,
- "rified": 41301,
- "rift": 35357,
- "rig": 4359,
- "rigan": 35631,
- "riger": 18096,
- "right": 3506,
- "righteous": 49955,
- "rights": 28046,
- "rik": 12602,
- "rika": 28716,
- "rike": 8760,
- "rikes": 18445,
- "riks": 39370,
- "ril": 22379,
- "rill": 20190,
- "rils": 41408,
- "rily": 28904,
- "rim": 3036,
- "riminal": 22157,
- "rimination": 22550,
- "rimp": 23750,
- "rin": 12769,
- "rina": 22267,
- "rine": 7640,
- "ring": 1806,
- "ringe": 38229,
- "rings": 33173,
- "rington": 24833,
- "rint": 22272,
- "rio": 27250,
- "rior": 7701,
- "riors": 8657,
- "riot": 36671,
- "riots": 44447,
- "riott": 43517,
- "rious": 32527,
- "rip": 5528,
- "ripp": 14602,
- "ript": 1968,
- "ription": 2918,
- "rique": 33865,
- "rir": 29283,
- "ris": 2442,
- "rise": 17163,
- "rises": 26064,
- "rish": 37518,
- "rising": 22610,
- "risis": 42841,
- "risk": 19121,
- "risome": 47400,
- "rison": 7426,
- "rist": 1585,
- "rists": 37326,
- "rit": 799,
- "ritch": 46510,
- "rite": 6525,
- "riter": 43407,
- "rites": 23156,
- "ritic": 46015,
- "ritical": 36487,
- "rities": 19491,
- "rition": 10168,
- "ritional": 21297,
- "ritis": 27398,
- "rito": 39834,
- "ritten": 9108,
- "rity": 10138,
- "ritz": 29574,
- "rium": 19172,
- "rius": 48969,
- "riv": 15104,
- "rival": 43171,
- "rive": 11590,
- "rived": 36207,
- "river": 38291,
- "rix": 8609,
- "riz": 47847,
- "rl": 45895,
- "rm": 26224,
- "rn": 35906,
- "ro": 305,
- "roach": 28562,
- "road": 6344,
- "roads": 21372,
- "rob": 22609,
- "robat": 40655,
- "robe": 25481,
- "roc": 12204,
- "rocal": 43270,
- "rock": 10823,
- "rocket": 30431,
- "rod": 14892,
- "rodu": 2076,
- "roe": 20646,
- "rog": 3828,
- "rogen": 8648,
- "rogens": 48686,
- "rogram": 39529,
- "roid": 3882,
- "roit": 7775,
- "rol": 3225,
- "role": 18090,
- "rolet": 33087,
- "roleum": 21945,
- "roll": 2487,
- "rolled": 8375,
- "roller": 10646,
- "rollers": 36667,
- "rolley": 42639,
- "rolling": 18886,
- "rollment": 48108,
- "rolog": 40329,
- "rology": 31142,
- "rom": 398,
- "roma": 42902,
- "roman": 47119,
- "romancer": 38211,
- "rome": 5998,
- "romeda": 32291,
- "romising": 47112,
- "rompt": 45700,
- "romptu": 49255,
- "romy": 50228,
- "ron": 1313,
- "rone": 33171,
- "rones": 9821,
- "rongh": 36670,
- "ronic": 4565,
- "ronics": 20844,
- "rons": 12212,
- "ront": 4298,
- "rontal": 39321,
- "roo": 42407,
- "room": 3823,
- "rooms": 9649,
- "root": 15763,
- "roots": 19150,
- "rop": 1773,
- "roph": 10051,
- "rophe": 22599,
- "rophic": 18191,
- "ropolis": 25986,
- "ropolitan": 14823,
- "ropri": 9219,
- "ropy": 28338,
- "ror": 1472,
- "rored": 34640,
- "rors": 5965,
- "ros": 4951,
- "rosc": 45943,
- "rose": 13698,
- "rosis": 37172,
- "ross": 1214,
- "rosse": 39314,
- "rosso": 21074,
- "rossover": 23954,
- "rost": 23341,
- "rot": 10599,
- "rote": 2519,
- "rotein": 35574,
- "roth": 33640,
- "rots": 24744,
- "rou": 472,
- "rouch": 48626,
- "roud": 5493,
- "rough": 740,
- "rought": 2909,
- "round": 744,
- "rounded": 39262,
- "rounder": 45788,
- "roup": 3233,
- "roups": 14459,
- "rous": 7596,
- "rouse": 46494,
- "route": 38629,
- "rov": 18657,
- "rovers": 31257,
- "roversial": 46927,
- "row": 808,
- "rowd": 3986,
- "rower": 46992,
- "rowing": 11577,
- "rown": 2053,
- "rows": 8516,
- "rowth": 13046,
- "rox": 13907,
- "roximately": 24378,
- "roxy": 42059,
- "roy": 3287,
- "roying": 38295,
- "rozen": 42005,
- "rpm": 48235,
- "rr": 21062,
- "rs": 3808,
- "rss": 42216,
- "rt": 17034,
- "ru": 622,
- "ruary": 3728,
- "rub": 25089,
- "ruby": 49137,
- "ruce": 26524,
- "ruciating": 48404,
- "ruck": 30915,
- "ruct": 1356,
- "ruction": 2762,
- "ructose": 32275,
- "ructure": 5620,
- "rue": 24508,
- "rued": 21556,
- "ruff": 30622,
- "rug": 2143,
- "rugged": 21901,
- "ruit": 4872,
- "ruits": 50187,
- "rule": 25135,
- "rules": 38785,
- "ruly": 34715,
- "rum": 6582,
- "rums": 45241,
- "run": 5143,
- "runner": 16737,
- "runners": 36740,
- "running": 20270,
- "runs": 48381,
- "runtime": 43282,
- "rup": 12618,
- "rupal": 34585,
- "rupt": 3622,
- "rupted": 31590,
- "ruption": 6417,
- "rupulous": 46272,
- "rus": 14932,
- "rush": 37357,
- "rust": 11469,
- "rw": 31653,
- "rx": 40914,
- "ry": 563,
- "ryan": 29038,
- "ryce": 28169,
- "rying": 14992,
- "rylic": 34554,
- "ryn": 29441,
- "rypt": 6012,
- "rypted": 15109,
- "ryption": 13168,
- "rys": 19753,
- "ryu": 49056,
- "ré": 29350,
- "s": 82,
- "sa": 11400,
- "sac": 30584,
- "saf": 49585,
- "safe": 21230,
- "safety": 44708,
- "said": 30079,
- "sal": 21680,
- "sale": 21378,
- "sam": 37687,
- "sama": 33843,
- "same": 31642,
- "sample": 39873,
- "san": 12807,
- "sand": 38142,
- "sat": 49720,
- "sav": 39308,
- "save": 21928,
- "saving": 29336,
- "saw": 43439,
- "say": 16706,
- "sb": 36299,
- "sbm": 32310,
- "sburg": 30359,
- "sburgh": 11931,
- "sc": 1416,
- "scale": 9888,
- "scan": 35836,
- "scape": 6794,
- "scar": 13034,
- "scene": 29734,
- "scenes": 28123,
- "sch": 20601,
- "sche": 15952,
- "schild": 35058,
- "school": 14347,
- "sci": 36216,
- "science": 16801,
- "scient": 25346,
- "scientific": 41355,
- "scill": 22360,
- "scl": 38528,
- "scope": 29982,
- "score": 26675,
- "scoring": 46536,
- "screen": 9612,
- "scrib": 40075,
- "scribe": 12522,
- "scribed": 47495,
- "script": 12048,
- "scription": 33584,
- "scripts": 46521,
- "scroll": 48728,
- "sd": 21282,
- "se": 325,
- "sea": 8583,
- "search": 12947,
- "season": 6230,
- "seat": 24073,
- "sec": 2363,
- "second": 12227,
- "secondary": 38238,
- "seconds": 43012,
- "secret": 21078,
- "sect": 8831,
- "section": 5458,
- "sectional": 44330,
- "sections": 23946,
- "sector": 34914,
- "secure": 22390,
- "security": 12961,
- "secut": 4552,
- "secution": 9534,
- "sed": 36622,
- "see": 3826,
- "seed": 28826,
- "seeing": 42041,
- "seek": 36163,
- "seekers": 47971,
- "seeking": 38515,
- "seen": 15898,
- "sei": 36455,
- "sein": 20719,
- "sel": 741,
- "selage": 45217,
- "select": 19738,
- "selected": 34213,
- "selection": 49283,
- "seless": 10950,
- "self": 944,
- "sell": 7255,
- "seller": 32932,
- "selling": 16473,
- "sels": 14002,
- "selves": 2020,
- "sem": 43616,
- "semb": 4428,
- "semble": 15140,
- "sembly": 5997,
- "sen": 6248,
- "senal": 10298,
- "send": 21280,
- "sense": 33819,
- "sensitive": 30176,
- "sent": 34086,
- "separ": 25512,
- "seq": 41068,
- "sequ": 3107,
- "sequence": 43167,
- "sequent": 44399,
- "sequently": 20415,
- "ser": 2655,
- "serial": 46911,
- "series": 25076,
- "serious": 34009,
- "sers": 17720,
- "serv": 3168,
- "served": 45852,
- "server": 15388,
- "service": 15271,
- "services": 30416,
- "serving": 31293,
- "ses": 8448,
- "session": 29891,
- "set": 2617,
- "sets": 28709,
- "sett": 17744,
- "setting": 33990,
- "settings": 33692,
- "setup": 40406,
- "seven": 26548,
- "sever": 28116,
- "severe": 43070,
- "sex": 8044,
- "sexual": 18338,
- "sey": 4397,
- "seys": 27717,
- "sf": 28202,
- "sg": 45213,
- "sh": 1477,
- "sha": 26270,
- "shadow": 19106,
- "shake": 32431,
- "shall": 49271,
- "shape": 43358,
- "shaped": 16760,
- "shapeshifter": 33929,
- "share": 20077,
- "shared": 28710,
- "sharing": 21987,
- "sharp": 48554,
- "shaw": 32832,
- "she": 7091,
- "shed": 35762,
- "sheet": 21760,
- "sheets": 42011,
- "shell": 29149,
- "shi": 44019,
- "shield": 26662,
- "shift": 30846,
- "shine": 19489,
- "ship": 6720,
- "ships": 26313,
- "shire": 10932,
- "shirt": 15600,
- "shirts": 23231,
- "shit": 16211,
- "shock": 39563,
- "shoot": 30408,
- "shop": 24643,
- "shore": 14640,
- "short": 19509,
- "shot": 9442,
- "shots": 20910,
- "should": 21754,
- "show": 12860,
- "shown": 42579,
- "shows": 49596,
- "shr": 36007,
- "shut": 49625,
- "si": 13396,
- "sic": 21383,
- "sid": 30255,
- "side": 1589,
- "sided": 22339,
- "sie": 44524,
- "sight": 18627,
- "sighted": 44068,
- "sign": 12683,
- "signed": 32696,
- "significant": 36591,
- "sil": 18217,
- "silver": 40503,
- "sim": 14323,
- "similar": 38610,
- "simple": 36439,
- "sin": 31369,
- "since": 20777,
- "sing": 12215,
- "single": 29762,
- "sis": 13429,
- "sit": 48937,
- "site": 15654,
- "sites": 49315,
- "six": 19412,
- "size": 7857,
- "sized": 13982,
- "sk": 8135,
- "ski": 20545,
- "skill": 42401,
- "skilled": 44885,
- "skin": 20407,
- "skinned": 41412,
- "skip": 48267,
- "skirts": 28383,
- "sky": 15688,
- "sl": 6649,
- "slaught": 30929,
- "slave": 36341,
- "sle": 26738,
- "sleep": 42832,
- "slice": 48369,
- "slot": 43384,
- "slow": 38246,
- "sm": 5796,
- "small": 17470,
- "smanship": 49820,
- "smart": 27004,
- "smith": 21453,
- "smoking": 48783,
- "sn": 16184,
- "snap": 45380,
- "so": 568,
- "soDeliveryDate": 39811,
- "soType": 39803,
- "soc": 35634,
- "social": 14557,
- "socket": 44971,
- "soever": 15485,
- "sofar": 38649,
- "soft": 4215,
- "software": 43776,
- "sol": 34453,
- "sold": 24120,
- "sole": 6753,
- "solete": 23869,
- "solid": 39390,
- "some": 11246,
- "someone": 46248,
- "something": 18927,
- "sometimes": 29810,
- "son": 1559,
- "song": 34050,
- "sonian": 35202,
- "soon": 36194,
- "sorry": 41599,
- "sort": 30619,
- "sound": 23661,
- "sounding": 39686,
- "source": 10459,
- "south": 35782,
- "sov": 47272,
- "sp": 2777,
- "space": 13200,
- "span": 12626,
- "spawn": 48183,
- "spe": 4125,
- "speak": 47350,
- "speaking": 25159,
- "spec": 16684,
- "special": 20887,
- "species": 35448,
- "specific": 11423,
- "specified": 23599,
- "spect": 4443,
- "spection": 31308,
- "spective": 49540,
- "speech": 45862,
- "speed": 12287,
- "spell": 46143,
- "spin": 39706,
- "spir": 45564,
- "spirit": 38685,
- "spl": 22018,
- "split": 35312,
- "spoken": 19842,
- "spons": 20587,
- "sponsored": 25427,
- "sports": 32945,
- "spot": 20485,
- "spr": 34975,
- "spread": 43639,
- "spring": 16469,
- "sq": 31166,
- "sql": 25410,
- "squ": 16485,
- "square": 23415,
- "sr": 27891,
- "src": 10677,
- "ss": 824,
- "ssh": 45824,
- "ssl": 45163,
- "sson": 16528,
- "st": 301,
- "sta": 38031,
- "stab": 39029,
- "stable": 31284,
- "stack": 25558,
- "stad": 24107,
- "stadt": 38863,
- "staff": 28120,
- "stage": 14247,
- "stained": 44279,
- "stairs": 17617,
- "stakes": 32540,
- "staking": 40031,
- "stal": 7757,
- "stall": 32989,
- "stals": 41076,
- "stan": 14192,
- "stanbul": 24179,
- "stand": 1481,
- "standard": 20307,
- "standing": 5646,
- "stant": 18797,
- "stantial": 41321,
- "star": 7364,
- "stars": 30783,
- "start": 9688,
- "started": 46981,
- "starter": 12339,
- "starting": 38690,
- "stasy": 31695,
- "stat": 14269,
- "state": 5219,
- "stated": 21989,
- "statement": 26090,
- "states": 27219,
- "static": 12708,
- "station": 17529,
- "stats": 34242,
- "status": 13376,
- "stay": 31712,
- "std": 19282,
- "ste": 4169,
- "stead": 28044,
- "steam": 21465,
- "steamapps": 31881,
- "sted": 30679,
- "steel": 44822,
- "steen": 42580,
- "stein": 5714,
- "stellar": 28732,
- "stem": 927,
- "sten": 26400,
- "step": 9662,
- "steps": 20214,
- "ster": 1706,
- "sterdam": 22506,
- "sters": 5937,
- "stery": 41991,
- "sth": 48476,
- "stic": 11268,
- "stice": 43788,
- "stick": 13915,
- "sticks": 34810,
- "still": 24219,
- "stim": 42003,
- "stitial": 18167,
- "stock": 13578,
- "stocks": 29522,
- "ston": 3743,
- "stone": 6440,
- "stones": 28750,
- "stood": 6501,
- "stop": 11338,
- "storage": 35350,
- "store": 8095,
- "stores": 43409,
- "stories": 50164,
- "storm": 12135,
- "storms": 38563,
- "story": 13571,
- "stown": 27928,
- "str": 2536,
- "stra": 12044,
- "stract": 8709,
- "straight": 42729,
- "strap": 26418,
- "strate": 23104,
- "stration": 12401,
- "stre": 22853,
- "stream": 5532,
- "street": 25662,
- "strength": 41402,
- "stress": 41494,
- "stretched": 49729,
- "stri": 33565,
- "strike": 33069,
- "string": 8841,
- "strings": 37336,
- "strip": 36311,
- "stro": 20661,
- "stroke": 30757,
- "strom": 20282,
- "strong": 11576,
- "stros": 48288,
- "strous": 22501,
- "stru": 19554,
- "struct": 7249,
- "structed": 16242,
- "struction": 15019,
- "strument": 43872,
- "sts": 6448,
- "stud": 19149,
- "student": 50139,
- "study": 44517,
- "stuff": 41094,
- "sty": 34365,
- "style": 7635,
- "styles": 47720,
- "su": 2385,
- "sub": 7266,
- "subject": 32796,
- "submit": 46002,
- "success": 13138,
- "successful": 17212,
- "successfully": 37351,
- "such": 10508,
- "sudo": 24032,
- "suff": 37333,
- "sufficient": 46790,
- "suggest": 47811,
- "suit": 6063,
- "suits": 16554,
- "sum": 16345,
- "summary": 49736,
- "sun": 19155,
- "sung": 9854,
- "sup": 37330,
- "super": 16668,
- "supp": 18608,
- "support": 11284,
- "supported": 15999,
- "sur": 11793,
- "sure": 19532,
- "surface": 42029,
- "surprisingly": 41199,
- "surv": 48846,
- "susp": 40409,
- "sv": 21370,
- "sw": 2032,
- "swe": 46280,
- "sweet": 34751,
- "swer": 17845,
- "swers": 37848,
- "swick": 30961,
- "swing": 46737,
- "switch": 31943,
- "sword": 30553,
- "sworth": 30567,
- "sy": 1837,
- "sych": 2924,
- "sylv": 9163,
- "sylvania": 9270,
- "sym": 37047,
- "syn": 28869,
- "sync": 27261,
- "sys": 17597,
- "system": 10057,
- "t": 83,
- "ta": 8326,
- "tab": 8658,
- "table": 11487,
- "taboola": 10658,
- "tackle": 36346,
- "tag": 12985,
- "tags": 31499,
- "tail": 13199,
- "tailed": 34966,
- "tails": 26404,
- "tain": 3153,
- "tained": 4644,
- "taining": 7339,
- "tainment": 10738,
- "tains": 12143,
- "take": 20657,
- "taker": 30157,
- "taking": 26103,
- "tal": 39240,
- "tale": 29429,
- "talk": 16620,
- "talking": 48186,
- "tall": 35429,
- "tan": 38006,
- "tank": 28451,
- "tap": 44335,
- "tar": 18870,
- "target": 16793,
- "tarian": 14012,
- "tarians": 28266,
- "task": 35943,
- "tax": 19290,
- "tc": 23047,
- "tch": 38664,
- "td": 8671,
- "te": 660,
- "team": 15097,
- "tec": 36281,
- "tech": 13670,
- "techn": 23873,
- "technical": 47944,
- "technology": 45503,
- "ted": 1513,
- "teen": 7821,
- "teenth": 20283,
- "tein": 22006,
- "tek": 35424,
- "tel": 37524,
- "tele": 46813,
- "tell": 33331,
- "telling": 18072,
- "tem": 11498,
- "temp": 29510,
- "template": 28243,
- "ten": 1452,
- "tenance": 8219,
- "teness": 43205,
- "ter": 353,
- "tera": 49600,
- "terday": 6432,
- "tered": 4400,
- "tering": 20212,
- "terior": 14172,
- "term": 4354,
- "termin": 23705,
- "termination": 41382,
- "terms": 38707,
- "tern": 759,
- "ternal": 4358,
- "ternally": 30262,
- "terness": 34697,
- "ternity": 19682,
- "terror": 14007,
- "terrorism": 19541,
- "terrorist": 42002,
- "ters": 1010,
- "terson": 23192,
- "tery": 11471,
- "tes": 4879,
- "tesque": 37422,
- "test": 9288,
- "tested": 39612,
- "testers": 27205,
- "testing": 33407,
- "tests": 41989,
- "tesy": 27090,
- "tex": 16886,
- "text": 5239,
- "texture": 41293,
- "tf": 27110,
- "tg": 25297,
- "th": 400,
- "tha": 12898,
- "thal": 11669,
- "than": 14813,
- "thank": 40716,
- "thanks": 27547,
- "that": 5562,
- "the": 1169,
- "their": 24571,
- "thel": 37274,
- "theless": 9603,
- "them": 18855,
- "theme": 43810,
- "themed": 26966,
- "then": 8524,
- "thening": 20563,
- "thens": 43895,
- "ther": 490,
- "there": 8117,
- "thereal": 37827,
- "thereum": 17733,
- "these": 27218,
- "they": 9930,
- "thia": 31079,
- "thin": 40871,
- "thing": 1197,
- "things": 27971,
- "think": 14925,
- "thinkable": 37510,
- "thinking": 28973,
- "third": 17089,
- "thirds": 17936,
- "thirst": 48832,
- "this": 5661,
- "thodox": 12836,
- "thood": 12951,
- "thora": 34261,
- "those": 25591,
- "though": 2016,
- "thought": 28895,
- "thouse": 23931,
- "thread": 16663,
- "threat": 19971,
- "threatening": 26159,
- "three": 15542,
- "thren": 25941,
- "thritis": 34043,
- "thro": 26110,
- "throp": 11360,
- "through": 9579,
- "throw": 16939,
- "ths": 9998,
- "thumbnails": 18670,
- "thur": 11098,
- "thus": 26239,
- "thy": 20057,
- "ti": 20259,
- "tic": 13370,
- "tical": 22869,
- "tick": 42298,
- "ticket": 43350,
- "tics": 14094,
- "tie": 36224,
- "tier": 24948,
- "ties": 4278,
- "tif": 49929,
- "tight": 33464,
- "til": 47163,
- "tile": 40927,
- "tim": 16514,
- "time": 2435,
- "timeout": 48678,
- "timer": 45016,
- "times": 22355,
- "tin": 43701,
- "ting": 889,
- "tiny": 44152,
- "tion": 5378,
- "tions": 45240,
- "tip": 22504,
- "tips": 41315,
- "tis": 48010,
- "title": 7839,
- "tk": 30488,
- "tl": 28781,
- "tle": 7100,
- "tm": 17209,
- "tml": 20369,
- "tmp": 22065,
- "tn": 34106,
- "tnc": 26642,
- "to": 1462,
- "toc": 40301,
- "today": 40838,
- "toe": 44579,
- "together": 45525,
- "toggle": 44256,
- "token": 30001,
- "told": 44040,
- "tom": 39532,
- "ton": 1122,
- "tone": 41527,
- "tones": 36257,
- "tons": 27288,
- "too": 18820,
- "tool": 25981,
- "tools": 31391,
- "top": 4852,
- "topia": 46575,
- "topic": 26652,
- "tops": 35011,
- "tor": 13165,
- "torn": 45910,
- "total": 23350,
- "touch": 29332,
- "tower": 36170,
- "town": 12735,
- "tp": 34788,
- "tr": 2213,
- "tra": 9535,
- "trace": 40546,
- "track": 11659,
- "tracking": 36280,
- "tracks": 46074,
- "trade": 25351,
- "traditional": 36380,
- "train": 27432,
- "trained": 35311,
- "training": 34409,
- "trak": 44195,
- "trans": 7645,
- "transfer": 39437,
- "transform": 35636,
- "translation": 41519,
- "trap": 46670,
- "traumatic": 41521,
- "travel": 35927,
- "tre": 33945,
- "treated": 37182,
- "treatment": 42487,
- "tree": 21048,
- "tri": 28461,
- "trial": 45994,
- "trigger": 46284,
- "trip": 39813,
- "trl": 14859,
- "tro": 23528,
- "trop": 48385,
- "true": 7942,
- "trump": 40954,
- "trust": 38087,
- "truth": 35310,
- "try": 28311,
- "ts": 912,
- "tsky": 30394,
- "tsy": 34293,
- "tt": 926,
- "tta": 25854,
- "tted": 28734,
- "tten": 32407,
- "ttes": 13036,
- "tti": 35671,
- "ttle": 23296,
- "tto": 33955,
- "ttp": 29281,
- "tty": 42852,
- "tu": 28047,
- "tub": 37995,
- "tube": 29302,
- "tumblr": 45364,
- "tun": 28286,
- "tur": 36590,
- "turn": 15344,
- "turned": 33886,
- "tv": 14981,
- "tw": 4246,
- "twitch": 31844,
- "twitter": 6956,
- "two": 11545,
- "tx": 17602,
- "txt": 14116,
- "ty": 774,
- "tyard": 30308,
- "tymology": 43408,
- "typ": 28004,
- "type": 4906,
- "types": 19199,
- "typically": 48126,
- "tz": 22877,
- "u": 84,
- "ua": 6413,
- "uable": 7153,
- "uably": 14632,
- "uador": 24201,
- "ual": 723,
- "uala": 41944,
- "uality": 25775,
- "ually": 935,
- "uan": 7258,
- "uana": 5020,
- "uania": 29743,
- "uart": 19986,
- "uary": 2838,
- "uate": 4985,
- "uated": 6605,
- "uates": 12632,
- "uating": 11927,
- "uation": 2288,
- "uations": 6055,
- "uay": 30106,
- "ub": 549,
- "uba": 22013,
- "ubb": 33670,
- "ubby": 38393,
- "ube": 3266,
- "uben": 44636,
- "uber": 18478,
- "uberty": 34237,
- "ubes": 29080,
- "ubi": 29603,
- "ubis": 46676,
- "uble": 26664,
- "ublic": 841,
- "ublished": 33286,
- "ubric": 29812,
- "ubs": 23161,
- "ubuntu": 32230,
- "uc": 1229,
- "uca": 43120,
- "ucc": 18863,
- "ucci": 27501,
- "uce": 7234,
- "uced": 19513,
- "ucer": 48915,
- "uces": 26873,
- "uch": 794,
- "ucha": 48022,
- "uchi": 22200,
- "uchin": 43416,
- "uchs": 37533,
- "uci": 42008,
- "ucing": 25648,
- "uck": 1347,
- "ucked": 17758,
- "ucker": 12603,
- "ucket": 38811,
- "ucking": 19296,
- "uckland": 28789,
- "uckle": 29687,
- "uckles": 34083,
- "ucks": 6238,
- "ucky": 5309,
- "ucl": 36616,
- "ucle": 14913,
- "uclear": 4016,
- "uct": 4782,
- "uction": 8110,
- "uctions": 20847,
- "uctive": 45857,
- "uctor": 33029,
- "ud": 463,
- "uda": 15339,
- "udd": 4185,
- "udden": 16557,
- "uddenly": 18865,
- "udder": 41686,
- "uddin": 44008,
- "udding": 33926,
- "uddle": 24500,
- "uddled": 32745,
- "uddy": 21584,
- "ude": 2507,
- "udeau": 16229,
- "udeb": 46092,
- "uded": 19289,
- "uden": 44452,
- "udence": 42581,
- "uder": 26651,
- "uders": 48739,
- "udes": 8401,
- "udge": 12587,
- "udget": 29427,
- "udging": 38840,
- "udi": 47928,
- "udic": 28673,
- "udicrous": 33784,
- "uding": 26570,
- "udo": 12003,
- "udos": 42418,
- "uds": 24786,
- "ue": 518,
- "uebl": 45749,
- "ued": 1739,
- "uel": 2731,
- "ueless": 38835,
- "ueller": 16466,
- "uer": 15573,
- "uers": 42178,
- "ues": 947,
- "uesday": 3322,
- "uese": 20506,
- "uez": 14870,
- "uf": 3046,
- "ufact": 3603,
- "uff": 1648,
- "uffed": 18339,
- "uffer": 13712,
- "ufficient": 15267,
- "uffle": 18137,
- "uffs": 18058,
- "uffy": 15352,
- "ug": 1018,
- "uga": 30302,
- "ugal": 43778,
- "ugar": 35652,
- "uge": 2217,
- "ugen": 42740,
- "ugg": 6837,
- "uggage": 29672,
- "uggest": 29212,
- "uggets": 26550,
- "uggish": 36295,
- "uggle": 33498,
- "ugh": 6724,
- "ught": 8951,
- "ugi": 45659,
- "ugs": 10339,
- "ugu": 45284,
- "uh": 7456,
- "ui": 9019,
- "uid": 27112,
- "uild": 3547,
- "uilding": 6963,
- "uilt": 21955,
- "uin": 48441,
- "uine": 8327,
- "uing": 4250,
- "uint": 28611,
- "uish": 32091,
- "uit": 5013,
- "uitive": 33740,
- "uitous": 42412,
- "uits": 15379,
- "uity": 14834,
- "uj": 23577,
- "ujah": 46024,
- "uk": 2724,
- "uka": 14852,
- "uke": 4649,
- "uked": 48809,
- "ukemia": 43505,
- "ukes": 31469,
- "uki": 11308,
- "uko": 29794,
- "ukong": 46654,
- "uku": 33263,
- "ul": 377,
- "ula": 4712,
- "ular": 934,
- "ularity": 33737,
- "ulas": 25283,
- "ulate": 5039,
- "ulated": 4817,
- "ulates": 15968,
- "ulating": 8306,
- "ulation": 1741,
- "ulations": 5768,
- "ulative": 13628,
- "ulator": 8927,
- "ulators": 24325,
- "ulatory": 21386,
- "uld": 32926,
- "ule": 2261,
- "uled": 6309,
- "ulence": 32401,
- "ulent": 15288,
- "uler": 18173,
- "ules": 5028,
- "ulet": 25132,
- "ulf": 4754,
- "ulhu": 36828,
- "uli": 32176,
- "ulia": 43640,
- "ulic": 28575,
- "uliffe": 45228,
- "ulin": 11599,
- "uling": 16619,
- "ulk": 12171,
- "ulkan": 31263,
- "ull": 724,
- "ulla": 47972,
- "ullah": 38665,
- "ullivan": 16040,
- "ully": 2132,
- "ulner": 5697,
- "ulnerability": 40920,
- "ulnerable": 38828,
- "ulo": 43348,
- "ulous": 6985,
- "ulously": 18117,
- "ulp": 29528,
- "ulpt": 13327,
- "uls": 5753,
- "ulse": 9615,
- "ulsion": 15204,
- "ulsive": 22220,
- "ult": 586,
- "ultan": 30454,
- "ultane": 9560,
- "ultimate": 44818,
- "ulton": 37944,
- "ults": 8376,
- "ultural": 8596,
- "ulture": 6456,
- "ulty": 10672,
- "ultz": 22150,
- "ulu": 15712,
- "ulum": 14452,
- "ulus": 23515,
- "uly": 2062,
- "ulz": 37314,
- "um": 388,
- "uma": 7487,
- "umably": 31303,
- "uman": 3778,
- "umann": 40062,
- "umar": 44844,
- "umat": 27798,
- "umatic": 16735,
- "umb": 2178,
- "umbai": 21645,
- "umber": 4494,
- "umbered": 26584,
- "umbers": 17024,
- "umbing": 28149,
- "umble": 10344,
- "umbled": 11137,
- "umbledore": 25549,
- "umbles": 25329,
- "umbling": 14739,
- "umblr": 15566,
- "umbn": 10269,
- "umbnail": 20566,
- "umbnails": 13668,
- "umbo": 29309,
- "umbs": 18146,
- "ume": 2454,
- "umed": 18940,
- "umen": 20080,
- "ument": 1713,
- "umenthal": 42300,
- "uments": 2886,
- "umer": 6975,
- "umerable": 30831,
- "umeric": 39223,
- "umerous": 31385,
- "umers": 31260,
- "umes": 8139,
- "umi": 12994,
- "umin": 7230,
- "uminati": 37200,
- "uming": 12595,
- "uminium": 35241,
- "uminum": 13074,
- "umm": 13929,
- "ummer": 31647,
- "ummies": 39578,
- "ummy": 13513,
- "umn": 4182,
- "umni": 25402,
- "umo": 43712,
- "ump": 931,
- "umped": 27073,
- "umper": 15829,
- "umph": 12875,
- "umping": 25218,
- "umps": 8142,
- "umption": 24098,
- "umpy": 32152,
- "ums": 5700,
- "umsy": 37133,
- "un": 403,
- "una": 9613,
- "unal": 18835,
- "unc": 19524,
- "unch": 3316,
- "unci": 49652,
- "unciation": 24978,
- "uncle": 29942,
- "unct": 16260,
- "unction": 4575,
- "unctions": 46797,
- "uncture": 39187,
- "und": 917,
- "unda": 46535,
- "undai": 44591,
- "under": 4625,
- "unders": 41116,
- "undle": 31249,
- "undo": 41204,
- "undown": 41609,
- "undred": 3229,
- "undreds": 20960,
- "undrum": 46859,
- "undy": 45459,
- "une": 1726,
- "uned": 40881,
- "uner": 38886,
- "unes": 4015,
- "ung": 2150,
- "ungle": 13687,
- "uni": 35657,
- "unia": 39934,
- "unic": 46903,
- "unicip": 9462,
- "unin": 38453,
- "uning": 46493,
- "union": 24592,
- "unique": 34642,
- "unit": 20850,
- "united": 41187,
- "units": 41667,
- "unity": 9531,
- "universal": 40082,
- "unk": 2954,
- "unker": 21705,
- "unknown": 34680,
- "unks": 14125,
- "unky": 28898,
- "unless": 25252,
- "unn": 20935,
- "unning": 16596,
- "unny": 16948,
- "uno": 36909,
- "uns": 13271,
- "unsigned": 43375,
- "unt": 2797,
- "unta": 44424,
- "untarily": 49605,
- "untary": 26468,
- "unte": 6311,
- "until": 28446,
- "untled": 46343,
- "unts": 34115,
- "untu": 11157,
- "uo": 20895,
- "uous": 5623,
- "uously": 24987,
- "up": 929,
- "update": 19119,
- "updated": 43162,
- "upe": 48722,
- "uper": 48568,
- "uph": 25689,
- "uphem": 45640,
- "upid": 7658,
- "upiter": 21251,
- "uple": 29291,
- "upload": 25850,
- "uploads": 39920,
- "upon": 27287,
- "upp": 7211,
- "upper": 45828,
- "uppet": 44933,
- "ups": 4739,
- "upt": 37623,
- "upuncture": 42223,
- "ur": 333,
- "ura": 5330,
- "urable": 11970,
- "uracy": 23843,
- "urai": 16998,
- "ural": 1523,
- "urally": 20221,
- "uran": 42211,
- "urance": 3874,
- "urances": 31741,
- "uras": 17786,
- "urat": 39928,
- "urate": 15537,
- "urated": 49293,
- "uration": 3924,
- "urations": 20074,
- "urb": 5945,
- "urban": 32679,
- "urbed": 37694,
- "urch": 2575,
- "urchase": 18737,
- "urches": 12730,
- "urd": 2799,
- "urden": 42568,
- "urdue": 30345,
- "urdy": 22876,
- "ure": 495,
- "ureau": 6262,
- "ured": 1522,
- "ureen": 49851,
- "uren": 23532,
- "urer": 15051,
- "urers": 17496,
- "ures": 942,
- "urg": 3686,
- "urga": 45098,
- "urger": 32650,
- "urgical": 31839,
- "urgy": 38140,
- "uri": 9900,
- "uria": 34484,
- "uries": 4740,
- "uring": 870,
- "urion": 40956,
- "urious": 16421,
- "uristic": 27915,
- "urities": 10886,
- "urity": 1684,
- "urized": 44796,
- "url": 6371,
- "urn": 700,
- "urnal": 35735,
- "urned": 44866,
- "uro": 1434,
- "uron": 44372,
- "urous": 29277,
- "urrection": 21384,
- "urred": 12808,
- "urrence": 33928,
- "urrencies": 28018,
- "urrency": 13382,
- "urrent": 6657,
- "urring": 14924,
- "urry": 16682,
- "urs": 1834,
- "ursday": 3479,
- "urse": 12321,
- "ursed": 17539,
- "urses": 46998,
- "ursion": 24197,
- "ursions": 42394,
- "ursive": 30753,
- "ursor": 21471,
- "urst": 24962,
- "urt": 3325,
- "urther": 1914,
- "urtle": 17964,
- "urtles": 25195,
- "uru": 14717,
- "urus": 31891,
- "ury": 1601,
- "us": 385,
- "usa": 22064,
- "usable": 31979,
- "usage": 26060,
- "usal": 6775,
- "usalem": 10555,
- "usat": 37937,
- "usb": 43319,
- "usc": 16241,
- "uscript": 15817,
- "use": 1904,
- "used": 1484,
- "user": 7220,
- "userc": 43298,
- "usercontent": 43667,
- "username": 29460,
- "users": 18417,
- "uses": 2664,
- "useum": 6744,
- "ush": 1530,
- "usha": 46213,
- "ushed": 7474,
- "usher": 34055,
- "ushes": 17237,
- "ushi": 17731,
- "ushima": 30474,
- "ushing": 8023,
- "using": 3500,
- "usion": 4241,
- "usional": 41780,
- "usions": 15880,
- "usive": 11350,
- "usk": 17990,
- "usky": 42431,
- "usp": 17723,
- "usr": 14629,
- "usra": 28352,
- "uss": 1046,
- "ussed": 29569,
- "ussen": 35951,
- "ussia": 31269,
- "ussian": 31562,
- "ussie": 43480,
- "ussion": 11956,
- "ussions": 21585,
- "ussy": 14650,
- "ust": 436,
- "ustain": 19542,
- "ustainable": 24196,
- "usted": 8459,
- "uster": 5819,
- "usterity": 20761,
- "usters": 13654,
- "usting": 32620,
- "ustom": 1824,
- "ustomed": 22646,
- "ustration": 44027,
- "usual": 37850,
- "usually": 23073,
- "ut": 315,
- "uta": 29822,
- "utable": 18187,
- "utan": 37878,
- "utation": 7094,
- "utations": 32855,
- "utch": 7140,
- "ute": 1133,
- "uted": 7241,
- "uten": 7809,
- "utenant": 15340,
- "utenberg": 19028,
- "uter": 11894,
- "uters": 5843,
- "uterte": 23314,
- "utes": 1769,
- "utf": 40477,
- "uth": 1071,
- "uther": 12866,
- "utherford": 46923,
- "utherland": 45384,
- "uthor": 1457,
- "uti": 47966,
- "utic": 18089,
- "utical": 14224,
- "utics": 48063,
- "uties": 8249,
- "util": 22602,
- "utils": 26791,
- "uting": 15129,
- "ution": 1009,
- "utions": 3508,
- "utive": 8827,
- "utm": 26841,
- "uto": 9390,
- "uton": 32894,
- "utonium": 43078,
- "utor": 38409,
- "utorial": 44917,
- "utory": 17957,
- "utra": 35076,
- "utral": 6815,
- "uts": 5500,
- "utsch": 40768,
- "utsche": 30433,
- "utsu": 36567,
- "utt": 15318,
- "utter": 10381,
- "uttered": 46322,
- "uttering": 33598,
- "utters": 46973,
- "utterstock": 28819,
- "utton": 21115,
- "uture": 1832,
- "uty": 3935,
- "utz": 27839,
- "uu": 12303,
- "uum": 13814,
- "uv": 14795,
- "uve": 45177,
- "uvian": 50013,
- "ux": 2821,
- "uxe": 18095,
- "uy": 4669,
- "uyomi": 40012,
- "uz": 10277,
- "uzz": 4715,
- "uzzle": 9625,
- "v": 85,
- "vP": 47322,
- "va": 6862,
- "vable": 23765,
- "vacc": 37839,
- "vae": 33353,
- "vag": 29821,
- "val": 2100,
- "vale": 41161,
- "valid": 12102,
- "vals": 12786,
- "value": 8367,
- "valued": 39728,
- "values": 27160,
- "van": 10438,
- "vana": 33175,
- "vance": 19259,
- "vant": 4520,
- "vantage": 38815,
- "var": 7785,
- "vard": 10187,
- "vari": 25641,
- "variable": 45286,
- "vas": 11017,
- "vasive": 23747,
- "vati": 36868,
- "vation": 10473,
- "vc": 28435,
- "vd": 20306,
- "ve": 303,
- "vec": 35138,
- "vector": 31364,
- "ved": 1079,
- "veh": 33892,
- "vel": 626,
- "veland": 9731,
- "velength": 26623,
- "vell": 29333,
- "velop": 1091,
- "velt": 18065,
- "ven": 574,
- "venant": 15330,
- "venants": 43773,
- "venge": 18674,
- "venient": 48109,
- "vent": 1151,
- "venth": 20987,
- "vention": 4018,
- "ventional": 20405,
- "ventions": 16593,
- "ventory": 17158,
- "venture": 5388,
- "ventures": 10065,
- "ventus": 35648,
- "venue": 4080,
- "ver": 332,
- "verage": 1857,
- "verages": 23118,
- "verb": 19011,
- "verbal": 46953,
- "verbs": 46211,
- "vere": 4119,
- "vered": 21917,
- "verend": 37713,
- "verett": 33395,
- "verified": 47684,
- "vern": 933,
- "vernight": 47443,
- "verning": 13974,
- "vernment": 11355,
- "vers": 690,
- "verse": 4399,
- "versely": 21243,
- "versible": 37393,
- "version": 9641,
- "versions": 47178,
- "versive": 40099,
- "verson": 49589,
- "vert": 1851,
- "verted": 13658,
- "verting": 48820,
- "vertis": 3346,
- "vertisement": 4060,
- "vertisements": 11371,
- "vertising": 31809,
- "verts": 24040,
- "verty": 8077,
- "very": 548,
- "ves": 1158,
- "vest": 4223,
- "vet": 16809,
- "vette": 33573,
- "vey": 3304,
- "veyard": 21563,
- "vez": 33425,
- "vg": 45119,
- "vi": 8903,
- "via": 8869,
- "viation": 47625,
- "vic": 25531,
- "vice": 28281,
- "vich": 49547,
- "vict": 32433,
- "vid": 16921,
- "video": 15588,
- "videos": 32861,
- "vidia": 21744,
- "vier": 49663,
- "view": 1177,
- "views": 33571,
- "vik": 28930,
- "viks": 45901,
- "vil": 2991,
- "vill": 41082,
- "ville": 4244,
- "vim": 31124,
- "vin": 7114,
- "vind": 50172,
- "vine": 26818,
- "ving": 1075,
- "viol": 17069,
- "violence": 37502,
- "violent": 24498,
- "vious": 1442,
- "viously": 8647,
- "vir": 37040,
- "viron": 2268,
- "vironment": 2468,
- "vironments": 12103,
- "virt": 48940,
- "virtual": 32844,
- "vis": 4703,
- "vised": 16149,
- "visible": 23504,
- "vision": 10178,
- "visor": 13131,
- "visors": 27681,
- "visory": 41783,
- "visual": 41464,
- "vity": 21319,
- "vl": 19279,
- "vm": 14761,
- "vo": 13038,
- "voc": 18893,
- "voice": 38888,
- "void": 19382,
- "vol": 10396,
- "volent": 29078,
- "volt": 37764,
- "volume": 29048,
- "von": 26982,
- "vor": 20867,
- "vote": 27257,
- "votes": 29307,
- "vous": 31222,
- "voy": 40024,
- "vp": 36133,
- "vr": 37020,
- "vre": 43933,
- "vs": 14259,
- "vt": 36540,
- "vu": 40939,
- "vv": 25093,
- "vy": 7670,
- "w": 86,
- "wa": 10247,
- "wage": 21482,
- "wagen": 29160,
- "wagon": 41127,
- "wait": 17077,
- "wake": 48530,
- "wal": 16783,
- "wald": 21667,
- "walk": 11152,
- "walker": 20783,
- "walking": 44065,
- "wall": 11930,
- "wallet": 44623,
- "wan": 8149,
- "wana": 49484,
- "wang": 47562,
- "want": 42949,
- "war": 5767,
- "ward": 904,
- "wards": 2017,
- "ware": 1574,
- "wark": 48542,
- "warm": 31975,
- "warming": 48133,
- "warn": 40539,
- "warning": 43917,
- "wart": 24657,
- "warts": 26586,
- "was": 9776,
- "wash": 34670,
- "washed": 45462,
- "washer": 45146,
- "washing": 38524,
- "wat": 47261,
- "watch": 8340,
- "watching": 50042,
- "water": 7050,
- "waters": 41555,
- "waukee": 15428,
- "wav": 45137,
- "wave": 19204,
- "waves": 32569,
- "way": 1014,
- "wayne": 43932,
- "ways": 1322,
- "wb": 39346,
- "wcs": 12712,
- "wcsstore": 12781,
- "wd": 16993,
- "we": 732,
- "weak": 38695,
- "wealth": 14298,
- "weapon": 28741,
- "weapons": 33999,
- "wear": 13927,
- "weather": 23563,
- "web": 12384,
- "webkit": 43648,
- "wed": 19103,
- "weed": 39054,
- "week": 10464,
- "weekly": 45291,
- "ween": 975,
- "weeney": 41681,
- "weet": 7277,
- "wegian": 20684,
- "wei": 42990,
- "weight": 6551,
- "weights": 43775,
- "well": 4053,
- "wen": 21006,
- "went": 19963,
- "wer": 15448,
- "were": 22474,
- "wered": 8279,
- "west": 7038,
- "western": 14197,
- "wh": 1929,
- "what": 10919,
- "whatever": 39664,
- "whe": 12491,
- "wheel": 22001,
- "whel": 30613,
- "whelming": 36433,
- "when": 12518,
- "where": 3003,
- "whether": 25356,
- "which": 4758,
- "while": 4514,
- "white": 11186,
- "who": 8727,
- "whose": 38159,
- "why": 22850,
- "wi": 37686,
- "wic": 22664,
- "wich": 11451,
- "wick": 16239,
- "wid": 28029,
- "wide": 4421,
- "widget": 42655,
- "width": 10394,
- "wife": 22095,
- "wig": 28033,
- "wik": 20763,
- "wiki": 15466,
- "wikipedia": 31266,
- "wild": 21992,
- "will": 10594,
- "win": 5404,
- "wind": 7972,
- "window": 17497,
- "windows": 28457,
- "wine": 39002,
- "wing": 5469,
- "wings": 48819,
- "winner": 39791,
- "winning": 14463,
- "winter": 40078,
- "wire": 21809,
- "wired": 44236,
- "wise": 3083,
- "wit": 39289,
- "witch": 42248,
- "with": 4480,
- "within": 33479,
- "without": 19419,
- "withstanding": 20701,
- "witz": 28155,
- "wives": 35234,
- "wk": 43021,
- "wl": 40989,
- "wm": 26377,
- "wn": 675,
- "wo": 21638,
- "wolf": 18829,
- "wolves": 29664,
- "woman": 8580,
- "women": 25878,
- "won": 26502,
- "wood": 3822,
- "woods": 39493,
- "word": 4775,
- "wordpress": 40346,
- "words": 10879,
- "work": 1818,
- "worked": 32931,
- "worker": 28816,
- "workers": 22896,
- "working": 16090,
- "works": 5225,
- "workshop": 38067,
- "world": 6894,
- "worldly": 49366,
- "worm": 25323,
- "worms": 49617,
- "worn": 34565,
- "worst": 41430,
- "worth": 9268,
- "worthiness": 48756,
- "worthy": 18275,
- "would": 19188,
- "wow": 42773,
- "wp": 24142,
- "wr": 18351,
- "wra": 29988,
- "wrap": 37150,
- "wrapper": 48553,
- "wreck": 39238,
- "wright": 29995,
- "writ": 8933,
- "write": 13564,
- "writer": 16002,
- "writers": 34422,
- "writing": 16502,
- "written": 15266,
- "wrong": 36460,
- "wrote": 42910,
- "ws": 18504,
- "wt": 46569,
- "wu": 43812,
- "ww": 1383,
- "www": 2503,
- "wx": 49345,
- "wy": 21768,
- "wyn": 27612,
- "x": 87,
- "xa": 27865,
- "xb": 30894,
- "xc": 25306,
- "xd": 24954,
- "xe": 27705,
- "xes": 48169,
- "xf": 26152,
- "xff": 47596,
- "xi": 29992,
- "xia": 36072,
- "xiety": 35753,
- "xious": 48392,
- "xit": 10198,
- "xml": 19875,
- "xon": 23813,
- "xp": 42372,
- "xs": 34223,
- "xt": 742,
- "xtap": 42915,
- "xton": 22874,
- "xual": 5541,
- "xus": 40832,
- "xx": 5324,
- "xxx": 31811,
- "xxxx": 12343,
- "xxxxxxxx": 24223,
- "xy": 5431,
- "y": 88,
- "ya": 3972,
- "yah": 46848,
- "yahoo": 40774,
- "yan": 4121,
- "yang": 17859,
- "yard": 9413,
- "yards": 33750,
- "ycle": 39297,
- "yd": 5173,
- "yden": 43955,
- "ydia": 30708,
- "ye": 5948,
- "yeah": 43669,
- "year": 1941,
- "years": 19002,
- "yellow": 36022,
- "yer": 9860,
- "yers": 21200,
- "yes": 8505,
- "yet": 25907,
- "yg": 35641,
- "yi": 48111,
- "ying": 1112,
- "yip": 39666,
- "yk": 48361,
- "yl": 2645,
- "ylan": 18554,
- "yle": 2349,
- "ylene": 37880,
- "yles": 24327,
- "yll": 25727,
- "ylon": 15158,
- "ylum": 11183,
- "ym": 4948,
- "ymes": 22009,
- "ymm": 26621,
- "ymph": 20896,
- "yn": 2047,
- "yna": 46434,
- "ynam": 4989,
- "ynamic": 28995,
- "ynasty": 19488,
- "ync": 13361,
- "ynchron": 24871,
- "ynchronous": 31301,
- "yne": 39547,
- "ynes": 25337,
- "ynski": 40008,
- "ynt": 33567,
- "ynthesis": 44411,
- "yo": 8226,
- "yon": 19181,
- "yond": 3243,
- "you": 5832,
- "young": 35465,
- "your": 14108,
- "yout": 32015,
- "youtu": 32594,
- "youtube": 11604,
- "yp": 4464,
- "ype": 2981,
- "ypes": 9497,
- "yr": 2417,
- "yre": 35759,
- "yrics": 14279,
- "yright": 4766,
- "yrights": 49158,
- "yrim": 17302,
- "yrinth": 21324,
- "yrs": 48489,
- "yrus": 21180,
- "ys": 893,
- "ysc": 28349,
- "ysical": 15380,
- "ysics": 23154,
- "ysis": 3097,
- "yson": 19699,
- "yss": 33968,
- "yssey": 23784,
- "ystem": 6781,
- "yt": 20760,
- "yth": 5272,
- "ythm": 34853,
- "ython": 7535,
- "yton": 31616,
- "yu": 24767,
- "yx": 28391,
- "yy": 22556,
- "yz": 45579,
- "z": 89,
- "za": 4496,
- "zac": 49897,
- "zag": 50183,
- "zai": 35142,
- "zan": 15201,
- "zanne": 38395,
- "zar": 41046,
- "zb": 14969,
- "zbek": 40413,
- "zbollah": 21677,
- "ze": 2736,
- "zeb": 38130,
- "zech": 15356,
- "zed": 8863,
- "zee": 42871,
- "zees": 43727,
- "zek": 43130,
- "zel": 17396,
- "zen": 4801,
- "zens": 8247,
- "zer": 9107,
- "zero": 22570,
- "zers": 47031,
- "zes": 12271,
- "zh": 23548,
- "zhen": 46732,
- "zhou": 38536,
- "zi": 17027,
- "zie": 49746,
- "zig": 38262,
- "zik": 47303,
- "zilla": 16496,
- "zin": 42140,
- "zing": 9510,
- "zinski": 46394,
- "zip": 13344,
- "zl": 48274,
- "zman": 32054,
- "zn": 47347,
- "zo": 10872,
- "zon": 26361,
- "zona": 7551,
- "zone": 11340,
- "zos": 37925,
- "zsche": 37467,
- "zu": 27624,
- "zx": 42592,
- "zy": 7357,
- "zyk": 46355,
- "zyme": 24266,
- "zynski": 47143,
- "zz": 3019,
- "zza": 34443,
- "zzi": 46218,
- "zzle": 26413,
- "zzo": 47802,
- "zzy": 31570,
- "{": 90,
- "{\"": 4895,
- "{\\": 31478,
- "{{": 27007,
- "|": 91,
- "||": 15886,
- "||||": 42210,
- "}": 92,
- "}\"": 36786,
- "})": 30072,
- "});": 22133,
- "},": 5512,
- "},\"": 9063,
- "},{\"": 8762,
- "}.": 27422,
- "}:": 38362,
- "};": 19629,
- "}\\": 32239,
- "}{": 18477,
- "}}": 11709,
- "}}}": 42535,
- "~": 93,
- "~~": 4907,
- "~~~~": 8728,
- "~~~~~~~~": 15116,
- "~~~~~~~~~~~~~~~~": 27156,
- "¡": 94,
- "¢": 95,
- "£": 96,
- "£ı": 6408,
- "¤": 97,
- "¥": 98,
- "¥µ": 35069,
- "¥ŀ": 13945,
- "¦": 99,
- "§": 100,
- "¨": 101,
- "©": 102,
- "©¶æ": 47490,
- "©¶æ¥µ": 47703,
- "ª": 103,
- "«": 104,
- "«ĺ": 45865,
- "¬": 105,
- "¬¼": 45539,
- "®": 106,
- "¯": 107,
- "°": 108,
- "±": 109,
- "²": 110,
- "²¾": 39333,
- "³": 111,
- "´": 112,
- "µ": 113,
- "¶": 114,
- "¶æ": 35050,
- "¶ħ": 41678,
- "·": 115,
- "¸": 116,
- "¹": 117,
- "º": 118,
- "»": 119,
- "»Ĵ": 36596,
- "¼": 120,
- "½": 121,
- "¾": 122,
- "¿": 123,
- "¿½": 4204,
- "À": 124,
- "Á": 125,
- "Â": 126,
- "¢": 44359,
- "£": 14988,
- "§": 16273,
- "¨": 37102,
- "©": 16224,
- "«": 24328,
- "®": 7461,
- "®,": 45088,
- "¯": 5196,
- "¯¯": 5367,
- "¯¯¯¯": 8980,
- "¯¯¯¯¯¯¯¯": 15243,
- "¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯": 27006,
- "°": 7200,
- "±": 22519,
- "²": 31185,
- "´": 18265,
- "¶": 26604,
- "·": 9129,
- "··": 35147,
- "º": 36165,
- "»": 17730,
- "½": 23141,
- "Âł": 1849,
- "³³": 4603,
- "³³³": 33477,
- "³³³³": 8828,
- "³³³³³³³³": 17811,
- "³³³³³³³³³³³³³³³³": 39172,
- "ÂŃ": 3907,
- "Ã": 127,
- "á": 6557,
- "án": 21162,
- "ás": 40138,
- "â": 22940,
- "ã": 26102,
- "ão": 28749,
- "ä": 11033,
- "Ã¥": 29090,
- "æ": 21241,
- "ç": 16175,
- "ça": 50041,
- "è": 14064,
- "ère": 35979,
- "é": 2634,
- "ée": 22161,
- "én": 35942,
- "ér": 42445,
- "és": 20954,
- "ét": 25125,
- "ê": 25792,
- "ë": 26689,
- "î": 34803,
- "ï": 26884,
- "ïve": 38776,
- "ð": 27214,
- "ñ": 12654,
- "ña": 30644,
- "ño": 31329,
- "ó": 10205,
- "ón": 18840,
- "ô": 27083,
- "ö": 9101,
- "ön": 48863,
- "ör": 30570,
- "ø": 24172,
- "ú": 21356,
- "û": 42324,
- "ü": 9116,
- "ür": 25151,
- "ÃĤ": 5523,
- "Ãĥ": 5746,
- "ÃĥÃĤ": 5808,
- "ÃĥÃĤÃĥÃĤ": 5815,
- "ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ": 9364,
- "ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ": 14827,
- "ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ": 23090,
- "ÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤÃĥÃĤ": 35496,
- "Ãī": 38351,
- "Ãį": 38638,
- "ÃįÃį": 43569,
- "ÃĹ": 12906,
- "ÃĽ": 34543,
- "ÃĽÃĽ": 48396,
- "ÃŁ": 39683,
- "Ãł": 24247,
- "ÃŃ": 8836,
- "ÃŃa": 29690,
- "ÃŃn": 39588,
- "ÃŃs": 41200,
- "Ä": 128,
- "Ä«": 18962,
- "ı": 30102,
- "Äģ": 10235,
- "Äĩ": 38325,
- "Äį": 46195,
- "Äĵ": 27092,
- "ÄŁ": 33133,
- "Å": 129,
- "Å¡": 32790,
- "Å«": 20317,
- "ÅĤ": 41615,
- "Åį": 13090,
- "ÅŁ": 46481,
- "Æ": 130,
- "Ç": 131,
- "È": 132,
- "É": 133,
- "Ê": 134,
- "Ë": 135,
- "ËĪ": 45990,
- "Ëľ": 41185,
- "Ì": 136,
- "̶": 48869,
- "Í": 137,
- "Î": 138,
- "α": 17394,
- "β": 26638,
- "γ": 42063,
- "ε": 30950,
- "ι": 29945,
- "κ": 43000,
- "λ": 39377,
- "μ": 34703,
- "ν": 26180,
- "ο": 26517,
- "Ï": 139,
- "ÏĢ": 46582,
- "Ïģ": 33643,
- "ÏĤ": 35558,
- "Ïĥ": 38392,
- "ÏĦ": 32830,
- "Ïī": 49535,
- "Ð": 140,
- "а": 16142,
- "в": 38857,
- "д": 43666,
- "е": 16843,
- "и": 18849,
- "к": 31583,
- "л": 30143,
- "м": 43108,
- "н": 22177,
- "о": 15166,
- "оÐ": 25443,
- "Ñ": 141,
- "ÑĢ": 21169,
- "Ñģ": 21727,
- "ÑĤ": 20375,
- "Ñĥ": 35072,
- "Ñĭ": 45035,
- "ÑĮ": 45367,
- "Ñı": 40623,
- "Ò": 142,
- "Ó": 143,
- "Ô": 144,
- "Õ": 145,
- "Ö": 146,
- "Ö¼": 47903,
- "×": 147,
- "ר": 37778,
- "ש": 50227,
- "ת": 42064,
- "×IJ": 42973,
- "×ij": 49603,
- "×Ķ": 38269,
- "×ķ": 27072,
- "×Ļ": 25529,
- "×Ļ×": 33951,
- "׾": 40010,
- "×ŀ": 49168,
- "Ø": 148,
- "ا": 12919,
- "اØ": 34247,
- "اÙĦ": 23525,
- "ب": 39848,
- "Ø©": 45632,
- "ت": 41486,
- "د": 38843,
- "ر": 26897,
- "س": 45692,
- "ع": 44690,
- "Ù": 149,
- "ÙĦ": 13862,
- "Ùħ": 25405,
- "ÙĨ": 23338,
- "Ùĩ": 29519,
- "ÙĪ": 30335,
- "ÙĬ": 22654,
- "Ùİ": 24333,
- "ÙIJ": 44208,
- "ÙĴ": 48763,
- "Ú": 150,
- "Û": 151,
- "Ü": 152,
- "Ý": 153,
- "Þ": 154,
- "ß": 155,
- "à": 156,
- "à¤": 11976,
- "ा": 48077,
- "à¥": 24231,
- "à¦": 48071,
- "à¨": 19469,
- "à©": 43297,
- "à¸": 19567,
- "à¹": 31479,
- "à¼": 41340,
- "á": 157,
- "áµ": 39611,
- "á¸": 41585,
- "á¹": 26292,
- "á½": 45495,
- "â": 158,
- "âĢ": 447,
- "âĢ¢": 3581,
- "âĢ¢âĢ¢": 22838,
- "âĢ¢âĢ¢âĢ¢âĢ¢": 39967,
- "âĢ¦": 1399,
- "âĢ¦\"": 9962,
- "âĢ¦)": 38418,
- "âĢ¦.": 11580,
- "âĢ¦.\"": 50248,
- "âĢ¦..": 30864,
- "âĢ¦]": 21476,
- "âĢ¦âĢ¦": 7398,
- "âĢ¦âĢ¦âĢ¦âĢ¦": 15864,
- "âĢ¦âĢ¦âĢ¦âĢ¦âĢ¦âĢ¦âĢ¦âĢ¦": 29146,
- "âĢ²": 17478,
- "âĢ³": 12237,
- "âĢĭ": 9525,
- "âĢĭâĢĭ": 39009,
- "âĢİ": 48261,
- "âĢIJ": 9333,
- "âĢij": 20977,
- "âĢĵ": 1906,
- "âĢĵâĢĵ": 25608,
- "âĢĶ": 960,
- "âĢĶ\"": 19056,
- "âĢĶ-": 44839,
- "âĢĶâĢĶ": 4500,
- "âĢĶâĢĶâĢĶâĢĶ": 8184,
- "âĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶ": 14950,
- "âĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶâĢĶ": 30542,
- "âĢķ": 31857,
- "âĢł": 33912,
- "âģ": 46256,
- "âĤ¬": 26391,
- "âĦ¢": 8151,
- "âĦ¢:": 41333,
- "âĨ": 29705,
- "âĨij": 48541,
- "âĨĴ": 39310,
- "âĪ": 24861,
- "âĪĴ": 14095,
- "âī": 35705,
- "âĵĺ": 45563,
- "âĶ": 6552,
- "âĶĢ": 7280,
- "âĶĢâĶĢ": 8418,
- "âĶĢâĶĢâĶĢâĶĢ": 16068,
- "âĶĢâĶĢâĶĢâĶĢâĶĢâĶĢâĶĢâĶĢ": 28542,
- "âĶģ": 47486,
- "âķ": 22880,
- "âķIJ": 28670,
- "âķIJâķIJ": 31732,
- "âĸ": 5008,
- "âĸ¬": 47530,
- "âĸ¬âĸ¬": 49843,
- "âĸº": 45717,
- "âĸĢ": 44033,
- "âĸĦ": 45786,
- "âĸĪ": 8115,
- "âĸĪâĸĪ": 9968,
- "âĸĪâĸĪâĸĪâĸĪ": 20503,
- "âĸĪâĸĪâĸĪâĸĪâĸĪâĸĪâĸĪâĸĪ": 49527,
- "âĸij": 22110,
- "âĸijâĸij": 27534,
- "âĸĴ": 40516,
- "âĸĵ": 38626,
- "âĸł": 29316,
- "âĹ": 15926,
- "âĹ¼": 48366,
- "âĹı": 28133,
- "âĺ": 24583,
- "âĺħ": 15583,
- "âĺħâĺħ": 28353,
- "âĺĨ": 35283,
- "âĻ": 17992,
- "âĻ¥": 39908,
- "âĻ¦": 41298,
- "âľ": 26486,
- "âĿ": 32391,
- "ã": 159,
- "ãĢ": 5099,
- "ãĢģ": 23513,
- "ãĢĤ": 16764,
- "ãĢĮ": 13697,
- "ãĢį": 13700,
- "ãĢİ": 40493,
- "ãĢı": 40549,
- "ãĢIJ": 31854,
- "ãĢij": 31817,
- "ãģ": 2515,
- "ãģ£": 33180,
- "ãģ¦": 28134,
- "ãģ§": 30640,
- "ãģ¨": 30201,
- "ãģª": 26945,
- "ãģ«": 28618,
- "ãģ®": 5641,
- "ãģ®å": 15474,
- "ãģ®å®": 49149,
- "ãģ®æ": 27032,
- "ãģ®ç": 17683,
- "ãģ®é": 33426,
- "ãģ®éŃĶ": 34633,
- "ãģ¯": 31676,
- "ãģ¾": 30159,
- "ãģĤ": 40948,
- "ãģĦ": 18566,
- "ãģĨ": 29557,
- "ãģĭ": 27370,
- "ãģĮ": 35585,
- "ãģį": 33778,
- "ãģı": 31917,
- "ãģĵ": 46036,
- "ãģķ": 43357,
- "ãģĹ": 22180,
- "ãģĻ": 33623,
- "ãģŁ": 25224,
- "ãģł": 46777,
- "ãĤ": 1792,
- "ãĤ¡": 25362,
- "ãĤ¢": 11839,
- "ãĤ¢ãĥ«": 47794,
- "ãĤ£": 16646,
- "ãĤ¤": 11482,
- "ãĤ¤ãĥĪ": 42396,
- "ãĤ¦": 16165,
- "ãĤ¦ãĤ¹": 34103,
- "ãĤ§": 24806,
- "ãĤ¨": 23544,
- "ãĤ¨ãĥ«": 46948,
- "ãĤ©": 37662,
- "ãĤª": 20513,
- "ãĤ«": 21763,
- "ãĤ¬": 23728,
- "ãĤ®": 43899,
- "ãĤ¯": 14099,
- "ãĤ°": 26095,
- "ãĤ±": 41658,
- "ãĤ³": 24679,
- "ãĤ´": 17933,
- "ãĤ´ãĥ³": 22997,
- "ãĤµ": 26503,
- "ãĤ¶": 48458,
- "ãĤ·": 15661,
- "ãĤ·ãĥ£": 39467,
- "ãĤ¸": 21091,
- "ãĤ¹": 8943,
- "ãĤ¹ãĥĪ": 43302,
- "ãĤº": 37426,
- "ãĤ»": 47271,
- "ãĤ¼": 30432,
- "ãĤ¼ãĤ¦ãĤ¹": 43361,
- "ãĤ½": 47559,
- "ãĤ¿": 23376,
- "ãĤĤ": 43266,
- "ãĤī": 36853,
- "ãĤĬ": 28255,
- "ãĤĭ": 25748,
- "ãĤĮ": 39258,
- "ãĤĴ": 31758,
- "ãĤĵ": 22174,
- "ãĤŃ": 25084,
- "ãĥ": 1209,
- "ãĥ¡": 26998,
- "ãĥ¢": 40361,
- "ãĥ£": 23131,
- "ãĥ¤": 37858,
- "ãĥ¥": 24440,
- "ãĥ©": 9263,
- "ãĥ©ãĥ³": 48204,
- "ãĥª": 12675,
- "ãĥ«": 9202,
- "ãĥ¬": 24186,
- "ãĥ¯": 25589,
- "ãĥ¯ãĥ³": 42983,
- "ãĥ³": 6527,
- "ãĥ³ãĤ¸": 45823,
- "ãĥ´": 29752,
- "ãĥ´ãĤ¡": 44444,
- "ãĥ»": 4707,
- "ãĥ¼": 6312,
- "ãĥ¼ãĤ¯": 42869,
- "ãĥ¼ãĥ": 12045,
- "ãĥ¼ãĥ«": 43353,
- "ãĥ¼ãĥ³": 31708,
- "ãĥ¼ãĥĨ": 44326,
- "ãĥ¼ãĥĨãĤ£": 44686,
- "ãĥĢ": 27852,
- "ãĥģ": 31090,
- "ãĥĥ": 14777,
- "ãĥĥãĤ¯": 35702,
- "ãĥĥãĥĪ": 35799,
- "ãĥĥãĥī": 45435,
- "ãĥĦ": 41115,
- "ãĥĨ": 24336,
- "ãĥĨãĤ£": 44431,
- "ãĥĩ": 21959,
- "ãĥĩãĤ£": 40629,
- "ãĥĪ": 13298,
- "ãĥī": 13765,
- "ãĥīãĥ©": 19073,
- "ãĥīãĥ©ãĤ´ãĥ³": 24731,
- "ãĥĬ": 26229,
- "ãĥĭ": 30165,
- "ãĥį": 44916,
- "ãĥİ": 25053,
- "ãĥı": 37412,
- "ãĥIJ": 29659,
- "ãĥij": 32546,
- "ãĥĵ": 36922,
- "ãĥķ": 17681,
- "ãĥķãĤ¡": 41939,
- "ãĥķãĤ©": 48457,
- "ãĥĸ": 24001,
- "ãĥĹ": 30965,
- "ãĥĺ": 23363,
- "ãĥĺãĥ©": 34473,
- "ãĥĻ": 35604,
- "ãĥŀ": 20115,
- "ãĥŁ": 27542,
- "ãĥł": 25795,
- "ãĥŃ": 16253,
- "ãħĭ": 35098,
- "ãħĭãħĭ": 40345,
- "ä": 160,
- "ä¸": 10310,
- "ä¸Ģ": 31660,
- "ä¸ī": 49011,
- "ä¸Ĭ": 41468,
- "ä¸į": 38834,
- "ä¸Ń": 40792,
- "ä¹": 20046,
- "ä¹ĭ": 45298,
- "äº": 12859,
- "人": 21689,
- "äºĶ": 49390,
- "ä»": 20015,
- "代": 47987,
- "ä¼": 27670,
- "ä½": 19526,
- "使": 45635,
- "ä½ľ": 43291,
- "ä¿": 46479,
- "å": 161,
- "å£": 18004,
- "士": 18803,
- "å¤": 13783,
- "大": 32014,
- "天": 25465,
- "å¥": 25001,
- "女": 42637,
- "å¦": 36685,
- "å§": 34650,
- "姫": 40235,
- "å®": 22522,
- "å¯": 43380,
- "å°": 22887,
- "å°Ĩ": 49546,
- "å·": 32432,
- "å¸": 30585,
- "å¹": 33176,
- "åº": 41753,
- "å¼": 28156,
- "å½": 37605,
- "å¾": 36181,
- "å¿": 33232,
- "åĤ": 43636,
- "åħ": 17739,
- "åħī": 46268,
- "åĨ": 37863,
- "åĩ": 49035,
- "åĪ": 26344,
- "åī": 30298,
- "åĬ": 27950,
- "åĭ": 47947,
- "åĮ": 44293,
- "åį": 39355,
- "åİ": 43889,
- "åı": 20998,
- "åIJ": 28938,
- "åij": 37772,
- "åĽ": 32368,
- "åľ": 28839,
- "åŃ": 27764,
- "åŃIJ": 36310,
- "æ": 162,
- "æ©": 43897,
- "æ©Ł": 49960,
- "æ°": 36365,
- "æ³": 37345,
- "æµ": 38184,
- "æĢ": 45250,
- "æĥ": 46349,
- "æĦ": 35707,
- "æĪ": 22755,
- "æĪ¦": 36704,
- "æī": 33699,
- "æķ": 46763,
- "æĸ": 23877,
- "æĸ¹": 43095,
- "æĹ": 33768,
- "æĺ": 23626,
- "æĺ¯": 42468,
- "æľ": 17312,
- "æĿ": 30266,
- "æł": 43718,
- "æŃ": 29826,
- "æѦ": 49476,
- "ç": 163,
- "ç¥ŀ": 15351,
- "ç«": 44165,
- "ç·": 45784,
- "çĦ": 47078,
- "çī": 31965,
- "çīĪ": 48304,
- "çĭ": 45379,
- "çİĭ": 25581,
- "çIJ": 49426,
- "çĶ": 18796,
- "çĶ°": 35572,
- "çĶŁ": 37955,
- "çķ": 45911,
- "çļ": 19021,
- "çļĦ": 21410,
- "çĽ": 33566,
- "çľ": 40367,
- "è": 164,
- "è¡": 26193,
- "è£": 32518,
- "è£ħ": 35318,
- "è¦": 17358,
- "è¦ļéĨĴ": 23614,
- "èª": 45739,
- "è¯": 46237,
- "è»": 43102,
- "è¿": 32573,
- "èĢ": 32003,
- "èĢħ": 38519,
- "èĥ": 47797,
- "èĪ": 48958,
- "é": 165,
- "é£": 45617,
- "é»Ĵ": 44112,
- "é¾": 11737,
- "é¾į": 11885,
- "é¾įå": 19049,
- "é¾įå¥": 39820,
- "é¾įå¥ij士": 39821,
- "é¾įåĸļ士": 33454,
- "éĢ": 34460,
- "éģ": 34402,
- "éĥ": 32849,
- "éĩ": 34932,
- "éĸ": 38461,
- "éĹ": 29785,
- "éĹĺ": 42234,
- "éļ": 49694,
- "éĽ": 37239,
- "éŃĶ": 20804,
- "ê": 166,
- "ë": 167,
- "ëĭ": 46695,
- "ì": 168,
- "ìĿ": 35975,
- "í": 169,
- "íķ": 47991,
- "î": 170,
- "îĢ": 29773,
- "ï": 171,
- "ï¸": 35266,
- "ï¸ı": 37929,
- "�": 4210,
- "��": 6353,
- "���": 48585,
- "����": 12100,
- "ð": 172,
- "ðĿ": 47728,
- "ðŁ": 8582,
- "ðŁij": 41840,
- "ðŁĺ": 47249,
- "ñ": 173,
- "ò": 174,
- "ó": 175,
- "ô": 176,
- "õ": 177,
- "ö": 178,
- "÷": 179,
- "ø": 180,
- "ù": 181,
- "ú": 182,
- "û": 183,
- "ü": 184,
- "ý": 185,
- "þ": 186,
- "ÿ": 187,
- "Ā": 188,
- "ā": 189,
- "Ă": 190,
- "ă": 191,
- "Ą": 192,
- "ą": 193,
- "Ć": 194,
- "ć": 195,
- "Ĉ": 196,
- "ĉ": 197,
- "Ċ": 198,
- "ĊÂł": 44320,
- "ĊĊ": 628,
- "ċ": 199,
- "Č": 200,
- "č": 201,
- "Ď": 202,
- "ď": 203,
- "Đ": 204,
- "đ": 205,
- "Ē": 206,
- "ē": 207,
- "Ĕ": 208,
- "ĕ": 209,
- "Ė": 210,
- "ė": 211,
- "Ę": 212,
- "ę": 213,
- "Ě": 214,
- "ě": 215,
- "Ĝ": 216,
- "ĝ": 217,
- "Ğ": 218,
- "ğ": 219,
- "Ġ": 220,
- "Ġ!": 5145,
- "Ġ!!": 37867,
- "Ġ!=": 14512,
- "Ġ\"": 366,
- "Ġ\"\"": 13538,
- "Ġ\"\"\"": 37227,
- "Ġ\"#": 25113,
- "Ġ\"$": 17971,
- "Ġ\"$:/": 32047,
- "Ġ\"%": 36521,
- "Ġ\"'": 24018,
- "Ġ\"(": 30629,
- "Ġ\"+": 43825,
- "Ġ\",": 33172,
- "Ġ\"-": 27444,
- "Ġ\".": 27071,
- "Ġ\"...": 27896,
- "Ġ\"/": 12813,
- "Ġ\"<": 33490,
- "Ġ\"@": 44212,
- "Ġ\"[": 12878,
- "Ġ\"\\": 37082,
- "Ġ\"_": 45434,
- "Ġ\"{": 45144,
- "Ġ\"âĢ¦": 29368,
- "Ġ#": 1303,
- "Ġ##": 22492,
- "Ġ###": 44386,
- "Ġ#####": 46424,
- "Ġ$": 720,
- "Ġ$$": 32382,
- "Ġ$(": 29568,
- "Ġ$\\": 39280,
- "Ġ$_": 40111,
- "Ġ${": 25597,
- "Ġ%": 4064,
- "Ġ%%": 43313,
- "Ġ&": 1222,
- "Ġ&&": 11405,
- "Ġ'": 705,
- "Ġ''": 10148,
- "Ġ'(": 29513,
- "Ġ',": 46083,
- "Ġ'.": 45302,
- "Ġ'/": 31051,
- "Ġ'[": 44438,
- "Ġ(": 357,
- "Ġ(!": 22759,
- "Ġ(\"": 5855,
- "Ġ(#": 17426,
- "Ġ($": 7198,
- "Ġ($)": 45491,
- "Ġ(%": 37633,
- "Ġ(%)": 11509,
- "Ġ(&": 35494,
- "Ġ('": 19203,
- "Ġ((": 14808,
- "Ġ()": 7499,
- "Ġ())": 32865,
- "Ġ());": 38377,
- "Ġ(),": 29994,
- "Ġ().": 27972,
- "Ġ();": 13979,
- "Ġ(*": 20789,
- "Ġ(+": 11502,
- "Ġ(-": 13841,
- "Ġ(.": 20262,
- "Ġ(/": 50247,
- "Ġ(<": 38155,
- "Ġ(=": 46121,
- "Ġ(>": 45160,
- "Ġ(?,": 32843,
- "Ġ(@": 4275,
- "Ġ([": 29565,
- "Ġ(_": 44104,
- "Ġ({": 37913,
- "Ġ(~": 31034,
- "Ġ(£": 23068,
- "Ġ(âĪĴ": 35508,
- "Ġ)": 1267,
- "Ġ))": 15306,
- "Ġ)))": 47282,
- "Ġ));": 29226,
- "Ġ),": 10612,
- "Ġ).": 6739,
- "Ġ):": 15179,
- "Ġ);": 5619,
- "Ġ)]": 48600,
- "Ġ*": 1635,
- "Ġ*)": 31936,
- "Ġ**": 12429,
- "Ġ***": 17202,
- "Ġ****": 25998,
- "Ġ********************************": 41906,
- "Ġ*.": 46866,
- "Ġ*/": 9466,
- "Ġ+": 1343,
- "Ġ+#": 43053,
- "Ġ++": 19969,
- "Ġ+++": 49954,
- "Ġ+---": 40703,
- "Ġ+/-": 29694,
- "Ġ+=": 15853,
- "Ġ,": 837,
- "Ġ,\"": 42911,
- "Ġ-": 532,
- "Ġ--": 1377,
- "Ġ---": 11420,
- "Ġ----": 13498,
- "Ġ-----": 37404,
- "Ġ------": 40103,
- "Ġ-------": 35656,
- "Ġ--------": 24200,
- "Ġ---------": 45337,
- "Ġ----------------": 34400,
- "Ġ--------------------": 41436,
- "Ġ--------------------------------": 20368,
- "Ġ----------------------------------------------------------------": 16529,
- "Ġ-->": 14610,
- "Ġ-=": 48185,
- "Ġ->": 4613,
- "Ġ.": 764,
- "Ġ.\"": 22135,
- "Ġ.)": 46328,
- "Ġ..": 11485,
- "Ġ...": 2644,
- "Ġ...\"": 35713,
- "Ġ....": 19424,
- "Ġ......": 47082,
- "Ġ........": 20004,
- "Ġ..........": 39864,
- "Ġ..............": 44912,
- "Ġ................": 44713,
- "Ġ./": 24457,
- "Ġ._": 47540,
- "Ġ/": 1220,
- "Ġ/*": 11900,
- "Ġ/**": 42638,
- "Ġ//": 3373,
- "Ġ///": 34013,
- "Ġ//[": 31161,
- "Ġ/>": 11037,
- "Ġ0": 657,
- "Ġ00": 3571,
- "Ġ000": 12877,
- "Ġ0000": 17643,
- "Ġ000000": 41853,
- "Ġ00000000": 27551,
- "Ġ0004": 38326,
- "Ġ01": 5534,
- "Ġ02": 7816,
- "Ġ03": 7643,
- "Ġ04": 8702,
- "Ġ05": 8870,
- "Ġ06": 9130,
- "Ġ07": 8753,
- "Ġ08": 8487,
- "Ġ09": 7769,
- "Ġ1": 352,
- "Ġ10": 838,
- "Ġ100": 1802,
- "Ġ1000": 8576,
- "Ġ10000": 33028,
- "Ġ101": 8949,
- "Ġ102": 15143,
- "Ġ1024": 28119,
- "Ġ103": 15349,
- "Ġ104": 14436,
- "Ġ105": 13343,
- "Ġ1050": 47235,
- "Ġ106": 15696,
- "Ġ107": 16226,
- "Ġ1070": 49616,
- "Ġ108": 15495,
- "Ġ1080": 17729,
- "Ġ109": 16003,
- "Ġ11": 1367,
- "Ġ110": 9796,
- "Ġ1100": 36566,
- "Ġ111": 13374,
- "Ġ112": 13539,
- "Ġ113": 17318,
- "Ġ114": 17342,
- "Ġ115": 12279,
- "Ġ116": 18693,
- "Ġ117": 19048,
- "Ġ118": 19035,
- "Ġ119": 15136,
- "Ġ12": 1105,
- "Ġ120": 7982,
- "Ġ1200": 24938,
- "Ġ121": 20416,
- "Ġ122": 19409,
- "Ġ123": 17031,
- "Ġ124": 19755,
- "Ġ125": 13151,
- "Ġ126": 19710,
- "Ġ127": 18112,
- "Ġ128": 13108,
- "Ġ1280": 37674,
- "Ġ129": 20248,
- "Ġ13": 1511,
- "Ġ130": 11323,
- "Ġ1300": 36058,
- "Ġ131": 23134,
- "Ġ132": 21761,
- "Ġ133": 22169,
- "Ġ134": 22352,
- "Ġ135": 17501,
- "Ġ136": 21056,
- "Ġ137": 21643,
- "Ġ138": 21503,
- "Ġ139": 23666,
- "Ġ14": 1478,
- "Ġ140": 12713,
- "Ġ1400": 36641,
- "Ġ141": 25500,
- "Ġ142": 25181,
- "Ġ143": 24356,
- "Ġ144": 20224,
- "Ġ1440": 49557,
- "Ġ145": 20299,
- "Ġ146": 22986,
- "Ġ147": 22909,
- "Ġ148": 22613,
- "Ġ149": 24041,
- "Ġ15": 1315,
- "Ġ150": 6640,
- "Ġ1500": 20007,
- "Ġ151": 25326,
- "Ġ152": 24848,
- "Ġ153": 24652,
- "Ġ154": 24235,
- "Ġ155": 20708,
- "Ġ156": 23871,
- "Ġ157": 23313,
- "Ġ158": 24063,
- "Ġ159": 26422,
- "Ġ16": 1467,
- "Ġ160": 13454,
- "Ġ1600": 26143,
- "Ġ161": 27829,
- "Ġ162": 25090,
- "Ġ163": 26826,
- "Ġ164": 25307,
- "Ġ165": 21409,
- "Ġ166": 26753,
- "Ġ167": 26118,
- "Ġ168": 23378,
- "Ġ169": 27191,
- "Ġ17": 1596,
- "Ġ170": 16677,
- "Ġ1700": 35665,
- "Ġ171": 28369,
- "Ġ172": 23120,
- "Ġ173": 28174,
- "Ġ174": 27621,
- "Ġ175": 19038,
- "Ġ176": 26937,
- "Ġ177": 26607,
- "Ġ178": 27368,
- "Ġ179": 27228,
- "Ġ18": 1248,
- "Ġ180": 11546,
- "Ġ1800": 21431,
- "Ġ181": 30110,
- "Ġ182": 28581,
- "Ġ183": 28551,
- "Ġ1830": 45440,
- "Ġ184": 28598,
- "Ġ1840": 47784,
- "Ġ185": 22855,
- "Ġ1850": 35745,
- "Ġ186": 28481,
- "Ġ1860": 37637,
- "Ġ1861": 45278,
- "Ġ1862": 49658,
- "Ġ1863": 47072,
- "Ġ1865": 47801,
- "Ġ187": 27649,
- "Ġ1870": 37667,
- "Ġ188": 27778,
- "Ġ1880": 34865,
- "Ġ1886": 49539,
- "Ġ1888": 49584,
- "Ġ1889": 49545,
- "Ġ189": 27230,
- "Ġ1890": 31982,
- "Ġ1893": 48889,
- "Ġ1895": 46425,
- "Ġ1896": 46723,
- "Ġ1897": 49429,
- "Ġ1898": 46244,
- "Ġ1899": 47465,
- "Ġ19": 678,
- "Ġ190": 19884,
- "Ġ1900": 21489,
- "Ġ1901": 39923,
- "Ġ1902": 45611,
- "Ġ1903": 41625,
- "Ġ1904": 43785,
- "Ġ1905": 37166,
- "Ġ1906": 40538,
- "Ġ1907": 41435,
- "Ġ1908": 40417,
- "Ġ1909": 41507,
- "Ġ191": 31009,
- "Ġ1910": 31953,
- "Ġ1911": 32216,
- "Ġ1912": 34463,
- "Ġ1913": 35145,
- "Ġ1914": 26833,
- "Ġ1915": 32062,
- "Ġ1916": 32811,
- "Ġ1917": 24168,
- "Ġ1918": 25859,
- "Ġ1919": 30992,
- "Ġ192": 17817,
- "Ġ1920": 14062,
- "Ġ1921": 35369,
- "Ġ1922": 36094,
- "Ġ1923": 37272,
- "Ġ1924": 37547,
- "Ġ1925": 36864,
- "Ġ1926": 38525,
- "Ġ1927": 36565,
- "Ġ1928": 35768,
- "Ġ1929": 31883,
- "Ġ193": 29691,
- "Ġ1930": 15533,
- "Ġ1931": 34625,
- "Ġ1932": 32471,
- "Ġ1933": 26539,
- "Ġ1934": 29300,
- "Ġ1935": 30704,
- "Ġ1936": 27653,
- "Ġ1937": 28684,
- "Ġ1938": 28017,
- "Ġ1939": 24414,
- "Ġ194": 30483,
- "Ġ1940": 16236,
- "Ġ1941": 23234,
- "Ġ1942": 22458,
- "Ġ1943": 21577,
- "Ġ1944": 16994,
- "Ġ1945": 15761,
- "Ġ1946": 22717,
- "Ġ1947": 21709,
- "Ġ1948": 21794,
- "Ġ1949": 24977,
- "Ġ195": 24793,
- "Ġ1950": 11445,
- "Ġ1951": 27937,
- "Ġ1952": 26352,
- "Ġ1953": 24217,
- "Ġ1954": 24718,
- "Ġ1955": 25325,
- "Ġ1956": 25190,
- "Ġ1957": 25177,
- "Ġ1958": 24648,
- "Ġ1959": 23859,
- "Ġ196": 28817,
- "Ġ1960": 9507,
- "Ġ1961": 20510,
- "Ġ1962": 20033,
- "Ġ1963": 19342,
- "Ġ1964": 17575,
- "Ġ1965": 17672,
- "Ġ1966": 19322,
- "Ġ1967": 15904,
- "Ġ1968": 15963,
- "Ġ1969": 16450,
- "Ġ197": 29903,
- "Ġ1970": 8069,
- "Ġ1971": 16382,
- "Ġ1972": 16101,
- "Ġ1973": 15674,
- "Ġ1974": 16489,
- "Ġ1975": 15231,
- "Ġ1976": 15408,
- "Ġ1977": 15589,
- "Ġ1978": 15524,
- "Ġ1979": 13521,
- "Ġ198": 2757,
- "Ġ1980": 7169,
- "Ġ1981": 14745,
- "Ġ1982": 14489,
- "Ġ1983": 13540,
- "Ġ1984": 12844,
- "Ġ1985": 12863,
- "Ġ1986": 12113,
- "Ġ1987": 12923,
- "Ġ1988": 12122,
- "Ġ1989": 11104,
- "Ġ199": 1594,
- "Ġ1990": 6303,
- "Ġ1991": 10249,
- "Ġ1992": 9768,
- "Ġ1993": 9656,
- "Ġ1994": 9162,
- "Ġ1995": 8735,
- "Ġ1996": 8235,
- "Ġ1997": 8309,
- "Ġ1998": 7795,
- "Ġ1999": 7358,
- "Ġ2": 362,
- "Ġ20": 1160,
- "Ġ200": 939,
- "Ġ2000": 4751,
- "Ġ2001": 5878,
- "Ġ2002": 6244,
- "Ġ2003": 5816,
- "Ġ2004": 5472,
- "Ġ2005": 5075,
- "Ġ2006": 4793,
- "Ġ2007": 4343,
- "Ġ2008": 3648,
- "Ġ2009": 3717,
- "Ġ201": 580,
- "Ġ2010": 3050,
- "Ġ2011": 2813,
- "Ġ2012": 2321,
- "Ġ2013": 2211,
- "Ġ2014": 1946,
- "Ġ2015": 1853,
- "Ġ2016": 1584,
- "Ġ2017": 2177,
- "Ġ2018": 2864,
- "Ġ2019": 13130,
- "Ġ202": 22131,
- "Ġ2020": 12131,
- "Ġ2021": 33448,
- "Ġ2022": 33160,
- "Ġ2024": 48609,
- "Ġ2025": 32190,
- "Ġ203": 27408,
- "Ġ2030": 25054,
- "Ġ204": 26956,
- "Ġ2048": 36117,
- "Ġ205": 22538,
- "Ġ2050": 32215,
- "Ġ206": 27253,
- "Ġ207": 27791,
- "Ġ208": 27121,
- "Ġ209": 28815,
- "Ġ21": 2310,
- "Ġ210": 20064,
- "Ġ2100": 38123,
- "Ġ211": 28714,
- "Ġ212": 23679,
- "Ġ213": 28658,
- "Ġ214": 28277,
- "Ġ215": 22951,
- "Ġ216": 26881,
- "Ġ217": 24894,
- "Ġ218": 29217,
- "Ġ219": 30453,
- "Ġ22": 2534,
- "Ġ220": 15629,
- "Ġ221": 31566,
- "Ġ222": 27795,
- "Ġ223": 30299,
- "Ġ224": 26063,
- "Ġ225": 18500,
- "Ġ226": 31510,
- "Ġ227": 30989,
- "Ġ228": 29041,
- "Ġ229": 31064,
- "Ġ23": 2242,
- "Ġ230": 18395,
- "Ġ231": 34598,
- "Ġ232": 31773,
- "Ġ233": 30435,
- "Ġ234": 34323,
- "Ġ235": 28878,
- "Ġ236": 34044,
- "Ġ237": 34385,
- "Ġ238": 32544,
- "Ġ239": 32817,
- "Ġ24": 1987,
- "Ġ240": 14956,
- "Ġ2400": 48548,
- "Ġ241": 35150,
- "Ġ242": 34353,
- "Ġ243": 35989,
- "Ġ244": 35264,
- "Ġ245": 29637,
- "Ġ246": 34951,
- "Ġ247": 30179,
- "Ġ248": 32996,
- "Ġ249": 34620,
- "Ġ25": 1679,
- "Ġ250": 8646,
- "Ġ2500": 33507,
- "Ġ251": 34489,
- "Ġ252": 25264,
- "Ġ253": 32056,
- "Ġ254": 35360,
- "Ġ255": 14280,
- "Ġ256": 17759,
- "Ġ257": 36100,
- "Ġ258": 37528,
- "Ġ259": 37831,
- "Ġ26": 2608,
- "Ġ260": 21148,
- "Ġ2600": 47197,
- "Ġ261": 39166,
- "Ġ262": 35404,
- "Ġ263": 39135,
- "Ġ264": 32158,
- "Ġ265": 32090,
- "Ġ266": 37737,
- "Ġ267": 37364,
- "Ġ268": 36678,
- "Ġ269": 38249,
- "Ġ27": 2681,
- "Ġ270": 20479,
- "Ġ271": 33797,
- "Ġ272": 38107,
- "Ġ273": 38549,
- "Ġ274": 39768,
- "Ġ275": 25829,
- "Ġ276": 38147,
- "Ġ277": 38703,
- "Ġ278": 39174,
- "Ġ279": 39466,
- "Ġ28": 2579,
- "Ġ280": 21355,
- "Ġ281": 39882,
- "Ġ282": 41810,
- "Ġ283": 42032,
- "Ġ284": 40654,
- "Ġ285": 33015,
- "Ġ286": 39697,
- "Ġ287": 38721,
- "Ġ288": 35419,
- "Ġ289": 38902,
- "Ġ29": 2808,
- "Ġ290": 26481,
- "Ġ291": 43336,
- "Ġ292": 41569,
- "Ġ293": 37224,
- "Ġ294": 41235,
- "Ġ295": 34772,
- "Ġ296": 41922,
- "Ġ297": 41103,
- "Ġ298": 37576,
- "Ġ299": 31011,
- "Ġ3": 513,
- "Ġ30": 1542,
- "Ġ300": 5867,
- "Ġ3000": 20343,
- "Ġ301": 25643,
- "Ġ302": 32591,
- "Ġ303": 30727,
- "Ġ304": 31672,
- "Ġ305": 32747,
- "Ġ306": 37255,
- "Ġ307": 38369,
- "Ġ308": 35617,
- "Ġ309": 40286,
- "Ġ31": 3261,
- "Ġ310": 28947,
- "Ġ311": 35592,
- "Ġ312": 34465,
- "Ġ313": 35897,
- "Ġ314": 34085,
- "Ġ315": 32647,
- "Ġ316": 34131,
- "Ġ317": 37563,
- "Ġ318": 39320,
- "Ġ319": 40385,
- "Ġ32": 3933,
- "Ġ320": 20959,
- "Ġ321": 39595,
- "Ġ322": 38831,
- "Ġ323": 38446,
- "Ġ324": 38595,
- "Ġ325": 29524,
- "Ġ326": 40660,
- "Ġ327": 36203,
- "Ġ328": 39093,
- "Ġ329": 42141,
- "Ġ33": 4747,
- "Ġ330": 25508,
- "Ġ331": 43722,
- "Ġ332": 41423,
- "Ġ333": 23460,
- "Ġ334": 42819,
- "Ġ335": 37144,
- "Ġ336": 38867,
- "Ġ337": 42294,
- "Ġ338": 40736,
- "Ġ339": 42489,
- "Ġ34": 4974,
- "Ġ340": 28560,
- "Ġ341": 43155,
- "Ġ342": 44341,
- "Ġ343": 37290,
- "Ġ344": 43686,
- "Ġ345": 39937,
- "Ġ346": 44729,
- "Ġ347": 43292,
- "Ġ348": 44084,
- "Ġ349": 44367,
- "Ġ35": 3439,
- "Ġ350": 13803,
- "Ġ351": 44417,
- "Ġ352": 44063,
- "Ġ353": 47567,
- "Ġ354": 46752,
- "Ġ355": 36561,
- "Ġ356": 44552,
- "Ġ357": 45210,
- "Ġ358": 41761,
- "Ġ359": 41934,
- "Ġ36": 4570,
- "Ġ360": 11470,
- "Ġ361": 47744,
- "Ġ363": 49327,
- "Ġ364": 44969,
- "Ġ365": 21268,
- "Ġ366": 44856,
- "Ġ367": 40884,
- "Ġ368": 43019,
- "Ġ369": 45620,
- "Ġ37": 5214,
- "Ġ370": 28687,
- "Ġ371": 47343,
- "Ġ372": 46633,
- "Ġ373": 47946,
- "Ġ374": 49020,
- "Ġ375": 29414,
- "Ġ376": 44622,
- "Ġ377": 42163,
- "Ġ378": 45473,
- "Ġ379": 45937,
- "Ġ38": 4353,
- "Ġ380": 29101,
- "Ġ383": 49814,
- "Ġ384": 40400,
- "Ġ385": 44826,
- "Ġ386": 48340,
- "Ġ387": 49689,
- "Ġ388": 43550,
- "Ġ389": 49633,
- "Ġ39": 5014,
- "Ġ390": 33882,
- "Ġ392": 48207,
- "Ġ395": 42321,
- "Ġ396": 48758,
- "Ġ398": 39260,
- "Ġ399": 43927,
- "Ġ4": 604,
- "Ġ40": 2319,
- "Ġ400": 7337,
- "Ġ4000": 30123,
- "Ġ401": 22219,
- "Ġ402": 42622,
- "Ġ403": 38210,
- "Ġ404": 32320,
- "Ġ405": 36966,
- "Ġ406": 45439,
- "Ġ407": 41879,
- "Ġ408": 41247,
- "Ġ409": 48132,
- "Ġ4090": 48908,
- "Ġ4096": 42479,
- "Ġ41": 6073,
- "Ġ410": 32921,
- "Ġ411": 43184,
- "Ġ412": 42215,
- "Ġ413": 46618,
- "Ġ414": 45900,
- "Ġ415": 40643,
- "Ġ416": 38158,
- "Ġ417": 47580,
- "Ġ418": 45959,
- "Ġ419": 48475,
- "Ġ42": 5433,
- "Ġ420": 28262,
- "Ġ421": 49294,
- "Ġ422": 46588,
- "Ġ423": 49125,
- "Ġ424": 48252,
- "Ġ425": 36959,
- "Ġ426": 48065,
- "Ġ427": 45345,
- "Ġ428": 45063,
- "Ġ429": 42313,
- "Ġ43": 5946,
- "Ġ430": 35090,
- "Ġ432": 46393,
- "Ġ433": 47407,
- "Ġ435": 42671,
- "Ġ436": 50038,
- "Ġ44": 5846,
- "Ġ440": 33879,
- "Ġ443": 40384,
- "Ġ444": 45095,
- "Ġ445": 48655,
- "Ġ448": 49989,
- "Ġ45": 4153,
- "Ġ450": 18523,
- "Ġ451": 49356,
- "Ġ455": 46839,
- "Ġ457": 47996,
- "Ġ458": 50154,
- "Ġ46": 6337,
- "Ġ460": 34091,
- "Ġ465": 49669,
- "Ġ47": 6298,
- "Ġ470": 38634,
- "Ġ475": 45881,
- "Ġ48": 4764,
- "Ġ480": 23487,
- "Ġ49": 5125,
- "Ġ490": 45601,
- "Ġ499": 48391,
- "Ġ5": 642,
- "Ġ50": 2026,
- "Ġ500": 5323,
- "Ġ5000": 23336,
- "Ġ501": 24555,
- "Ġ502": 47233,
- "Ġ503": 44541,
- "Ġ504": 41612,
- "Ġ505": 43367,
- "Ġ51": 6885,
- "Ġ510": 35148,
- "Ġ512": 22243,
- "Ġ52": 6740,
- "Ġ520": 36141,
- "Ġ525": 45719,
- "Ġ529": 49888,
- "Ġ53": 7192,
- "Ġ530": 40585,
- "Ġ54": 7175,
- "Ġ540": 38190,
- "Ġ55": 5996,
- "Ġ550": 25240,
- "Ġ555": 44717,
- "Ġ56": 7265,
- "Ġ560": 38089,
- "Ġ57": 7632,
- "Ġ570": 44626,
- "Ġ58": 7618,
- "Ġ580": 41234,
- "Ġ59": 7863,
- "Ġ6": 718,
- "Ġ60": 3126,
- "Ġ600": 10053,
- "Ġ6000": 39064,
- "Ġ601": 49231,
- "Ġ608": 39084,
- "Ġ61": 8454,
- "Ġ610": 44300,
- "Ġ62": 8190,
- "Ġ620": 45469,
- "Ġ625": 48868,
- "Ġ63": 8093,
- "Ġ630": 44505,
- "Ġ64": 5598,
- "Ġ640": 33759,
- "Ġ65": 6135,
- "Ġ650": 22626,
- "Ġ655": 45021,
- "Ġ66": 7930,
- "Ġ660": 41717,
- "Ġ666": 43364,
- "Ġ67": 8275,
- "Ġ670": 48136,
- "Ġ68": 8257,
- "Ġ680": 40554,
- "Ġ69": 8644,
- "Ġ698": 39861,
- "Ġ7": 767,
- "Ġ70": 4317,
- "Ġ700": 13037,
- "Ġ7000": 50205,
- "Ġ701": 48173,
- "Ġ702": 43379,
- "Ġ71": 9166,
- "Ġ72": 7724,
- "Ġ720": 26250,
- "Ġ73": 8854,
- "Ġ737": 37517,
- "Ġ74": 8915,
- "Ġ747": 45600,
- "Ġ75": 5441,
- "Ġ750": 19683,
- "Ġ76": 8684,
- "Ġ760": 48284,
- "Ġ768": 46720,
- "Ġ77": 8541,
- "Ġ770": 44586,
- "Ġ777": 35534,
- "Ġ78": 8699,
- "Ġ780": 41287,
- "Ġ79": 9225,
- "Ġ8": 807,
- "Ġ80": 4019,
- "Ġ800": 10460,
- "Ġ8000": 38055,
- "Ġ802": 33121,
- "Ġ808": 41241,
- "Ġ81": 9773,
- "Ġ82": 9415,
- "Ġ820": 48964,
- "Ġ83": 9698,
- "Ġ84": 9508,
- "Ġ840": 48777,
- "Ġ85": 7600,
- "Ġ850": 30607,
- "Ġ86": 9849,
- "Ġ87": 10083,
- "Ġ88": 9193,
- "Ġ89": 9919,
- "Ġ9": 860,
- "Ġ90": 4101,
- "Ġ900": 15897,
- "Ġ9000": 50138,
- "Ġ91": 10495,
- "Ġ911": 16679,
- "Ġ92": 10190,
- "Ġ920": 47679,
- "Ġ93": 10261,
- "Ġ94": 10048,
- "Ġ95": 6957,
- "Ġ950": 38384,
- "Ġ96": 9907,
- "Ġ960": 41263,
- "Ġ97": 10111,
- "Ġ970": 40463,
- "Ġ978": 41417,
- "Ġ98": 9661,
- "Ġ980": 32614,
- "Ġ99": 7388,
- "Ġ999": 36006,
- "Ġ:": 1058,
- "Ġ:(": 36147,
- "Ġ:)": 14373,
- "Ġ:-)": 47226,
- "Ġ::": 7904,
- "Ġ:=": 19039,
- "Ġ;": 2162,
- "Ġ;)": 35540,
- "Ġ;;": 36792,
- "Ġ<": 1279,
- "Ġ
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/aws/aws.py b/api/core/tools/provider/builtin/aws/aws.py
deleted file mode 100644
index f81b5dbd27d17c..00000000000000
--- a/api/core/tools/provider/builtin/aws/aws.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.aws.tools.sagemaker_text_rerank import SageMakerReRankTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SageMakerProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- SageMakerReRankTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "sagemaker_endpoint": "",
- "query": "misaka mikoto",
- "candidate_texts": "hello$$$hello world",
- "topk": 5,
- "aws_region": "",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/aws/aws.yaml b/api/core/tools/provider/builtin/aws/aws.yaml
deleted file mode 100644
index 847c6824a53df6..00000000000000
--- a/api/core/tools/provider/builtin/aws/aws.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: AWS
- name: aws
- label:
- en_US: AWS
- zh_Hans: 亚马逊云科技
- pt_BR: AWS
- description:
- en_US: Services on AWS.
- zh_Hans: 亚马逊云科技的各类服务
- pt_BR: Services on AWS.
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py b/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py
deleted file mode 100644
index b224ff5258c879..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import json
-import logging
-from typing import Any, Union
-
-import boto3 # type: ignore
-from botocore.exceptions import BotoCoreError # type: ignore
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-
-class GuardrailParameters(BaseModel):
- guardrail_id: str = Field(..., description="The identifier of the guardrail")
- guardrail_version: str = Field(..., description="The version of the guardrail")
- source: str = Field(..., description="The source of the content")
- text: str = Field(..., description="The text to apply the guardrail to")
- aws_region: str = Field(..., description="AWS region for the Bedrock client")
-
-
-class ApplyGuardrailTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the ApplyGuardrail tool
- """
- try:
- # Validate and parse input parameters
- params = GuardrailParameters(**tool_parameters)
-
- # Initialize AWS client
- bedrock_client = boto3.client("bedrock-runtime", region_name=params.aws_region)
-
- # Apply guardrail
- response = bedrock_client.apply_guardrail(
- guardrailIdentifier=params.guardrail_id,
- guardrailVersion=params.guardrail_version,
- source=params.source,
- content=[{"text": {"text": params.text}}],
- )
-
- logger.info(f"Raw response from AWS: {json.dumps(response, indent=2)}")
-
- # Check for empty response
- if not response:
- return self.create_text_message(text="Received empty response from AWS Bedrock.")
-
- # Process the result
- action = response.get("action", "No action specified")
- outputs = response.get("outputs", [])
- output = outputs[0].get("text", "No output received") if outputs else "No output received"
- assessments = response.get("assessments", [])
-
- # Format assessments
- formatted_assessments = []
- for assessment in assessments:
- for policy_type, policy_data in assessment.items():
- if isinstance(policy_data, dict) and "topics" in policy_data:
- for topic in policy_data["topics"]:
- formatted_assessments.append(
- f"Policy: {policy_type}, Topic: {topic['name']}, Type: {topic['type']},"
- f" Action: {topic['action']}"
- )
- else:
- formatted_assessments.append(f"Policy: {policy_type}, Data: {policy_data}")
-
- result = f"Action: {action}\n "
- result += f"Output: {output}\n "
- if formatted_assessments:
- result += "Assessments:\n " + "\n ".join(formatted_assessments) + "\n "
- # result += f"Full response: {json.dumps(response, indent=2, ensure_ascii=False)}"
-
- return self.create_text_message(text=result)
-
- except BotoCoreError as e:
- error_message = f"AWS service error: {str(e)}"
- logger.error(error_message, exc_info=True)
- return self.create_text_message(text=error_message)
- except json.JSONDecodeError as e:
- error_message = f"JSON parsing error: {str(e)}"
- logger.error(error_message, exc_info=True)
- return self.create_text_message(text=error_message)
- except Exception as e:
- error_message = f"An unexpected error occurred: {str(e)}"
- logger.error(error_message, exc_info=True)
- return self.create_text_message(text=error_message)
diff --git a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.yaml b/api/core/tools/provider/builtin/aws/tools/apply_guardrail.yaml
deleted file mode 100644
index 66044e4ea84fe1..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
-identity:
- name: apply_guardrail
- author: AWS
- label:
- en_US: Content Moderation Guardrails
- zh_Hans: 内容审查护栏
-description:
- human:
- en_US: Content Moderation Guardrails utilizes the ApplyGuardrail API, a feature of Guardrails for Amazon Bedrock. This API is capable of evaluating input prompts and model responses for all Foundation Models (FMs), including those on Amazon Bedrock, custom FMs, and third-party FMs. By implementing this functionality, organizations can achieve centralized governance across all their generative AI applications, thereby enhancing control and consistency in content moderation.
- zh_Hans: 内容审查护栏采用 Guardrails for Amazon Bedrock 功能中的 ApplyGuardrail API 。ApplyGuardrail 可以评估所有基础模型(FMs)的输入提示和模型响应,包括 Amazon Bedrock 上的 FMs、自定义 FMs 和第三方 FMs。通过实施这一功能, 组织可以在所有生成式 AI 应用程序中实现集中化的治理,从而增强内容审核的控制力和一致性。
- llm: Content Moderation Guardrails utilizes the ApplyGuardrail API, a feature of Guardrails for Amazon Bedrock. This API is capable of evaluating input prompts and model responses for all Foundation Models (FMs), including those on Amazon Bedrock, custom FMs, and third-party FMs. By implementing this functionality, organizations can achieve centralized governance across all their generative AI applications, thereby enhancing control and consistency in content moderation.
-parameters:
- - name: guardrail_id
- type: string
- required: true
- label:
- en_US: Guardrail ID
- zh_Hans: Guardrail ID
- human_description:
- en_US: Please enter the ID of the Guardrail that has already been created on Amazon Bedrock, for example 'qk5nk0e4b77b'.
- zh_Hans: 请输入已经在 Amazon Bedrock 上创建好的 Guardrail ID, 例如 'qk5nk0e4b77b'.
- llm_description: Please enter the ID of the Guardrail that has already been created on Amazon Bedrock, for example 'qk5nk0e4b77b'.
- form: form
- - name: guardrail_version
- type: string
- required: true
- label:
- en_US: Guardrail Version Number
- zh_Hans: Guardrail 版本号码
- human_description:
- en_US: Please enter the published version of the Guardrail ID that has already been created on Amazon Bedrock. This is typically a version number, such as 2.
- zh_Hans: 请输入已经在Amazon Bedrock 上创建好的Guardrail ID发布的版本, 通常使用版本号, 例如2.
- llm_description: Please enter the published version of the Guardrail ID that has already been created on Amazon Bedrock. This is typically a version number, such as 2.
- form: form
- - name: source
- type: string
- required: true
- label:
- en_US: Content Source (INPUT or OUTPUT)
- zh_Hans: 内容来源 (INPUT or OUTPUT)
- human_description:
- en_US: The source of data used in the request to apply the guardrail. Valid Values "INPUT | OUTPUT"
- zh_Hans: 用于应用护栏的请求中所使用的数据来源。有效值为 "INPUT | OUTPUT"
- llm_description: The source of data used in the request to apply the guardrail. Valid Values "INPUT | OUTPUT"
- form: form
- - name: text
- type: string
- required: true
- label:
- en_US: Content to be reviewed
- zh_Hans: 待审查内容
- human_description:
- en_US: The content used for requesting guardrail review, which can be either user input or LLM output.
- zh_Hans: 用于请求护栏审查的内容,可以是用户输入或 LLM 输出。
- llm_description: The content used for requesting guardrail review, which can be either user input or LLM output.
- form: llm
- - name: aws_region
- type: string
- required: true
- label:
- en_US: AWS Region
- zh_Hans: AWS 区域
- human_description:
- en_US: Please enter the AWS region for the Bedrock client, for example 'us-east-1'.
- zh_Hans: 请输入 Bedrock 客户端的 AWS 区域,例如 'us-east-1'。
- llm_description: Please enter the AWS region for the Bedrock client, for example 'us-east-1'.
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py
deleted file mode 100644
index 19e7bfa76eb844..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import json
-import operator
-from typing import Any, Optional, Union
-
-import boto3
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BedrockRetrieveTool(BuiltinTool):
- bedrock_client: Any = None
- knowledge_base_id: str = None
- topk: int = None
-
- def _bedrock_retrieve(
- self,
- query_input: str,
- knowledge_base_id: str,
- num_results: int,
- search_type: str,
- rerank_model_id: str,
- metadata_filter: Optional[dict] = None,
- ):
- try:
- retrieval_query = {"text": query_input}
-
- if search_type not in ["HYBRID", "SEMANTIC"]:
- raise RuntimeException("search_type should be HYBRID or SEMANTIC")
-
- retrieval_configuration = {
- "vectorSearchConfiguration": {"numberOfResults": num_results, "overrideSearchType": search_type}
- }
-
- if rerank_model_id != "default":
- model_for_rerank_arn = f"arn:aws:bedrock:us-west-2::foundation-model/{rerank_model_id}"
- rerankingConfiguration = {
- "bedrockRerankingConfiguration": {
- "numberOfRerankedResults": num_results,
- "modelConfiguration": {"modelArn": model_for_rerank_arn},
- },
- "type": "BEDROCK_RERANKING_MODEL",
- }
-
- retrieval_configuration["vectorSearchConfiguration"]["rerankingConfiguration"] = rerankingConfiguration
- retrieval_configuration["vectorSearchConfiguration"]["numberOfResults"] = num_results * 5
-
- # 如果有元数据过滤条件,则添加到检索配置中
- if metadata_filter:
- retrieval_configuration["vectorSearchConfiguration"]["filter"] = metadata_filter
-
- response = self.bedrock_client.retrieve(
- knowledgeBaseId=knowledge_base_id,
- retrievalQuery=retrieval_query,
- retrievalConfiguration=retrieval_configuration,
- )
-
- results = []
- for result in response.get("retrievalResults", []):
- results.append(
- {
- "content": result.get("content", {}).get("text", ""),
- "score": result.get("score", 0.0),
- "metadata": result.get("metadata", {}),
- }
- )
-
- return results
- except Exception as e:
- raise Exception(f"Error retrieving from knowledge base: {str(e)}")
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- line = 0
- # Initialize Bedrock client if not already initialized
- if not self.bedrock_client:
- aws_region = tool_parameters.get("aws_region")
- aws_access_key_id = tool_parameters.get("aws_access_key_id")
- aws_secret_access_key = tool_parameters.get("aws_secret_access_key")
-
- client_kwargs = {"service_name": "bedrock-agent-runtime", "region_name": aws_region or None}
-
- # Only add credentials if both access key and secret key are provided
- if aws_access_key_id and aws_secret_access_key:
- client_kwargs.update(
- {"aws_access_key_id": aws_access_key_id, "aws_secret_access_key": aws_secret_access_key}
- )
-
- self.bedrock_client = boto3.client(**client_kwargs)
- except Exception as e:
- return self.create_text_message(f"Failed to initialize Bedrock client: {str(e)}")
-
- try:
- line = 1
- if not self.knowledge_base_id:
- self.knowledge_base_id = tool_parameters.get("knowledge_base_id")
- if not self.knowledge_base_id:
- return self.create_text_message("Please provide knowledge_base_id")
-
- line = 2
- if not self.topk:
- self.topk = tool_parameters.get("topk", 5)
-
- line = 3
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Please input query")
-
- # 获取元数据过滤条件(如果存在)
- metadata_filter_str = tool_parameters.get("metadata_filter")
- metadata_filter = json.loads(metadata_filter_str) if metadata_filter_str else None
-
- search_type = tool_parameters.get("search_type")
- rerank_model_id = tool_parameters.get("rerank_model_id")
-
- line = 4
- retrieved_docs = self._bedrock_retrieve(
- query_input=query,
- knowledge_base_id=self.knowledge_base_id,
- num_results=self.topk,
- search_type=search_type,
- rerank_model_id=rerank_model_id,
- metadata_filter=metadata_filter,
- )
-
- line = 5
- # Sort results by score in descending order
- sorted_docs = sorted(retrieved_docs, key=operator.itemgetter("score"), reverse=True)
-
- line = 6
- result_type = tool_parameters.get("result_type")
- if result_type == "json":
- return [self.create_json_message(res) for res in sorted_docs]
- else:
- text = ""
- for i, res in enumerate(sorted_docs):
- text += f"{i + 1}: {res['content']}\n"
- return self.create_text_message(text)
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}, line : {line}")
-
- def validate_parameters(self, parameters: dict[str, Any]) -> None:
- """
- Validate the parameters
- """
- if not parameters.get("knowledge_base_id"):
- raise ValueError("knowledge_base_id is required")
-
- if not parameters.get("query"):
- raise ValueError("query is required")
-
- metadata_filter_str = parameters.get("metadata_filter")
- if metadata_filter_str and not isinstance(json.loads(metadata_filter_str), dict):
- raise ValueError("metadata_filter must be a valid JSON object")
diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml
deleted file mode 100644
index d0c520b39298d1..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml
+++ /dev/null
@@ -1,179 +0,0 @@
-identity:
- name: bedrock_retrieve
- author: AWS
- label:
- en_US: Bedrock Retrieve
- zh_Hans: Bedrock检索
- pt_BR: Bedrock Retrieve
- icon: icon.svg
-
-description:
- human:
- en_US: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base. You can find deploy instructions on Github Repo - https://github.com/aws-samples/dify-aws-tool
- zh_Hans: Amazon Bedrock知识库检索工具, 请参考 Github Repo - https://github.com/aws-samples/dify-aws-tool上的部署说明
- pt_BR: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base.
- llm: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base. You can find deploy instructions on Github Repo - https://github.com/aws-samples/dify-aws-tool
-
-parameters:
- - name: aws_region
- type: string
- required: false
- label:
- en_US: AWS Region
- zh_Hans: AWS区域
- human_description:
- en_US: AWS region for the Bedrock service
- zh_Hans: Bedrock服务的AWS区域
- form: form
-
- - name: aws_access_key_id
- type: string
- required: false
- label:
- en_US: AWS Access Key ID
- zh_Hans: AWS访问密钥ID
- human_description:
- en_US: AWS access key ID for authentication (optional)
- zh_Hans: 用于身份验证的AWS访问密钥ID(可选)
- form: form
-
- - name: aws_secret_access_key
- type: string
- required: false
- label:
- en_US: AWS Secret Access Key
- zh_Hans: AWS秘密访问密钥
- human_description:
- en_US: AWS secret access key for authentication (optional)
- zh_Hans: 用于身份验证的AWS秘密访问密钥(可选)
- form: form
-
- - name: result_type
- type: select
- required: true
- label:
- en_US: result type
- zh_Hans: 结果类型
- human_description:
- en_US: return a list of json or texts
- zh_Hans: 返回一个列表,内容是json还是纯文本
- default: text
- options:
- - value: json
- label:
- en_US: JSON
- zh_Hans: JSON
- - value: text
- label:
- en_US: Text
- zh_Hans: 文本
- form: form
-
- - name: knowledge_base_id
- type: string
- required: true
- label:
- en_US: Bedrock Knowledge Base ID
- zh_Hans: Bedrock知识库ID
- pt_BR: Bedrock Knowledge Base ID
- human_description:
- en_US: ID of the Bedrock Knowledge Base to retrieve from
- zh_Hans: 用于检索的Bedrock知识库ID
- pt_BR: ID of the Bedrock Knowledge Base to retrieve from
- llm_description: ID of the Bedrock Knowledge Base to retrieve from
- form: form
-
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: The search query to retrieve relevant information
- zh_Hans: 用于检索相关信息的查询语句
- pt_BR: The search query to retrieve relevant information
- llm_description: The search query to retrieve relevant information
- form: llm
-
- - name: topk
- type: number
- required: false
- form: form
- label:
- en_US: Limit for results count
- zh_Hans: 返回结果数量限制
- pt_BR: Limit for results count
- human_description:
- en_US: Maximum number of results to return
- zh_Hans: 最大返回结果数量
- pt_BR: Maximum number of results to return
- min: 1
- max: 10
- default: 5
-
- - name: search_type
- type: select
- required: false
- label:
- en_US: search type
- zh_Hans: 搜索类型
- pt_BR: search type
- human_description:
- en_US: search type
- zh_Hans: 搜索类型
- pt_BR: search type
- llm_description: search type
- default: SEMANTIC
- options:
- - value: SEMANTIC
- label:
- en_US: SEMANTIC
- zh_Hans: 语义搜索
- - value: HYBRID
- label:
- en_US: HYBRID
- zh_Hans: 混合搜索
- form: form
-
- - name: rerank_model_id
- type: select
- required: false
- label:
- en_US: rerank model id
- zh_Hans: 重拍模型ID
- pt_BR: rerank model id
- human_description:
- en_US: rerank model id
- zh_Hans: 重拍模型ID
- pt_BR: rerank model id
- llm_description: rerank model id
- default: default
- options:
- - value: default
- label:
- en_US: default
- zh_Hans: 默认
- - value: cohere.rerank-v3-5:0
- label:
- en_US: cohere.rerank-v3-5:0
- zh_Hans: cohere.rerank-v3-5:0
- - value: amazon.rerank-v1:0
- label:
- en_US: amazon.rerank-v1:0
- zh_Hans: amazon.rerank-v1:0
- form: form
-
- - name: metadata_filter # Additional parameter for metadata filtering
- type: string # String type, expects JSON-formatted filter conditions
- required: false # Optional field - can be omitted
- label:
- en_US: Metadata Filter
- zh_Hans: 元数据过滤器
- pt_BR: Metadata Filter
- human_description:
- en_US: 'JSON formatted filter conditions for metadata (e.g., {"greaterThan": {"key: "aaa", "value": 10}})'
- zh_Hans: '元数据的JSON格式过滤条件(例如,{{"greaterThan": {"key: "aaa", "value": 10}})'
- pt_BR: 'JSON formatted filter conditions for metadata (e.g., {"greaterThan": {"key: "aaa", "value": 10}})'
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.py b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.py
deleted file mode 100644
index 3717aac344fe5d..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import json
-from typing import Any
-
-import boto3
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BedrockRetrieveAndGenerateTool(BuiltinTool):
- bedrock_client: Any = None
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> ToolInvokeMessage:
- try:
- # Initialize Bedrock client if not already initialized
- if not self.bedrock_client:
- aws_region = tool_parameters.get("aws_region")
- aws_access_key_id = tool_parameters.get("aws_access_key_id")
- aws_secret_access_key = tool_parameters.get("aws_secret_access_key")
-
- client_kwargs = {"service_name": "bedrock-agent-runtime", "region_name": aws_region or None}
-
- # Only add credentials if both access key and secret key are provided
- if aws_access_key_id and aws_secret_access_key:
- client_kwargs.update(
- {"aws_access_key_id": aws_access_key_id, "aws_secret_access_key": aws_secret_access_key}
- )
-
- self.bedrock_client = boto3.client(**client_kwargs)
- except Exception as e:
- return self.create_text_message(f"Failed to initialize Bedrock client: {str(e)}")
-
- try:
- request_config = {}
-
- # Set input configuration
- input_text = tool_parameters.get("input")
- if input_text:
- request_config["input"] = {"text": input_text}
-
- # Build retrieve and generate configuration
- config_type = tool_parameters.get("type")
- retrieve_generate_config = {"type": config_type}
-
- # Add configuration based on type
- if config_type == "KNOWLEDGE_BASE":
- kb_config_str = tool_parameters.get("knowledge_base_configuration")
- kb_config = json.loads(kb_config_str) if kb_config_str else None
- retrieve_generate_config["knowledgeBaseConfiguration"] = kb_config
- else: # EXTERNAL_SOURCES
- es_config_str = tool_parameters.get("external_sources_configuration")
- es_config = json.loads(kb_config_str) if es_config_str else None
- retrieve_generate_config["externalSourcesConfiguration"] = es_config
-
- request_config["retrieveAndGenerateConfiguration"] = retrieve_generate_config
-
- # Parse session configuration
- session_config_str = tool_parameters.get("session_configuration")
- session_config = json.loads(session_config_str) if session_config_str else None
- if session_config:
- request_config["sessionConfiguration"] = session_config
-
- # Add session ID if provided
- session_id = tool_parameters.get("session_id")
- if session_id:
- request_config["sessionId"] = session_id
-
- # Send request
- response = self.bedrock_client.retrieve_and_generate(**request_config)
-
- # Process response
- result = {"output": response.get("output", {}).get("text", ""), "citations": []}
-
- # Process citations
- for citation in response.get("citations", []):
- citation_info = {
- "text": citation.get("generatedResponsePart", {}).get("textResponsePart", {}).get("text", ""),
- "references": [],
- }
-
- for ref in citation.get("retrievedReferences", []):
- reference = {
- "content": ref.get("content", {}).get("text", ""),
- "metadata": ref.get("metadata", {}),
- "location": None,
- }
-
- location = ref.get("location", {})
- if location.get("type") == "S3":
- reference["location"] = location.get("s3Location", {}).get("uri")
-
- citation_info["references"].append(reference)
-
- result["citations"].append(citation_info)
- result_type = tool_parameters.get("result_type")
- if result_type == "json":
- return self.create_json_message(result)
- elif result_type == "text-with-citations":
- return self.create_text_message(result)
- else:
- return self.create_text_message(result.get("output"))
- except json.JSONDecodeError as e:
- return self.create_text_message(f"Invalid JSON format: {str(e)}")
- except Exception as e:
- return self.create_text_message(f"Tool invocation error: {str(e)}")
-
- def validate_parameters(self, parameters: dict[str, Any]) -> None:
- """Validate the parameters"""
- # Validate required parameters
- if not parameters.get("input"):
- raise ValueError("input is required")
- if not parameters.get("type"):
- raise ValueError("type is required")
-
- # Validate JSON configurations
- json_configs = ["knowledge_base_configuration", "external_sources_configuration", "session_configuration"]
- for config in json_configs:
- if config_value := parameters.get(config):
- try:
- json.loads(config_value)
- except json.JSONDecodeError:
- raise ValueError(f"{config} must be a valid JSON string")
-
- # Validate configuration type
- config_type = parameters.get("type")
- if config_type not in ["KNOWLEDGE_BASE", "EXTERNAL_SOURCES"]:
- raise ValueError("type must be either KNOWLEDGE_BASE or EXTERNAL_SOURCES")
-
- # Validate type-specific configuration
- if config_type == "KNOWLEDGE_BASE" and not parameters.get("knowledge_base_configuration"):
- raise ValueError("knowledge_base_configuration is required when type is KNOWLEDGE_BASE")
- elif config_type == "EXTERNAL_SOURCES" and not parameters.get("external_sources_configuration"):
- raise ValueError("external_sources_configuration is required when type is EXTERNAL_SOURCES")
diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.yaml b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.yaml
deleted file mode 100644
index 68f418fc5caa76..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve_and_generate.yaml
+++ /dev/null
@@ -1,148 +0,0 @@
-identity:
- name: bedrock_retrieve_and_generate
- author: AWS
- label:
- en_US: Bedrock Retrieve and Generate
- zh_Hans: Bedrock检索和生成
- icon: icon.svg
-
-description:
- human:
- en_US: "This is an advanced usage of Bedrock Retrieve. Please refer to the API documentation for detailed parameters and paste them into the corresponding Knowledge Base Configuration or External Sources Configuration"
- zh_Hans: "这个工具为Bedrock Retrieve的高级用法,请参考API设置详细的参数,并粘贴到对应的知识库配置或者外部源配置"
- llm: A tool for retrieving and generating information using Amazon Bedrock Knowledge Base
-
-parameters:
- - name: aws_region
- type: string
- required: false
- label:
- en_US: AWS Region
- zh_Hans: AWS区域
- human_description:
- en_US: AWS region for the Bedrock service
- zh_Hans: Bedrock服务的AWS区域
- form: form
-
- - name: aws_access_key_id
- type: string
- required: false
- label:
- en_US: AWS Access Key ID
- zh_Hans: AWS访问密钥ID
- human_description:
- en_US: AWS access key ID for authentication (optional)
- zh_Hans: 用于身份验证的AWS访问密钥ID(可选)
- form: form
-
- - name: aws_secret_access_key
- type: string
- required: false
- label:
- en_US: AWS Secret Access Key
- zh_Hans: AWS秘密访问密钥
- human_description:
- en_US: AWS secret access key for authentication (optional)
- zh_Hans: 用于身份验证的AWS秘密访问密钥(可选)
- form: form
-
- - name: result_type
- type: select
- required: true
- label:
- en_US: result type
- zh_Hans: 结果类型
- human_description:
- en_US: return a list of json or texts
- zh_Hans: 返回一个列表,内容是json还是纯文本
- default: text
- options:
- - value: json
- label:
- en_US: JSON
- zh_Hans: JSON
- - value: text
- label:
- en_US: Text
- zh_Hans: 文本
- - value: text-with-citations
- label:
- en_US: Text With Citations
- zh_Hans: 文本(包含引用)
- form: form
-
- - name: input
- type: string
- required: true
- label:
- en_US: Input Text
- zh_Hans: 输入文本
- human_description:
- en_US: The text query to retrieve information
- zh_Hans: 用于检索信息的文本查询
- form: llm
-
- - name: type
- type: select
- required: true
- label:
- en_US: Configuration Type
- zh_Hans: 配置类型
- human_description:
- en_US: Type of retrieve and generate configuration
- zh_Hans: 检索和生成配置的类型
- options:
- - value: KNOWLEDGE_BASE
- label:
- en_US: Knowledge Base
- zh_Hans: 知识库
- - value: EXTERNAL_SOURCES
- label:
- en_US: External Sources
- zh_Hans: 外部源
- form: form
-
- - name: knowledge_base_configuration
- type: string
- required: false
- label:
- en_US: Knowledge Base Configuration
- zh_Hans: 知识库配置
- human_description:
- en_US: Please refer to @https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/retrieve_and_generate.html#retrieve-and-generate for complete parameters and paste them here
- zh_Hans: 请参考 https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/retrieve_and_generate.html#retrieve-and-generate 配置完整的参数并粘贴到这里
- form: form
-
- - name: external_sources_configuration
- type: string
- required: false
- label:
- en_US: External Sources Configuration
- zh_Hans: 外部源配置
- human_description:
- en_US: Please refer to https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/retrieve_and_generate.html#retrieve-and-generate for complete parameters and paste them here
- zh_Hans: 请参考 https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/retrieve_and_generate.html#retrieve-and-generate 配置完整的参数并粘贴到这里
- form: form
-
- - name: session_configuration
- type: string
- required: false
- label:
- en_US: Session Configuration
- zh_Hans: 会话配置
- human_description:
- en_US: JSON formatted session configuration
- zh_Hans: JSON格式的会话配置
- default: ""
- form: form
-
- - name: session_id
- type: string
- required: false
- label:
- en_US: Session ID
- zh_Hans: 会话ID
- human_description:
- en_US: Session ID for continuous conversations
- zh_Hans: 用于连续对话的会话ID
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py b/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py
deleted file mode 100644
index b6d16d2759c30e..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import json
-from typing import Any, Union
-
-import boto3 # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class LambdaTranslateUtilsTool(BuiltinTool):
- lambda_client: Any = None
-
- def _invoke_lambda(self, text_content, src_lang, dest_lang, model_id, dictionary_name, request_type, lambda_name):
- msg = {
- "src_contents": [text_content],
- "src_lang": src_lang,
- "dest_lang": dest_lang,
- "dictionary_id": dictionary_name,
- "request_type": request_type,
- "model_id": model_id,
- }
-
- invoke_response = self.lambda_client.invoke(
- FunctionName=lambda_name, InvocationType="RequestResponse", Payload=json.dumps(msg)
- )
- response_body = invoke_response["Payload"]
-
- response_str = response_body.read().decode("unicode_escape")
-
- return response_str
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- line = 0
- try:
- if not self.lambda_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.lambda_client = boto3.client("lambda", region_name=aws_region)
- else:
- self.lambda_client = boto3.client("lambda")
-
- line = 1
- text_content = tool_parameters.get("text_content", "")
- if not text_content:
- return self.create_text_message("Please input text_content")
-
- line = 2
- src_lang = tool_parameters.get("src_lang", "")
- if not src_lang:
- return self.create_text_message("Please input src_lang")
-
- line = 3
- dest_lang = tool_parameters.get("dest_lang", "")
- if not dest_lang:
- return self.create_text_message("Please input dest_lang")
-
- line = 4
- lambda_name = tool_parameters.get("lambda_name", "")
- if not lambda_name:
- return self.create_text_message("Please input lambda_name")
-
- line = 5
- request_type = tool_parameters.get("request_type", "")
- if not request_type:
- return self.create_text_message("Please input request_type")
-
- line = 6
- model_id = tool_parameters.get("model_id", "")
- if not model_id:
- return self.create_text_message("Please input model_id")
-
- line = 7
- dictionary_name = tool_parameters.get("dictionary_name", "")
- if not dictionary_name:
- return self.create_text_message("Please input dictionary_name")
-
- result = self._invoke_lambda(
- text_content, src_lang, dest_lang, model_id, dictionary_name, request_type, lambda_name
- )
-
- return self.create_text_message(text=result)
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}, line : {line}")
diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.yaml b/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.yaml
deleted file mode 100644
index 646602fcd6c245..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.yaml
+++ /dev/null
@@ -1,134 +0,0 @@
-identity:
- name: lambda_translate_utils
- author: AWS
- label:
- en_US: TranslateTool
- zh_Hans: 翻译工具
- pt_BR: TranslateTool
- icon: icon.svg
-description:
- human:
- en_US: A util tools for LLM translation, extra deployment is needed on AWS. Please refer Github Repo - https://github.com/aws-samples/rag-based-translation-with-dynamodb-and-bedrock
- zh_Hans: 大语言模型翻译工具(专词映射获取),需要在AWS上进行额外部署,可参考Github Repo - https://github.com/aws-samples/rag-based-translation-with-dynamodb-and-bedrock
- pt_BR: A util tools for LLM translation, specific Lambda Function deployment is needed on AWS. Please refer Github Repo - https://github.com/aws-samples/rag-based-translation-with-dynamodb-and-bedrock
- llm: A util tools for translation.
-parameters:
- - name: text_content
- type: string
- required: true
- label:
- en_US: source content for translation
- zh_Hans: 待翻译原文
- pt_BR: source content for translation
- human_description:
- en_US: source content for translation
- zh_Hans: 待翻译原文
- pt_BR: source content for translation
- llm_description: source content for translation
- form: llm
- - name: src_lang
- type: string
- required: true
- label:
- en_US: source language code
- zh_Hans: 原文语言代号
- pt_BR: source language code
- human_description:
- en_US: source language code
- zh_Hans: 原文语言代号
- pt_BR: source language code
- llm_description: source language code
- form: llm
- - name: dest_lang
- type: string
- required: true
- label:
- en_US: target language code
- zh_Hans: 目标语言代号
- pt_BR: target language code
- human_description:
- en_US: target language code
- zh_Hans: 目标语言代号
- pt_BR: target language code
- llm_description: target language code
- form: llm
- - name: aws_region
- type: string
- required: false
- label:
- en_US: region of Lambda
- zh_Hans: Lambda 所在的region
- pt_BR: region of Lambda
- human_description:
- en_US: region of Lambda
- zh_Hans: Lambda 所在的region
- pt_BR: region of Lambda
- llm_description: region of Lambda
- form: form
- - name: model_id
- type: string
- required: false
- default: anthropic.claude-3-sonnet-20240229-v1:0
- label:
- en_US: LLM model_id in bedrock
- zh_Hans: bedrock上的大语言模型model_id
- pt_BR: LLM model_id in bedrock
- human_description:
- en_US: LLM model_id in bedrock
- zh_Hans: bedrock上的大语言模型model_id
- pt_BR: LLM model_id in bedrock
- llm_description: LLM model_id in bedrock
- form: form
- - name: dictionary_name
- type: string
- required: false
- label:
- en_US: dictionary name for term mapping
- zh_Hans: 专词映射表名称
- pt_BR: dictionary name for term mapping
- human_description:
- en_US: dictionary name for term mapping
- zh_Hans: 专词映射表名称
- pt_BR: dictionary name for term mapping
- llm_description: dictionary name for term mapping
- form: form
- - name: request_type
- type: select
- required: false
- label:
- en_US: request type
- zh_Hans: 请求类型
- pt_BR: request type
- human_description:
- en_US: request type
- zh_Hans: 请求类型
- pt_BR: request type
- default: term_mapping
- options:
- - value: term_mapping
- label:
- en_US: term_mapping
- zh_Hans: 专词映射
- - value: segment_only
- label:
- en_US: segment_only
- zh_Hans: 仅切词
- - value: translate
- label:
- en_US: translate
- zh_Hans: 翻译内容
- form: form
- - name: lambda_name
- type: string
- default: "translate_tool"
- required: true
- label:
- en_US: AWS Lambda for term mapping retrieval
- zh_Hans: 专词召回映射 - AWS Lambda
- pt_BR: lambda name for term mapping retrieval
- human_description:
- en_US: AWS Lambda for term mapping retrieval
- zh_Hans: 专词召回映射 - AWS Lambda
- pt_BR: AWS Lambda for term mapping retrieval
- llm_description: AWS Lambda for term mapping retrieval
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py b/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py
deleted file mode 100644
index 01bc596346c231..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import json
-import logging
-from typing import Any, Union
-
-import boto3 # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-console_handler = logging.StreamHandler()
-logger.addHandler(console_handler)
-
-
-class LambdaYamlToJsonTool(BuiltinTool):
- lambda_client: Any = None
-
- def _invoke_lambda(self, lambda_name: str, yaml_content: str) -> str:
- msg = {"body": yaml_content}
- logger.info(json.dumps(msg))
-
- invoke_response = self.lambda_client.invoke(
- FunctionName=lambda_name, InvocationType="RequestResponse", Payload=json.dumps(msg)
- )
- response_body = invoke_response["Payload"]
-
- response_str = response_body.read().decode("utf-8")
- resp_json = json.loads(response_str)
-
- logger.info(resp_json)
- if resp_json["statusCode"] != 200:
- raise Exception(f"Invalid status code: {response_str}")
-
- return resp_json["body"]
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- if not self.lambda_client:
- aws_region = tool_parameters.get("aws_region") # todo: move aws_region out, and update client region
- if aws_region:
- self.lambda_client = boto3.client("lambda", region_name=aws_region)
- else:
- self.lambda_client = boto3.client("lambda")
-
- yaml_content = tool_parameters.get("yaml_content", "")
- if not yaml_content:
- return self.create_text_message("Please input yaml_content")
-
- lambda_name = tool_parameters.get("lambda_name", "")
- if not lambda_name:
- return self.create_text_message("Please input lambda_name")
- logger.debug(f"{json.dumps(tool_parameters, indent=2, ensure_ascii=False)}")
-
- result = self._invoke_lambda(lambda_name, yaml_content)
- logger.debug(result)
-
- return self.create_text_message(result)
- except Exception as e:
- return self.create_text_message(f"Exception: {str(e)}")
-
- console_handler.flush()
diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.yaml b/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.yaml
deleted file mode 100644
index 919c285348df83..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.yaml
+++ /dev/null
@@ -1,53 +0,0 @@
-identity:
- name: lambda_yaml_to_json
- author: AWS
- label:
- en_US: LambdaYamlToJson
- zh_Hans: LambdaYamlToJson
- pt_BR: LambdaYamlToJson
- icon: icon.svg
-description:
- human:
- en_US: A tool to convert yaml to json using AWS Lambda.
- zh_Hans: 将 YAML 转为 JSON 的工具(通过AWS Lambda)。
- pt_BR: A tool to convert yaml to json using AWS Lambda.
- llm: A tool to convert yaml to json.
-parameters:
- - name: yaml_content
- type: string
- required: true
- label:
- en_US: YAML content to convert for
- zh_Hans: YAML 内容
- pt_BR: YAML content to convert for
- human_description:
- en_US: YAML content to convert for
- zh_Hans: YAML 内容
- pt_BR: YAML content to convert for
- llm_description: YAML content to convert for
- form: llm
- - name: aws_region
- type: string
- required: false
- label:
- en_US: region of lambda
- zh_Hans: Lambda 所在的region
- pt_BR: region of lambda
- human_description:
- en_US: region of lambda
- zh_Hans: Lambda 所在的region
- pt_BR: region of lambda
- llm_description: region of lambda
- form: form
- - name: lambda_name
- type: string
- required: false
- label:
- en_US: name of lambda
- zh_Hans: Lambda 名称
- pt_BR: name of lambda
- human_description:
- en_US: name of lambda
- zh_Hans: Lambda 名称
- pt_BR: name of lambda
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/nova_canvas.py b/api/core/tools/provider/builtin/aws/tools/nova_canvas.py
deleted file mode 100644
index 954dbe35a4a784..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/nova_canvas.py
+++ /dev/null
@@ -1,357 +0,0 @@
-import base64
-import json
-import logging
-import re
-from datetime import datetime
-from typing import Any, Union
-from urllib.parse import urlparse
-
-import boto3
-
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
-from core.tools.tool.builtin_tool import BuiltinTool
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-
-class NovaCanvasTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke AWS Bedrock Nova Canvas model for image generation
- """
- # Get common parameters
- prompt = tool_parameters.get("prompt", "")
- image_output_s3uri = tool_parameters.get("image_output_s3uri", "").strip()
- if not prompt:
- return self.create_text_message("Please provide a text prompt for image generation.")
- if not image_output_s3uri or urlparse(image_output_s3uri).scheme != "s3":
- return self.create_text_message("Please provide an valid S3 URI for image output.")
-
- task_type = tool_parameters.get("task_type", "TEXT_IMAGE")
- aws_region = tool_parameters.get("aws_region", "us-east-1")
-
- # Get common image generation config parameters
- width = tool_parameters.get("width", 1024)
- height = tool_parameters.get("height", 1024)
- cfg_scale = tool_parameters.get("cfg_scale", 8.0)
- negative_prompt = tool_parameters.get("negative_prompt", "")
- seed = tool_parameters.get("seed", 0)
- quality = tool_parameters.get("quality", "standard")
-
- # Handle S3 image if provided
- image_input_s3uri = tool_parameters.get("image_input_s3uri", "")
- if task_type != "TEXT_IMAGE":
- if not image_input_s3uri or urlparse(image_input_s3uri).scheme != "s3":
- return self.create_text_message("Please provide a valid S3 URI for image to image generation.")
-
- # Parse S3 URI
- parsed_uri = urlparse(image_input_s3uri)
- bucket = parsed_uri.netloc
- key = parsed_uri.path.lstrip("/")
-
- # Initialize S3 client and download image
- s3_client = boto3.client("s3")
- response = s3_client.get_object(Bucket=bucket, Key=key)
- image_data = response["Body"].read()
-
- # Base64 encode the image
- input_image = base64.b64encode(image_data).decode("utf-8")
-
- try:
- # Initialize Bedrock client
- bedrock = boto3.client(service_name="bedrock-runtime", region_name=aws_region)
-
- # Base image generation config
- image_generation_config = {
- "width": width,
- "height": height,
- "cfgScale": cfg_scale,
- "seed": seed,
- "numberOfImages": 1,
- "quality": quality,
- }
-
- # Prepare request body based on task type
- body = {"imageGenerationConfig": image_generation_config}
-
- if task_type == "TEXT_IMAGE":
- body["taskType"] = "TEXT_IMAGE"
- body["textToImageParams"] = {"text": prompt}
- if negative_prompt:
- body["textToImageParams"]["negativeText"] = negative_prompt
-
- elif task_type == "COLOR_GUIDED_GENERATION":
- colors = tool_parameters.get("colors", "#ff8080-#ffb280-#ffe680-#ffe680")
- if not self._validate_color_string(colors):
- return self.create_text_message("Please provide valid colors in hexadecimal format.")
-
- body["taskType"] = "COLOR_GUIDED_GENERATION"
- body["colorGuidedGenerationParams"] = {
- "colors": colors.split("-"),
- "referenceImage": input_image,
- "text": prompt,
- }
- if negative_prompt:
- body["colorGuidedGenerationParams"]["negativeText"] = negative_prompt
-
- elif task_type == "IMAGE_VARIATION":
- similarity_strength = tool_parameters.get("similarity_strength", 0.5)
-
- body["taskType"] = "IMAGE_VARIATION"
- body["imageVariationParams"] = {
- "images": [input_image],
- "similarityStrength": similarity_strength,
- "text": prompt,
- }
- if negative_prompt:
- body["imageVariationParams"]["negativeText"] = negative_prompt
-
- elif task_type == "INPAINTING":
- mask_prompt = tool_parameters.get("mask_prompt")
- if not mask_prompt:
- return self.create_text_message("Please provide a mask prompt for image inpainting.")
-
- body["taskType"] = "INPAINTING"
- body["inPaintingParams"] = {"image": input_image, "maskPrompt": mask_prompt, "text": prompt}
- if negative_prompt:
- body["inPaintingParams"]["negativeText"] = negative_prompt
-
- elif task_type == "OUTPAINTING":
- mask_prompt = tool_parameters.get("mask_prompt")
- if not mask_prompt:
- return self.create_text_message("Please provide a mask prompt for image outpainting.")
- outpainting_mode = tool_parameters.get("outpainting_mode", "DEFAULT")
-
- body["taskType"] = "OUTPAINTING"
- body["outPaintingParams"] = {
- "image": input_image,
- "maskPrompt": mask_prompt,
- "outPaintingMode": outpainting_mode,
- "text": prompt,
- }
- if negative_prompt:
- body["outPaintingParams"]["negativeText"] = negative_prompt
-
- elif task_type == "BACKGROUND_REMOVAL":
- body["taskType"] = "BACKGROUND_REMOVAL"
- body["backgroundRemovalParams"] = {"image": input_image}
-
- else:
- return self.create_text_message(f"Unsupported task type: {task_type}")
-
- # Call Nova Canvas model
- response = bedrock.invoke_model(
- body=json.dumps(body),
- modelId="amazon.nova-canvas-v1:0",
- accept="application/json",
- contentType="application/json",
- )
-
- # Process response
- response_body = json.loads(response.get("body").read())
- if response_body.get("error"):
- raise Exception(f"Error in model response: {response_body.get('error')}")
- base64_image = response_body.get("images")[0]
-
- # Upload to S3 if image_output_s3uri is provided
- try:
- # Parse S3 URI for output
- parsed_uri = urlparse(image_output_s3uri)
- output_bucket = parsed_uri.netloc
- output_base_path = parsed_uri.path.lstrip("/")
- # Generate filename with timestamp
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- output_key = f"{output_base_path}/canvas-output-{timestamp}.png"
-
- # Initialize S3 client if not already done
- s3_client = boto3.client("s3", region_name=aws_region)
-
- # Decode base64 image and upload to S3
- image_data = base64.b64decode(base64_image)
- s3_client.put_object(Bucket=output_bucket, Key=output_key, Body=image_data, ContentType="image/png")
- logger.info(f"Image uploaded to s3://{output_bucket}/{output_key}")
- except Exception as e:
- logger.exception("Failed to upload image to S3")
- # Return image
- return [
- self.create_text_message(f"Image is available at: s3://{output_bucket}/{output_key}"),
- self.create_blob_message(
- blob=base64.b64decode(base64_image),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- ),
- ]
-
- except Exception as e:
- return self.create_text_message(f"Failed to generate image: {str(e)}")
-
- def _validate_color_string(self, color_string) -> bool:
- color_pattern = r"^#[0-9a-fA-F]{6}(?:-#[0-9a-fA-F]{6})*$"
-
- if re.match(color_pattern, color_string):
- return True
- return False
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- parameters = [
- ToolParameter(
- name="prompt",
- label=I18nObject(en_US="Prompt", zh_Hans="提示词"),
- type=ToolParameter.ToolParameterType.STRING,
- required=True,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="Text description of the image you want to generate or modify",
- zh_Hans="您想要生成或修改的图像的文本描述",
- ),
- llm_description="Describe the image you want to generate or how you want to modify the input image",
- ),
- ToolParameter(
- name="image_input_s3uri",
- label=I18nObject(en_US="Input image s3 uri", zh_Hans="输入图片的s3 uri"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(en_US="Image to be modified", zh_Hans="想要修改的图片"),
- ),
- ToolParameter(
- name="image_output_s3uri",
- label=I18nObject(en_US="Output Image S3 URI", zh_Hans="输出图片的S3 URI目录"),
- type=ToolParameter.ToolParameterType.STRING,
- required=True,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="S3 URI where the generated image should be uploaded", zh_Hans="生成的图像应该上传到的S3 URI"
- ),
- ),
- ToolParameter(
- name="width",
- label=I18nObject(en_US="Width", zh_Hans="宽度"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=1024,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Width of the generated image", zh_Hans="生成图像的宽度"),
- ),
- ToolParameter(
- name="height",
- label=I18nObject(en_US="Height", zh_Hans="高度"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=1024,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Height of the generated image", zh_Hans="生成图像的高度"),
- ),
- ToolParameter(
- name="cfg_scale",
- label=I18nObject(en_US="CFG Scale", zh_Hans="CFG比例"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=8.0,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="How strongly the image should conform to the prompt", zh_Hans="图像应该多大程度上符合提示词"
- ),
- ),
- ToolParameter(
- name="negative_prompt",
- label=I18nObject(en_US="Negative Prompt", zh_Hans="负面提示词"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default="",
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="Things you don't want in the generated image", zh_Hans="您不想在生成的图像中出现的内容"
- ),
- ),
- ToolParameter(
- name="seed",
- label=I18nObject(en_US="Seed", zh_Hans="种子值"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=0,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Random seed for image generation", zh_Hans="图像生成的随机种子"),
- ),
- ToolParameter(
- name="aws_region",
- label=I18nObject(en_US="AWS Region", zh_Hans="AWS 区域"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default="us-east-1",
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="AWS region for Bedrock service", zh_Hans="Bedrock 服务的 AWS 区域"),
- ),
- ToolParameter(
- name="task_type",
- label=I18nObject(en_US="Task Type", zh_Hans="任务类型"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default="TEXT_IMAGE",
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(en_US="Type of image generation task", zh_Hans="图像生成任务的类型"),
- ),
- ToolParameter(
- name="quality",
- label=I18nObject(en_US="Quality", zh_Hans="质量"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default="standard",
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="Quality of the generated image (standard or premium)", zh_Hans="生成图像的质量(标准或高级)"
- ),
- ),
- ToolParameter(
- name="colors",
- label=I18nObject(en_US="Colors", zh_Hans="颜色"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="List of colors for color-guided generation, example: #ff8080-#ffb280-#ffe680-#ffe680",
- zh_Hans="颜色引导生成的颜色列表, 例子: #ff8080-#ffb280-#ffe680-#ffe680",
- ),
- ),
- ToolParameter(
- name="similarity_strength",
- label=I18nObject(en_US="Similarity Strength", zh_Hans="相似度强度"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=0.5,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="How similar the generated image should be to the input image (0.0 to 1.0)",
- zh_Hans="生成的图像应该与输入图像的相似程度(0.0到1.0)",
- ),
- ),
- ToolParameter(
- name="mask_prompt",
- label=I18nObject(en_US="Mask Prompt", zh_Hans="蒙版提示词"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="Text description to generate mask for inpainting/outpainting",
- zh_Hans="用于生成内补绘制/外补绘制蒙版的文本描述",
- ),
- ),
- ToolParameter(
- name="outpainting_mode",
- label=I18nObject(en_US="Outpainting Mode", zh_Hans="外补绘制模式"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default="DEFAULT",
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="Mode for outpainting (DEFAULT or other supported modes)",
- zh_Hans="外补绘制的模式(DEFAULT或其他支持的模式)",
- ),
- ),
- ]
-
- return parameters
diff --git a/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml b/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml
deleted file mode 100644
index a72fd9c8efcce1..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml
+++ /dev/null
@@ -1,175 +0,0 @@
-identity:
- name: nova_canvas
- author: AWS
- label:
- en_US: AWS Bedrock Nova Canvas
- zh_Hans: AWS Bedrock Nova Canvas
- icon: icon.svg
-description:
- human:
- en_US: A tool for generating and modifying images using AWS Bedrock's Nova Canvas model. Supports text-to-image, color-guided generation, image variation, inpainting, outpainting, and background removal. Input parameters reference https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html
- zh_Hans: 使用 AWS Bedrock 的 Nova Canvas 模型生成和修改图像的工具。支持文生图、颜色引导生成、图像变体、内补绘制、外补绘制和背景移除功能, 输入参数参考 https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html。
- llm: Generate or modify images using AWS Bedrock's Nova Canvas model with multiple task types including text-to-image, color-guided generation, image variation, inpainting, outpainting, and background removal.
-parameters:
- - name: task_type
- type: string
- required: false
- default: TEXT_IMAGE
- label:
- en_US: Task Type
- zh_Hans: 任务类型
- human_description:
- en_US: Type of image generation task (TEXT_IMAGE, COLOR_GUIDED_GENERATION, IMAGE_VARIATION, INPAINTING, OUTPAINTING, BACKGROUND_REMOVAL)
- zh_Hans: 图像生成任务的类型(文生图、颜色引导生成、图像变体、内补绘制、外补绘制、背景移除)
- form: llm
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- human_description:
- en_US: Text description of the image you want to generate or modify
- zh_Hans: 您想要生成或修改的图像的文本描述
- llm_description: Describe the image you want to generate or how you want to modify the input image
- form: llm
- - name: image_input_s3uri
- type: string
- required: false
- label:
- en_US: Input image s3 uri
- zh_Hans: 输入图片的s3 uri
- human_description:
- en_US: The input image to modify (required for all modes except TEXT_IMAGE)
- zh_Hans: 要修改的输入图像(除文生图外的所有模式都需要)
- llm_description: The input image you want to modify. Required for all modes except TEXT_IMAGE.
- form: llm
- - name: image_output_s3uri
- type: string
- required: true
- label:
- en_US: Output S3 URI
- zh_Hans: 输出S3 URI
- human_description:
- en_US: The S3 URI where the generated image will be saved. If provided, the image will be uploaded with name format canvas-output-{timestamp}.png
- zh_Hans: 生成的图像将保存到的S3 URI。如果提供,图像将以canvas-output-{timestamp}.png的格式上传
- llm_description: Optional S3 URI where the generated image will be uploaded. The image will be saved with a timestamp-based filename.
- form: form
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: Negative Prompt
- zh_Hans: 负面提示词
- human_description:
- en_US: Things you don't want in the generated image
- zh_Hans: 您不想在生成的图像中出现的内容
- form: llm
- - name: width
- type: number
- required: false
- label:
- en_US: Width
- zh_Hans: 宽度
- human_description:
- en_US: Width of the generated image
- zh_Hans: 生成图像的宽度
- form: form
- default: 1024
- - name: height
- type: number
- required: false
- label:
- en_US: Height
- zh_Hans: 高度
- human_description:
- en_US: Height of the generated image
- zh_Hans: 生成图像的高度
- form: form
- default: 1024
- - name: cfg_scale
- type: number
- required: false
- label:
- en_US: CFG Scale
- zh_Hans: CFG比例
- human_description:
- en_US: How strongly the image should conform to the prompt
- zh_Hans: 图像应该多大程度上符合提示词
- form: form
- default: 8.0
- - name: seed
- type: number
- required: false
- label:
- en_US: Seed
- zh_Hans: 种子值
- human_description:
- en_US: Random seed for image generation
- zh_Hans: 图像生成的随机种子
- form: form
- default: 0
- - name: aws_region
- type: string
- required: false
- default: us-east-1
- label:
- en_US: AWS Region
- zh_Hans: AWS 区域
- human_description:
- en_US: AWS region for Bedrock service
- zh_Hans: Bedrock 服务的 AWS 区域
- form: form
- - name: quality
- type: string
- required: false
- default: standard
- label:
- en_US: Quality
- zh_Hans: 质量
- human_description:
- en_US: Quality of the generated image (standard or premium)
- zh_Hans: 生成图像的质量(标准或高级)
- form: form
- - name: colors
- type: string
- required: false
- label:
- en_US: Colors
- zh_Hans: 颜色
- human_description:
- en_US: List of colors for color-guided generation
- zh_Hans: 颜色引导生成的颜色列表
- form: form
- - name: similarity_strength
- type: number
- required: false
- default: 0.5
- label:
- en_US: Similarity Strength
- zh_Hans: 相似度强度
- human_description:
- en_US: How similar the generated image should be to the input image (0.0 to 1.0)
- zh_Hans: 生成的图像应该与输入图像的相似程度(0.0到1.0)
- form: form
- - name: mask_prompt
- type: string
- required: false
- label:
- en_US: Mask Prompt
- zh_Hans: 蒙版提示词
- human_description:
- en_US: Text description to generate mask for inpainting/outpainting
- zh_Hans: 用于生成内补绘制/外补绘制蒙版的文本描述
- form: llm
- - name: outpainting_mode
- type: string
- required: false
- default: DEFAULT
- label:
- en_US: Outpainting Mode
- zh_Hans: 外补绘制模式
- human_description:
- en_US: Mode for outpainting (DEFAULT or other supported modes)
- zh_Hans: 外补绘制的模式(DEFAULT或其他支持的模式)
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/nova_reel.py b/api/core/tools/provider/builtin/aws/tools/nova_reel.py
deleted file mode 100644
index 848df0b36bfb5a..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/nova_reel.py
+++ /dev/null
@@ -1,370 +0,0 @@
-import base64
-import logging
-import time
-from io import BytesIO
-from typing import Any, Optional, Union
-from urllib.parse import urlparse
-
-import boto3
-from botocore.exceptions import ClientError
-from PIL import Image
-
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
-from core.tools.tool.builtin_tool import BuiltinTool
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-NOVA_REEL_DEFAULT_REGION = "us-east-1"
-NOVA_REEL_DEFAULT_DIMENSION = "1280x720"
-NOVA_REEL_DEFAULT_FPS = 24
-NOVA_REEL_DEFAULT_DURATION = 6
-NOVA_REEL_MODEL_ID = "amazon.nova-reel-v1:0"
-NOVA_REEL_STATUS_CHECK_INTERVAL = 5
-
-# Image requirements
-NOVA_REEL_REQUIRED_IMAGE_WIDTH = 1280
-NOVA_REEL_REQUIRED_IMAGE_HEIGHT = 720
-NOVA_REEL_REQUIRED_IMAGE_MODE = "RGB"
-
-
-class NovaReelTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke AWS Bedrock Nova Reel model for video generation.
-
- Args:
- user_id: The ID of the user making the request
- tool_parameters: Dictionary containing the tool parameters
-
- Returns:
- ToolInvokeMessage containing either the video content or status information
- """
- try:
- # Validate and extract parameters
- params = self._validate_and_extract_parameters(tool_parameters)
- if isinstance(params, ToolInvokeMessage):
- return params
-
- # Initialize AWS clients
- bedrock, s3_client = self._initialize_aws_clients(params["aws_region"])
-
- # Prepare model input
- model_input = self._prepare_model_input(params, s3_client)
- if isinstance(model_input, ToolInvokeMessage):
- return model_input
-
- # Start video generation
- invocation = self._start_video_generation(bedrock, model_input, params["video_output_s3uri"])
- invocation_arn = invocation["invocationArn"]
-
- # Handle async/sync mode
- return self._handle_generation_mode(bedrock, s3_client, invocation_arn, params["async_mode"])
-
- except ClientError as e:
- error_code = e.response.get("Error", {}).get("Code", "Unknown")
- error_message = e.response.get("Error", {}).get("Message", str(e))
- logger.exception(f"AWS API error: {error_code} - {error_message}")
- return self.create_text_message(f"AWS service error: {error_code} - {error_message}")
- except Exception as e:
- logger.error(f"Unexpected error in video generation: {str(e)}", exc_info=True)
- return self.create_text_message(f"Failed to generate video: {str(e)}")
-
- def _validate_and_extract_parameters(
- self, tool_parameters: dict[str, Any]
- ) -> Union[dict[str, Any], ToolInvokeMessage]:
- """Validate and extract parameters from the input dictionary."""
- prompt = tool_parameters.get("prompt", "")
- video_output_s3uri = tool_parameters.get("video_output_s3uri", "").strip()
-
- # Validate required parameters
- if not prompt:
- return self.create_text_message("Please provide a text prompt for video generation.")
- if not video_output_s3uri:
- return self.create_text_message("Please provide an S3 URI for video output.")
-
- # Validate S3 URI format
- if not video_output_s3uri.startswith("s3://"):
- return self.create_text_message("Invalid S3 URI format. Must start with 's3://'")
-
- # Ensure S3 URI ends with '/'
- video_output_s3uri = video_output_s3uri if video_output_s3uri.endswith("/") else video_output_s3uri + "/"
-
- return {
- "prompt": prompt,
- "video_output_s3uri": video_output_s3uri,
- "image_input_s3uri": tool_parameters.get("image_input_s3uri", "").strip(),
- "aws_region": tool_parameters.get("aws_region", NOVA_REEL_DEFAULT_REGION),
- "dimension": tool_parameters.get("dimension", NOVA_REEL_DEFAULT_DIMENSION),
- "seed": int(tool_parameters.get("seed", 0)),
- "fps": int(tool_parameters.get("fps", NOVA_REEL_DEFAULT_FPS)),
- "duration": int(tool_parameters.get("duration", NOVA_REEL_DEFAULT_DURATION)),
- "async_mode": bool(tool_parameters.get("async", True)),
- }
-
- def _initialize_aws_clients(self, region: str) -> tuple[Any, Any]:
- """Initialize AWS Bedrock and S3 clients."""
- bedrock = boto3.client(service_name="bedrock-runtime", region_name=region)
- s3_client = boto3.client("s3", region_name=region)
- return bedrock, s3_client
-
- def _prepare_model_input(self, params: dict[str, Any], s3_client: Any) -> Union[dict[str, Any], ToolInvokeMessage]:
- """Prepare the input for the Nova Reel model."""
- model_input = {
- "taskType": "TEXT_VIDEO",
- "textToVideoParams": {"text": params["prompt"]},
- "videoGenerationConfig": {
- "durationSeconds": params["duration"],
- "fps": params["fps"],
- "dimension": params["dimension"],
- "seed": params["seed"],
- },
- }
-
- # Add image if provided
- if params["image_input_s3uri"]:
- try:
- image_data = self._get_image_from_s3(s3_client, params["image_input_s3uri"])
- if not image_data:
- return self.create_text_message("Failed to retrieve image from S3")
-
- # Process and validate image
- processed_image = self._process_and_validate_image(image_data)
- if isinstance(processed_image, ToolInvokeMessage):
- return processed_image
-
- # Convert processed image to base64
- img_buffer = BytesIO()
- processed_image.save(img_buffer, format="PNG")
- img_buffer.seek(0)
- input_image_base64 = base64.b64encode(img_buffer.getvalue()).decode("utf-8")
-
- model_input["textToVideoParams"]["images"] = [
- {"format": "png", "source": {"bytes": input_image_base64}}
- ]
- except Exception as e:
- logger.error(f"Error processing input image: {str(e)}", exc_info=True)
- return self.create_text_message(f"Failed to process input image: {str(e)}")
-
- return model_input
-
- def _process_and_validate_image(self, image_data: bytes) -> Union[Image.Image, ToolInvokeMessage]:
- """
- Process and validate the input image according to Nova Reel requirements.
-
- Requirements:
- - Must be 1280x720 pixels
- - Must be RGB format (8 bits per channel)
- - If PNG, alpha channel must not have transparent/translucent pixels
- """
- try:
- # Open image
- img = Image.open(BytesIO(image_data))
-
- # Convert RGBA to RGB if needed, ensuring no transparency
- if img.mode == "RGBA":
- # Check for transparency
- if img.getchannel("A").getextrema()[0] < 255:
- return self.create_text_message(
- "PNG image contains transparent or translucent pixels, which is not supported. "
- "Please provide an image without transparency."
- )
- # Convert to RGB
- img = img.convert("RGB")
- elif img.mode != "RGB":
- # Convert any other mode to RGB
- img = img.convert("RGB")
-
- # Validate/adjust dimensions
- if img.size != (NOVA_REEL_REQUIRED_IMAGE_WIDTH, NOVA_REEL_REQUIRED_IMAGE_HEIGHT):
- logger.warning(
- f"Image dimensions {img.size} do not match required dimensions "
- f"({NOVA_REEL_REQUIRED_IMAGE_WIDTH}x{NOVA_REEL_REQUIRED_IMAGE_HEIGHT}). Resizing..."
- )
- img = img.resize(
- (NOVA_REEL_REQUIRED_IMAGE_WIDTH, NOVA_REEL_REQUIRED_IMAGE_HEIGHT), Image.Resampling.LANCZOS
- )
-
- # Validate bit depth
- if img.mode != NOVA_REEL_REQUIRED_IMAGE_MODE:
- return self.create_text_message(
- f"Image must be in {NOVA_REEL_REQUIRED_IMAGE_MODE} mode with 8 bits per channel"
- )
-
- return img
-
- except Exception as e:
- logger.error(f"Error processing image: {str(e)}", exc_info=True)
- return self.create_text_message(
- "Failed to process image. Please ensure the image is a valid JPEG or PNG file."
- )
-
- def _get_image_from_s3(self, s3_client: Any, s3_uri: str) -> Optional[bytes]:
- """Download and return image data from S3."""
- parsed_uri = urlparse(s3_uri)
- bucket = parsed_uri.netloc
- key = parsed_uri.path.lstrip("/")
-
- response = s3_client.get_object(Bucket=bucket, Key=key)
- return response["Body"].read()
-
- def _start_video_generation(self, bedrock: Any, model_input: dict[str, Any], output_s3uri: str) -> dict[str, Any]:
- """Start the async video generation process."""
- return bedrock.start_async_invoke(
- modelId=NOVA_REEL_MODEL_ID,
- modelInput=model_input,
- outputDataConfig={"s3OutputDataConfig": {"s3Uri": output_s3uri}},
- )
-
- def _handle_generation_mode(
- self, bedrock: Any, s3_client: Any, invocation_arn: str, async_mode: bool
- ) -> ToolInvokeMessage:
- """Handle async or sync video generation mode."""
- invocation_response = bedrock.get_async_invoke(invocationArn=invocation_arn)
- video_path = invocation_response["outputDataConfig"]["s3OutputDataConfig"]["s3Uri"]
- video_uri = f"{video_path}/output.mp4"
-
- if async_mode:
- return self.create_text_message(
- f"Video generation started.\nInvocation ARN: {invocation_arn}\nVideo will be available at: {video_uri}"
- )
-
- return self._wait_for_completion(bedrock, s3_client, invocation_arn)
-
- def _wait_for_completion(self, bedrock: Any, s3_client: Any, invocation_arn: str) -> ToolInvokeMessage:
- """Wait for video generation completion and handle the result."""
- while True:
- status_response = bedrock.get_async_invoke(invocationArn=invocation_arn)
- status = status_response["status"]
- video_path = status_response["outputDataConfig"]["s3OutputDataConfig"]["s3Uri"]
-
- if status == "Completed":
- return self._handle_completed_video(s3_client, video_path)
- elif status == "Failed":
- failure_message = status_response.get("failureMessage", "Unknown error")
- return self.create_text_message(f"Video generation failed.\nError: {failure_message}")
- elif status == "InProgress":
- time.sleep(NOVA_REEL_STATUS_CHECK_INTERVAL)
- else:
- return self.create_text_message(f"Unexpected status: {status}")
-
- def _handle_completed_video(self, s3_client: Any, video_path: str) -> ToolInvokeMessage:
- """Handle completed video generation and return the result."""
- parsed_uri = urlparse(video_path)
- bucket = parsed_uri.netloc
- key = parsed_uri.path.lstrip("/") + "/output.mp4"
-
- try:
- response = s3_client.get_object(Bucket=bucket, Key=key)
- video_content = response["Body"].read()
- return [
- self.create_text_message(f"Video is available at: {video_path}/output.mp4"),
- self.create_blob_message(blob=video_content, meta={"mime_type": "video/mp4"}, save_as="output.mp4"),
- ]
- except Exception as e:
- logger.error(f"Error downloading video: {str(e)}", exc_info=True)
- return self.create_text_message(
- f"Video generation completed but failed to download video: {str(e)}\n"
- f"Video is available at: s3://{bucket}/{key}"
- )
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- """Define the tool's runtime parameters."""
- parameters = [
- ToolParameter(
- name="prompt",
- label=I18nObject(en_US="Prompt", zh_Hans="提示词"),
- type=ToolParameter.ToolParameterType.STRING,
- required=True,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="Text description of the video you want to generate", zh_Hans="您想要生成的视频的文本描述"
- ),
- llm_description="Describe the video you want to generate",
- ),
- ToolParameter(
- name="video_output_s3uri",
- label=I18nObject(en_US="Output S3 URI", zh_Hans="输出S3 URI"),
- type=ToolParameter.ToolParameterType.STRING,
- required=True,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="S3 URI where the generated video will be stored", zh_Hans="生成的视频将存储的S3 URI"
- ),
- ),
- ToolParameter(
- name="dimension",
- label=I18nObject(en_US="Dimension", zh_Hans="尺寸"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default=NOVA_REEL_DEFAULT_DIMENSION,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Video dimensions (width x height)", zh_Hans="视频尺寸(宽 x 高)"),
- ),
- ToolParameter(
- name="duration",
- label=I18nObject(en_US="Duration", zh_Hans="时长"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=NOVA_REEL_DEFAULT_DURATION,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Video duration in seconds", zh_Hans="视频时长(秒)"),
- ),
- ToolParameter(
- name="seed",
- label=I18nObject(en_US="Seed", zh_Hans="种子值"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=0,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="Random seed for video generation", zh_Hans="视频生成的随机种子"),
- ),
- ToolParameter(
- name="fps",
- label=I18nObject(en_US="FPS", zh_Hans="帧率"),
- type=ToolParameter.ToolParameterType.NUMBER,
- required=False,
- default=NOVA_REEL_DEFAULT_FPS,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(
- en_US="Frames per second for the generated video", zh_Hans="生成视频的每秒帧数"
- ),
- ),
- ToolParameter(
- name="async",
- label=I18nObject(en_US="Async Mode", zh_Hans="异步模式"),
- type=ToolParameter.ToolParameterType.BOOLEAN,
- required=False,
- default=True,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="Whether to run in async mode (return immediately) or sync mode (wait for completion)",
- zh_Hans="是否以异步模式运行(立即返回)或同步模式(等待完成)",
- ),
- ),
- ToolParameter(
- name="aws_region",
- label=I18nObject(en_US="AWS Region", zh_Hans="AWS 区域"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- default=NOVA_REEL_DEFAULT_REGION,
- form=ToolParameter.ToolParameterForm.FORM,
- human_description=I18nObject(en_US="AWS region for Bedrock service", zh_Hans="Bedrock 服务的 AWS 区域"),
- ),
- ToolParameter(
- name="image_input_s3uri",
- label=I18nObject(en_US="Input Image S3 URI", zh_Hans="输入图像S3 URI"),
- type=ToolParameter.ToolParameterType.STRING,
- required=False,
- form=ToolParameter.ToolParameterForm.LLM,
- human_description=I18nObject(
- en_US="S3 URI of the input image (1280x720 JPEG/PNG) to use as first frame",
- zh_Hans="用作第一帧的输入图像(1280x720 JPEG/PNG)的S3 URI",
- ),
- ),
- ]
-
- return parameters
diff --git a/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml b/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml
deleted file mode 100644
index 16df5ba5c9d1e3..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml
+++ /dev/null
@@ -1,124 +0,0 @@
-identity:
- name: nova_reel
- author: AWS
- label:
- en_US: AWS Bedrock Nova Reel
- zh_Hans: AWS Bedrock Nova Reel
- icon: icon.svg
-description:
- human:
- en_US: A tool for generating videos using AWS Bedrock's Nova Reel model. Supports text-to-video generation and image-to-video generation with customizable parameters like duration, FPS, and dimensions. Input parameters reference https://docs.aws.amazon.com/nova/latest/userguide/video-generation.html
- zh_Hans: 使用 AWS Bedrock 的 Nova Reel 模型生成视频的工具。支持文本生成视频和图像生成视频功能,可自定义持续时间、帧率和尺寸等参数。输入参数参考 https://docs.aws.amazon.com/nova/latest/userguide/video-generation.html
- llm: Generate videos using AWS Bedrock's Nova Reel model with support for both text-to-video and image-to-video generation, allowing customization of video properties like duration, frame rate, and resolution.
-
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- human_description:
- en_US: Text description of the video you want to generate
- zh_Hans: 您想要生成的视频的文本描述
- llm_description: Describe the video you want to generate
- form: llm
-
- - name: video_output_s3uri
- type: string
- required: true
- label:
- en_US: Output S3 URI
- zh_Hans: 输出S3 URI
- human_description:
- en_US: S3 URI where the generated video will be stored
- zh_Hans: 生成的视频将存储的S3 URI
- form: form
-
- - name: dimension
- type: string
- required: false
- default: 1280x720
- label:
- en_US: Dimension
- zh_Hans: 尺寸
- human_description:
- en_US: Video dimensions (width x height)
- zh_Hans: 视频尺寸(宽 x 高)
- form: form
-
- - name: duration
- type: number
- required: false
- default: 6
- label:
- en_US: Duration
- zh_Hans: 时长
- human_description:
- en_US: Video duration in seconds
- zh_Hans: 视频时长(秒)
- form: form
-
- - name: seed
- type: number
- required: false
- default: 0
- label:
- en_US: Seed
- zh_Hans: 种子值
- human_description:
- en_US: Random seed for video generation
- zh_Hans: 视频生成的随机种子
- form: form
-
- - name: fps
- type: number
- required: false
- default: 24
- label:
- en_US: FPS
- zh_Hans: 帧率
- human_description:
- en_US: Frames per second for the generated video
- zh_Hans: 生成视频的每秒帧数
- form: form
-
- - name: async
- type: boolean
- required: false
- default: true
- label:
- en_US: Async Mode
- zh_Hans: 异步模式
- human_description:
- en_US: Whether to run in async mode (return immediately) or sync mode (wait for completion)
- zh_Hans: 是否以异步模式运行(立即返回)或同步模式(等待完成)
- form: llm
-
- - name: aws_region
- type: string
- required: false
- default: us-east-1
- label:
- en_US: AWS Region
- zh_Hans: AWS 区域
- human_description:
- en_US: AWS region for Bedrock service
- zh_Hans: Bedrock 服务的 AWS 区域
- form: form
-
- - name: image_input_s3uri
- type: string
- required: false
- label:
- en_US: Input Image S3 URI
- zh_Hans: 输入图像S3 URI
- human_description:
- en_US: S3 URI of the input image (1280x720 JPEG/PNG) to use as first frame
- zh_Hans: 用作第一帧的输入图像(1280x720 JPEG/PNG)的S3 URI
- form: llm
-
-development:
- dependencies:
- - boto3
- - pillow
diff --git a/api/core/tools/provider/builtin/aws/tools/s3_operator.py b/api/core/tools/provider/builtin/aws/tools/s3_operator.py
deleted file mode 100644
index e4026b07a87310..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/s3_operator.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from typing import Any, Union
-from urllib.parse import urlparse
-
-import boto3
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class S3Operator(BuiltinTool):
- s3_client: Any = None
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- # Initialize S3 client if not already done
- if not self.s3_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.s3_client = boto3.client("s3", region_name=aws_region)
- else:
- self.s3_client = boto3.client("s3")
-
- # Parse S3 URI
- s3_uri = tool_parameters.get("s3_uri")
- if not s3_uri:
- return self.create_text_message("s3_uri parameter is required")
-
- parsed_uri = urlparse(s3_uri)
- if parsed_uri.scheme != "s3":
- return self.create_text_message("Invalid S3 URI format. Must start with 's3://'")
-
- bucket = parsed_uri.netloc
- # Remove leading slash from key
- key = parsed_uri.path.lstrip("/")
-
- operation_type = tool_parameters.get("operation_type", "read")
- generate_presign_url = tool_parameters.get("generate_presign_url", False)
- presign_expiry = int(tool_parameters.get("presign_expiry", 3600)) # default 1 hour
-
- if operation_type == "write":
- text_content = tool_parameters.get("text_content")
- if not text_content:
- return self.create_text_message("text_content parameter is required for write operation")
-
- # Write content to S3
- self.s3_client.put_object(Bucket=bucket, Key=key, Body=text_content.encode("utf-8"))
- result = f"s3://{bucket}/{key}"
-
- # Generate presigned URL for the written object if requested
- if generate_presign_url:
- result = self.s3_client.generate_presigned_url(
- "get_object", Params={"Bucket": bucket, "Key": key}, ExpiresIn=presign_expiry
- )
-
- else: # read operation
- # Get object from S3
- response = self.s3_client.get_object(Bucket=bucket, Key=key)
- result = response["Body"].read().decode("utf-8")
-
- # Generate presigned URL if requested
- if generate_presign_url:
- result = self.s3_client.generate_presigned_url(
- "get_object", Params={"Bucket": bucket, "Key": key}, ExpiresIn=presign_expiry
- )
-
- return self.create_text_message(text=result)
-
- except self.s3_client.exceptions.NoSuchBucket:
- return self.create_text_message(f"Bucket '{bucket}' does not exist")
- except self.s3_client.exceptions.NoSuchKey:
- return self.create_text_message(f"Object '{key}' does not exist in bucket '{bucket}'")
- except Exception as e:
- return self.create_text_message(f"Exception: {str(e)}")
diff --git a/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml b/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml
deleted file mode 100644
index 642fc2966e9b6d..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml
+++ /dev/null
@@ -1,98 +0,0 @@
-identity:
- name: s3_operator
- author: AWS
- label:
- en_US: AWS S3 Operator
- zh_Hans: AWS S3 读写器
- pt_BR: AWS S3 Operator
- icon: icon.svg
-description:
- human:
- en_US: AWS S3 Writer and Reader
- zh_Hans: 读写S3 bucket中的文件
- pt_BR: AWS S3 Writer and Reader
- llm: AWS S3 Writer and Reader
-parameters:
- - name: text_content
- type: string
- required: false
- label:
- en_US: The text to write
- zh_Hans: 待写入的文本
- pt_BR: The text to write
- human_description:
- en_US: The text to write
- zh_Hans: 待写入的文本
- pt_BR: The text to write
- llm_description: The text to write
- form: llm
- - name: s3_uri
- type: string
- required: true
- label:
- en_US: s3 uri
- zh_Hans: s3 uri
- pt_BR: s3 uri
- human_description:
- en_US: s3 uri
- zh_Hans: s3 uri
- pt_BR: s3 uri
- llm_description: s3 uri
- form: llm
- - name: aws_region
- type: string
- required: true
- label:
- en_US: region of bucket
- zh_Hans: bucket 所在的region
- pt_BR: region of bucket
- human_description:
- en_US: region of bucket
- zh_Hans: bucket 所在的region
- pt_BR: region of bucket
- llm_description: region of bucket
- form: form
- - name: operation_type
- type: select
- required: true
- label:
- en_US: operation type
- zh_Hans: 操作类型
- pt_BR: operation type
- human_description:
- en_US: operation type
- zh_Hans: 操作类型
- pt_BR: operation type
- default: read
- options:
- - value: read
- label:
- en_US: read
- zh_Hans: 读
- - value: write
- label:
- en_US: write
- zh_Hans: 写
- form: form
- - name: generate_presign_url
- type: boolean
- required: false
- label:
- en_US: Generate presigned URL
- zh_Hans: 生成预签名URL
- human_description:
- en_US: Whether to generate a presigned URL for the S3 object
- zh_Hans: 是否生成S3对象的预签名URL
- default: false
- form: form
- - name: presign_expiry
- type: number
- required: false
- label:
- en_US: Presigned URL expiration time
- zh_Hans: 预签名URL有效期
- human_description:
- en_US: Expiration time in seconds for the presigned URL
- zh_Hans: 预签名URL的有效期(秒)
- default: 3600
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.py b/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.py
deleted file mode 100644
index 3d88f28dbd2fc7..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import json
-from typing import Any, Union
-
-import boto3
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-# Define label mappings
-LABEL_MAPPING = {0: "SAFE", 1: "NO_SAFE"}
-
-
-class ContentModerationTool(BuiltinTool):
- sagemaker_client: Any = None
- sagemaker_endpoint: str = None
-
- def _invoke_sagemaker(self, payload: dict, endpoint: str):
- response = self.sagemaker_client.invoke_endpoint(
- EndpointName=endpoint,
- Body=json.dumps(payload),
- ContentType="application/json",
- )
- # Parse response
- response_body = response["Body"].read().decode("utf8")
-
- json_obj = json.loads(response_body)
-
- # Handle nested JSON if present
- if isinstance(json_obj, dict) and "body" in json_obj:
- body_content = json.loads(json_obj["body"])
- prediction_result = body_content.get("prediction")
- else:
- prediction_result = json_obj.get("prediction")
-
- # Map labels and return
- result = LABEL_MAPPING.get(prediction_result, "NO_SAFE") # If not found in mapping, default to NO_SAFE
- return result
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- if not self.sagemaker_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region)
- else:
- self.sagemaker_client = boto3.client("sagemaker-runtime")
-
- if not self.sagemaker_endpoint:
- self.sagemaker_endpoint = tool_parameters.get("sagemaker_endpoint")
-
- content_text = tool_parameters.get("content_text")
-
- payload = {"text": content_text}
-
- result = self._invoke_sagemaker(payload, self.sagemaker_endpoint)
-
- return self.create_text_message(text=result)
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}")
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.yaml b/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.yaml
deleted file mode 100644
index 76dcb89632f270..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_chinese_toxicity_detector.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-identity:
- name: chinese_toxicity_detector
- author: AWS
- label:
- en_US: Chinese Toxicity Detector
- zh_Hans: 中文有害内容检测
- icon: icon.svg
-description:
- human:
- en_US: A tool to detect Chinese toxicity
- zh_Hans: 检测中文有害内容的工具
- llm: A tool that checks if Chinese content is safe for work
-parameters:
- - name: sagemaker_endpoint
- type: string
- required: true
- label:
- en_US: sagemaker endpoint for moderation
- zh_Hans: 内容审核的SageMaker端点
- human_description:
- en_US: sagemaker endpoint for content moderation
- zh_Hans: 内容审核的SageMaker端点
- llm_description: sagemaker endpoint for content moderation
- form: form
- - name: content_text
- type: string
- required: true
- label:
- en_US: content text
- zh_Hans: 待审核文本
- human_description:
- en_US: text content to be moderated
- zh_Hans: 需要审核的文本内容
- llm_description: text content to be moderated
- form: llm
- - name: aws_region
- type: string
- required: false
- label:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- human_description:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- llm_description: region of sagemaker endpoint
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py b/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py
deleted file mode 100644
index 8320bd84efa440..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import json
-import operator
-from typing import Any, Union
-
-import boto3 # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SageMakerReRankTool(BuiltinTool):
- sagemaker_client: Any = None
- sagemaker_endpoint: str = None
-
- def _sagemaker_rerank(self, query_input: str, docs: list[str], rerank_endpoint: str):
- inputs = [query_input] * len(docs)
- response_model = self.sagemaker_client.invoke_endpoint(
- EndpointName=rerank_endpoint,
- Body=json.dumps({"inputs": inputs, "docs": docs}),
- ContentType="application/json",
- )
- json_str = response_model["Body"].read().decode("utf8")
- json_obj = json.loads(json_str)
- scores = json_obj["scores"]
- return scores if isinstance(scores, list) else [scores]
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- line = 0
- try:
- if not self.sagemaker_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region)
- else:
- self.sagemaker_client = boto3.client("sagemaker-runtime")
-
- line = 1
- if not self.sagemaker_endpoint:
- self.sagemaker_endpoint = tool_parameters.get("sagemaker_endpoint")
-
- line = 2
- topk = tool_parameters.get("topk", 5)
-
- line = 3
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Please input query")
-
- line = 4
- candidate_texts = tool_parameters.get("candidate_texts")
- if not candidate_texts:
- return self.create_text_message("Please input candidate_texts")
-
- line = 5
- candidate_docs = json.loads(candidate_texts)
- docs = [item.get("content") for item in candidate_docs]
-
- line = 6
- scores = self._sagemaker_rerank(query_input=query, docs=docs, rerank_endpoint=self.sagemaker_endpoint)
-
- line = 7
- for idx in range(len(candidate_docs)):
- candidate_docs[idx]["score"] = scores[idx]
-
- line = 8
- sorted_candidate_docs = sorted(candidate_docs, key=operator.itemgetter("score"), reverse=True)
-
- line = 9
- return [self.create_json_message(res) for res in sorted_candidate_docs[:topk]]
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}, line : {line}")
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.yaml b/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.yaml
deleted file mode 100644
index d1dfdb9f84a858..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.yaml
+++ /dev/null
@@ -1,82 +0,0 @@
-identity:
- name: sagemaker_text_rerank
- author: AWS
- label:
- en_US: SagemakerRerank
- zh_Hans: Sagemaker重排序
- pt_BR: SagemakerRerank
- icon: icon.svg
-description:
- human:
- en_US: A tool for performing text similarity ranking. You can find deploy notebook on Github Repo - https://github.com/aws-samples/dify-aws-tool
- zh_Hans: Sagemaker重排序工具, 请参考 Github Repo - https://github.com/aws-samples/dify-aws-tool上的部署脚本
- pt_BR: A tool for performing text similarity ranking.
- llm: A tool for performing text similarity ranking. You can find deploy notebook on Github Repo - https://github.com/aws-samples/dify-aws-tool
-parameters:
- - name: sagemaker_endpoint
- type: string
- required: true
- label:
- en_US: sagemaker endpoint for reranking
- zh_Hans: 重排序的SageMaker 端点
- pt_BR: sagemaker endpoint for reranking
- human_description:
- en_US: sagemaker endpoint for reranking
- zh_Hans: 重排序的SageMaker 端点
- pt_BR: sagemaker endpoint for reranking
- llm_description: sagemaker endpoint for reranking
- form: form
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: key words for searching
- zh_Hans: 查询关键词
- pt_BR: key words for searching
- llm_description: key words for searching
- form: llm
- - name: candidate_texts
- type: string
- required: true
- label:
- en_US: text candidates
- zh_Hans: 候选文本
- pt_BR: text candidates
- human_description:
- en_US: searched candidates by query
- zh_Hans: 查询文本搜到候选文本
- pt_BR: searched candidates by query
- llm_description: searched candidates by query
- form: llm
- - name: topk
- type: number
- required: false
- form: form
- label:
- en_US: Limit for results count
- zh_Hans: 返回个数限制
- pt_BR: Limit for results count
- human_description:
- en_US: Limit for results count
- zh_Hans: 返回个数限制
- pt_BR: Limit for results count
- min: 1
- max: 10
- default: 5
- - name: aws_region
- type: string
- required: false
- label:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- pt_BR: region of sagemaker endpoint
- human_description:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- pt_BR: region of sagemaker endpoint
- llm_description: region of sagemaker endpoint
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py b/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py
deleted file mode 100644
index 55cff89798a4eb..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import json
-from enum import Enum
-from typing import Any, Optional, Union
-
-import boto3 # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class TTSModelType(Enum):
- PresetVoice = "PresetVoice"
- CloneVoice = "CloneVoice"
- CloneVoice_CrossLingual = "CloneVoice_CrossLingual"
- InstructVoice = "InstructVoice"
-
-
-class SageMakerTTSTool(BuiltinTool):
- sagemaker_client: Any = None
- sagemaker_endpoint: str | None = None
- s3_client: Any = None
- comprehend_client: Any = None
-
- def _detect_lang_code(self, content: str, map_dict: Optional[dict] = None):
- map_dict = {"zh": "<|zh|>", "en": "<|en|>", "ja": "<|jp|>", "zh-TW": "<|yue|>", "ko": "<|ko|>"}
-
- response = self.comprehend_client.detect_dominant_language(Text=content)
- language_code = response["Languages"][0]["LanguageCode"]
- return map_dict.get(language_code, "<|zh|>")
-
- def _build_tts_payload(
- self,
- model_type: str,
- content_text: str,
- model_role: str,
- prompt_text: str,
- prompt_audio: str,
- instruct_text: str,
- ):
- if model_type == TTSModelType.PresetVoice.value and model_role:
- return {"tts_text": content_text, "role": model_role}
- if model_type == TTSModelType.CloneVoice.value and prompt_text and prompt_audio:
- return {"tts_text": content_text, "prompt_text": prompt_text, "prompt_audio": prompt_audio}
- if model_type == TTSModelType.CloneVoice_CrossLingual.value and prompt_audio:
- lang_tag = self._detect_lang_code(content_text)
- return {"tts_text": f"{content_text}", "prompt_audio": prompt_audio, "lang_tag": lang_tag}
- if model_type == TTSModelType.InstructVoice.value and instruct_text and model_role:
- return {"tts_text": content_text, "role": model_role, "instruct_text": instruct_text}
-
- raise RuntimeError(f"Invalid params for {model_type}")
-
- def _invoke_sagemaker(self, payload: dict, endpoint: str):
- response_model = self.sagemaker_client.invoke_endpoint(
- EndpointName=endpoint,
- Body=json.dumps(payload),
- ContentType="application/json",
- )
- json_str = response_model["Body"].read().decode("utf8")
- json_obj = json.loads(json_str)
- return json_obj
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- if not self.sagemaker_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region)
- self.s3_client = boto3.client("s3", region_name=aws_region)
- self.comprehend_client = boto3.client("comprehend", region_name=aws_region)
- else:
- self.sagemaker_client = boto3.client("sagemaker-runtime")
- self.s3_client = boto3.client("s3")
- self.comprehend_client = boto3.client("comprehend")
-
- if not self.sagemaker_endpoint:
- self.sagemaker_endpoint = tool_parameters.get("sagemaker_endpoint")
-
- tts_text = tool_parameters.get("tts_text")
- tts_infer_type = tool_parameters.get("tts_infer_type")
-
- voice = tool_parameters.get("voice")
- mock_voice_audio = tool_parameters.get("mock_voice_audio")
- mock_voice_text = tool_parameters.get("mock_voice_text")
- voice_instruct_prompt = tool_parameters.get("voice_instruct_prompt")
- payload = self._build_tts_payload(
- tts_infer_type, tts_text, voice, mock_voice_text, mock_voice_audio, voice_instruct_prompt
- )
-
- result = self._invoke_sagemaker(payload, self.sagemaker_endpoint)
-
- return self.create_text_message(text=result["s3_presign_url"])
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}")
diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.yaml b/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.yaml
deleted file mode 100644
index a6a61dd4aa519a..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.yaml
+++ /dev/null
@@ -1,149 +0,0 @@
-identity:
- name: sagemaker_tts
- author: AWS
- label:
- en_US: SagemakerTTS
- zh_Hans: Sagemaker语音合成
- pt_BR: SagemakerTTS
- icon: icon.svg
-description:
- human:
- en_US: A tool for Speech synthesis - https://github.com/aws-samples/dify-aws-tool
- zh_Hans: Sagemaker语音合成工具, 请参考 Github Repo - https://github.com/aws-samples/dify-aws-tool上的部署脚本
- pt_BR: A tool for Speech synthesis.
- llm: A tool for Speech synthesis. You can find deploy notebook on Github Repo - https://github.com/aws-samples/dify-aws-tool
-parameters:
- - name: sagemaker_endpoint
- type: string
- required: true
- label:
- en_US: sagemaker endpoint for tts
- zh_Hans: 语音生成的SageMaker端点
- pt_BR: sagemaker endpoint for tts
- human_description:
- en_US: sagemaker endpoint for tts
- zh_Hans: 语音生成的SageMaker端点
- pt_BR: sagemaker endpoint for tts
- llm_description: sagemaker endpoint for tts
- form: form
- - name: tts_text
- type: string
- required: true
- label:
- en_US: tts text
- zh_Hans: 语音合成原文
- pt_BR: tts text
- human_description:
- en_US: tts text
- zh_Hans: 语音合成原文
- pt_BR: tts text
- llm_description: tts text
- form: llm
- - name: tts_infer_type
- type: select
- required: false
- label:
- en_US: tts infer type
- zh_Hans: 合成方式
- pt_BR: tts infer type
- human_description:
- en_US: tts infer type
- zh_Hans: 合成方式
- pt_BR: tts infer type
- llm_description: tts infer type
- options:
- - value: PresetVoice
- label:
- en_US: preset voice
- zh_Hans: 预置音色
- - value: CloneVoice
- label:
- en_US: clone voice
- zh_Hans: 克隆音色
- - value: CloneVoice_CrossLingual
- label:
- en_US: clone crossLingual voice
- zh_Hans: 克隆音色(跨语言)
- - value: InstructVoice
- label:
- en_US: instruct voice
- zh_Hans: 指令音色
- form: form
- - name: voice
- type: select
- required: false
- label:
- en_US: preset voice
- zh_Hans: 预置音色
- pt_BR: preset voice
- human_description:
- en_US: preset voice
- zh_Hans: 预置音色
- pt_BR: preset voice
- llm_description: preset voice
- options:
- - value: 中文男
- label:
- en_US: zh-cn male
- zh_Hans: 中文男
- - value: 中文女
- label:
- en_US: zh-cn female
- zh_Hans: 中文女
- - value: 粤语女
- label:
- en_US: zh-TW female
- zh_Hans: 粤语女
- form: form
- - name: mock_voice_audio
- type: string
- required: false
- label:
- en_US: clone voice link
- zh_Hans: 克隆音频链接
- pt_BR: clone voice link
- human_description:
- en_US: clone voice link
- zh_Hans: 克隆音频链接
- pt_BR: clone voice link
- llm_description: clone voice link
- form: llm
- - name: mock_voice_text
- type: string
- required: false
- label:
- en_US: text of clone voice
- zh_Hans: 克隆音频对应文本
- pt_BR: text of clone voice
- human_description:
- en_US: text of clone voice
- zh_Hans: 克隆音频对应文本
- pt_BR: text of clone voice
- llm_description: text of clone voice
- form: llm
- - name: voice_instruct_prompt
- type: string
- required: false
- label:
- en_US: instruct prompt for voice
- zh_Hans: 音色指令文本
- pt_BR: instruct prompt for voice
- human_description:
- en_US: instruct prompt for voice
- zh_Hans: 音色指令文本
- pt_BR: instruct prompt for voice
- llm_description: instruct prompt for voice
- form: llm
- - name: aws_region
- type: string
- required: false
- label:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- pt_BR: region of sagemaker endpoint
- human_description:
- en_US: region of sagemaker endpoint
- zh_Hans: SageMaker 端点所在的region
- pt_BR: region of sagemaker endpoint
- llm_description: region of sagemaker endpoint
- form: form
diff --git a/api/core/tools/provider/builtin/aws/tools/transcribe_asr.py b/api/core/tools/provider/builtin/aws/tools/transcribe_asr.py
deleted file mode 100644
index 7520f6bca8b1ce..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/transcribe_asr.py
+++ /dev/null
@@ -1,418 +0,0 @@
-import json
-import logging
-import os
-import re
-import time
-import uuid
-from typing import Any, Union
-from urllib.parse import urlparse
-
-import boto3
-import requests
-from botocore.exceptions import ClientError
-from requests.exceptions import RequestException
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-
-LanguageCodeOptions = [
- "af-ZA",
- "ar-AE",
- "ar-SA",
- "da-DK",
- "de-CH",
- "de-DE",
- "en-AB",
- "en-AU",
- "en-GB",
- "en-IE",
- "en-IN",
- "en-US",
- "en-WL",
- "es-ES",
- "es-US",
- "fa-IR",
- "fr-CA",
- "fr-FR",
- "he-IL",
- "hi-IN",
- "id-ID",
- "it-IT",
- "ja-JP",
- "ko-KR",
- "ms-MY",
- "nl-NL",
- "pt-BR",
- "pt-PT",
- "ru-RU",
- "ta-IN",
- "te-IN",
- "tr-TR",
- "zh-CN",
- "zh-TW",
- "th-TH",
- "en-ZA",
- "en-NZ",
- "vi-VN",
- "sv-SE",
- "ab-GE",
- "ast-ES",
- "az-AZ",
- "ba-RU",
- "be-BY",
- "bg-BG",
- "bn-IN",
- "bs-BA",
- "ca-ES",
- "ckb-IQ",
- "ckb-IR",
- "cs-CZ",
- "cy-WL",
- "el-GR",
- "et-ET",
- "eu-ES",
- "fi-FI",
- "gl-ES",
- "gu-IN",
- "ha-NG",
- "hr-HR",
- "hu-HU",
- "hy-AM",
- "is-IS",
- "ka-GE",
- "kab-DZ",
- "kk-KZ",
- "kn-IN",
- "ky-KG",
- "lg-IN",
- "lt-LT",
- "lv-LV",
- "mhr-RU",
- "mi-NZ",
- "mk-MK",
- "ml-IN",
- "mn-MN",
- "mr-IN",
- "mt-MT",
- "no-NO",
- "or-IN",
- "pa-IN",
- "pl-PL",
- "ps-AF",
- "ro-RO",
- "rw-RW",
- "si-LK",
- "sk-SK",
- "sl-SI",
- "so-SO",
- "sr-RS",
- "su-ID",
- "sw-BI",
- "sw-KE",
- "sw-RW",
- "sw-TZ",
- "sw-UG",
- "tl-PH",
- "tt-RU",
- "ug-CN",
- "uk-UA",
- "uz-UZ",
- "wo-SN",
- "zu-ZA",
-]
-
-MediaFormat = ["mp3", "mp4", "wav", "flac", "ogg", "amr", "webm", "m4a"]
-
-
-def is_url(text):
- if not text:
- return False
- text = text.strip()
- # Regular expression pattern for URL validation
- pattern = re.compile(
- r"^" # Start of the string
- r"(?:http|https)://" # Protocol (http or https)
- r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # Domain
- r"localhost|" # localhost
- r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # IP address
- r"(?::\d+)?" # Optional port
- r"(?:/?|[/?]\S+)" # Path
- r"$", # End of the string
- re.IGNORECASE,
- )
- return bool(pattern.match(text))
-
-
-def upload_file_from_url_to_s3(s3_client, url, bucket_name, s3_key=None, max_retries=3):
- """
- Upload a file from a URL to an S3 bucket with retries and better error handling.
-
- Parameters:
- - s3_client
- - url (str): The URL of the file to upload
- - bucket_name (str): The name of the S3 bucket
- - s3_key (str): The desired key (path) in S3. If None, will use the filename from URL
- - max_retries (int): Maximum number of retry attempts
-
- Returns:
- - tuple: (bool, str) - (Success status, Message)
- """
-
- # Validate inputs
- if not url or not bucket_name:
- return False, "URL and bucket name are required"
-
- retry_count = 0
- while retry_count < max_retries:
- try:
- # Download the file from URL
- response = requests.get(url, stream=True, timeout=30)
- response.raise_for_status()
-
- # If s3_key is not provided, try to get filename from URL
- if not s3_key:
- parsed_url = urlparse(url)
- filename = os.path.basename(parsed_url.path.split("/file-preview")[0])
- s3_key = "transcribe-files/" + filename
-
- # Upload the file to S3
- s3_client.upload_fileobj(
- response.raw,
- bucket_name,
- s3_key,
- ExtraArgs={
- "ContentType": response.headers.get("content-type"),
- "ACL": "private", # Ensure the uploaded file is private
- },
- )
-
- return f"s3://{bucket_name}/{s3_key}", f"Successfully uploaded file to s3://{bucket_name}/{s3_key}"
-
- except RequestException as e:
- retry_count += 1
- if retry_count == max_retries:
- return None, f"Failed to download file from URL after {max_retries} attempts: {str(e)}"
- continue
-
- except ClientError as e:
- return None, f"AWS S3 error: {str(e)}"
-
- except Exception as e:
- return None, f"Unexpected error: {str(e)}"
-
- return None, "Maximum retries exceeded"
-
-
-class TranscribeTool(BuiltinTool):
- s3_client: Any = None
- transcribe_client: Any = None
-
- """
- Note that you must include one of LanguageCode, IdentifyLanguage,
- or IdentifyMultipleLanguages in your request.
- If you include more than one of these parameters, your transcription job fails.
- """
-
- def _transcribe_audio(self, audio_file_uri, file_type, **extra_args):
- uuid_str = str(uuid.uuid4())
- job_name = f"{int(time.time())}-{uuid_str}"
- try:
- # Start transcription job
- response = self.transcribe_client.start_transcription_job(
- TranscriptionJobName=job_name, Media={"MediaFileUri": audio_file_uri}, **extra_args
- )
-
- # Wait for the job to complete
- while True:
- status = self.transcribe_client.get_transcription_job(TranscriptionJobName=job_name)
- if status["TranscriptionJob"]["TranscriptionJobStatus"] in ["COMPLETED", "FAILED"]:
- break
- time.sleep(5)
-
- if status["TranscriptionJob"]["TranscriptionJobStatus"] == "COMPLETED":
- return status["TranscriptionJob"]["Transcript"]["TranscriptFileUri"], None
- else:
- return None, f"Error: TranscriptionJobStatus:{status['TranscriptionJob']['TranscriptionJobStatus']} "
-
- except Exception as e:
- return None, f"Error: {str(e)}"
-
- def _download_and_read_transcript(self, transcript_file_uri: str, max_retries: int = 3) -> tuple[str, str]:
- """
- Download and read the transcript file from the given URI.
-
- Parameters:
- - transcript_file_uri (str): The URI of the transcript file
- - max_retries (int): Maximum number of retry attempts
-
- Returns:
- - tuple: (text, error) - (Transcribed text if successful, error message if failed)
- """
- retry_count = 0
- while retry_count < max_retries:
- try:
- # Download the transcript file
- response = requests.get(transcript_file_uri, timeout=30)
- response.raise_for_status()
-
- # Parse the JSON content
- transcript_data = response.json()
-
- # Check if speaker labels are present and enabled
- has_speaker_labels = (
- "results" in transcript_data
- and "speaker_labels" in transcript_data["results"]
- and "segments" in transcript_data["results"]["speaker_labels"]
- )
-
- if has_speaker_labels:
- # Get speaker segments
- segments = transcript_data["results"]["speaker_labels"]["segments"]
- items = transcript_data["results"]["items"]
-
- # Create a mapping of start_time -> speaker_label
- time_to_speaker = {}
- for segment in segments:
- speaker_label = segment["speaker_label"]
- for item in segment["items"]:
- time_to_speaker[item["start_time"]] = speaker_label
-
- # Build transcript with speaker labels
- current_speaker = None
- transcript_parts = []
-
- for item in items:
- # Skip non-pronunciation items (like punctuation)
- if item["type"] == "punctuation":
- transcript_parts.append(item["alternatives"][0]["content"])
- continue
-
- start_time = item["start_time"]
- speaker = time_to_speaker.get(start_time)
-
- if speaker != current_speaker:
- current_speaker = speaker
- transcript_parts.append(f"\n[{speaker}]: ")
-
- transcript_parts.append(item["alternatives"][0]["content"])
-
- return " ".join(transcript_parts).strip(), None
- else:
- # Extract the transcription text
- # The transcript text is typically in the 'results' -> 'transcripts' array
- if "results" in transcript_data and "transcripts" in transcript_data["results"]:
- transcripts = transcript_data["results"]["transcripts"]
- if transcripts:
- # Combine all transcript segments
- full_text = " ".join(t.get("transcript", "") for t in transcripts)
- return full_text, None
-
- return None, "No transcripts found in the response"
-
- except requests.exceptions.RequestException as e:
- retry_count += 1
- if retry_count == max_retries:
- return None, f"Failed to download transcript file after {max_retries} attempts: {str(e)}"
- continue
-
- except json.JSONDecodeError as e:
- return None, f"Failed to parse transcript JSON: {str(e)}"
-
- except Exception as e:
- return None, f"Unexpected error while processing transcript: {str(e)}"
-
- return None, "Maximum retries exceeded"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- if not self.transcribe_client:
- aws_region = tool_parameters.get("aws_region")
- if aws_region:
- self.transcribe_client = boto3.client("transcribe", region_name=aws_region)
- self.s3_client = boto3.client("s3", region_name=aws_region)
- else:
- self.transcribe_client = boto3.client("transcribe")
- self.s3_client = boto3.client("s3")
-
- file_url = tool_parameters.get("file_url")
- file_type = tool_parameters.get("file_type")
- language_code = tool_parameters.get("language_code")
- identify_language = tool_parameters.get("identify_language", True)
- identify_multiple_languages = tool_parameters.get("identify_multiple_languages", False)
- language_options_str = tool_parameters.get("language_options")
- s3_bucket_name = tool_parameters.get("s3_bucket_name")
- ShowSpeakerLabels = tool_parameters.get("ShowSpeakerLabels", True)
- MaxSpeakerLabels = tool_parameters.get("MaxSpeakerLabels", 2)
-
- # Check the input params
- if not s3_bucket_name:
- return self.create_text_message(text="s3_bucket_name is required")
- language_options = None
- if language_options_str:
- language_options = language_options_str.split("|")
- for lang in language_options:
- if lang not in LanguageCodeOptions:
- return self.create_text_message(
- text=f"{lang} is not supported, should be one of {LanguageCodeOptions}"
- )
- if language_code and language_code not in LanguageCodeOptions:
- err_msg = f"language_code:{language_code} is not supported, should be one of {LanguageCodeOptions}"
- return self.create_text_message(text=err_msg)
-
- err_msg = f"identify_language:{identify_language}, \
- identify_multiple_languages:{identify_multiple_languages}, \
- Note that you must include one of LanguageCode, IdentifyLanguage, \
- or IdentifyMultipleLanguages in your request. \
- If you include more than one of these parameters, \
- your transcription job fails."
- if not language_code:
- if identify_language and identify_multiple_languages:
- return self.create_text_message(text=err_msg)
- else:
- if identify_language or identify_multiple_languages:
- return self.create_text_message(text=err_msg)
-
- extra_args = {
- "IdentifyLanguage": identify_language,
- "IdentifyMultipleLanguages": identify_multiple_languages,
- }
- if language_code:
- extra_args["LanguageCode"] = language_code
- if language_options:
- extra_args["LanguageOptions"] = language_options
- if ShowSpeakerLabels:
- extra_args["Settings"] = {"ShowSpeakerLabels": ShowSpeakerLabels, "MaxSpeakerLabels": MaxSpeakerLabels}
-
- # upload to s3 bucket
- s3_path_result, error = upload_file_from_url_to_s3(self.s3_client, url=file_url, bucket_name=s3_bucket_name)
- if not s3_path_result:
- return self.create_text_message(text=error)
-
- transcript_file_uri, error = self._transcribe_audio(
- audio_file_uri=s3_path_result,
- file_type=file_type,
- **extra_args,
- )
- if not transcript_file_uri:
- return self.create_text_message(text=error)
-
- # Download and read the transcript
- transcript_text, error = self._download_and_read_transcript(transcript_file_uri)
- if not transcript_text:
- return self.create_text_message(text=error)
-
- return self.create_text_message(text=transcript_text)
-
- except Exception as e:
- return self.create_text_message(f"Exception {str(e)}")
diff --git a/api/core/tools/provider/builtin/aws/tools/transcribe_asr.yaml b/api/core/tools/provider/builtin/aws/tools/transcribe_asr.yaml
deleted file mode 100644
index 0dccd615d272dd..00000000000000
--- a/api/core/tools/provider/builtin/aws/tools/transcribe_asr.yaml
+++ /dev/null
@@ -1,133 +0,0 @@
-identity:
- name: transcribe_asr
- author: AWS
- label:
- en_US: TranscribeASR
- zh_Hans: Transcribe语音识别转录
- pt_BR: TranscribeASR
- icon: icon.svg
-description:
- human:
- en_US: A tool for ASR (Automatic Speech Recognition) - https://github.com/aws-samples/dify-aws-tool
- zh_Hans: AWS 语音识别转录服务, 请参考 https://aws.amazon.com/cn/pm/transcribe/#Learn_More_About_Amazon_Transcribe
- pt_BR: A tool for ASR (Automatic Speech Recognition).
- llm: A tool for ASR (Automatic Speech Recognition).
-parameters:
- - name: file_url
- type: string
- required: true
- label:
- en_US: video or audio file url for transcribe
- zh_Hans: 语音或者视频文件url
- pt_BR: video or audio file url for transcribe
- human_description:
- en_US: video or audio file url for transcribe
- zh_Hans: 语音或者视频文件url
- pt_BR: video or audio file url for transcribe
- llm_description: video or audio file url for transcribe
- form: llm
- - name: language_code
- type: string
- required: false
- label:
- en_US: Language Code
- zh_Hans: 语言编码
- pt_BR: Language Code
- human_description:
- en_US: The language code used to create your transcription job. refer to :https://docs.aws.amazon.com/transcribe/latest/dg/supported-languages.html
- zh_Hans: 语言编码,例如zh-CN, en-US 可参考 https://docs.aws.amazon.com/transcribe/latest/dg/supported-languages.html
- pt_BR: The language code used to create your transcription job. refer to :https://docs.aws.amazon.com/transcribe/latest/dg/supported-languages.html
- llm_description: The language code used to create your transcription job.
- form: llm
- - name: identify_language
- type: boolean
- default: true
- required: false
- label:
- en_US: Automactically Identify Language
- zh_Hans: 自动识别语言
- pt_BR: Automactically Identify Language
- human_description:
- en_US: Automactically Identify Language
- zh_Hans: 自动识别语言
- pt_BR: Automactically Identify Language
- llm_description: Enable Automactically Identify Language
- form: form
- - name: identify_multiple_languages
- type: boolean
- required: false
- label:
- en_US: Automactically Identify Multiple Languages
- zh_Hans: 自动识别多种语言
- pt_BR: Automactically Identify Multiple Languages
- human_description:
- en_US: Automactically Identify Multiple Languages
- zh_Hans: 自动识别多种语言
- pt_BR: Automactically Identify Multiple Languages
- llm_description: Enable Automactically Identify Multiple Languages
- form: form
- - name: language_options
- type: string
- required: false
- label:
- en_US: Language Options
- zh_Hans: 语言种类选项
- pt_BR: Language Options
- human_description:
- en_US: Seperated by |, e.g:zh-CN|en-US, You can specify two or more language codes that represent the languages you think may be present in your media
- zh_Hans: 您可以指定两个或更多的语言代码来表示您认为可能出现在媒体中的语言。用|分隔,如 zh-CN|en-US
- pt_BR: Seperated by |, e.g:zh-CN|en-US, You can specify two or more language codes that represent the languages you think may be present in your media
- llm_description: Seperated by |, e.g:zh-CN|en-US, You can specify two or more language codes that represent the languages you think may be present in your media
- form: llm
- - name: s3_bucket_name
- type: string
- required: true
- label:
- en_US: s3 bucket name
- zh_Hans: s3 存储桶名称
- pt_BR: s3 bucket name
- human_description:
- en_US: s3 bucket name to store transcribe files (don't add prefix s3://)
- zh_Hans: s3 存储桶名称,用于存储转录文件 (不需要前缀 s3://)
- pt_BR: s3 bucket name to store transcribe files (don't add prefix s3://)
- llm_description: s3 bucket name to store transcribe files
- form: form
- - name: ShowSpeakerLabels
- type: boolean
- required: true
- default: true
- label:
- en_US: ShowSpeakerLabels
- zh_Hans: 显示说话人标签
- pt_BR: ShowSpeakerLabels
- human_description:
- en_US: Enables speaker partitioning (diarization) in your transcription output
- zh_Hans: 在转录输出中启用说话人分区(说话人分离)
- pt_BR: Enables speaker partitioning (diarization) in your transcription output
- llm_description: Enables speaker partitioning (diarization) in your transcription output
- form: form
- - name: MaxSpeakerLabels
- type: number
- required: true
- default: 2
- label:
- en_US: MaxSpeakerLabels
- zh_Hans: 说话人标签数量
- pt_BR: MaxSpeakerLabels
- human_description:
- en_US: Specify the maximum number of speakers you want to partition in your media
- zh_Hans: 指定您希望在媒体中划分的最多演讲者数量。
- pt_BR: Specify the maximum number of speakers you want to partition in your media
- llm_description: Specify the maximum number of speakers you want to partition in your media
- form: form
- - name: aws_region
- type: string
- required: false
- label:
- en_US: AWS Region
- zh_Hans: AWS 区域
- human_description:
- en_US: Please enter the AWS region for the transcribe service, for example 'us-east-1'.
- zh_Hans: 请输入Transcribe的 AWS 区域,例如 'us-east-1'。
- llm_description: Please enter the AWS region for the transcribe service, for example 'us-east-1'.
- form: form
diff --git a/api/core/tools/provider/builtin/azuredalle/__init__.py b/api/core/tools/provider/builtin/azuredalle/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/azuredalle/_assets/icon.png b/api/core/tools/provider/builtin/azuredalle/_assets/icon.png
deleted file mode 100644
index 7083a3f638e9a1..00000000000000
Binary files a/api/core/tools/provider/builtin/azuredalle/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/azuredalle/azuredalle.py b/api/core/tools/provider/builtin/azuredalle/azuredalle.py
deleted file mode 100644
index 1fab0d03a28ff3..00000000000000
--- a/api/core/tools/provider/builtin/azuredalle/azuredalle.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.azuredalle.tools.dalle3 import DallE3Tool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class AzureDALLEProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- DallE3Tool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"prompt": "cute girl, blue eyes, white hair, anime style", "size": "square", "n": 1},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/azuredalle/azuredalle.yaml b/api/core/tools/provider/builtin/azuredalle/azuredalle.yaml
deleted file mode 100644
index 4353e0c4862f61..00000000000000
--- a/api/core/tools/provider/builtin/azuredalle/azuredalle.yaml
+++ /dev/null
@@ -1,76 +0,0 @@
-identity:
- author: Leslie
- name: azuredalle
- label:
- en_US: Azure DALL-E
- zh_Hans: Azure DALL-E 绘画
- pt_BR: Azure DALL-E
- description:
- en_US: Azure DALL-E art
- zh_Hans: Azure DALL-E 绘画
- pt_BR: Azure DALL-E art
- icon: icon.png
- tags:
- - image
- - productivity
-credentials_for_provider:
- azure_openai_api_key:
- type: secret-input
- required: true
- label:
- en_US: API key
- zh_Hans: 密钥
- pt_BR: API key
- help:
- en_US: Please input your Azure OpenAI API key
- zh_Hans: 请输入你的 Azure OpenAI API key
- pt_BR: Introduza a sua chave de API OpenAI do Azure
- placeholder:
- en_US: Please input your Azure OpenAI API key
- zh_Hans: 请输入你的 Azure OpenAI API key
- pt_BR: Introduza a sua chave de API OpenAI do Azure
- azure_openai_api_model_name:
- type: text-input
- required: true
- label:
- en_US: Deployment Name
- zh_Hans: 部署名称
- pt_BR: Nome da Implantação
- help:
- en_US: Please input the name of your Azure Openai DALL-E API deployment
- zh_Hans: 请输入你的 Azure Openai DALL-E API 部署名称
- pt_BR: Insira o nome da implantação da API DALL-E do Azure Openai
- placeholder:
- en_US: Please input the name of your Azure Openai DALL-E API deployment
- zh_Hans: 请输入你的 Azure Openai DALL-E API 部署名称
- pt_BR: Insira o nome da implantação da API DALL-E do Azure Openai
- azure_openai_base_url:
- type: text-input
- required: true
- label:
- en_US: API Endpoint URL
- zh_Hans: API 域名
- pt_BR: API Endpoint URL
- help:
- en_US: Please input your Azure OpenAI Endpoint URL, e.g. https://xxx.openai.azure.com/
- zh_Hans: 请输入你的 Azure OpenAI API域名,例如:https://xxx.openai.azure.com/
- pt_BR: Introduza a URL do Azure OpenAI Endpoint, e.g. https://xxx.openai.azure.com/
- placeholder:
- en_US: Please input your Azure OpenAI Endpoint URL, e.g. https://xxx.openai.azure.com/
- zh_Hans: 请输入你的 Azure OpenAI API域名,例如:https://xxx.openai.azure.com/
- pt_BR: Introduza a URL do Azure OpenAI Endpoint, e.g. https://xxx.openai.azure.com/
- azure_openai_api_version:
- type: text-input
- required: true
- label:
- en_US: API Version
- zh_Hans: API 版本
- pt_BR: API Version
- help:
- en_US: Please input your Azure OpenAI API Version,e.g. 2023-12-01-preview
- zh_Hans: 请输入你的 Azure OpenAI API 版本,例如:2023-12-01-preview
- pt_BR: Introduza a versão da API OpenAI do Azure,e.g. 2023-12-01-preview
- placeholder:
- en_US: Please input your Azure OpenAI API Version,e.g. 2023-12-01-preview
- zh_Hans: 请输入你的 Azure OpenAI API 版本,例如:2023-12-01-preview
- pt_BR: Introduza a versão da API OpenAI do Azure,e.g. 2023-12-01-preview
diff --git a/api/core/tools/provider/builtin/azuredalle/tools/dalle3.py b/api/core/tools/provider/builtin/azuredalle/tools/dalle3.py
deleted file mode 100644
index cfa3cfb092803a..00000000000000
--- a/api/core/tools/provider/builtin/azuredalle/tools/dalle3.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import random
-from base64 import b64decode
-from typing import Any, Union
-
-from openai import AzureOpenAI
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DallE3Tool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- client = AzureOpenAI(
- api_version=self.runtime.credentials["azure_openai_api_version"],
- azure_endpoint=self.runtime.credentials["azure_openai_base_url"],
- api_key=self.runtime.credentials["azure_openai_api_key"],
- )
-
- SIZE_MAPPING = {
- "square": "1024x1024",
- "vertical": "1024x1792",
- "horizontal": "1792x1024",
- }
-
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
- # get size
- size = SIZE_MAPPING[tool_parameters.get("size", "square")]
- # get n
- n = tool_parameters.get("n", 1)
- # get quality
- quality = tool_parameters.get("quality", "standard")
- if quality not in {"standard", "hd"}:
- return self.create_text_message("Invalid quality")
- # get style
- style = tool_parameters.get("style", "vivid")
- if style not in {"natural", "vivid"}:
- return self.create_text_message("Invalid style")
- # set extra body
- seed_id = tool_parameters.get("seed_id", self._generate_random_id(8))
- extra_body = {"seed": seed_id}
-
- # call openapi dalle3
- model = self.runtime.credentials["azure_openai_api_model_name"]
- response = client.images.generate(
- prompt=prompt,
- model=model,
- size=size,
- n=n,
- extra_body=extra_body,
- style=style,
- quality=quality,
- response_format="b64_json",
- )
-
- result = []
-
- for image in response.data:
- result.append(
- self.create_blob_message(
- blob=b64decode(image.b64_json),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
- result.append(self.create_text_message(f"\nGenerate image source to Seed ID: {seed_id}"))
-
- return result
-
- @staticmethod
- def _generate_random_id(length=8):
- characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- random_id = "".join(random.choices(characters, k=length))
- return random_id
diff --git a/api/core/tools/provider/builtin/azuredalle/tools/dalle3.yaml b/api/core/tools/provider/builtin/azuredalle/tools/dalle3.yaml
deleted file mode 100644
index e256748e8f7188..00000000000000
--- a/api/core/tools/provider/builtin/azuredalle/tools/dalle3.yaml
+++ /dev/null
@@ -1,136 +0,0 @@
-identity:
- name: azure_dalle3
- author: Leslie
- label:
- en_US: Azure DALL-E 3
- zh_Hans: Azure DALL-E 3 绘画
- pt_BR: Azure DALL-E 3
- description:
- en_US: DALL-E 3 is a powerful drawing tool that can draw the image you want based on your prompt, compared to DallE 2, DallE 3 has stronger drawing ability, but it will consume more resources
- zh_Hans: DALL-E 3 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像,相比于DallE 2, DallE 3拥有更强的绘画能力,但会消耗更多的资源
- pt_BR: DALL-E 3 é uma poderosa ferramenta de desenho que pode desenhar a imagem que você deseja com base em seu prompt, em comparação com DallE 2, DallE 3 tem uma capacidade de desenho mais forte, mas consumirá mais recursos
-description:
- human:
- en_US: DALL-E is a text to image tool
- zh_Hans: DALL-E 是一个文本到图像的工具
- pt_BR: DALL-E é uma ferramenta de texto para imagem
- llm: DALL-E is a tool used to generate images from text
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of DallE 3
- zh_Hans: 图像提示词,您可以查看 DallE 3 的官方文档
- pt_BR: Imagem prompt, você pode verificar a documentação oficial do DallE 3
- llm_description: Image prompt of DallE 3, you should describe the image you want to generate as a list of words as possible as detailed
- form: llm
- - name: seed_id
- type: string
- required: false
- label:
- en_US: Seed ID
- zh_Hans: 种子ID
- pt_BR: ID da semente
- human_description:
- en_US: Image generation seed ID to ensure consistency of series generated images
- zh_Hans: 图像生成种子ID,确保系列生成图像的一致性
- pt_BR: ID de semente de geração de imagem para garantir a consistência das imagens geradas em série
- llm_description: If the user requests image consistency, extract the seed ID from the user's question or context.The seed id consists of an 8-bit string containing uppercase and lowercase letters and numbers
- form: llm
- - name: size
- type: select
- required: true
- human_description:
- en_US: selecting the image size
- zh_Hans: 选择图像大小
- pt_BR: seleccionar o tamanho da imagem
- label:
- en_US: Image size
- zh_Hans: 图像大小
- pt_BR: Tamanho da imagem
- form: form
- options:
- - value: square
- label:
- en_US: Squre(1024x1024)
- zh_Hans: 方(1024x1024)
- pt_BR: Squire(1024x1024)
- - value: vertical
- label:
- en_US: Vertical(1024x1792)
- zh_Hans: 竖屏(1024x1792)
- pt_BR: Vertical(1024x1792)
- - value: horizontal
- label:
- en_US: Horizontal(1792x1024)
- zh_Hans: 横屏(1792x1024)
- pt_BR: Horizontal(1792x1024)
- default: square
- - name: n
- type: number
- required: true
- human_description:
- en_US: selecting the number of images
- zh_Hans: 选择图像数量
- pt_BR: seleccionar o número de imagens
- label:
- en_US: Number of images
- zh_Hans: 图像数量
- pt_BR: Número de imagens
- form: form
- min: 1
- max: 1
- default: 1
- - name: quality
- type: select
- required: true
- human_description:
- en_US: selecting the image quality
- zh_Hans: 选择图像质量
- pt_BR: seleccionar a qualidade da imagem
- label:
- en_US: Image quality
- zh_Hans: 图像质量
- pt_BR: Qualidade da imagem
- form: form
- options:
- - value: standard
- label:
- en_US: Standard
- zh_Hans: 标准
- pt_BR: Normal
- - value: hd
- label:
- en_US: HD
- zh_Hans: 高清
- pt_BR: HD
- default: standard
- - name: style
- type: select
- required: true
- human_description:
- en_US: selecting the image style
- zh_Hans: 选择图像风格
- pt_BR: seleccionar o estilo da imagem
- label:
- en_US: Image style
- zh_Hans: 图像风格
- pt_BR: Estilo da imagem
- form: form
- options:
- - value: vivid
- label:
- en_US: Vivid
- zh_Hans: 生动
- pt_BR: Vívido
- - value: natural
- label:
- en_US: Natural
- zh_Hans: 自然
- pt_BR: Natural
- default: vivid
diff --git a/api/core/tools/provider/builtin/baidu_translate/_assets/icon.png b/api/core/tools/provider/builtin/baidu_translate/_assets/icon.png
deleted file mode 100644
index 8eb8f21513ba7d..00000000000000
Binary files a/api/core/tools/provider/builtin/baidu_translate/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/baidu_translate/_baidu_translate_tool_base.py b/api/core/tools/provider/builtin/baidu_translate/_baidu_translate_tool_base.py
deleted file mode 100644
index ce907c3c616e07..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/_baidu_translate_tool_base.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from hashlib import md5
-
-
-class BaiduTranslateToolBase:
- def _get_sign(self, appid, secret, salt, query):
- """
- get baidu translate sign
- """
- # concatenate the string in the order of appid+q+salt+secret
- str = appid + query + salt + secret
- return md5(str.encode("utf-8")).hexdigest()
diff --git a/api/core/tools/provider/builtin/baidu_translate/baidu_translate.py b/api/core/tools/provider/builtin/baidu_translate/baidu_translate.py
deleted file mode 100644
index cccd2f8c8fc478..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/baidu_translate.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.baidu_translate.tools.translate import BaiduTranslateTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class BaiduTranslateProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- BaiduTranslateTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(user_id="", tool_parameters={"q": "这是一段测试文本", "from": "auto", "to": "en"})
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/baidu_translate/baidu_translate.yaml b/api/core/tools/provider/builtin/baidu_translate/baidu_translate.yaml
deleted file mode 100644
index 06dadeeefc9cde..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/baidu_translate.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-identity:
- author: Xiao Ley
- name: baidu_translate
- label:
- en_US: Baidu Translate
- zh_Hans: 百度翻译
- description:
- en_US: Translate text using Baidu
- zh_Hans: 使用百度进行翻译
- icon: icon.png
- tags:
- - utilities
-credentials_for_provider:
- appid:
- type: secret-input
- required: true
- label:
- en_US: Baidu translate appid
- zh_Hans: Baidu translate appid
- placeholder:
- en_US: Please input your Baidu translate appid
- zh_Hans: 请输入你的百度翻译 appid
- help:
- en_US: Get your Baidu translate appid from Baidu translate
- zh_Hans: 从百度翻译开放平台获取你的 appid
- url: https://api.fanyi.baidu.com
- secret:
- type: secret-input
- required: true
- label:
- en_US: Baidu translate secret
- zh_Hans: Baidu translate secret
- placeholder:
- en_US: Please input your Baidu translate secret
- zh_Hans: 请输入你的百度翻译 secret
- help:
- en_US: Get your Baidu translate secret from Baidu translate
- zh_Hans: 从百度翻译开放平台获取你的 secret
- url: https://api.fanyi.baidu.com
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.py b/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.py
deleted file mode 100644
index ff5cf32ddc7525..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import random
-from hashlib import md5
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.baidu_translate._baidu_translate_tool_base import BaiduTranslateToolBase
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BaiduFieldTranslateTool(BuiltinTool, BaiduTranslateToolBase):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- BAIDU_FIELD_TRANSLATE_URL = "https://fanyi-api.baidu.com/api/trans/vip/fieldtranslate"
-
- appid = self.runtime.credentials.get("appid", "")
- if not appid:
- raise ValueError("invalid baidu translate appid")
-
- secret = self.runtime.credentials.get("secret", "")
- if not secret:
- raise ValueError("invalid baidu translate secret")
-
- q = tool_parameters.get("q", "")
- if not q:
- raise ValueError("Please input text to translate")
-
- from_ = tool_parameters.get("from", "")
- if not from_:
- raise ValueError("Please select source language")
-
- to = tool_parameters.get("to", "")
- if not to:
- raise ValueError("Please select destination language")
-
- domain = tool_parameters.get("domain", "")
- if not domain:
- raise ValueError("Please select domain")
-
- salt = str(random.randint(32768, 16777215))
- sign = self._get_sign(appid, secret, salt, q, domain)
-
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
- params = {
- "q": q,
- "from": from_,
- "to": to,
- "appid": appid,
- "salt": salt,
- "domain": domain,
- "sign": sign,
- "needIntervene": 1,
- }
- try:
- response = requests.post(BAIDU_FIELD_TRANSLATE_URL, headers=headers, data=params)
- result = response.json()
-
- if "trans_result" in result:
- result_text = result["trans_result"][0]["dst"]
- else:
- result_text = f"{result['error_code']}: {result['error_msg']}"
-
- return self.create_text_message(str(result_text))
- except requests.RequestException as e:
- raise ValueError(f"Translation service error: {e}")
- except Exception:
- raise ValueError("Translation service error, please check the network")
-
- def _get_sign(self, appid, secret, salt, query, domain):
- str = appid + query + salt + domain + secret
- return md5(str.encode("utf-8")).hexdigest()
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.yaml b/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.yaml
deleted file mode 100644
index de51fddbaea422..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/fieldtranslate.yaml
+++ /dev/null
@@ -1,123 +0,0 @@
-identity:
- name: field_translate
- author: Xiao Ley
- label:
- en_US: Field translate
- zh_Hans: 百度领域翻译
-description:
- human:
- en_US: A tool for Baidu Field translate (Currently, the fields of "novel" and "wiki" only support Chinese to English translation. If the language direction is set to English to Chinese, the default output will be a universal translation result).
- zh_Hans: 百度领域翻译,提供多种领域的文本翻译(目前“网络文学领域”和“人文社科领域”仅支持中到英,如设置语言方向为英到中,则默认输出通用翻译结果)
- llm: A tool for Baidu Field translate
-parameters:
- - name: q
- type: string
- required: true
- label:
- en_US: Text content
- zh_Hans: 文本内容
- human_description:
- en_US: Text content to be translated
- zh_Hans: 需要翻译的文本内容
- llm_description: Text content to be translated
- form: llm
- - name: from
- type: select
- required: true
- label:
- en_US: source language
- zh_Hans: 源语言
- human_description:
- en_US: The source language of the input text
- zh_Hans: 输入的文本的源语言
- default: auto
- form: form
- options:
- - value: auto
- label:
- en_US: auto
- zh_Hans: 自动检测
- - value: zh
- label:
- en_US: Chinese
- zh_Hans: 中文
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - name: to
- type: select
- required: true
- label:
- en_US: destination language
- zh_Hans: 目标语言
- human_description:
- en_US: The destination language of the input text
- zh_Hans: 输入文本的目标语言
- default: en
- form: form
- options:
- - value: zh
- label:
- en_US: Chinese
- zh_Hans: 中文
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - name: domain
- type: select
- required: true
- label:
- en_US: domain
- zh_Hans: 领域
- human_description:
- en_US: The domain of the input text
- zh_Hans: 输入文本的领域
- default: novel
- form: form
- options:
- - value: it
- label:
- en_US: it
- zh_Hans: 信息技术领域
- - value: finance
- label:
- en_US: finance
- zh_Hans: 金融财经领域
- - value: machinery
- label:
- en_US: machinery
- zh_Hans: 机械制造领域
- - value: senimed
- label:
- en_US: senimed
- zh_Hans: 生物医药领域
- - value: novel
- label:
- en_US: novel (only support Chinese to English translation)
- zh_Hans: 网络文学领域(仅支持中到英)
- - value: academic
- label:
- en_US: academic
- zh_Hans: 学术论文领域
- - value: aerospace
- label:
- en_US: aerospace
- zh_Hans: 航空航天领域
- - value: wiki
- label:
- en_US: wiki (only support Chinese to English translation)
- zh_Hans: 人文社科领域(仅支持中到英)
- - value: news
- label:
- en_US: news
- zh_Hans: 新闻咨询领域
- - value: law
- label:
- en_US: law
- zh_Hans: 法律法规领域
- - value: contract
- label:
- en_US: contract
- zh_Hans: 合同领域
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/language.py b/api/core/tools/provider/builtin/baidu_translate/tools/language.py
deleted file mode 100644
index b7fd692b7d1904..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/language.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import random
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.baidu_translate._baidu_translate_tool_base import BaiduTranslateToolBase
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BaiduLanguageTool(BuiltinTool, BaiduTranslateToolBase):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- BAIDU_LANGUAGE_URL = "https://fanyi-api.baidu.com/api/trans/vip/language"
-
- appid = self.runtime.credentials.get("appid", "")
- if not appid:
- raise ValueError("invalid baidu translate appid")
-
- secret = self.runtime.credentials.get("secret", "")
- if not secret:
- raise ValueError("invalid baidu translate secret")
-
- q = tool_parameters.get("q", "")
- if not q:
- raise ValueError("Please input text to translate")
-
- description_language = tool_parameters.get("description_language", "English")
-
- salt = str(random.randint(32768, 16777215))
- sign = self._get_sign(appid, secret, salt, q)
-
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
- params = {
- "q": q,
- "appid": appid,
- "salt": salt,
- "sign": sign,
- }
-
- try:
- response = requests.post(BAIDU_LANGUAGE_URL, params=params, headers=headers)
- result = response.json()
- if "error_code" not in result:
- raise ValueError("Translation service error, please check the network")
-
- result_text = ""
- if result["error_code"] != 0:
- result_text = f"{result['error_code']}: {result['error_msg']}"
- else:
- result_text = result["data"]["src"]
- result_text = self.mapping_result(description_language, result_text)
-
- return self.create_text_message(result_text)
- except requests.RequestException as e:
- raise ValueError(f"Translation service error: {e}")
- except Exception:
- raise ValueError("Translation service error, please check the network")
-
- def mapping_result(self, description_language: str, result: str) -> str:
- """
- mapping result
- """
- mapping = {
- "English": {
- "zh": "Chinese",
- "en": "English",
- "jp": "Japanese",
- "kor": "Korean",
- "th": "Thai",
- "vie": "Vietnamese",
- "ru": "Russian",
- },
- "Chinese": {
- "zh": "中文",
- "en": "英文",
- "jp": "日文",
- "kor": "韩文",
- "th": "泰语",
- "vie": "越南语",
- "ru": "俄语",
- },
- }
-
- language_mapping = mapping.get(description_language)
- if not language_mapping:
- return result
-
- return language_mapping.get(result, result)
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/language.yaml b/api/core/tools/provider/builtin/baidu_translate/tools/language.yaml
deleted file mode 100644
index 60cca2e288a622..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/language.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-identity:
- name: language
- author: Xiao Ley
- label:
- en_US: Baidu Language
- zh_Hans: 百度语种识别
-description:
- human:
- en_US: A tool for Baidu Language, support Chinese, English, Japanese, Korean, Thai, Vietnamese and Russian
- zh_Hans: 使用百度进行语种识别,支持的语种:中文、英语、日语、韩语、泰语、越南语和俄语
- llm: A tool for Baidu Language
-parameters:
- - name: q
- type: string
- required: true
- label:
- en_US: Text content
- zh_Hans: 文本内容
- human_description:
- en_US: Text content to be recognized
- zh_Hans: 需要识别语言的文本内容
- llm_description: Text content to be recognized
- form: llm
- - name: description_language
- type: select
- required: true
- label:
- en_US: Description language
- zh_Hans: 描述语言
- human_description:
- en_US: Describe the language used to identify the results
- zh_Hans: 描述识别结果所用的语言
- default: Chinese
- form: form
- options:
- - value: Chinese
- label:
- en_US: Chinese
- zh_Hans: 中文
- - value: English
- label:
- en_US: English
- zh_Hans: 英语
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/translate.py b/api/core/tools/provider/builtin/baidu_translate/tools/translate.py
deleted file mode 100644
index 0d25466a7060fc..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/translate.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import random
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.baidu_translate._baidu_translate_tool_base import BaiduTranslateToolBase
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BaiduTranslateTool(BuiltinTool, BaiduTranslateToolBase):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- BAIDU_TRANSLATE_URL = "https://fanyi-api.baidu.com/api/trans/vip/translate"
-
- appid = self.runtime.credentials.get("appid", "")
- if not appid:
- raise ValueError("invalid baidu translate appid")
-
- secret = self.runtime.credentials.get("secret", "")
- if not secret:
- raise ValueError("invalid baidu translate secret")
-
- q = tool_parameters.get("q", "")
- if not q:
- raise ValueError("Please input text to translate")
-
- from_ = tool_parameters.get("from", "")
- if not from_:
- raise ValueError("Please select source language")
-
- to = tool_parameters.get("to", "")
- if not to:
- raise ValueError("Please select destination language")
-
- salt = str(random.randint(32768, 16777215))
- sign = self._get_sign(appid, secret, salt, q)
-
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
- params = {
- "q": q,
- "from": from_,
- "to": to,
- "appid": appid,
- "salt": salt,
- "sign": sign,
- }
- try:
- response = requests.post(BAIDU_TRANSLATE_URL, params=params, headers=headers)
- result = response.json()
-
- if "trans_result" in result:
- result_text = result["trans_result"][0]["dst"]
- else:
- result_text = f"{result['error_code']}: {result['error_msg']}"
-
- return self.create_text_message(str(result_text))
- except requests.RequestException as e:
- raise ValueError(f"Translation service error: {e}")
- except Exception:
- raise ValueError("Translation service error, please check the network")
diff --git a/api/core/tools/provider/builtin/baidu_translate/tools/translate.yaml b/api/core/tools/provider/builtin/baidu_translate/tools/translate.yaml
deleted file mode 100644
index c8ff32cb6bb1f1..00000000000000
--- a/api/core/tools/provider/builtin/baidu_translate/tools/translate.yaml
+++ /dev/null
@@ -1,275 +0,0 @@
-identity:
- name: translate
- author: Xiao Ley
- label:
- en_US: Translate
- zh_Hans: 百度翻译
-description:
- human:
- en_US: A tool for Baidu Translate
- zh_Hans: 百度翻译
- llm: A tool for Baidu Translate
-parameters:
- - name: q
- type: string
- required: true
- label:
- en_US: Text content
- zh_Hans: 文本内容
- human_description:
- en_US: Text content to be translated
- zh_Hans: 需要翻译的文本内容
- llm_description: Text content to be translated
- form: llm
- - name: from
- type: select
- required: true
- label:
- en_US: source language
- zh_Hans: 源语言
- human_description:
- en_US: The source language of the input text
- zh_Hans: 输入的文本的源语言
- default: auto
- form: form
- options:
- - value: auto
- label:
- en_US: auto
- zh_Hans: 自动检测
- - value: zh
- label:
- en_US: Chinese
- zh_Hans: 中文
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: cht
- label:
- en_US: Traditional Chinese
- zh_Hans: 繁体中文
- - value: yue
- label:
- en_US: Yue
- zh_Hans: 粤语
- - value: wyw
- label:
- en_US: Wyw
- zh_Hans: 文言文
- - value: jp
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kor
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: fra
- label:
- en_US: French
- zh_Hans: 法语
- - value: spa
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: ara
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: bul
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: est
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: dan
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: fin
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: rom
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: slo
- label:
- en_US: Slovak
- zh_Hans: 斯洛文尼亚语
- - value: swe
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: vie
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- - name: to
- type: select
- required: true
- label:
- en_US: destination language
- zh_Hans: 目标语言
- human_description:
- en_US: The destination language of the input text
- zh_Hans: 输入文本的目标语言
- default: en
- form: form
- options:
- - value: zh
- label:
- en_US: Chinese
- zh_Hans: 中文
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: cht
- label:
- en_US: Traditional Chinese
- zh_Hans: 繁体中文
- - value: yue
- label:
- en_US: Yue
- zh_Hans: 粤语
- - value: wyw
- label:
- en_US: Wyw
- zh_Hans: 文言文
- - value: jp
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kor
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: fra
- label:
- en_US: French
- zh_Hans: 法语
- - value: spa
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: ara
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: bul
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: est
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: dan
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: fin
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: rom
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: slo
- label:
- en_US: Slovak
- zh_Hans: 斯洛文尼亚语
- - value: swe
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: vie
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
diff --git a/api/core/tools/provider/builtin/bing/_assets/icon.svg b/api/core/tools/provider/builtin/bing/_assets/icon.svg
deleted file mode 100644
index a94de7971d35b7..00000000000000
--- a/api/core/tools/provider/builtin/bing/_assets/icon.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/bing/bing.py b/api/core/tools/provider/builtin/bing/bing.py
deleted file mode 100644
index c71128be4a784f..00000000000000
--- a/api/core/tools/provider/builtin/bing/bing.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.bing.tools.bing_web_search import BingSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class BingProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- BingSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).validate_credentials(
- credentials=credentials,
- tool_parameters={
- "query": "test",
- "result_type": "link",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/bing/bing.yaml b/api/core/tools/provider/builtin/bing/bing.yaml
deleted file mode 100644
index 1ab17d5294b37c..00000000000000
--- a/api/core/tools/provider/builtin/bing/bing.yaml
+++ /dev/null
@@ -1,107 +0,0 @@
-identity:
- author: Dify
- name: bing
- label:
- en_US: Bing
- zh_Hans: Bing
- pt_BR: Bing
- description:
- en_US: Bing Search
- zh_Hans: Bing 搜索
- pt_BR: Bing Search
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- subscription_key:
- type: secret-input
- required: true
- label:
- en_US: Bing subscription key
- zh_Hans: Bing subscription key
- pt_BR: Bing subscription key
- placeholder:
- en_US: Please input your Bing subscription key
- zh_Hans: 请输入你的 Bing subscription key
- pt_BR: Please input your Bing subscription key
- help:
- en_US: Get your Bing subscription key from Bing
- zh_Hans: 从 Bing 获取您的 Bing subscription key
- pt_BR: Get your Bing subscription key from Bing
- url: https://www.microsoft.com/cognitive-services/en-us/bing-web-search-api
- server_url:
- type: text-input
- required: false
- label:
- en_US: Bing endpoint
- zh_Hans: Bing endpoint
- pt_BR: Bing endpoint
- placeholder:
- en_US: Please input your Bing endpoint
- zh_Hans: 请输入你的 Bing 端点
- pt_BR: Please input your Bing endpoint
- help:
- en_US: An endpoint is like "https://api.bing.microsoft.com/v7.0/search"
- zh_Hans: 例如 "https://api.bing.microsoft.com/v7.0/search"
- pt_BR: An endpoint is like "https://api.bing.microsoft.com/v7.0/search"
- default: https://api.bing.microsoft.com/v7.0/search
- allow_entities:
- type: boolean
- required: false
- label:
- en_US: Allow Entities Search
- zh_Hans: 支持实体搜索
- pt_BR: Allow Entities Search
- help:
- en_US: Does your subscription plan allow entity search
- zh_Hans: 您的订阅计划是否支持实体搜索
- pt_BR: Does your subscription plan allow entity search
- default: true
- allow_web_pages:
- type: boolean
- required: false
- label:
- en_US: Allow Web Pages Search
- zh_Hans: 支持网页搜索
- pt_BR: Allow Web Pages Search
- help:
- en_US: Does your subscription plan allow web pages search
- zh_Hans: 您的订阅计划是否支持网页搜索
- pt_BR: Does your subscription plan allow web pages search
- default: true
- allow_computation:
- type: boolean
- required: false
- label:
- en_US: Allow Computation Search
- zh_Hans: 支持计算搜索
- pt_BR: Allow Computation Search
- help:
- en_US: Does your subscription plan allow computation search
- zh_Hans: 您的订阅计划是否支持计算搜索
- pt_BR: Does your subscription plan allow computation search
- default: false
- allow_news:
- type: boolean
- required: false
- label:
- en_US: Allow News Search
- zh_Hans: 支持新闻搜索
- pt_BR: Allow News Search
- help:
- en_US: Does your subscription plan allow news search
- zh_Hans: 您的订阅计划是否支持新闻搜索
- pt_BR: Does your subscription plan allow news search
- default: false
- allow_related_searches:
- type: boolean
- required: false
- label:
- en_US: Allow Related Searches
- zh_Hans: 支持相关搜索
- pt_BR: Allow Related Searches
- help:
- en_US: Does your subscription plan allow related searches
- zh_Hans: 您的订阅计划是否支持相关搜索
- pt_BR: Does your subscription plan allow related searches
- default: false
diff --git a/api/core/tools/provider/builtin/bing/tools/bing_web_search.py b/api/core/tools/provider/builtin/bing/tools/bing_web_search.py
deleted file mode 100644
index 0de693698363f8..00000000000000
--- a/api/core/tools/provider/builtin/bing/tools/bing_web_search.py
+++ /dev/null
@@ -1,237 +0,0 @@
-from typing import Any, Union
-from urllib.parse import quote
-
-from requests import get
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BingSearchTool(BuiltinTool):
- url: str = "https://api.bing.microsoft.com/v7.0/search"
-
- def _invoke_bing(
- self,
- user_id: str,
- server_url: str,
- subscription_key: str,
- query: str,
- limit: int,
- result_type: str,
- market: str,
- lang: str,
- filters: list[str],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke bing search
- """
- market_code = f"{lang}-{market}"
- accept_language = f"{lang},{market_code};q=0.9"
- headers = {"Ocp-Apim-Subscription-Key": subscription_key, "Accept-Language": accept_language}
-
- query = quote(query)
- server_url = f"{server_url}?q={query}&mkt={market_code}&count={limit}&responseFilter={','.join(filters)}"
- response = get(server_url, headers=headers)
-
- if response.status_code != 200:
- raise Exception(f"Error {response.status_code}: {response.text}")
-
- response = response.json()
- search_results = response["webPages"]["value"][:limit] if "webPages" in response else []
- related_searches = response["relatedSearches"]["value"] if "relatedSearches" in response else []
- entities = response["entities"]["value"] if "entities" in response else []
- news = response["news"]["value"] if "news" in response else []
- computation = response["computation"]["value"] if "computation" in response else None
-
- if result_type == "link":
- results = []
- if search_results:
- for result in search_results:
- url = f": {result['url']}" if "url" in result else ""
- results.append(self.create_text_message(text=f"{result['name']}{url}"))
-
- if entities:
- for entity in entities:
- url = f": {entity['url']}" if "url" in entity else ""
- results.append(self.create_text_message(text=f"{entity.get('name', '')}{url}"))
-
- if news:
- for news_item in news:
- url = f": {news_item['url']}" if "url" in news_item else ""
- results.append(self.create_text_message(text=f"{news_item.get('name', '')}{url}"))
-
- if related_searches:
- for related in related_searches:
- url = f": {related['displayText']}" if "displayText" in related else ""
- results.append(self.create_text_message(text=f"{related.get('displayText', '')}{url}"))
-
- return results
- elif result_type == "json":
- result = {}
- if search_results:
- result["organic"] = [
- {
- "title": item.get("name", ""),
- "snippet": item.get("snippet", ""),
- "url": item.get("url", ""),
- "siteName": item.get("siteName", ""),
- }
- for item in search_results
- ]
-
- if computation and "expression" in computation and "value" in computation:
- result["computation"] = {"expression": computation["expression"], "value": computation["value"]}
-
- if entities:
- result["entities"] = [
- {
- "name": item.get("name", ""),
- "url": item.get("url", ""),
- "description": item.get("description", ""),
- }
- for item in entities
- ]
-
- if news:
- result["news"] = [{"name": item.get("name", ""), "url": item.get("url", "")} for item in news]
-
- if related_searches:
- result["related searches"] = [
- {"displayText": item.get("displayText", ""), "url": item.get("webSearchUrl", "")} for item in news
- ]
-
- return self.create_json_message(result)
- else:
- # construct text
- text = ""
- if search_results:
- for i, result in enumerate(search_results):
- text += f"{i + 1}: {result.get('name', '')} - {result.get('snippet', '')}\n"
-
- if computation and "expression" in computation and "value" in computation:
- text += "\nComputation:\n"
- text += f"{computation['expression']} = {computation['value']}\n"
-
- if entities:
- text += "\nEntities:\n"
- for entity in entities:
- url = f"- {entity['url']}" if "url" in entity else ""
- text += f"{entity.get('name', '')}{url}\n"
-
- if news:
- text += "\nNews:\n"
- for news_item in news:
- url = f"- {news_item['url']}" if "url" in news_item else ""
- text += f"{news_item.get('name', '')}{url}\n"
-
- if related_searches:
- text += "\n\nRelated Searches:\n"
- for related in related_searches:
- url = f"- {related['webSearchUrl']}" if "webSearchUrl" in related else ""
- text += f"{related.get('displayText', '')}{url}\n"
-
- return self.create_text_message(text=self.summary(user_id=user_id, content=text))
-
- def validate_credentials(self, credentials: dict[str, Any], tool_parameters: dict[str, Any]) -> None:
- key = credentials.get("subscription_key")
- if not key:
- raise Exception("subscription_key is required")
-
- server_url = credentials.get("server_url")
- if not server_url:
- server_url = self.url
-
- query = tool_parameters.get("query")
- if not query:
- raise Exception("query is required")
-
- limit = min(tool_parameters.get("limit", 5), 10)
- result_type = tool_parameters.get("result_type", "text") or "text"
-
- market = tool_parameters.get("market", "US")
- lang = tool_parameters.get("language", "en")
- filter = []
-
- if credentials.get("allow_entities", False):
- filter.append("Entities")
-
- if credentials.get("allow_computation", False):
- filter.append("Computation")
-
- if credentials.get("allow_news", False):
- filter.append("News")
-
- if credentials.get("allow_related_searches", False):
- filter.append("RelatedSearches")
-
- if credentials.get("allow_web_pages", False):
- filter.append("WebPages")
-
- if not filter:
- raise Exception("At least one filter is required")
-
- self._invoke_bing(
- user_id="test",
- server_url=server_url,
- subscription_key=key,
- query=query,
- limit=limit,
- result_type=result_type,
- market=market,
- lang=lang,
- filters=filter,
- )
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
-
- key = self.runtime.credentials.get("subscription_key", None)
- if not key:
- raise Exception("subscription_key is required")
-
- server_url = self.runtime.credentials.get("server_url", None)
- if not server_url:
- server_url = self.url
-
- query = tool_parameters.get("query")
- if not query:
- raise Exception("query is required")
-
- limit = min(tool_parameters.get("limit", 5), 10)
- result_type = tool_parameters.get("result_type", "text") or "text"
-
- market = tool_parameters.get("market", "US")
- lang = tool_parameters.get("language", "en")
- filter = []
-
- if tool_parameters.get("enable_computation", False):
- filter.append("Computation")
- if tool_parameters.get("enable_entities", False):
- filter.append("Entities")
- if tool_parameters.get("enable_news", False):
- filter.append("News")
- if tool_parameters.get("enable_related_search", False):
- filter.append("RelatedSearches")
- if tool_parameters.get("enable_webpages", False):
- filter.append("WebPages")
-
- if not filter:
- raise Exception("At least one filter is required")
-
- return self._invoke_bing(
- user_id=user_id,
- server_url=server_url,
- subscription_key=key,
- query=query,
- limit=limit,
- result_type=result_type,
- market=market,
- lang=lang,
- filters=filter,
- )
diff --git a/api/core/tools/provider/builtin/bing/tools/bing_web_search.yaml b/api/core/tools/provider/builtin/bing/tools/bing_web_search.yaml
deleted file mode 100644
index f5c932c37b3014..00000000000000
--- a/api/core/tools/provider/builtin/bing/tools/bing_web_search.yaml
+++ /dev/null
@@ -1,589 +0,0 @@
-identity:
- name: bing_web_search
- author: Dify
- label:
- en_US: BingWebSearch
- zh_Hans: 必应网页搜索
- pt_BR: BingWebSearch
-description:
- human:
- en_US: A tool for performing a Bing SERP search and extracting snippets and webpages.Input should be a search query.
- zh_Hans: 一个用于执行 Bing SERP 搜索并提取片段和网页的工具。输入应该是一个搜索查询。
- pt_BR: A tool for performing a Bing SERP search and extracting snippets and webpages.Input should be a search query.
- llm: A tool for performing a Bing SERP search and extracting snippets and webpages.Input should be a search query.
-parameters:
- - name: query
- type: string
- required: true
- form: llm
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: used for searching
- zh_Hans: 用于搜索网页内容
- pt_BR: used for searching
- llm_description: key words for searching
- - name: enable_computation
- type: boolean
- required: false
- form: form
- label:
- en_US: Enable computation
- zh_Hans: 启用计算
- pt_BR: Enable computation
- human_description:
- en_US: enable computation
- zh_Hans: 启用计算
- pt_BR: enable computation
- default: false
- - name: enable_entities
- type: boolean
- required: false
- form: form
- label:
- en_US: Enable entities
- zh_Hans: 启用实体搜索
- pt_BR: Enable entities
- human_description:
- en_US: enable entities
- zh_Hans: 启用实体搜索
- pt_BR: enable entities
- default: true
- - name: enable_news
- type: boolean
- required: false
- form: form
- label:
- en_US: Enable news
- zh_Hans: 启用新闻搜索
- pt_BR: Enable news
- human_description:
- en_US: enable news
- zh_Hans: 启用新闻搜索
- pt_BR: enable news
- default: false
- - name: enable_related_search
- type: boolean
- required: false
- form: form
- label:
- en_US: Enable related search
- zh_Hans: 启用相关搜索
- pt_BR: Enable related search
- human_description:
- en_US: enable related search
- zh_Hans: 启用相关搜索
- pt_BR: enable related search
- default: false
- - name: enable_webpages
- type: boolean
- required: false
- form: form
- label:
- en_US: Enable webpages search
- zh_Hans: 启用网页搜索
- pt_BR: Enable webpages search
- human_description:
- en_US: enable webpages search
- zh_Hans: 启用网页搜索
- pt_BR: enable webpages search
- default: true
- - name: limit
- type: number
- required: true
- form: form
- label:
- en_US: Limit for results length
- zh_Hans: 返回长度限制
- pt_BR: Limit for results length
- human_description:
- en_US: limit the number of results
- zh_Hans: 限制返回结果的数量
- pt_BR: limit the number of results
- min: 1
- max: 10
- default: 5
- - name: result_type
- type: select
- required: true
- label:
- en_US: result type
- zh_Hans: 结果类型
- pt_BR: result type
- human_description:
- en_US: return a list of links, json or texts
- zh_Hans: 返回一个列表,内容是链接、json还是纯文本
- pt_BR: return a list of links, json or texts
- default: text
- options:
- - value: link
- label:
- en_US: Link
- zh_Hans: 链接
- pt_BR: Link
- - value: json
- label:
- en_US: JSON
- zh_Hans: JSON
- pt_BR: JSON
- - value: text
- label:
- en_US: Text
- zh_Hans: 文本
- pt_BR: Text
- form: form
- - name: market
- type: select
- label:
- en_US: Market
- zh_Hans: 市场
- pt_BR: Market
- human_description:
- en_US: market takes responsibility for the region
- zh_Hans: 市场决定了搜索结果的地区
- pt_BR: market takes responsibility for the region
- required: false
- form: form
- default: US
- options:
- - value: AR
- label:
- en_US: Argentina
- zh_Hans: 阿根廷
- pt_BR: Argentina
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Austria
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Belgium
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colombia
- - value: CN
- label:
- en_US: China
- zh_Hans: 中国
- pt_BR: China
- - value: CZ
- label:
- en_US: Czech Republic
- zh_Hans: 捷克共和国
- pt_BR: Czech Republic
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Denmark
- - value: FI
- label:
- en_US: Finland
- zh_Hans: 芬兰
- pt_BR: Finland
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonesia
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Italy
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malaysia
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: Mexico
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Netherlands
- - value: NZ
- label:
- en_US: New Zealand
- zh_Hans: 新西兰
- pt_BR: New Zealand
- - value: 'NO'
- label:
- en_US: Norway
- zh_Hans: 挪威
- pt_BR: Norway
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Philippines
- - value: PL
- label:
- en_US: Poland
- zh_Hans: 波兰
- pt_BR: Poland
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: RU
- label:
- en_US: Russia
- zh_Hans: 俄罗斯
- pt_BR: Russia
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Saudi Arabia
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: South Africa
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Spain
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Switzerland
- - value: TW
- label:
- en_US: Taiwan
- zh_Hans: 台湾
- pt_BR: Taiwan
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Thailand
- - value: TR
- label:
- en_US: Turkey
- zh_Hans: 土耳其
- pt_BR: Turkey
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - name: language
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- pt_BR: Language
- human_description:
- en_US: language takes responsibility for the language of the search result
- zh_Hans: 语言决定了搜索结果的语言
- pt_BR: language takes responsibility for the language of the search result
- required: false
- default: en
- form: form
- options:
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- pt_BR: Arabic
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- pt_BR: Bulgarian
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- pt_BR: Catalan
- - value: zh-hans
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- pt_BR: Chinese (Simplified)
- - value: zh-hant
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- pt_BR: Chinese (Traditional)
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- pt_BR: Czech
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- pt_BR: Danish
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- pt_BR: Dutch
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- pt_BR: English
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- pt_BR: Estonian
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- pt_BR: Finnish
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- pt_BR: French
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- pt_BR: German
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- pt_BR: Greek
- - value: he
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- pt_BR: Hebrew
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- pt_BR: Hindi
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- pt_BR: Hungarian
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- pt_BR: Indonesian
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- pt_BR: Italian
- - value: jp
- label:
- en_US: Japanese
- zh_Hans: 日语
- pt_BR: Japanese
- - value: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- pt_BR: Kannada
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- pt_BR: Korean
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- pt_BR: Latvian
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- pt_BR: Lithuanian
- - value: ms
- label:
- en_US: Malay
- zh_Hans: 马来语
- pt_BR: Malay
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- pt_BR: Malayalam
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- pt_BR: Marathi
- - value: nb
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- pt_BR: Norwegian
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- pt_BR: Polish
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- pt_BR: Portuguese (Brazil)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- pt_BR: Portuguese (Portugal)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- pt_BR: Punjabi
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- pt_BR: Romanian
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- pt_BR: Russian
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- pt_BR: Serbian
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- pt_BR: Slovak
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- pt_BR: Slovenian
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- pt_BR: Spanish
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- pt_BR: Swedish
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- pt_BR: Tamil
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- pt_BR: Telugu
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- pt_BR: Thai
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- pt_BR: Turkish
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- pt_BR: Ukrainian
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- pt_BR: Vietnamese
diff --git a/api/core/tools/provider/builtin/brave/_assets/icon.svg b/api/core/tools/provider/builtin/brave/_assets/icon.svg
deleted file mode 100644
index d059f7c5161e98..00000000000000
--- a/api/core/tools/provider/builtin/brave/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/brave/brave.py b/api/core/tools/provider/builtin/brave/brave.py
deleted file mode 100644
index c24ee67334083b..00000000000000
--- a/api/core/tools/provider/builtin/brave/brave.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.brave.tools.brave_search import BraveSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class BraveProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- BraveSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "Sachin Tendulkar",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/brave/brave.yaml b/api/core/tools/provider/builtin/brave/brave.yaml
deleted file mode 100644
index 2b0dcc0188caf8..00000000000000
--- a/api/core/tools/provider/builtin/brave/brave.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-identity:
- author: Yash Parmar
- name: brave
- label:
- en_US: Brave
- zh_Hans: Brave
- pt_BR: Brave
- description:
- en_US: Brave
- zh_Hans: Brave
- pt_BR: Brave
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- brave_search_api_key:
- type: secret-input
- required: true
- label:
- en_US: Brave Search API key
- zh_Hans: Brave Search API key
- pt_BR: Brave Search API key
- placeholder:
- en_US: Please input your Brave Search API key
- zh_Hans: 请输入你的 Brave Search API key
- pt_BR: Please input your Brave Search API key
- help:
- en_US: Get your Brave Search API key from Brave
- zh_Hans: 从 Brave 获取您的 Brave Search API key
- pt_BR: Get your Brave Search API key from Brave
- url: https://brave.com/search/api/
- base_url:
- type: text-input
- required: false
- label:
- en_US: Brave server's Base URL
- zh_Hans: Brave服务器的API URL
- placeholder:
- en_US: https://api.search.brave.com/res/v1/web/search
diff --git a/api/core/tools/provider/builtin/brave/tools/brave_search.py b/api/core/tools/provider/builtin/brave/tools/brave_search.py
deleted file mode 100644
index c34362ae52ecac..00000000000000
--- a/api/core/tools/provider/builtin/brave/tools/brave_search.py
+++ /dev/null
@@ -1,138 +0,0 @@
-import json
-from typing import Any, Optional
-
-import requests
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-BRAVE_BASE_URL = "https://api.search.brave.com/res/v1/web/search"
-
-
-class BraveSearchWrapper(BaseModel):
- """Wrapper around the Brave search engine."""
-
- api_key: str
- """The API key to use for the Brave search engine."""
- search_kwargs: dict = Field(default_factory=dict)
- """Additional keyword arguments to pass to the search request."""
- base_url: str = BRAVE_BASE_URL
- """The base URL for the Brave search engine."""
- ensure_ascii: bool = True
- """Ensure the JSON output is ASCII encoded."""
-
- def run(self, query: str) -> str:
- """Query the Brave search engine and return the results as a JSON string.
-
- Args:
- query: The query to search for.
-
- Returns: The results as a JSON string.
-
- """
- web_search_results = self._search_request(query=query)
- final_results = [
- {
- "title": item.get("title"),
- "link": item.get("url"),
- "snippet": item.get("description"),
- }
- for item in web_search_results
- ]
- return json.dumps(final_results, ensure_ascii=self.ensure_ascii)
-
- def _search_request(self, query: str) -> list[dict]:
- headers = {
- "X-Subscription-Token": self.api_key,
- "Accept": "application/json",
- }
- req = requests.PreparedRequest()
- params = {**self.search_kwargs, **{"q": query}}
- req.prepare_url(self.base_url, params)
- if req.url is None:
- raise ValueError("prepared url is None, this should not happen")
-
- response = requests.get(req.url, headers=headers)
- if not response.ok:
- raise Exception(f"HTTP error {response.status_code}")
-
- return response.json().get("web", {}).get("results", [])
-
-
-class BraveSearch(BaseModel):
- """Tool that queries the BraveSearch."""
-
- name: str = "brave_search"
- description: str = (
- "a search engine. "
- "useful for when you need to answer questions about current events."
- " input should be a search query."
- )
- search_wrapper: BraveSearchWrapper
-
- @classmethod
- def from_api_key(
- cls, api_key: str, base_url: str, search_kwargs: Optional[dict] = None, ensure_ascii: bool = True, **kwargs: Any
- ) -> "BraveSearch":
- """Create a tool from an api key.
-
- Args:
- api_key: The api key to use.
- search_kwargs: Any additional kwargs to pass to the search wrapper.
- **kwargs: Any additional kwargs to pass to the tool.
-
- Returns:
- A tool.
- """
- wrapper = BraveSearchWrapper(
- api_key=api_key, base_url=base_url, search_kwargs=search_kwargs or {}, ensure_ascii=ensure_ascii
- )
- return cls(search_wrapper=wrapper, **kwargs)
-
- def _run(
- self,
- query: str,
- ) -> str:
- """Use the tool."""
- return self.search_wrapper.run(query)
-
-
-class BraveSearchTool(BuiltinTool):
- """
- Tool for performing a search using Brave search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invoke the Brave search tool.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Any]): The parameters for the tool invocation.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation.
- """
- query = tool_parameters.get("query", "")
- count = tool_parameters.get("count", 3)
- api_key = self.runtime.credentials["brave_search_api_key"]
- base_url = self.runtime.credentials.get("base_url", BRAVE_BASE_URL)
- ensure_ascii = tool_parameters.get("ensure_ascii", True)
-
- if len(base_url) == 0:
- base_url = BRAVE_BASE_URL
-
- if not query:
- return self.create_text_message("Please input query")
-
- tool = BraveSearch.from_api_key(
- api_key=api_key, base_url=base_url, search_kwargs={"count": count}, ensure_ascii=ensure_ascii
- )
-
- results = tool._run(query)
-
- if not results:
- return self.create_text_message(f"No results found for '{query}' in Tavily")
- else:
- return self.create_text_message(text=results)
diff --git a/api/core/tools/provider/builtin/brave/tools/brave_search.yaml b/api/core/tools/provider/builtin/brave/tools/brave_search.yaml
deleted file mode 100644
index 5222a375f84cee..00000000000000
--- a/api/core/tools/provider/builtin/brave/tools/brave_search.yaml
+++ /dev/null
@@ -1,53 +0,0 @@
-identity:
- name: brave_search
- author: Yash Parmar
- label:
- en_US: BraveSearch
- zh_Hans: BraveSearch
- pt_BR: BraveSearch
-description:
- human:
- en_US: BraveSearch is a privacy-focused search engine that leverages its own index to deliver unbiased, independent, and fast search results. It's designed to respect user privacy by not tracking searches or personal information, making it a secure choice for those concerned about online privacy.
- zh_Hans: BraveSearch 是一个注重隐私的搜索引擎,它利用自己的索引来提供公正、独立和快速的搜索结果。它旨在通过不跟踪搜索或个人信息来尊重用户隐私,为那些关注在线隐私的用户提供了一个安全的选择。
- pt_BR: BraveSearch é um mecanismo de busca focado na privacidade que utiliza seu próprio índice para entregar resultados de busca imparciais, independentes e rápidos. Ele é projetado para respeitar a privacidade do usuário, não rastreando buscas ou informações pessoais, tornando-se uma escolha segura para aqueles preocupados com a privacidade online.
- llm: BraveSearch is a privacy-centric search engine utilizing its unique index to offer unbiased, independent, and swift search results. It aims to protect user privacy by avoiding the tracking of search activities or personal data, presenting a secure option for users mindful of their online privacy.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: The text input used for initiating searches on the web, focusing on delivering relevant and accurate results without compromising user privacy.
- zh_Hans: 用于在网上启动搜索的文本输入,专注于提供相关且准确的结果,同时不妨碍用户隐私。
- pt_BR: A entrada de texto usada para iniciar pesquisas na web, focada em entregar resultados relevantes e precisos sem comprometer a privacidade do usuário.
- llm_description: Keywords or phrases entered to perform searches, aimed at providing relevant and precise results while ensuring the privacy of the user is maintained.
- form: llm
- - name: count
- type: number
- required: false
- default: 3
- label:
- en_US: Result count
- zh_Hans: 结果数量
- pt_BR: Contagem de resultados
- human_description:
- en_US: The number of search results to return, allowing users to control the breadth of their search output.
- zh_Hans: 要返回的搜索结果数量,允许用户控制他们搜索输出的广度。
- pt_BR: O número de resultados de pesquisa a serem retornados, permitindo que os usuários controlem a amplitude de sua saída de pesquisa.
- llm_description: Specifies the amount of search results to be displayed, offering users the ability to adjust the scope of their search findings.
- form: llm
- - name: ensure_ascii
- type: boolean
- default: true
- label:
- en_US: Ensure ASCII
- zh_Hans: 确保 ASCII
- pt_BR: Ensure ASCII
- human_description:
- en_US: Ensure the JSON output is ASCII encoded
- zh_Hans: 确保输出的 JSON 是 ASCII 编码
- pt_BR: Ensure the JSON output is ASCII encoded
- form: form
diff --git a/api/core/tools/provider/builtin/chart/_assets/icon.png b/api/core/tools/provider/builtin/chart/_assets/icon.png
deleted file mode 100644
index 878e56a0512c31..00000000000000
Binary files a/api/core/tools/provider/builtin/chart/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/chart/chart.py b/api/core/tools/provider/builtin/chart/chart.py
deleted file mode 100644
index 8fa647d9ed8138..00000000000000
--- a/api/core/tools/provider/builtin/chart/chart.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import matplotlib
-import matplotlib.pyplot as plt
-from matplotlib.font_manager import FontProperties, fontManager
-
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-def set_chinese_font():
- to_find_fonts = [
- "PingFang SC",
- "SimHei",
- "Microsoft YaHei",
- "STSong",
- "SimSun",
- "Arial Unicode MS",
- "Noto Sans CJK SC",
- "Noto Sans CJK JP",
- ]
- installed_fonts = frozenset(fontInfo.name for fontInfo in fontManager.ttflist)
- for font in to_find_fonts:
- if font in installed_fonts:
- return FontProperties(font)
-
- return FontProperties()
-
-
-# use non-interactive backend to prevent `RuntimeError: main thread is not in main loop`
-matplotlib.use("Agg")
-# use a business theme
-plt.style.use("seaborn-v0_8-darkgrid")
-plt.rcParams["axes.unicode_minus"] = False
-font_properties = set_chinese_font()
-plt.rcParams["font.family"] = font_properties.get_name()
-
-
-class ChartProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- pass
diff --git a/api/core/tools/provider/builtin/chart/chart.yaml b/api/core/tools/provider/builtin/chart/chart.yaml
deleted file mode 100644
index ad0d9a6cd688cf..00000000000000
--- a/api/core/tools/provider/builtin/chart/chart.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-identity:
- author: Dify
- name: chart
- label:
- en_US: ChartGenerator
- zh_Hans: 图表生成
- pt_BR: Gerador de gráficos
- description:
- en_US: Chart Generator is a tool for generating statistical charts like bar chart, line chart, pie chart, etc.
- zh_Hans: 图表生成是一个用于生成可视化图表的工具,你可以通过它来生成柱状图、折线图、饼图等各类图表
- pt_BR: O Gerador de gráficos é uma ferramenta para gerar gráficos estatísticos como gráfico de barras, gráfico de linhas, gráfico de pizza, etc.
- icon: icon.png
- tags:
- - design
- - productivity
- - utilities
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/chart/tools/bar.py b/api/core/tools/provider/builtin/chart/tools/bar.py
deleted file mode 100644
index 20ce5e138b5bfe..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/bar.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import io
-from typing import Any, Union
-
-import matplotlib.pyplot as plt
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class BarChartTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- data = tool_parameters.get("data", "")
- if not data:
- return self.create_text_message("Please input data")
- data = data.split(";")
-
- # if all data is int, convert to int
- if all(i.isdigit() for i in data):
- data = [int(i) for i in data]
- else:
- data = [float(i) for i in data]
-
- axis = tool_parameters.get("x_axis") or None
- if axis:
- axis = axis.split(";")
- if len(axis) != len(data):
- axis = None
-
- flg, ax = plt.subplots(figsize=(10, 8))
-
- if axis:
- axis = [label[:10] + "..." if len(label) > 10 else label for label in axis]
- ax.set_xticklabels(axis, rotation=45, ha="right")
- # ensure all labels, including duplicates, are correctly displayed
- ax.bar(range(len(data)), data)
- ax.set_xticks(range(len(data)))
- else:
- ax.bar(range(len(data)), data)
-
- buf = io.BytesIO()
- flg.savefig(buf, format="png")
- buf.seek(0)
- plt.close(flg)
-
- return [
- self.create_text_message("the bar chart is saved as an image."),
- self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
- ]
diff --git a/api/core/tools/provider/builtin/chart/tools/bar.yaml b/api/core/tools/provider/builtin/chart/tools/bar.yaml
deleted file mode 100644
index ee7405f6810efa..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/bar.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-identity:
- name: bar_chart
- author: Dify
- label:
- en_US: Bar Chart
- zh_Hans: 柱状图
- pt_BR: Gráfico de barras
- icon: icon.svg
-description:
- human:
- en_US: Bar chart
- zh_Hans: 柱状图
- pt_BR: Gráfico de barras
- llm: generate a bar chart with input data
-parameters:
- - name: data
- type: string
- required: true
- label:
- en_US: data
- zh_Hans: 数据
- pt_BR: dados
- human_description:
- en_US: data for generating chart, each number should be separated by ";"
- zh_Hans: 用于生成柱状图的数据,每个数字之间用 ";" 分隔
- pt_BR: dados para gerar gráfico de barras, cada número deve ser separado por ";"
- llm_description: data for generating bar chart, data should be a string contains a list of numbers like "1;2;3;4;5"
- form: llm
- - name: x_axis
- type: string
- required: false
- label:
- en_US: X Axis
- zh_Hans: x 轴
- pt_BR: Eixo X
- human_description:
- en_US: X axis for chart, each text should be separated by ";"
- zh_Hans: 柱状图的 x 轴,每个文本之间用 ";" 分隔
- pt_BR: Eixo X para gráfico de barras, cada texto deve ser separado por ";"
- llm_description: x axis for bar chart, x axis should be a string contains a list of texts like "a;b;c;1;2" in order to match the data
- form: llm
diff --git a/api/core/tools/provider/builtin/chart/tools/line.py b/api/core/tools/provider/builtin/chart/tools/line.py
deleted file mode 100644
index 39e8caac7ef609..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/line.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import io
-from typing import Any, Union
-
-import matplotlib.pyplot as plt
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class LinearChartTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- data = tool_parameters.get("data", "")
- if not data:
- return self.create_text_message("Please input data")
- data = data.split(";")
-
- axis = tool_parameters.get("x_axis") or None
- if axis:
- axis = axis.split(";")
- if len(axis) != len(data):
- axis = None
-
- # if all data is int, convert to int
- if all(i.isdigit() for i in data):
- data = [int(i) for i in data]
- else:
- data = [float(i) for i in data]
-
- flg, ax = plt.subplots(figsize=(10, 8))
-
- if axis:
- axis = [label[:10] + "..." if len(label) > 10 else label for label in axis]
- ax.set_xticklabels(axis, rotation=45, ha="right")
- ax.plot(axis, data)
- else:
- ax.plot(data)
-
- buf = io.BytesIO()
- flg.savefig(buf, format="png")
- buf.seek(0)
- plt.close(flg)
-
- return [
- self.create_text_message("the linear chart is saved as an image."),
- self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
- ]
diff --git a/api/core/tools/provider/builtin/chart/tools/line.yaml b/api/core/tools/provider/builtin/chart/tools/line.yaml
deleted file mode 100644
index 35ebe3b68bddb3..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/line.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-identity:
- name: line_chart
- author: Dify
- label:
- en_US: Linear Chart
- zh_Hans: 线性图表
- pt_BR: Gráfico linear
- icon: icon.svg
-description:
- human:
- en_US: linear chart
- zh_Hans: 线性图表
- pt_BR: Gráfico linear
- llm: generate a linear chart with input data
-parameters:
- - name: data
- type: string
- required: true
- label:
- en_US: data
- zh_Hans: 数据
- pt_BR: dados
- human_description:
- en_US: data for generating chart, each number should be separated by ";"
- zh_Hans: 用于生成线性图表的数据,每个数字之间用 ";" 分隔
- pt_BR: dados para gerar gráfico linear, cada número deve ser separado por ";"
- llm_description: data for generating linear chart, data should be a string contains a list of numbers like "1;2;3;4;5"
- form: llm
- - name: x_axis
- type: string
- required: false
- label:
- en_US: X Axis
- zh_Hans: x 轴
- pt_BR: Eixo X
- human_description:
- en_US: X axis for chart, each text should be separated by ";"
- zh_Hans: 线性图表的 x 轴,每个文本之间用 ";" 分隔
- pt_BR: Eixo X para gráfico linear, cada texto deve ser separado por ";"
- llm_description: x axis for linear chart, x axis should be a string contains a list of texts like "a;b;c;1;2" in order to match the data
- form: llm
diff --git a/api/core/tools/provider/builtin/chart/tools/pie.py b/api/core/tools/provider/builtin/chart/tools/pie.py
deleted file mode 100644
index 2c3b8a733eac9a..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/pie.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import io
-from typing import Any, Union
-
-import matplotlib.pyplot as plt
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class PieChartTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- data = tool_parameters.get("data", "")
- if not data:
- return self.create_text_message("Please input data")
- data = data.split(";")
- categories = tool_parameters.get("categories") or None
-
- # if all data is int, convert to int
- if all(i.isdigit() for i in data):
- data = [int(i) for i in data]
- else:
- data = [float(i) for i in data]
-
- flg, ax = plt.subplots()
-
- if categories:
- categories = categories.split(";")
- if len(categories) != len(data):
- categories = None
-
- if categories:
- ax.pie(data, labels=categories)
- else:
- ax.pie(data)
-
- buf = io.BytesIO()
- flg.savefig(buf, format="png")
- buf.seek(0)
- plt.close(flg)
-
- return [
- self.create_text_message("the pie chart is saved as an image."),
- self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
- ]
diff --git a/api/core/tools/provider/builtin/chart/tools/pie.yaml b/api/core/tools/provider/builtin/chart/tools/pie.yaml
deleted file mode 100644
index 541715cb7d86dd..00000000000000
--- a/api/core/tools/provider/builtin/chart/tools/pie.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-identity:
- name: pie_chart
- author: Dify
- label:
- en_US: Pie Chart
- zh_Hans: 饼图
- pt_BR: Gráfico de pizza
- icon: icon.svg
-description:
- human:
- en_US: Pie chart
- zh_Hans: 饼图
- pt_BR: Gráfico de pizza
- llm: generate a pie chart with input data
-parameters:
- - name: data
- type: string
- required: true
- label:
- en_US: data
- zh_Hans: 数据
- pt_BR: dados
- human_description:
- en_US: data for generating chart, each number should be separated by ";"
- zh_Hans: 用于生成饼图的数据,每个数字之间用 ";" 分隔
- pt_BR: dados para gerar gráfico de pizza, cada número deve ser separado por ";"
- llm_description: data for generating pie chart, data should be a string contains a list of numbers like "1;2;3;4;5"
- form: llm
- - name: categories
- type: string
- required: true
- label:
- en_US: Categories
- zh_Hans: 分类
- pt_BR: Categorias
- human_description:
- en_US: Categories for chart, each category should be separated by ";"
- zh_Hans: 饼图的分类,每个分类之间用 ";" 分隔
- pt_BR: Categorias para gráfico de pizza, cada categoria deve ser separada por ";"
- llm_description: categories for pie chart, categories should be a string contains a list of texts like "a;b;c;1;2" in order to match the data, each category should be split by ";"
- form: llm
diff --git a/api/core/tools/provider/builtin/code/code.py b/api/core/tools/provider/builtin/code/code.py
deleted file mode 100644
index 211417c9a431ed..00000000000000
--- a/api/core/tools/provider/builtin/code/code.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class CodeToolProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- pass
diff --git a/api/core/tools/provider/builtin/code/tools/simple_code.py b/api/core/tools/provider/builtin/code/tools/simple_code.py
deleted file mode 100644
index 632c9fc7f1451b..00000000000000
--- a/api/core/tools/provider/builtin/code/tools/simple_code.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SimpleCode(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- invoke simple code
- """
-
- language = tool_parameters.get("language", CodeLanguage.PYTHON3)
- code = tool_parameters.get("code", "")
-
- if language not in {CodeLanguage.PYTHON3, CodeLanguage.JAVASCRIPT}:
- raise ValueError(f"Only python3 and javascript are supported, not {language}")
-
- result = CodeExecutor.execute_code(language, "", code)
-
- return self.create_text_message(result)
diff --git a/api/core/tools/provider/builtin/cogview/__init__.py b/api/core/tools/provider/builtin/cogview/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/cogview/_assets/icon.png b/api/core/tools/provider/builtin/cogview/_assets/icon.png
deleted file mode 100644
index f0c1c24a02fc83..00000000000000
Binary files a/api/core/tools/provider/builtin/cogview/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/cogview/cogview.py b/api/core/tools/provider/builtin/cogview/cogview.py
deleted file mode 100644
index 6941ce86495693..00000000000000
--- a/api/core/tools/provider/builtin/cogview/cogview.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""Provide the input parameters type for the cogview provider class"""
-
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.cogview.tools.cogview3 import CogView3Tool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class COGVIEWProvider(BuiltinToolProviderController):
- """cogview provider"""
-
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- CogView3Tool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "prompt": "一个城市在水晶瓶中欢快生活的场景,水彩画风格,展现出微观与珠宝般的美丽。",
- "size": "square",
- "n": 1,
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e)) from e
diff --git a/api/core/tools/provider/builtin/cogview/cogview.yaml b/api/core/tools/provider/builtin/cogview/cogview.yaml
deleted file mode 100644
index 374b0e98d9122c..00000000000000
--- a/api/core/tools/provider/builtin/cogview/cogview.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-identity:
- author: Waffle
- name: cogview
- label:
- en_US: CogView
- zh_Hans: CogView 绘画
- pt_BR: CogView
- description:
- en_US: CogView art
- zh_Hans: CogView 绘画
- pt_BR: CogView art
- icon: icon.png
- tags:
- - image
- - productivity
-credentials_for_provider:
- zhipuai_api_key:
- type: secret-input
- required: true
- label:
- en_US: ZhipuAI API key
- zh_Hans: ZhipuAI API key
- pt_BR: ZhipuAI API key
- help:
- en_US: Please input your ZhipuAI API key
- zh_Hans: 请输入你的 ZhipuAI API key
- pt_BR: Please input your ZhipuAI API key
- placeholder:
- en_US: Please input your ZhipuAI API key
- zh_Hans: 请输入你的 ZhipuAI API key
- pt_BR: Please input your ZhipuAI API key
- zhipuai_organizaion_id:
- type: text-input
- required: false
- label:
- en_US: ZhipuAI organization ID
- zh_Hans: ZhipuAI organization ID
- pt_BR: ZhipuAI organization ID
- help:
- en_US: Please input your ZhipuAI organization ID
- zh_Hans: 请输入你的 ZhipuAI organization ID
- pt_BR: Please input your ZhipuAI organization ID
- placeholder:
- en_US: Please input your ZhipuAI organization ID
- zh_Hans: 请输入你的 ZhipuAI organization ID
- pt_BR: Please input your ZhipuAI organization ID
- zhipuai_base_url:
- type: text-input
- required: false
- label:
- en_US: ZhipuAI base URL
- zh_Hans: ZhipuAI base URL
- pt_BR: ZhipuAI base URL
- help:
- en_US: Please input your ZhipuAI base URL
- zh_Hans: 请输入你的 ZhipuAI base URL
- pt_BR: Please input your ZhipuAI base URL
- placeholder:
- en_US: Please input your ZhipuAI base URL
- zh_Hans: 请输入你的 ZhipuAI base URL
- pt_BR: Please input your ZhipuAI base URL
diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo.py b/api/core/tools/provider/builtin/cogview/tools/cogvideo.py
deleted file mode 100644
index a60062ca66abbf..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogvideo.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any, Union
-
-from zhipuai import ZhipuAI # type: ignore
-
-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
deleted file mode 100644
index 3df0cfcea938fa..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogvideo.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-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
deleted file mode 100644
index 3e24b74d2598a7..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from typing import Any, Union
-
-import httpx
-from zhipuai import ZhipuAI # type: ignore
-
-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
deleted file mode 100644
index fb2eb3ab130b81..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-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/cogview/tools/cogview3.py b/api/core/tools/provider/builtin/cogview/tools/cogview3.py
deleted file mode 100644
index 9aa781709a726c..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogview3.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import random
-from typing import Any, Union
-
-from zhipuai import ZhipuAI # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CogView3Tool(BuiltinTool):
- """CogView3 Tool"""
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke CogView3 tool
- """
- client = ZhipuAI(
- base_url=self.runtime.credentials["zhipuai_base_url"],
- api_key=self.runtime.credentials["zhipuai_api_key"],
- )
- size_mapping = {
- "square": "1024x1024",
- "vertical_768": "768x1344",
- "vertical_864": "864x1152",
- "horizontal_1344": "1344x768",
- "horizontal_1152": "1152x864",
- "widescreen_1440": "1440x720",
- "tallscreen_720": "720x1440",
- }
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
- # get size key
- size_key = tool_parameters.get("size", "square")
- # cogview-3-plus get size
- if size_key != "cogview_3":
- size = size_mapping[size_key]
- # get n
- n = tool_parameters.get("n", 1)
- # get quality
- quality = tool_parameters.get("quality", "standard")
- if quality not in {"standard", "hd"}:
- return self.create_text_message("Invalid quality")
- # get style
- style = tool_parameters.get("style", "vivid")
- if style not in {"natural", "vivid"}:
- return self.create_text_message("Invalid style")
- # set extra body
- seed_id = tool_parameters.get("seed_id", self._generate_random_id(8))
- extra_body = {"seed": seed_id}
- # cogview-3-plus
- if size_key != "cogview_3":
- response = client.images.generations(
- prompt=prompt,
- model="cogview-3-plus",
- size=size,
- n=n,
- extra_body=extra_body,
- style=style,
- quality=quality,
- response_format="b64_json",
- )
- # cogview-3
- else:
- response = client.images.generations(
- prompt=prompt,
- model="cogview-3",
- n=n,
- extra_body=extra_body,
- style=style,
- quality=quality,
- response_format="b64_json",
- )
- result = []
- for image in response.data:
- result.append(self.create_image_message(image=image.url))
- result.append(
- self.create_json_message(
- {
- "url": image.url,
- }
- )
- )
- return result
-
- @staticmethod
- def _generate_random_id(length=8):
- characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- random_id = "".join(random.choices(characters, k=length))
- return random_id
diff --git a/api/core/tools/provider/builtin/cogview/tools/cogview3.yaml b/api/core/tools/provider/builtin/cogview/tools/cogview3.yaml
deleted file mode 100644
index 9ab5c2729bf7a9..00000000000000
--- a/api/core/tools/provider/builtin/cogview/tools/cogview3.yaml
+++ /dev/null
@@ -1,148 +0,0 @@
-identity:
- name: cogview3
- author: Waffle
- label:
- en_US: CogView 3
- zh_Hans: CogView 3 绘画
- pt_BR: CogView 3
- description:
- en_US: CogView 3 is a powerful drawing tool that can draw the image you want based on your prompt
- zh_Hans: CogView 3 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像
- pt_BR: CogView 3 is a powerful drawing tool that can draw the image you want based on your prompt
-description:
- human:
- en_US: CogView 3 is a text to image tool
- zh_Hans: CogView 3 是一个文本到图像的工具
- pt_BR: CogView 3 is a text to image tool
- llm: CogView 3 is a tool used to generate images from text
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of CogView 3
- zh_Hans: 图像提示词,您可以查看 CogView 3 的官方文档
- pt_BR: Image prompt, you can check the official documentation of CogView 3
- llm_description: Image prompt of CogView 3, you should describe the image you want to generate as a list of words as possible as detailed
- form: llm
- - name: size
- type: select
- required: true
- human_description:
- en_US: selecting the image size
- zh_Hans: 选择图像大小
- pt_BR: selecting the image size
- label:
- en_US: Image size
- zh_Hans: 图像大小
- pt_BR: Image size
- form: form
- options:
- - value: cogview_3
- label:
- en_US: Square_cogview_3(1024x1024)
- zh_Hans: 方_cogview_3(1024x1024)
- pt_BR: Square_cogview_3(1024x1024)
- - value: square
- label:
- en_US: Square(1024x1024)
- zh_Hans: 方(1024x1024)
- pt_BR: Square(1024x1024)
- - value: vertical_768
- label:
- en_US: Vertical(768x1344)
- zh_Hans: 竖屏(768x1344)
- pt_BR: Vertical(768x1344)
- - value: vertical_864
- label:
- en_US: Vertical(864x1152)
- zh_Hans: 竖屏(864x1152)
- pt_BR: Vertical(864x1152)
- - value: horizontal_1344
- label:
- en_US: Horizontal(1344x768)
- zh_Hans: 横屏(1344x768)
- pt_BR: Horizontal(1344x768)
- - value: horizontal_1152
- label:
- en_US: Horizontal(1152x864)
- zh_Hans: 横屏(1152x864)
- pt_BR: Horizontal(1152x864)
- - value: widescreen_1440
- label:
- en_US: Widescreen(1440x720)
- zh_Hans: 宽屏(1440x720)
- pt_BR: Widescreen(1440x720)
- - value: tallscreen_720
- label:
- en_US: Tallscreen(720x1440)
- zh_Hans: 高屏(720x1440)
- pt_BR: Tallscreen(720x1440)
- default: square
- - name: n
- type: number
- required: true
- human_description:
- en_US: selecting the number of images
- zh_Hans: 选择图像数量
- pt_BR: selecting the number of images
- label:
- en_US: Number of images
- zh_Hans: 图像数量
- pt_BR: Number of images
- form: form
- min: 1
- max: 1
- default: 1
- - name: quality
- type: select
- required: true
- human_description:
- en_US: selecting the image quality
- zh_Hans: 选择图像质量
- pt_BR: selecting the image quality
- label:
- en_US: Image quality
- zh_Hans: 图像质量
- pt_BR: Image quality
- form: form
- options:
- - value: standard
- label:
- en_US: Standard
- zh_Hans: 标准
- pt_BR: Standard
- - value: hd
- label:
- en_US: HD
- zh_Hans: 高清
- pt_BR: HD
- default: standard
- - name: style
- type: select
- required: true
- human_description:
- en_US: selecting the image style
- zh_Hans: 选择图像风格
- pt_BR: selecting the image style
- label:
- en_US: Image style
- zh_Hans: 图像风格
- pt_BR: Image style
- form: form
- options:
- - value: vivid
- label:
- en_US: Vivid
- zh_Hans: 生动
- pt_BR: Vivid
- - value: natural
- label:
- en_US: Natural
- zh_Hans: 自然
- pt_BR: Natural
- default: vivid
diff --git a/api/core/tools/provider/builtin/comfyui/_assets/icon.png b/api/core/tools/provider/builtin/comfyui/_assets/icon.png
deleted file mode 100644
index 958ec5c5cfe296..00000000000000
Binary files a/api/core/tools/provider/builtin/comfyui/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/comfyui/comfyui.py b/api/core/tools/provider/builtin/comfyui/comfyui.py
deleted file mode 100644
index a8127dd23f1553..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/comfyui.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any
-
-import websocket
-from yarl import URL
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class ComfyUIProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- ws = websocket.WebSocket()
- base_url = URL(credentials.get("base_url"))
- ws_protocol = "ws"
- if base_url.scheme == "https":
- ws_protocol = "wss"
- ws_address = f"{ws_protocol}://{base_url.authority}/ws?clientId=test123"
-
- try:
- ws.connect(ws_address)
- except Exception as e:
- raise ToolProviderCredentialValidationError(f"can not connect to {ws_address}")
- finally:
- ws.close()
diff --git a/api/core/tools/provider/builtin/comfyui/comfyui.yaml b/api/core/tools/provider/builtin/comfyui/comfyui.yaml
deleted file mode 100644
index 24ae43cd44051e..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/comfyui.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- author: Qun
- name: comfyui
- label:
- en_US: ComfyUI
- zh_Hans: ComfyUI
- description:
- en_US: ComfyUI is a tool for generating images which can be deployed locally.
- zh_Hans: ComfyUI 是一个可以在本地部署的图片生成的工具。
- icon: icon.png
- tags:
- - image
-credentials_for_provider:
- base_url:
- type: text-input
- required: true
- label:
- en_US: The URL of ComfyUI Server
- zh_Hans: ComfyUI服务器的URL
- placeholder:
- en_US: Please input your ComfyUI server's Base URL
- zh_Hans: 请输入你的 ComfyUI 服务器的 Base URL
- url: https://docs.dify.ai/guides/tools/tool-configuration/comfyui
diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py b/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py
deleted file mode 100644
index 2bf10ce8ff2632..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import json
-import random
-import uuid
-
-import httpx
-from websocket import WebSocket
-from yarl import URL
-
-from core.file.file_manager import download
-from core.file.models import File
-
-
-class ComfyUiClient:
- def __init__(self, base_url: str):
- self.base_url = URL(base_url)
-
- def get_history(self, prompt_id: str) -> dict:
- res = httpx.get(str(self.base_url / "history"), params={"prompt_id": prompt_id})
- history = res.json()[prompt_id]
- return history
-
- def get_image(self, filename: str, subfolder: str, folder_type: str) -> bytes:
- response = httpx.get(
- str(self.base_url / "view"),
- params={"filename": filename, "subfolder": subfolder, "type": folder_type},
- )
- return response.content
-
- def upload_image(self, image_file: File) -> dict:
- file = download(image_file)
- files = {"image": (image_file.filename, file, image_file.mime_type), "overwrite": "true"}
- res = httpx.post(str(self.base_url / "upload/image"), files=files)
- return res.json()
-
- def queue_prompt(self, client_id: str, prompt: dict) -> str:
- res = httpx.post(str(self.base_url / "prompt"), json={"client_id": client_id, "prompt": prompt})
- prompt_id = res.json()["prompt_id"]
- return prompt_id
-
- def open_websocket_connection(self) -> tuple[WebSocket, str]:
- client_id = str(uuid.uuid4())
- ws = WebSocket()
- ws_protocol = "ws"
- if self.base_url.scheme == "https":
- ws_protocol = "wss"
- ws_address = f"{ws_protocol}://{self.base_url.authority}/ws?clientId={client_id}"
- ws.connect(ws_address)
- return ws, client_id
-
- def set_prompt_by_ksampler(self, origin_prompt: dict, positive_prompt: str, negative_prompt: str = "") -> dict:
- 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]
- positive_input_id = prompt.get(k_sampler)["inputs"]["positive"][0]
- prompt.get(positive_input_id)["inputs"]["text"] = positive_prompt
-
- if negative_prompt != "":
- negative_input_id = prompt.get(k_sampler)["inputs"]["negative"][0]
- prompt.get(negative_input_id)["inputs"]["text"] = negative_prompt
-
- return prompt
-
- def set_prompt_images_by_ids(self, origin_prompt: dict, image_names: list[str], image_ids: list[str]) -> dict:
- prompt = origin_prompt.copy()
- for index, image_node_id in enumerate(image_ids):
- prompt[image_node_id]["inputs"]["image"] = image_names[index]
- return prompt
-
- def set_prompt_images_by_default(self, origin_prompt: dict, image_names: list[str]) -> dict:
- prompt = origin_prompt.copy()
- id_to_class_type = {id: details["class_type"] for id, details in prompt.items()}
- load_image_nodes = [key for key, value in id_to_class_type.items() if value == "LoadImage"]
- for load_image, image_name in zip(load_image_nodes, image_names):
- 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 = []
-
- while True:
- out = ws.recv()
- if isinstance(out, str):
- message = json.loads(out)
- if message["type"] == "progress":
- data = message["data"]
- current_step = data["value"]
- print("In K-Sampler -> Step: ", current_step, " of: ", data["max"])
- if message["type"] == "execution_cached":
- data = message["data"]
- for itm in data["nodes"]:
- if itm not in finished_nodes:
- finished_nodes.append(itm)
- print("Progress: ", len(finished_nodes), "/", len(node_ids), " Tasks done")
- if message["type"] == "executing":
- data = message["data"]
- if data["node"] not in finished_nodes:
- finished_nodes.append(data["node"])
- print("Progress: ", len(finished_nodes), "/", len(node_ids), " Tasks done")
-
- if data["node"] is None and data["prompt_id"] == prompt_id:
- break # Execution is done
- else:
- continue
-
- def generate_image_by_prompt(self, prompt: dict) -> list[bytes]:
- try:
- ws, client_id = self.open_websocket_connection()
- prompt_id = self.queue_prompt(client_id, prompt)
- self.track_progress(prompt, ws, prompt_id)
- history = self.get_history(prompt_id)
- images = []
- for output in history["outputs"].values():
- for img in output.get("images", []):
- image_data = self.get_image(img["filename"], img["subfolder"], img["type"])
- images.append((image_data, img["filename"]))
- return images
- finally:
- ws.close()
diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.py b/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.py
deleted file mode 100644
index eaa4b0d0275568..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.py
+++ /dev/null
@@ -1,475 +0,0 @@
-import json
-import os
-import random
-import uuid
-from copy import deepcopy
-from enum import Enum
-from typing import Any, Union
-
-import websocket
-from httpx import get, post
-from yarl import URL
-
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolParameterOption
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SD_TXT2IMG_OPTIONS = {}
-LORA_NODE = {
- "inputs": {"lora_name": "", "strength_model": 1, "strength_clip": 1, "model": ["11", 0], "clip": ["11", 1]},
- "class_type": "LoraLoader",
- "_meta": {"title": "Load LoRA"},
-}
-FluxGuidanceNode = {
- "inputs": {"guidance": 3.5, "conditioning": ["6", 0]},
- "class_type": "FluxGuidance",
- "_meta": {"title": "FluxGuidance"},
-}
-
-
-class ModelType(Enum):
- SD15 = 1
- SDXL = 2
- SD3 = 3
- FLUX = 4
-
-
-class ComfyuiStableDiffusionTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # base url
- base_url = self.runtime.credentials.get("base_url", "")
- if not base_url:
- return self.create_text_message("Please input base_url")
-
- if tool_parameters.get("model"):
- self.runtime.credentials["model"] = tool_parameters["model"]
-
- model = self.runtime.credentials.get("model", None)
- if not model:
- return self.create_text_message("Please input model")
-
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
-
- # get negative prompt
- negative_prompt = tool_parameters.get("negative_prompt", "")
-
- # get size
- width = tool_parameters.get("width", 1024)
- height = tool_parameters.get("height", 1024)
-
- # get steps
- steps = tool_parameters.get("steps", 1)
-
- # get sampler_name
- sampler_name = tool_parameters.get("sampler_name", "euler")
-
- # scheduler
- scheduler = tool_parameters.get("scheduler", "normal")
-
- # get cfg
- cfg = tool_parameters.get("cfg", 7.0)
-
- # get model type
- model_type = tool_parameters.get("model_type", ModelType.SD15.name)
-
- # get lora
- # supports up to 3 loras
- lora_list = []
- lora_strength_list = []
- if tool_parameters.get("lora_1"):
- lora_list.append(tool_parameters["lora_1"])
- lora_strength_list.append(tool_parameters.get("lora_strength_1", 1))
- if tool_parameters.get("lora_2"):
- lora_list.append(tool_parameters["lora_2"])
- lora_strength_list.append(tool_parameters.get("lora_strength_2", 1))
- if tool_parameters.get("lora_3"):
- lora_list.append(tool_parameters["lora_3"])
- lora_strength_list.append(tool_parameters.get("lora_strength_3", 1))
-
- return self.text2img(
- base_url=base_url,
- model=model,
- model_type=model_type,
- prompt=prompt,
- negative_prompt=negative_prompt,
- width=width,
- height=height,
- steps=steps,
- sampler_name=sampler_name,
- scheduler=scheduler,
- cfg=cfg,
- lora_list=lora_list,
- lora_strength_list=lora_strength_list,
- )
-
- def get_checkpoints(self) -> list[str]:
- """
- get checkpoints
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "models" / "checkpoints")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- return []
- else:
- return response.json()
- except Exception as e:
- return []
-
- def get_loras(self) -> list[str]:
- """
- get loras
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "models" / "loras")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- return []
- else:
- return response.json()
- except Exception as e:
- return []
-
- def get_sample_methods(self) -> tuple[list[str], list[str]]:
- """
- get sample method
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return [], []
- api_url = str(URL(base_url) / "object_info" / "KSampler")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- return [], []
- else:
- data = response.json()["KSampler"]["input"]["required"]
- return data["sampler_name"][0], data["scheduler"][0]
- except Exception as e:
- return [], []
-
- def validate_models(self) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- validate models
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- raise ToolProviderCredentialValidationError("Please input base_url")
- model = self.runtime.credentials.get("model", None)
- if not model:
- raise ToolProviderCredentialValidationError("Please input model")
-
- api_url = str(URL(base_url) / "models" / "checkpoints")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("Failed to get models")
- else:
- models = response.json()
- if len([d for d in models if d == model]) > 0:
- return self.create_text_message(json.dumps(models))
- else:
- raise ToolProviderCredentialValidationError(f"model {model} does not exist")
- except Exception as e:
- raise ToolProviderCredentialValidationError(f"Failed to get models, {e}")
-
- def get_history(self, base_url, prompt_id):
- """
- get history
- """
- url = str(URL(base_url) / "history")
- respond = get(url, params={"prompt_id": prompt_id}, timeout=(2, 10))
- return respond.json()
-
- def download_image(self, base_url, filename, subfolder, folder_type):
- """
- download image
- """
- url = str(URL(base_url) / "view")
- response = get(url, params={"filename": filename, "subfolder": subfolder, "type": folder_type}, timeout=(2, 10))
- return response.content
-
- def queue_prompt_image(self, base_url, client_id, prompt):
- """
- send prompt task and rotate
- """
- # initiate task execution
- url = str(URL(base_url) / "prompt")
- respond = post(url, data=json.dumps({"client_id": client_id, "prompt": prompt}), timeout=(2, 10))
- prompt_id = respond.json()["prompt_id"]
-
- ws = websocket.WebSocket()
- if "https" in base_url:
- ws_url = base_url.replace("https", "ws")
- else:
- ws_url = base_url.replace("http", "ws")
- ws.connect(str(URL(f"{ws_url}") / "ws") + f"?clientId={client_id}", timeout=120)
-
- # websocket rotate execution status
- output_images = {}
- while True:
- out = ws.recv()
- if isinstance(out, str):
- message = json.loads(out)
- if message["type"] == "executing":
- data = message["data"]
- if data["node"] is None and data["prompt_id"] == prompt_id:
- break # Execution is done
- elif message["type"] == "status":
- data = message["data"]
- if data["status"]["exec_info"]["queue_remaining"] == 0 and data.get("sid"):
- break # Execution is done
- else:
- continue # previews are binary data
-
- # download image when execution finished
- history = self.get_history(base_url, prompt_id)[prompt_id]
- for o in history["outputs"]:
- for node_id in history["outputs"]:
- node_output = history["outputs"][node_id]
- if "images" in node_output:
- images_output = []
- for image in node_output["images"]:
- image_data = self.download_image(base_url, image["filename"], image["subfolder"], image["type"])
- images_output.append(image_data)
- output_images[node_id] = images_output
-
- ws.close()
-
- return output_images
-
- def text2img(
- self,
- base_url: str,
- model: str,
- model_type: str,
- prompt: str,
- negative_prompt: str,
- width: int,
- height: int,
- steps: int,
- sampler_name: str,
- scheduler: str,
- cfg: float,
- lora_list: list,
- lora_strength_list: list,
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- generate image
- """
- if not SD_TXT2IMG_OPTIONS:
- current_dir = os.path.dirname(os.path.realpath(__file__))
- with open(os.path.join(current_dir, "txt2img.json")) as file:
- SD_TXT2IMG_OPTIONS.update(json.load(file))
-
- draw_options = deepcopy(SD_TXT2IMG_OPTIONS)
- draw_options["3"]["inputs"]["steps"] = steps
- draw_options["3"]["inputs"]["sampler_name"] = sampler_name
- draw_options["3"]["inputs"]["scheduler"] = scheduler
- draw_options["3"]["inputs"]["cfg"] = cfg
- # generate different image when using same prompt next time
- draw_options["3"]["inputs"]["seed"] = random.randint(0, 100000000)
- draw_options["4"]["inputs"]["ckpt_name"] = model
- draw_options["5"]["inputs"]["width"] = width
- draw_options["5"]["inputs"]["height"] = height
- draw_options["6"]["inputs"]["text"] = prompt
- draw_options["7"]["inputs"]["text"] = negative_prompt
- # if the model is SD3 or FLUX series, the Latent class should be corresponding to SD3 Latent
- if model_type in {ModelType.SD3.name, ModelType.FLUX.name}:
- draw_options["5"]["class_type"] = "EmptySD3LatentImage"
-
- if lora_list:
- # last Lora node link to KSampler node
- draw_options["3"]["inputs"]["model"][0] = "10"
- # last Lora node link to positive and negative Clip node
- draw_options["6"]["inputs"]["clip"][0] = "10"
- draw_options["7"]["inputs"]["clip"][0] = "10"
- # every Lora node link to next Lora node, and Checkpoints node link to first Lora node
- for i, (lora, strength) in enumerate(zip(lora_list, lora_strength_list), 10):
- if i - 10 == len(lora_list) - 1:
- next_node_id = "4"
- else:
- next_node_id = str(i + 1)
- lora_node = deepcopy(LORA_NODE)
- lora_node["inputs"]["lora_name"] = lora
- lora_node["inputs"]["strength_model"] = strength
- lora_node["inputs"]["strength_clip"] = strength
- lora_node["inputs"]["model"][0] = next_node_id
- lora_node["inputs"]["clip"][0] = next_node_id
- draw_options[str(i)] = lora_node
-
- # FLUX need to add FluxGuidance Node
- if model_type == ModelType.FLUX.name:
- last_node_id = str(10 + len(lora_list))
- draw_options[last_node_id] = deepcopy(FluxGuidanceNode)
- draw_options[last_node_id]["inputs"]["conditioning"][0] = "6"
- draw_options["3"]["inputs"]["positive"][0] = last_node_id
-
- try:
- client_id = str(uuid.uuid4())
- result = self.queue_prompt_image(base_url, client_id, prompt=draw_options)
-
- # get first image
- image = b""
- for node in result:
- for img in result[node]:
- if img:
- image = img
- break
-
- return self.create_blob_message(
- blob=image, meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
-
- except Exception as e:
- return self.create_text_message(f"Failed to generate image: {str(e)}")
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- parameters = [
- ToolParameter(
- name="prompt",
- label=I18nObject(en_US="Prompt", zh_Hans="Prompt"),
- human_description=I18nObject(
- en_US="Image prompt, you can check the official documentation of Stable Diffusion",
- zh_Hans="图像提示词,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Image prompt of Stable Diffusion, you should describe the image "
- "you want to generate as a list of words as possible as detailed, "
- "the prompt must be written in English.",
- required=True,
- ),
- ]
- if self.runtime.credentials:
- try:
- models = self.get_checkpoints()
- if len(models) != 0:
- parameters.append(
- ToolParameter(
- name="model",
- label=I18nObject(en_US="Model", zh_Hans="Model"),
- human_description=I18nObject(
- en_US="Model of Stable Diffusion or FLUX, "
- "you can check the official documentation of Stable Diffusion or FLUX",
- zh_Hans="Stable Diffusion 或者 FLUX 的模型,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Model of Stable Diffusion or FLUX, "
- "you can check the official documentation of Stable Diffusion or FLUX",
- required=True,
- default=models[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in models
- ],
- )
- )
- loras = self.get_loras()
- if len(loras) != 0:
- for n in range(1, 4):
- parameters.append(
- ToolParameter(
- name=f"lora_{n}",
- label=I18nObject(en_US=f"Lora {n}", zh_Hans=f"Lora {n}"),
- human_description=I18nObject(
- en_US="Lora of Stable Diffusion, "
- "you can check the official documentation of Stable Diffusion",
- zh_Hans="Stable Diffusion 的 Lora 模型,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Lora of Stable Diffusion, "
- "you can check the official documentation of "
- "Stable Diffusion",
- required=False,
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in loras
- ],
- )
- )
- sample_methods, schedulers = self.get_sample_methods()
- if len(sample_methods) != 0:
- parameters.append(
- ToolParameter(
- name="sampler_name",
- label=I18nObject(en_US="Sampling method", zh_Hans="Sampling method"),
- human_description=I18nObject(
- en_US="Sampling method of Stable Diffusion, "
- "you can check the official documentation of Stable Diffusion",
- zh_Hans="Stable Diffusion 的Sampling method,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Sampling method of Stable Diffusion, "
- "you can check the official documentation of Stable Diffusion",
- required=True,
- default=sample_methods[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i))
- for i in sample_methods
- ],
- )
- )
- if len(schedulers) != 0:
- parameters.append(
- ToolParameter(
- name="scheduler",
- label=I18nObject(en_US="Scheduler", zh_Hans="Scheduler"),
- human_description=I18nObject(
- en_US="Scheduler of Stable Diffusion, "
- "you can check the official documentation of Stable Diffusion",
- zh_Hans="Stable Diffusion 的Scheduler,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Scheduler of Stable Diffusion, "
- "you can check the official documentation of Stable Diffusion",
- required=True,
- default=schedulers[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in schedulers
- ],
- )
- )
- parameters.append(
- ToolParameter(
- name="model_type",
- label=I18nObject(en_US="Model Type", zh_Hans="Model Type"),
- human_description=I18nObject(
- en_US="Model Type of Stable Diffusion or Flux, "
- "you can check the official documentation of Stable Diffusion or Flux",
- zh_Hans="Stable Diffusion 或 FLUX 的模型类型,"
- "您可以查看 Stable Diffusion 或 Flux 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Model Type of Stable Diffusion or Flux, "
- "you can check the official documentation of Stable Diffusion or Flux",
- required=True,
- default=ModelType.SD15.name,
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i))
- for i in ModelType.__members__
- ],
- )
- )
- except:
- pass
-
- return parameters
diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.yaml b/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.yaml
deleted file mode 100644
index 75fe746965196a..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_stable_diffusion.yaml
+++ /dev/null
@@ -1,212 +0,0 @@
-identity:
- name: txt2img
- author: Qun
- label:
- en_US: Txt2Img
- zh_Hans: Txt2Img
- pt_BR: Txt2Img
-description:
- human:
- en_US: a pre-defined comfyui workflow that can use one model and up to 3 loras to generate images. Support SD1.5, SDXL, SD3 and FLUX which contain text encoders/clip, but does not support models that requires a triple clip loader.
- zh_Hans: 一个预定义的 ComfyUI 工作流,可以使用一个模型和最多3个loras来生成图像。支持包含文本编码器/clip的SD1.5、SDXL、SD3和FLUX,但不支持需要clip加载器的模型。
- pt_BR: a pre-defined comfyui workflow that can use one model and up to 3 loras to generate images. Support SD1.5, SDXL, SD3 and FLUX which contain text encoders/clip, but does not support models that requires a triple clip loader.
- llm: draw the image you want based on your prompt.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of Stable Diffusion or FLUX
- zh_Hans: 图像提示词,您可以查看 Stable Diffusion 或者 FLUX 的官方文档
- pt_BR: Image prompt, you can check the official documentation of Stable Diffusion or FLUX
- llm_description: Image prompt of Stable Diffusion, you should describe the image you want to generate as a list of words as possible as detailed, the prompt must be written in English.
- form: llm
- - name: model
- type: string
- required: true
- label:
- en_US: Model Name
- zh_Hans: 模型名称
- pt_BR: Model Name
- human_description:
- en_US: Model Name
- zh_Hans: 模型名称
- pt_BR: Model Name
- form: form
- - name: model_type
- type: string
- required: true
- label:
- en_US: Model Type
- zh_Hans: 模型类型
- pt_BR: Model Type
- human_description:
- en_US: Model Type
- zh_Hans: 模型类型
- pt_BR: Model Type
- form: form
- - name: lora_1
- type: string
- required: false
- label:
- en_US: Lora 1
- zh_Hans: Lora 1
- pt_BR: Lora 1
- human_description:
- en_US: Lora 1
- zh_Hans: Lora 1
- pt_BR: Lora 1
- form: form
- - name: lora_strength_1
- type: number
- required: false
- label:
- en_US: Lora Strength 1
- zh_Hans: Lora Strength 1
- pt_BR: Lora Strength 1
- human_description:
- en_US: Lora Strength 1
- zh_Hans: Lora模型的权重
- pt_BR: Lora Strength 1
- form: form
- - name: steps
- type: number
- required: false
- label:
- en_US: Steps
- zh_Hans: Steps
- pt_BR: Steps
- human_description:
- en_US: Steps
- zh_Hans: Steps
- pt_BR: Steps
- form: form
- default: 20
- - name: width
- type: number
- required: false
- label:
- en_US: Width
- zh_Hans: Width
- pt_BR: Width
- human_description:
- en_US: Width
- zh_Hans: Width
- pt_BR: Width
- form: form
- default: 1024
- - name: height
- type: number
- required: false
- label:
- en_US: Height
- zh_Hans: Height
- pt_BR: Height
- human_description:
- en_US: Height
- zh_Hans: Height
- pt_BR: Height
- form: form
- default: 1024
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- pt_BR: Negative prompt
- human_description:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- pt_BR: Negative prompt
- form: form
- default: bad art, ugly, deformed, watermark, duplicated, discontinuous lines
- - name: cfg
- type: number
- required: false
- label:
- en_US: CFG Scale
- zh_Hans: CFG Scale
- pt_BR: CFG Scale
- human_description:
- en_US: CFG Scale
- zh_Hans: 提示词相关性(CFG Scale)
- pt_BR: CFG Scale
- form: form
- default: 7.0
- - name: sampler_name
- type: string
- required: false
- label:
- en_US: Sampling method
- zh_Hans: Sampling method
- pt_BR: Sampling method
- human_description:
- en_US: Sampling method
- zh_Hans: Sampling method
- pt_BR: Sampling method
- form: form
- - name: scheduler
- type: string
- required: false
- label:
- en_US: Scheduler
- zh_Hans: Scheduler
- pt_BR: Scheduler
- human_description:
- en_US: Scheduler
- zh_Hans: Scheduler
- pt_BR: Scheduler
- form: form
- - name: lora_2
- type: string
- required: false
- label:
- en_US: Lora 2
- zh_Hans: Lora 2
- pt_BR: Lora 2
- human_description:
- en_US: Lora 2
- zh_Hans: Lora 2
- pt_BR: Lora 2
- form: form
- - name: lora_strength_2
- type: number
- required: false
- label:
- en_US: Lora Strength 2
- zh_Hans: Lora Strength 2
- pt_BR: Lora Strength 2
- human_description:
- en_US: Lora Strength 2
- zh_Hans: Lora模型的权重
- pt_BR: Lora Strength 2
- form: form
- - name: lora_3
- type: string
- required: false
- label:
- en_US: Lora 3
- zh_Hans: Lora 3
- pt_BR: Lora 3
- human_description:
- en_US: Lora 3
- zh_Hans: Lora 3
- pt_BR: Lora 3
- form: form
- - name: lora_strength_3
- type: number
- required: false
- label:
- en_US: Lora Strength 3
- zh_Hans: Lora Strength 3
- pt_BR: Lora Strength 3
- human_description:
- en_US: Lora Strength 3
- zh_Hans: Lora模型的权重
- pt_BR: Lora Strength 3
- form: form
diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py
deleted file mode 100644
index eb085f221ebdda..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import json
-import mimetypes
-from typing import Any
-
-from core.file import FileType
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolParameterValidationError
-from core.tools.provider.builtin.comfyui.tools.comfyui_client import ComfyUiClient
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-def sanitize_json_string(s):
- escape_dict = {
- "\n": "\\n",
- "\r": "\\r",
- "\t": "\\t",
- "\b": "\\b",
- "\f": "\\f",
- }
- for char, escaped in escape_dict.items():
- s = s.replace(char, escaped)
-
- return s
-
-
-class ComfyUIWorkflowTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- comfyui = ComfyUiClient(self.runtime.credentials["base_url"])
-
- positive_prompt = tool_parameters.get("positive_prompt", "")
- negative_prompt = tool_parameters.get("negative_prompt", "")
- images = tool_parameters.get("images") or []
- workflow = tool_parameters.get("workflow_json")
- image_names = []
- for image in images:
- if image.type != FileType.IMAGE:
- continue
- image_name = comfyui.upload_image(image).get("name")
- image_names.append(image_name)
-
- set_prompt_with_ksampler = True
- if "{{positive_prompt}}" in workflow:
- set_prompt_with_ksampler = False
- workflow = workflow.replace("{{positive_prompt}}", positive_prompt.replace('"', "'"))
- workflow = workflow.replace("{{negative_prompt}}", negative_prompt.replace('"', "'"))
-
- try:
- prompt = json.loads(workflow)
- except json.JSONDecodeError:
- cleaned_string = sanitize_json_string(workflow)
- try:
- prompt = json.loads(cleaned_string)
- except:
- return self.create_text_message("the Workflow JSON is not correct")
-
- if set_prompt_with_ksampler:
- try:
- prompt = comfyui.set_prompt_by_ksampler(prompt, positive_prompt, negative_prompt)
- except:
- raise ToolParameterValidationError(
- "Failed set prompt with KSampler, try replace prompt to {{positive_prompt}} in your workflow json"
- )
-
- if image_names:
- if image_ids := tool_parameters.get("image_ids"):
- image_ids = image_ids.split(",")
- try:
- prompt = comfyui.set_prompt_images_by_ids(prompt, image_names, image_ids)
- except:
- raise ToolParameterValidationError("the Image Node ID List not match your upload image files.")
- 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 image_data, filename in images:
- result.append(
- self.create_blob_message(
- blob=image_data,
- meta={"mime_type": mimetypes.guess_type(filename)[0]},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
- return result
diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml
deleted file mode 100644
index 9428acbe943642..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-identity:
- name: workflow
- author: hjlarry
- label:
- en_US: workflow
- zh_Hans: 工作流
-description:
- human:
- en_US: Run ComfyUI workflow.
- zh_Hans: 运行ComfyUI工作流。
- llm: Run ComfyUI workflow.
-parameters:
- - name: positive_prompt
- type: string
- label:
- en_US: Prompt
- zh_Hans: 提示词
- llm_description: Image prompt, you should describe the image you want to generate as a list of words as possible as detailed, the prompt must be written in English.
- form: llm
- - name: negative_prompt
- type: string
- label:
- en_US: Negative Prompt
- zh_Hans: 负面提示词
- llm_description: Negative prompt, you should describe the image you don't want to generate as a list of words as possible as detailed, the prompt must be written in English.
- form: llm
- - name: images
- type: files
- label:
- en_US: Input Images
- zh_Hans: 输入的图片
- llm_description: The input images, used to transfer to the comfyui workflow to generate another image.
- form: llm
- - name: workflow_json
- type: string
- required: true
- label:
- en_US: Workflow JSON
- human_description:
- en_US: exported from ComfyUI workflow
- zh_Hans: 从ComfyUI的工作流中导出
- form: form
- - name: image_ids
- type: string
- label:
- en_US: Image Node ID List
- zh_Hans: 图片节点ID列表
- placeholder:
- en_US: Use commas to separate multiple node ID
- zh_Hans: 多个节点ID时使用半角逗号分隔
- human_description:
- 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/comfyui/tools/txt2img.json b/api/core/tools/provider/builtin/comfyui/tools/txt2img.json
deleted file mode 100644
index 8ea869ff106c38..00000000000000
--- a/api/core/tools/provider/builtin/comfyui/tools/txt2img.json
+++ /dev/null
@@ -1,107 +0,0 @@
-{
- "3": {
- "inputs": {
- "seed": 156680208700286,
- "steps": 20,
- "cfg": 8,
- "sampler_name": "euler",
- "scheduler": "normal",
- "denoise": 1,
- "model": [
- "4",
- 0
- ],
- "positive": [
- "6",
- 0
- ],
- "negative": [
- "7",
- 0
- ],
- "latent_image": [
- "5",
- 0
- ]
- },
- "class_type": "KSampler",
- "_meta": {
- "title": "KSampler"
- }
- },
- "4": {
- "inputs": {
- "ckpt_name": "3dAnimationDiffusion_v10.safetensors"
- },
- "class_type": "CheckpointLoaderSimple",
- "_meta": {
- "title": "Load Checkpoint"
- }
- },
- "5": {
- "inputs": {
- "width": 512,
- "height": 512,
- "batch_size": 1
- },
- "class_type": "EmptyLatentImage",
- "_meta": {
- "title": "Empty Latent Image"
- }
- },
- "6": {
- "inputs": {
- "text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
- "clip": [
- "4",
- 1
- ]
- },
- "class_type": "CLIPTextEncode",
- "_meta": {
- "title": "CLIP Text Encode (Prompt)"
- }
- },
- "7": {
- "inputs": {
- "text": "text, watermark",
- "clip": [
- "4",
- 1
- ]
- },
- "class_type": "CLIPTextEncode",
- "_meta": {
- "title": "CLIP Text Encode (Prompt)"
- }
- },
- "8": {
- "inputs": {
- "samples": [
- "3",
- 0
- ],
- "vae": [
- "4",
- 2
- ]
- },
- "class_type": "VAEDecode",
- "_meta": {
- "title": "VAE Decode"
- }
- },
- "9": {
- "inputs": {
- "filename_prefix": "ComfyUI",
- "images": [
- "8",
- 0
- ]
- },
- "class_type": "SaveImage",
- "_meta": {
- "title": "Save Image"
- }
- }
-}
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/crossref/_assets/icon.svg b/api/core/tools/provider/builtin/crossref/_assets/icon.svg
deleted file mode 100644
index aa629de7cb1660..00000000000000
--- a/api/core/tools/provider/builtin/crossref/_assets/icon.svg
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/crossref/crossref.py b/api/core/tools/provider/builtin/crossref/crossref.py
deleted file mode 100644
index 8ba3c1b48ae6d7..00000000000000
--- a/api/core/tools/provider/builtin/crossref/crossref.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.crossref.tools.query_doi import CrossRefQueryDOITool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class CrossRefProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- CrossRefQueryDOITool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "doi": "10.1007/s00894-022-05373-8",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/crossref/crossref.yaml b/api/core/tools/provider/builtin/crossref/crossref.yaml
deleted file mode 100644
index da67fbec3a480b..00000000000000
--- a/api/core/tools/provider/builtin/crossref/crossref.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-identity:
- author: Sakura4036
- name: crossref
- label:
- en_US: CrossRef
- zh_Hans: CrossRef
- description:
- en_US: Crossref is a cross-publisher reference linking registration query system using DOI technology created in 2000. Crossref establishes cross-database links between the reference list and citation full text of papers, making it very convenient for readers to access the full text of papers.
- zh_Hans: Crossref是于2000年创建的使用DOI技术的跨出版商参考文献链接注册查询系统。Crossref建立了在论文的参考文献列表和引文全文之间的跨数据库链接,使得读者能够非常便捷地获取文献全文。
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- mailto:
- type: text-input
- required: true
- label:
- en_US: email address
- zh_Hans: email地址
- pt_BR: email address
- placeholder:
- en_US: Please input your email address
- zh_Hans: 请输入你的email地址
- pt_BR: Please input your email address
- help:
- en_US: According to the requirements of Crossref, an email address is required
- zh_Hans: 根据Crossref的要求,需要提供一个邮箱地址
- pt_BR: According to the requirements of Crossref, an email address is required
- url: https://api.crossref.org/swagger-ui/index.html
diff --git a/api/core/tools/provider/builtin/crossref/tools/query_doi.py b/api/core/tools/provider/builtin/crossref/tools/query_doi.py
deleted file mode 100644
index 746139dd69d27b..00000000000000
--- a/api/core/tools/provider/builtin/crossref/tools/query_doi.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolParameterValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CrossRefQueryDOITool(BuiltinTool):
- """
- Tool for querying the metadata of a publication using its DOI.
- """
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- doi = tool_parameters.get("doi")
- if not doi:
- raise ToolParameterValidationError("doi is required.")
- # doc: https://github.com/CrossRef/rest-api-doc
- url = f"https://api.crossref.org/works/{doi}"
- response = requests.get(url)
- response.raise_for_status()
- response = response.json()
- message = response.get("message", {})
-
- return self.create_json_message(message)
diff --git a/api/core/tools/provider/builtin/crossref/tools/query_doi.yaml b/api/core/tools/provider/builtin/crossref/tools/query_doi.yaml
deleted file mode 100644
index 9c16da25edf2b3..00000000000000
--- a/api/core/tools/provider/builtin/crossref/tools/query_doi.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: crossref_query_doi
- author: Sakura4036
- label:
- en_US: CrossRef Query DOI
- zh_Hans: CrossRef DOI 查询
- pt_BR: CrossRef Query DOI
-description:
- human:
- en_US: A tool for searching literature information using CrossRef by DOI.
- zh_Hans: 一个使用CrossRef通过DOI获取文献信息的工具。
- pt_BR: A tool for searching literature information using CrossRef by DOI.
- llm: A tool for searching literature information using CrossRef by DOI.
-parameters:
- - name: doi
- type: string
- required: true
- label:
- en_US: DOI
- zh_Hans: DOI
- pt_BR: DOI
- llm_description: DOI for searching in CrossRef
- form: llm
diff --git a/api/core/tools/provider/builtin/crossref/tools/query_title.py b/api/core/tools/provider/builtin/crossref/tools/query_title.py
deleted file mode 100644
index e2452381832938..00000000000000
--- a/api/core/tools/provider/builtin/crossref/tools/query_title.py
+++ /dev/null
@@ -1,143 +0,0 @@
-import time
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-def convert_time_str_to_seconds(time_str: str) -> int:
- """
- Convert a time string to seconds.
- example: 1s -> 1, 1m30s -> 90, 1h30m -> 5400, 1h30m30s -> 5430
- """
- time_str = time_str.lower().strip().replace(" ", "")
- seconds = 0
- if "h" in time_str:
- hours, time_str = time_str.split("h")
- seconds += int(hours) * 3600
- if "m" in time_str:
- minutes, time_str = time_str.split("m")
- seconds += int(minutes) * 60
- if "s" in time_str:
- seconds += int(time_str.replace("s", ""))
- return seconds
-
-
-class CrossRefQueryTitleAPI:
- """
- Tool for querying the metadata of a publication using its title.
- Crossref API doc: https://github.com/CrossRef/rest-api-doc
- """
-
- query_url_template: str = "https://api.crossref.org/works?query.bibliographic={query}&rows={rows}&offset={offset}&sort={sort}&order={order}&mailto={mailto}"
- rate_limit: int = 50
- rate_interval: float = 1
- max_limit: int = 1000
-
- def __init__(self, mailto: str):
- self.mailto = mailto
-
- def _query(
- self,
- query: str,
- rows: int = 5,
- offset: int = 0,
- sort: str = "relevance",
- order: str = "desc",
- fuzzy_query: bool = False,
- ) -> list[dict]:
- """
- Query the metadata of a publication using its title.
- :param query: the title of the publication
- :param rows: the number of results to return
- :param sort: the sort field
- :param order: the sort order
- :param fuzzy_query: whether to return all items that match the query
- """
- url = self.query_url_template.format(
- query=query, rows=rows, offset=offset, sort=sort, order=order, mailto=self.mailto
- )
- response = requests.get(url)
- response.raise_for_status()
- rate_limit = int(response.headers["x-ratelimit-limit"])
- # convert time string to seconds
- rate_interval = convert_time_str_to_seconds(response.headers["x-ratelimit-interval"])
-
- self.rate_limit = rate_limit
- self.rate_interval = rate_interval
-
- response = response.json()
- if response["status"] != "ok":
- return []
-
- message = response["message"]
- if fuzzy_query:
- # fuzzy query return all items
- return message["items"]
- else:
- for paper in message["items"]:
- title = paper["title"][0]
- if title.lower() != query.lower():
- continue
- return [paper]
- return []
-
- def query(
- self, query: str, rows: int = 5, sort: str = "relevance", order: str = "desc", fuzzy_query: bool = False
- ) -> list[dict]:
- """
- Query the metadata of a publication using its title.
- :param query: the title of the publication
- :param rows: the number of results to return
- :param sort: the sort field
- :param order: the sort order
- :param fuzzy_query: whether to return all items that match the query
- """
- rows = min(rows, self.max_limit)
- if rows > self.rate_limit:
- # query multiple times
- query_times = rows // self.rate_limit + 1
- results = []
-
- for i in range(query_times):
- result = self._query(
- query,
- rows=self.rate_limit,
- offset=i * self.rate_limit,
- sort=sort,
- order=order,
- fuzzy_query=fuzzy_query,
- )
- if fuzzy_query:
- results.extend(result)
- else:
- # fuzzy_query=False, only one result
- if result:
- return result
- time.sleep(self.rate_interval)
- return results
- else:
- # query once
- return self._query(query, rows, sort=sort, order=order, fuzzy_query=fuzzy_query)
-
-
-class CrossRefQueryTitleTool(BuiltinTool):
- """
- Tool for querying the metadata of a publication using its title.
- """
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- query = tool_parameters.get("query")
- fuzzy_query = tool_parameters.get("fuzzy_query", False)
- rows = tool_parameters.get("rows", 3)
- sort = tool_parameters.get("sort", "relevance")
- order = tool_parameters.get("order", "desc")
- mailto = self.runtime.credentials["mailto"]
-
- result = CrossRefQueryTitleAPI(mailto).query(query, rows, sort, order, fuzzy_query)
-
- return [self.create_json_message(r) for r in result]
diff --git a/api/core/tools/provider/builtin/crossref/tools/query_title.yaml b/api/core/tools/provider/builtin/crossref/tools/query_title.yaml
deleted file mode 100644
index 5579c77f5293d3..00000000000000
--- a/api/core/tools/provider/builtin/crossref/tools/query_title.yaml
+++ /dev/null
@@ -1,105 +0,0 @@
-identity:
- name: crossref_query_title
- author: Sakura4036
- label:
- en_US: CrossRef Title Query
- zh_Hans: CrossRef 标题查询
- pt_BR: CrossRef Title Query
-description:
- human:
- en_US: A tool for querying literature information using CrossRef by title.
- zh_Hans: 一个使用CrossRef通过标题搜索文献信息的工具。
- pt_BR: A tool for querying literature information using CrossRef by title.
- llm: A tool for querying literature information using CrossRef by title.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: 标题
- zh_Hans: 查询语句
- pt_BR: 标题
- human_description:
- en_US: Query bibliographic information, useful for citation look up. Includes titles, authors, ISSNs and publication years
- zh_Hans: 用于搜索文献信息,有助于查找引用。包括标题,作者,ISSN和出版年份
- pt_BR: Query bibliographic information, useful for citation look up. Includes titles, authors, ISSNs and publication years
- llm_description: key words for querying in Web of Science
- form: llm
- - name: fuzzy_query
- type: boolean
- default: false
- label:
- en_US: Whether to fuzzy search
- zh_Hans: 是否模糊搜索
- pt_BR: Whether to fuzzy search
- human_description:
- en_US: used for selecting the query type, fuzzy query returns more results, precise query returns 1 or none
- zh_Hans: 用于选择搜索类型,模糊搜索返回更多结果,精确搜索返回1条结果或无
- pt_BR: used for selecting the query type, fuzzy query returns more results, precise query returns 1 or none
- form: form
- - name: limit
- type: number
- required: false
- label:
- en_US: max query number
- zh_Hans: 最大搜索数
- pt_BR: max query number
- human_description:
- en_US: max query number(fuzzy search returns the maximum number of results or precise search the maximum number of matches)
- zh_Hans: 最大搜索数(模糊搜索返回的最大结果数或精确搜索最大匹配数)
- pt_BR: max query number(fuzzy search returns the maximum number of results or precise search the maximum number of matches)
- form: llm
- default: 50
- - name: sort
- type: select
- required: true
- options:
- - value: relevance
- label:
- en_US: relevance
- zh_Hans: 相关性
- pt_BR: relevance
- - value: published
- label:
- en_US: publication date
- zh_Hans: 出版日期
- pt_BR: publication date
- - value: references-count
- label:
- en_US: references-count
- zh_Hans: 引用次数
- pt_BR: references-count
- default: relevance
- label:
- en_US: sorting field
- zh_Hans: 排序字段
- pt_BR: sorting field
- human_description:
- en_US: Sorting of query results
- zh_Hans: 检索结果的排序字段
- pt_BR: Sorting of query results
- form: form
- - name: order
- type: select
- required: true
- options:
- - value: desc
- label:
- en_US: descending
- zh_Hans: 降序
- pt_BR: descending
- - value: asc
- label:
- en_US: ascending
- zh_Hans: 升序
- pt_BR: ascending
- default: desc
- label:
- en_US: Order
- zh_Hans: 排序
- pt_BR: Order
- human_description:
- en_US: Order of query results
- zh_Hans: 检索结果的排序方式
- pt_BR: Order of query results
- form: form
diff --git a/api/core/tools/provider/builtin/dalle/__init__.py b/api/core/tools/provider/builtin/dalle/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/dalle/_assets/icon.png b/api/core/tools/provider/builtin/dalle/_assets/icon.png
deleted file mode 100644
index 5155a73059ed55..00000000000000
Binary files a/api/core/tools/provider/builtin/dalle/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/dalle/dalle.py b/api/core/tools/provider/builtin/dalle/dalle.py
deleted file mode 100644
index 5bd16e49e85e29..00000000000000
--- a/api/core/tools/provider/builtin/dalle/dalle.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.dalle.tools.dalle2 import DallE2Tool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DALLEProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- DallE2Tool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"prompt": "cute girl, blue eyes, white hair, anime style", "size": "small", "n": 1},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/dalle/dalle.yaml b/api/core/tools/provider/builtin/dalle/dalle.yaml
deleted file mode 100644
index 37cf93c28aae58..00000000000000
--- a/api/core/tools/provider/builtin/dalle/dalle.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-identity:
- author: Dify
- name: dalle
- label:
- en_US: DALL-E
- zh_Hans: DALL-E 绘画
- pt_BR: DALL-E
- description:
- en_US: DALL-E art
- zh_Hans: DALL-E 绘画
- pt_BR: DALL-E art
- icon: icon.png
- tags:
- - image
- - productivity
-credentials_for_provider:
- openai_api_key:
- type: secret-input
- required: true
- label:
- en_US: OpenAI API key
- zh_Hans: OpenAI API key
- pt_BR: OpenAI API key
- help:
- en_US: Please input your OpenAI API key
- zh_Hans: 请输入你的 OpenAI API key
- pt_BR: Please input your OpenAI API key
- placeholder:
- en_US: Please input your OpenAI API key
- zh_Hans: 请输入你的 OpenAI API key
- pt_BR: Please input your OpenAI API key
- openai_organization_id:
- type: text-input
- required: false
- label:
- en_US: OpenAI organization ID
- zh_Hans: OpenAI organization ID
- pt_BR: OpenAI organization ID
- help:
- en_US: Please input your OpenAI organization ID
- zh_Hans: 请输入你的 OpenAI organization ID
- pt_BR: Please input your OpenAI organization ID
- placeholder:
- en_US: Please input your OpenAI organization ID
- zh_Hans: 请输入你的 OpenAI organization ID
- pt_BR: Please input your OpenAI organization ID
- openai_base_url:
- type: text-input
- required: false
- label:
- en_US: OpenAI base URL
- zh_Hans: OpenAI base URL
- pt_BR: OpenAI base URL
- help:
- en_US: Please input your OpenAI base URL
- zh_Hans: 请输入你的 OpenAI base URL
- pt_BR: Please input your OpenAI base URL
- placeholder:
- en_US: Please input your OpenAI base URL
- zh_Hans: 请输入你的 OpenAI base URL
- pt_BR: Please input your OpenAI base URL
diff --git a/api/core/tools/provider/builtin/dalle/tools/dalle2.py b/api/core/tools/provider/builtin/dalle/tools/dalle2.py
deleted file mode 100644
index fbd7397292155e..00000000000000
--- a/api/core/tools/provider/builtin/dalle/tools/dalle2.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from base64 import b64decode
-from typing import Any, Union
-
-from openai import OpenAI
-from yarl import URL
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DallE2Tool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- openai_organization = self.runtime.credentials.get("openai_organization_id", None)
- if not openai_organization:
- openai_organization = None
- openai_base_url = self.runtime.credentials.get("openai_base_url", None)
- if not openai_base_url:
- openai_base_url = None
- else:
- openai_base_url = str(URL(openai_base_url) / "v1")
-
- client = OpenAI(
- api_key=self.runtime.credentials["openai_api_key"],
- base_url=openai_base_url,
- organization=openai_organization,
- )
-
- SIZE_MAPPING = {
- "small": "256x256",
- "medium": "512x512",
- "large": "1024x1024",
- }
-
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
-
- # get size
- size = SIZE_MAPPING[tool_parameters.get("size", "large")]
-
- # get n
- n = tool_parameters.get("n", 1)
-
- # call openapi dalle2
- response = client.images.generate(prompt=prompt, model="dall-e-2", size=size, n=n, response_format="b64_json")
-
- result = []
-
- for image in response.data:
- result.append(
- self.create_blob_message(
- blob=b64decode(image.b64_json),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
-
- return result
diff --git a/api/core/tools/provider/builtin/dalle/tools/dalle2.yaml b/api/core/tools/provider/builtin/dalle/tools/dalle2.yaml
deleted file mode 100644
index e43e5df8cddd9b..00000000000000
--- a/api/core/tools/provider/builtin/dalle/tools/dalle2.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-identity:
- name: dalle2
- author: Dify
- label:
- en_US: DALL-E 2
- zh_Hans: DALL-E 2 绘画
- description:
- en_US: DALL-E 2 is a powerful drawing tool that can draw the image you want based on your prompt
- zh_Hans: DALL-E 2 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像
- pt_BR: DALL-E 2 is a powerful drawing tool that can draw the image you want based on your prompt
-description:
- human:
- en_US: DALL-E is a text to image tool
- zh_Hans: DALL-E 是一个文本到图像的工具
- pt_BR: DALL-E is a text to image tool
- llm: DALL-E is a tool used to generate images from text
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of DallE 2
- zh_Hans: 图像提示词,您可以查看 DallE 2 的官方文档
- pt_BR: Image prompt, you can check the official documentation of DallE 2
- llm_description: Image prompt of DallE 2, you should describe the image you want to generate as a list of words as possible as detailed
- form: llm
- - name: size
- type: select
- required: true
- human_description:
- en_US: used for selecting the image size
- zh_Hans: 用于选择图像大小
- pt_BR: used for selecting the image size
- label:
- en_US: Image size
- zh_Hans: 图像大小
- pt_BR: Image size
- form: form
- options:
- - value: small
- label:
- en_US: Small(256x256)
- zh_Hans: 小(256x256)
- pt_BR: Small(256x256)
- - value: medium
- label:
- en_US: Medium(512x512)
- zh_Hans: 中(512x512)
- pt_BR: Medium(512x512)
- - value: large
- label:
- en_US: Large(1024x1024)
- zh_Hans: 大(1024x1024)
- pt_BR: Large(1024x1024)
- default: large
- - name: n
- type: number
- required: true
- human_description:
- en_US: used for selecting the number of images
- zh_Hans: 用于选择图像数量
- pt_BR: used for selecting the number of images
- label:
- en_US: Number of images
- zh_Hans: 图像数量
- pt_BR: Number of images
- form: form
- default: 1
- min: 1
- max: 10
diff --git a/api/core/tools/provider/builtin/dalle/tools/dalle3.py b/api/core/tools/provider/builtin/dalle/tools/dalle3.py
deleted file mode 100644
index af9aa6abb4bc3d..00000000000000
--- a/api/core/tools/provider/builtin/dalle/tools/dalle3.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import base64
-import random
-from typing import Any, Union
-
-from openai import OpenAI
-from yarl import URL
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DallE3Tool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- openai_organization = self.runtime.credentials.get("openai_organization_id", None)
- if not openai_organization:
- openai_organization = None
- openai_base_url = self.runtime.credentials.get("openai_base_url", None)
- if not openai_base_url:
- openai_base_url = None
- else:
- openai_base_url = str(URL(openai_base_url) / "v1")
-
- client = OpenAI(
- api_key=self.runtime.credentials["openai_api_key"],
- base_url=openai_base_url,
- organization=openai_organization,
- )
-
- SIZE_MAPPING = {
- "square": "1024x1024",
- "vertical": "1024x1792",
- "horizontal": "1792x1024",
- }
-
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
- # get size
- size = SIZE_MAPPING[tool_parameters.get("size", "square")]
- # get n
- n = tool_parameters.get("n", 1)
- # get quality
- quality = tool_parameters.get("quality", "standard")
- if quality not in {"standard", "hd"}:
- return self.create_text_message("Invalid quality")
- # get style
- style = tool_parameters.get("style", "vivid")
- if style not in {"natural", "vivid"}:
- return self.create_text_message("Invalid style")
-
- # call openapi dalle3
- response = client.images.generate(
- prompt=prompt, model="dall-e-3", size=size, n=n, style=style, quality=quality, response_format="b64_json"
- )
-
- result = []
-
- for image in response.data:
- mime_type, blob_image = DallE3Tool._decode_image(image.b64_json)
- blob_message = self.create_blob_message(
- blob=blob_image, meta={"mime_type": mime_type}, save_as=self.VariableKey.IMAGE
- )
- result.append(blob_message)
- return result
-
- @staticmethod
- def _decode_image(base64_image: str) -> tuple[str, bytes]:
- """
- Decode a base64 encoded image. If the image is not prefixed with a MIME type,
- it assumes 'image/png' as the default.
-
- :param base64_image: Base64 encoded image string
- :return: A tuple containing the MIME type and the decoded image bytes
- """
- if DallE3Tool._is_plain_base64(base64_image):
- return "image/png", base64.b64decode(base64_image)
- else:
- return DallE3Tool._extract_mime_and_data(base64_image)
-
- @staticmethod
- def _is_plain_base64(encoded_str: str) -> bool:
- """
- Check if the given encoded string is plain base64 without a MIME type prefix.
-
- :param encoded_str: Base64 encoded image string
- :return: True if the string is plain base64, False otherwise
- """
- return not encoded_str.startswith("data:image")
-
- @staticmethod
- def _extract_mime_and_data(encoded_str: str) -> tuple[str, bytes]:
- """
- Extract MIME type and image data from a base64 encoded string with a MIME type prefix.
-
- :param encoded_str: Base64 encoded image string with MIME type prefix
- :return: A tuple containing the MIME type and the decoded image bytes
- """
- mime_type = encoded_str.split(";")[0].split(":")[1]
- image_data_base64 = encoded_str.split(",")[1]
- decoded_data = base64.b64decode(image_data_base64)
- return mime_type, decoded_data
-
- @staticmethod
- def _generate_random_id(length=8):
- characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- random_id = "".join(random.choices(characters, k=length))
- return random_id
diff --git a/api/core/tools/provider/builtin/dalle/tools/dalle3.yaml b/api/core/tools/provider/builtin/dalle/tools/dalle3.yaml
deleted file mode 100644
index 0cea8af761e1e5..00000000000000
--- a/api/core/tools/provider/builtin/dalle/tools/dalle3.yaml
+++ /dev/null
@@ -1,123 +0,0 @@
-identity:
- name: dalle3
- author: Dify
- label:
- en_US: DALL-E 3
- zh_Hans: DALL-E 3 绘画
- pt_BR: DALL-E 3
- description:
- en_US: DALL-E 3 is a powerful drawing tool that can draw the image you want based on your prompt, compared to DallE 2, DallE 3 has stronger drawing ability, but it will consume more resources
- zh_Hans: DALL-E 3 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像,相比于DallE 2, DallE 3拥有更强的绘画能力,但会消耗更多的资源
- pt_BR: DALL-E 3 is a powerful drawing tool that can draw the image you want based on your prompt, compared to DallE 2, DallE 3 has stronger drawing ability, but it will consume more resources
-description:
- human:
- en_US: DALL-E is a text to image tool
- zh_Hans: DALL-E 是一个文本到图像的工具
- pt_BR: DALL-E is a text to image tool
- llm: DALL-E is a tool used to generate images from text
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of DallE 3
- zh_Hans: 图像提示词,您可以查看 DallE 3 的官方文档
- pt_BR: Image prompt, you can check the official documentation of DallE 3
- llm_description: Image prompt of DallE 3, you should describe the image you want to generate as a list of words as possible as detailed
- form: llm
- - name: size
- type: select
- required: true
- human_description:
- en_US: selecting the image size
- zh_Hans: 选择图像大小
- pt_BR: selecting the image size
- label:
- en_US: Image size
- zh_Hans: 图像大小
- pt_BR: Image size
- form: form
- options:
- - value: square
- label:
- en_US: Squre(1024x1024)
- zh_Hans: 方(1024x1024)
- pt_BR: Squre(1024x1024)
- - value: vertical
- label:
- en_US: Vertical(1024x1792)
- zh_Hans: 竖屏(1024x1792)
- pt_BR: Vertical(1024x1792)
- - value: horizontal
- label:
- en_US: Horizontal(1792x1024)
- zh_Hans: 横屏(1792x1024)
- pt_BR: Horizontal(1792x1024)
- default: square
- - name: n
- type: number
- required: true
- human_description:
- en_US: selecting the number of images
- zh_Hans: 选择图像数量
- pt_BR: selecting the number of images
- label:
- en_US: Number of images
- zh_Hans: 图像数量
- pt_BR: Number of images
- form: form
- min: 1
- max: 1
- default: 1
- - name: quality
- type: select
- required: true
- human_description:
- en_US: selecting the image quality
- zh_Hans: 选择图像质量
- pt_BR: selecting the image quality
- label:
- en_US: Image quality
- zh_Hans: 图像质量
- pt_BR: Image quality
- form: form
- options:
- - value: standard
- label:
- en_US: Standard
- zh_Hans: 标准
- pt_BR: Standard
- - value: hd
- label:
- en_US: HD
- zh_Hans: 高清
- pt_BR: HD
- default: standard
- - name: style
- type: select
- required: true
- human_description:
- en_US: selecting the image style
- zh_Hans: 选择图像风格
- pt_BR: selecting the image style
- label:
- en_US: Image style
- zh_Hans: 图像风格
- pt_BR: Image style
- form: form
- options:
- - value: vivid
- label:
- en_US: Vivid
- zh_Hans: 生动
- pt_BR: Vivid
- - value: natural
- label:
- en_US: Natural
- zh_Hans: 自然
- pt_BR: Natural
- default: vivid
diff --git a/api/core/tools/provider/builtin/devdocs/_assets/icon.svg b/api/core/tools/provider/builtin/devdocs/_assets/icon.svg
deleted file mode 100644
index c7a19fabfb18bf..00000000000000
--- a/api/core/tools/provider/builtin/devdocs/_assets/icon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/devdocs/devdocs.py b/api/core/tools/provider/builtin/devdocs/devdocs.py
deleted file mode 100644
index 446c1e548935c0..00000000000000
--- a/api/core/tools/provider/builtin/devdocs/devdocs.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.devdocs.tools.searchDevDocs import SearchDevDocsTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DevDocsProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- SearchDevDocsTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "doc": "python~3.12",
- "topic": "library/code",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/devdocs/devdocs.yaml b/api/core/tools/provider/builtin/devdocs/devdocs.yaml
deleted file mode 100644
index 7552f5a4973f7e..00000000000000
--- a/api/core/tools/provider/builtin/devdocs/devdocs.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-identity:
- author: Richards Tu
- name: devdocs
- label:
- en_US: DevDocs
- zh_Hans: DevDocs
- description:
- en_US: Get official developer documentations on DevDocs.
- zh_Hans: 从DevDocs获取官方开发者文档。
- icon: icon.svg
- tags:
- - search
- - productivity
diff --git a/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.py b/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.py
deleted file mode 100644
index 57cf6d7a308dba..00000000000000
--- a/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from typing import Any, Union
-
-import requests
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SearchDevDocsInput(BaseModel):
- doc: str = Field(..., description="The name of the documentation.")
- topic: str = Field(..., description="The path of the section/topic.")
-
-
-class SearchDevDocsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invokes the DevDocs search tool with the given user ID and tool parameters.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Any]): The parameters for the tool, including 'doc' and 'topic'.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation,
- which can be a single message or a list of messages.
- """
- doc = tool_parameters.get("doc", "")
- topic = tool_parameters.get("topic", "")
-
- if not doc:
- return self.create_text_message("Please provide the documentation name.")
- if not topic:
- return self.create_text_message("Please provide the topic path.")
-
- url = f"https://documents.devdocs.io/{doc}/{topic}.html"
- response = requests.get(url)
-
- if response.status_code == 200:
- content = response.text
- return self.create_text_message(self.summary(user_id=user_id, content=content))
- else:
- return self.create_text_message(
- f"Failed to retrieve the documentation. Status code: {response.status_code}"
- )
diff --git a/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.yaml b/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.yaml
deleted file mode 100644
index 2476db9da42d60..00000000000000
--- a/api/core/tools/provider/builtin/devdocs/tools/searchDevDocs.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-identity:
- name: searchDevDocs
- author: Richards Tu
- label:
- en_US: Search Developer Docs
- zh_Hans: 搜索开发者文档
-description:
- human:
- en_US: A tools for searching for a specific topic and path in DevDocs based on the provided documentation name and topic. Don't for get to add some shots in the system prompt; for example, the documentation name should be like \"vuex~4\", \"css\", or \"python~3.12\", while the topic should be like \"guide/actions\" for Vuex 4, \"display-box\" for CSS, or \"library/code\" for Python 3.12.
- zh_Hans: 一个用于根据提供的文档名称和主题,在DevDocs中搜索特定主题和路径的工具。不要忘记在系统提示词中添加一些示例;例如,文档名称应该是\"vuex~4\"、\"css\"或\"python~3.12\",而主题应该是\"guide/actions\"用于Vuex 4,\"display-box\"用于CSS,或\"library/code\"用于Python 3.12。
- llm: A tools for searching for specific developer documentation in DevDocs based on the provided documentation name and topic.
-parameters:
- - name: doc
- type: string
- required: true
- label:
- en_US: Documentation name
- zh_Hans: 文档名称
- human_description:
- en_US: The name of the documentation.
- zh_Hans: 文档名称。
- llm_description: The name of the documentation, such as \"vuex~4\", \"css\", or \"python~3.12\". The exact value should be identified by the user.
- form: llm
- - name: topic
- type: string
- required: true
- label:
- en_US: Topic name
- zh_Hans: 主题名称
- human_description:
- en_US: The path of the section/topic.
- zh_Hans: 文档主题的路径。
- llm_description: The path of the section/topic, such as \"guide/actions\" for Vuex 4, \"display-box\" for CSS, or \"library/code\" for Python 3.12.
- form: llm
diff --git a/api/core/tools/provider/builtin/did/_assets/icon.svg b/api/core/tools/provider/builtin/did/_assets/icon.svg
deleted file mode 100644
index c477d7cb71dea2..00000000000000
--- a/api/core/tools/provider/builtin/did/_assets/icon.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/did/did.py b/api/core/tools/provider/builtin/did/did.py
deleted file mode 100644
index 5af78794f625b7..00000000000000
--- a/api/core/tools/provider/builtin/did/did.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.did.tools.talks import TalksTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DIDProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- # Example validation using the D-ID talks tool
- TalksTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
- user_id="",
- tool_parameters={
- "source_url": "https://www.d-id.com/wp-content/uploads/2023/11/Hero-image-1.png",
- "text_input": "Hello, welcome to use D-ID tool in Dify",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/did/did.yaml b/api/core/tools/provider/builtin/did/did.yaml
deleted file mode 100644
index a70b71812e4648..00000000000000
--- a/api/core/tools/provider/builtin/did/did.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-identity:
- author: Matri Qi
- name: did
- label:
- en_US: D-ID
- description:
- en_US: D-ID is a tool enabling the creation of high-quality, custom videos of Digital Humans from a single image.
- icon: icon.svg
- tags:
- - videos
-credentials_for_provider:
- did_api_key:
- type: secret-input
- required: true
- label:
- en_US: D-ID API Key
- placeholder:
- en_US: Please input your D-ID API key
- help:
- en_US: Get your D-ID API key from your D-ID account settings.
- url: https://studio.d-id.com/account-settings
- base_url:
- type: text-input
- required: false
- label:
- en_US: D-ID server's Base URL
- placeholder:
- en_US: https://api.d-id.com
diff --git a/api/core/tools/provider/builtin/did/did_appx.py b/api/core/tools/provider/builtin/did/did_appx.py
deleted file mode 100644
index dca62f9e198262..00000000000000
--- a/api/core/tools/provider/builtin/did/did_appx.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import logging
-import time
-from collections.abc import Mapping
-from typing import Any
-
-import requests
-from requests.exceptions import HTTPError
-
-logger = logging.getLogger(__name__)
-
-
-class DIDApp:
- def __init__(self, api_key: str | None = None, base_url: str | None = None):
- self.api_key = api_key
- self.base_url = base_url or "https://api.d-id.com"
- if not self.api_key:
- raise ValueError("API key is required")
-
- def _prepare_headers(self, idempotency_key: str | None = None):
- headers = {"Content-Type": "application/json", "Authorization": f"Basic {self.api_key}"}
- if idempotency_key:
- headers["Idempotency-Key"] = idempotency_key
- return headers
-
- def _request(
- self,
- method: str,
- url: str,
- data: Mapping[str, Any] | None = None,
- headers: Mapping[str, str] | None = None,
- retries: int = 3,
- backoff_factor: float = 0.3,
- ) -> Mapping[str, Any] | None:
- for i in range(retries):
- try:
- response = requests.request(method, url, json=data, headers=headers)
- response.raise_for_status()
- return response.json()
- except requests.exceptions.RequestException as e:
- if i < retries - 1 and isinstance(e, HTTPError) and e.response.status_code >= 500:
- time.sleep(backoff_factor * (2**i))
- else:
- raise
- return None
-
- def talks(self, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs):
- endpoint = f"{self.base_url}/talks"
- headers = self._prepare_headers(idempotency_key)
- data = kwargs["params"]
- logger.debug(f"Send request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data, headers)
- if response is None:
- raise HTTPError("Failed to initiate D-ID talks after multiple retries")
- id: str = response["id"]
- if wait:
- return self._monitor_job_status(id=id, target="talks", poll_interval=poll_interval)
- return id
-
- def animations(self, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs):
- endpoint = f"{self.base_url}/animations"
- headers = self._prepare_headers(idempotency_key)
- data = kwargs["params"]
- logger.debug(f"Send request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data, headers)
- if response is None:
- raise HTTPError("Failed to initiate D-ID talks after multiple retries")
- id: str = response["id"]
- if wait:
- return self._monitor_job_status(target="animations", id=id, poll_interval=poll_interval)
- return id
-
- def check_did_status(self, target: str, id: str):
- endpoint = f"{self.base_url}/{target}/{id}"
- headers = self._prepare_headers()
- response = self._request("GET", endpoint, headers=headers)
- if response is None:
- raise HTTPError(f"Failed to check status for talks {id} after multiple retries")
- return response
-
- def _monitor_job_status(self, target: str, id: str, poll_interval: int):
- while True:
- status = self.check_did_status(target=target, id=id)
- if status["status"] == "done":
- return status
- elif status["status"] == "error" or status["status"] == "rejected":
- raise HTTPError(f"Talks {id} failed: {status['status']} {status.get('error', {}).get('description')}")
- time.sleep(poll_interval)
diff --git a/api/core/tools/provider/builtin/did/tools/animations.py b/api/core/tools/provider/builtin/did/tools/animations.py
deleted file mode 100644
index bc9d17e40d2878..00000000000000
--- a/api/core/tools/provider/builtin/did/tools/animations.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import json
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.did.did_appx import DIDApp
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class AnimationsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- app = DIDApp(api_key=self.runtime.credentials["did_api_key"], base_url=self.runtime.credentials["base_url"])
-
- driver_expressions_str = tool_parameters.get("driver_expressions")
- driver_expressions = json.loads(driver_expressions_str) if driver_expressions_str else None
-
- config = {
- "stitch": tool_parameters.get("stitch", True),
- "mute": tool_parameters.get("mute"),
- "result_format": tool_parameters.get("result_format") or "mp4",
- }
- config = {k: v for k, v in config.items() if v is not None and v != ""}
-
- options = {
- "source_url": tool_parameters["source_url"],
- "driver_url": tool_parameters.get("driver_url"),
- "config": config,
- }
- options = {k: v for k, v in options.items() if v is not None and v != ""}
-
- if not options.get("source_url"):
- raise ValueError("Source URL is required")
-
- if config.get("logo_url"):
- if not config.get("logo_x"):
- raise ValueError("Logo X position is required when logo URL is provided")
- if not config.get("logo_y"):
- raise ValueError("Logo Y position is required when logo URL is provided")
-
- animations_result = app.animations(params=options, wait=True)
-
- if not isinstance(animations_result, str):
- animations_result = json.dumps(animations_result, ensure_ascii=False, indent=4)
-
- if not animations_result:
- return self.create_text_message("D-ID animations request failed.")
-
- return self.create_text_message(animations_result)
diff --git a/api/core/tools/provider/builtin/did/tools/animations.yaml b/api/core/tools/provider/builtin/did/tools/animations.yaml
deleted file mode 100644
index 2a2036c7b2a88f..00000000000000
--- a/api/core/tools/provider/builtin/did/tools/animations.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-identity:
- name: animations
- author: Matri Qi
- label:
- en_US: Animations
-description:
- human:
- en_US: Animations enables to create videos matching head movements, expressions, emotions, and voice from a driver video and image.
- llm: Animations enables to create videos matching head movements, expressions, emotions, and voice from a driver video and image.
-parameters:
- - name: source_url
- type: string
- required: true
- label:
- en_US: source url
- human_description:
- en_US: The URL of the source image to be animated by the driver video, or a selection from the list of provided studio actors.
- llm_description: The URL of the source image to be animated by the driver video, or a selection from the list of provided studio actors.
- form: llm
- - name: driver_url
- type: string
- required: false
- label:
- en_US: driver url
- human_description:
- en_US: The URL of the driver video to drive the animation, or a provided driver name from D-ID.
- form: form
- - name: mute
- type: boolean
- required: false
- label:
- en_US: mute
- human_description:
- en_US: Mutes the driver sound in the animated video result, defaults to true
- form: form
- - name: stitch
- type: boolean
- required: false
- label:
- en_US: stitch
- human_description:
- en_US: If enabled, the driver video will be stitched with the animationing head video.
- form: form
- - name: logo_url
- type: string
- required: false
- label:
- en_US: logo url
- human_description:
- en_US: The URL of the logo image to be added to the animation video.
- form: form
- - name: logo_x
- type: number
- required: false
- label:
- en_US: logo position x
- human_description:
- en_US: The x position of the logo image in the animation video. It's required when logo url is provided.
- form: form
- - name: logo_y
- type: number
- required: false
- label:
- en_US: logo position y
- human_description:
- en_US: The y position of the logo image in the animation video. It's required when logo url is provided.
- form: form
- - name: result_format
- type: string
- default: mp4
- required: false
- label:
- en_US: result format
- human_description:
- en_US: The format of the result video.
- form: form
- options:
- - value: mp4
- label:
- en_US: mp4
- - value: gif
- label:
- en_US: gif
- - value: mov
- label:
- en_US: mov
diff --git a/api/core/tools/provider/builtin/did/tools/talks.py b/api/core/tools/provider/builtin/did/tools/talks.py
deleted file mode 100644
index d6f0c7ff179793..00000000000000
--- a/api/core/tools/provider/builtin/did/tools/talks.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import json
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.did.did_appx import DIDApp
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class TalksTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- app = DIDApp(api_key=self.runtime.credentials["did_api_key"], base_url=self.runtime.credentials["base_url"])
-
- driver_expressions_str = tool_parameters.get("driver_expressions")
- driver_expressions = json.loads(driver_expressions_str) if driver_expressions_str else None
-
- script = {
- "type": tool_parameters.get("script_type") or "text",
- "input": tool_parameters.get("text_input"),
- "audio_url": tool_parameters.get("audio_url"),
- "reduce_noise": tool_parameters.get("audio_reduce_noise", False),
- }
- script = {k: v for k, v in script.items() if v is not None and v != ""}
- config = {
- "stitch": tool_parameters.get("stitch", True),
- "sharpen": tool_parameters.get("sharpen"),
- "fluent": tool_parameters.get("fluent"),
- "result_format": tool_parameters.get("result_format") or "mp4",
- "pad_audio": tool_parameters.get("pad_audio"),
- "driver_expressions": driver_expressions,
- }
- config = {k: v for k, v in config.items() if v is not None and v != ""}
-
- options = {
- "source_url": tool_parameters["source_url"],
- "driver_url": tool_parameters.get("driver_url"),
- "script": script,
- "config": config,
- }
- options = {k: v for k, v in options.items() if v is not None and v != ""}
-
- if not options.get("source_url"):
- raise ValueError("Source URL is required")
-
- if script.get("type") == "audio":
- script.pop("input", None)
- if not script.get("audio_url"):
- raise ValueError("Audio URL is required for audio script type")
-
- if script.get("type") == "text":
- script.pop("audio_url", None)
- script.pop("reduce_noise", None)
- if not script.get("input"):
- raise ValueError("Text input is required for text script type")
-
- talks_result = app.talks(params=options, wait=True)
-
- if not isinstance(talks_result, str):
- talks_result = json.dumps(talks_result, ensure_ascii=False, indent=4)
-
- if not talks_result:
- return self.create_text_message("D-ID talks request failed.")
-
- return self.create_text_message(talks_result)
diff --git a/api/core/tools/provider/builtin/did/tools/talks.yaml b/api/core/tools/provider/builtin/did/tools/talks.yaml
deleted file mode 100644
index 88d430512923e4..00000000000000
--- a/api/core/tools/provider/builtin/did/tools/talks.yaml
+++ /dev/null
@@ -1,126 +0,0 @@
-identity:
- name: talks
- author: Matri Qi
- label:
- en_US: Talks
-description:
- human:
- en_US: Talks enables the creation of realistic talking head videos from text or audio inputs.
- llm: Talks enables the creation of realistic talking head videos from text or audio inputs.
-parameters:
- - name: source_url
- type: string
- required: true
- label:
- en_US: source url
- human_description:
- en_US: The URL of the source image to be animated by the driver video, or a selection from the list of provided studio actors.
- llm_description: The URL of the source image to be animated by the driver video, or a selection from the list of provided studio actors.
- form: llm
- - name: driver_url
- type: string
- required: false
- label:
- en_US: driver url
- human_description:
- en_US: The URL of the driver video to drive the talk, or a provided driver name from D-ID.
- form: form
- - name: script_type
- type: string
- required: false
- label:
- en_US: script type
- human_description:
- en_US: The type of the script.
- form: form
- options:
- - value: text
- label:
- en_US: text
- - value: audio
- label:
- en_US: audio
- - name: text_input
- type: string
- required: false
- label:
- en_US: text input
- human_description:
- en_US: The text input to be spoken by the talking head. Required when script type is text.
- form: form
- - name: audio_url
- type: string
- required: false
- label:
- en_US: audio url
- human_description:
- en_US: The URL of the audio file to be spoken by the talking head. Required when script type is audio.
- form: form
- - name: audio_reduce_noise
- type: boolean
- required: false
- label:
- en_US: audio reduce noise
- human_description:
- en_US: If enabled, the audio will be processed to reduce noise before being spoken by the talking head. It only works when script type is audio.
- form: form
- - name: stitch
- type: boolean
- required: false
- label:
- en_US: stitch
- human_description:
- en_US: If enabled, the driver video will be stitched with the talking head video.
- form: form
- - name: sharpen
- type: boolean
- required: false
- label:
- en_US: sharpen
- human_description:
- en_US: If enabled, the talking head video will be sharpened.
- form: form
- - name: result_format
- type: string
- required: false
- label:
- en_US: result format
- human_description:
- en_US: The format of the result video.
- form: form
- options:
- - value: mp4
- label:
- en_US: mp4
- - value: gif
- label:
- en_US: gif
- - value: mov
- label:
- en_US: mov
- - name: fluent
- type: boolean
- required: false
- label:
- en_US: fluent
- human_description:
- en_US: Interpolate between the last & first frames of the driver video When used together with pad_audio can create a seamless transition between videos of the same driver
- form: form
- - name: pad_audio
- type: number
- required: false
- label:
- en_US: pad audio
- human_description:
- en_US: Pad the audio with silence at the end (given in seconds) Will increase the video duration & the credits it consumes
- form: form
- min: 1
- max: 60
- - name: driver_expressions
- type: string
- required: false
- label:
- en_US: driver expressions
- human_description:
- en_US: timed expressions for animation. It should be an JSON array style string. Take D-ID documentation(https://docs.d-id.com/reference/createtalk) for more information.
- form: form
diff --git a/api/core/tools/provider/builtin/dingtalk/_assets/icon.svg b/api/core/tools/provider/builtin/dingtalk/_assets/icon.svg
deleted file mode 100644
index b60653b7a59409..00000000000000
--- a/api/core/tools/provider/builtin/dingtalk/_assets/icon.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/dingtalk/dingtalk.py b/api/core/tools/provider/builtin/dingtalk/dingtalk.py
deleted file mode 100644
index be1d5e099c2246..00000000000000
--- a/api/core/tools/provider/builtin/dingtalk/dingtalk.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from core.tools.provider.builtin.dingtalk.tools.dingtalk_group_bot import DingTalkGroupBotTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DingTalkProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- DingTalkGroupBotTool()
- pass
diff --git a/api/core/tools/provider/builtin/dingtalk/dingtalk.yaml b/api/core/tools/provider/builtin/dingtalk/dingtalk.yaml
deleted file mode 100644
index c922c140a8badc..00000000000000
--- a/api/core/tools/provider/builtin/dingtalk/dingtalk.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-identity:
- author: Bowen Liang
- name: dingtalk
- label:
- en_US: DingTalk
- zh_Hans: 钉钉
- pt_BR: DingTalk
- description:
- en_US: DingTalk group robot
- zh_Hans: 钉钉群机器人
- pt_BR: DingTalk group robot
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.py b/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.py
deleted file mode 100644
index f33ad5be59b403..00000000000000
--- a/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.py
+++ /dev/null
@@ -1,89 +0,0 @@
-import base64
-import hashlib
-import hmac
-import logging
-import time
-import urllib.parse
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DingTalkGroupBotTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- Dingtalk custom group robot API docs:
- https://open.dingtalk.com/document/orgapp/custom-robot-access
- """
- content = tool_parameters.get("content")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- access_token = tool_parameters.get("access_token")
- if not access_token:
- return self.create_text_message(
- "Invalid parameter access_token. "
- "Regarding information about security details,"
- "please refer to the DingTalk docs:"
- "https://open.dingtalk.com/document/robots/customize-robot-security-settings"
- )
-
- sign_secret = tool_parameters.get("sign_secret")
- if not sign_secret:
- return self.create_text_message(
- "Invalid parameter sign_secret. "
- "Regarding information about security details,"
- "please refer to the DingTalk docs:"
- "https://open.dingtalk.com/document/robots/customize-robot-security-settings"
- )
-
- msgtype = "text"
- api_url = "https://oapi.dingtalk.com/robot/send"
- headers = {
- "Content-Type": "application/json",
- }
- params = {
- "access_token": access_token,
- }
-
- self._apply_security_mechanism(params, sign_secret)
-
- payload = {
- "msgtype": msgtype,
- "text": {
- "content": content,
- },
- }
-
- try:
- res = httpx.post(api_url, headers=headers, params=params, json=payload)
- if res.is_success:
- return self.create_text_message("Text message sent successfully")
- else:
- return self.create_text_message(
- f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
-
- @staticmethod
- def _apply_security_mechanism(params: dict[str, Any], sign_secret: str):
- try:
- timestamp = str(round(time.time() * 1000))
- secret_enc = sign_secret.encode("utf-8")
- string_to_sign = f"{timestamp}\n{sign_secret}"
- string_to_sign_enc = string_to_sign.encode("utf-8")
- hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
- sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
-
- params["timestamp"] = timestamp
- params["sign"] = sign
- except Exception:
- msg = "Failed to apply security mechanism to the request."
- logging.exception(msg)
diff --git a/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.yaml b/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.yaml
deleted file mode 100644
index dc8a90b7193903..00000000000000
--- a/api/core/tools/provider/builtin/dingtalk/tools/dingtalk_group_bot.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-identity:
- name: dingtalk_group_bot
- author: Bowen Liang
- label:
- en_US: Send Group Message
- zh_Hans: 发送群消息
- pt_BR: Send Group Message
- icon: icon.svg
-description:
- human:
- en_US: Sending a group message on DingTalk via the webhook of group bot
- zh_Hans: 通过钉钉的群机器人webhook发送群消息
- pt_BR: Sending a group message on DingTalk via the webhook of group bot
- llm: A tool for sending messages to a chat group on DingTalk(钉钉) .
-parameters:
- - name: access_token
- type: secret-input
- required: true
- label:
- en_US: access token
- zh_Hans: access token
- pt_BR: access token
- human_description:
- en_US: access_token in the group robot webhook
- zh_Hans: 群自定义机器人webhook中access_token字段的值
- pt_BR: access_token in the group robot webhook
- form: form
- - name: sign_secret
- type: secret-input
- required: true
- label:
- en_US: secret key for signing
- zh_Hans: 加签秘钥
- pt_BR: secret key for signing
- human_description:
- en_US: secret key for signing
- zh_Hans: 加签秘钥
- pt_BR: secret key for signing
- form: form
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- pt_BR: content
- human_description:
- en_US: Content to sent to the group.
- zh_Hans: 群消息文本
- pt_BR: Content to sent to the group.
- llm_description: Content of the message
- form: llm
diff --git a/api/core/tools/provider/builtin/discord/_assets/icon.svg b/api/core/tools/provider/builtin/discord/_assets/icon.svg
deleted file mode 100644
index 177a0591f9cb08..00000000000000
--- a/api/core/tools/provider/builtin/discord/_assets/icon.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/discord/discord.py b/api/core/tools/provider/builtin/discord/discord.py
deleted file mode 100644
index c94824b591cd95..00000000000000
--- a/api/core/tools/provider/builtin/discord/discord.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin.discord.tools.discord_webhook import DiscordWebhookTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DiscordProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- DiscordWebhookTool()
diff --git a/api/core/tools/provider/builtin/discord/discord.yaml b/api/core/tools/provider/builtin/discord/discord.yaml
deleted file mode 100644
index 18b249b5229a0e..00000000000000
--- a/api/core/tools/provider/builtin/discord/discord.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-identity:
- author: Ice Yao
- name: discord
- label:
- en_US: Discord
- zh_Hans: Discord
- pt_BR: Discord
- description:
- en_US: Discord Webhook
- zh_Hans: Discord Webhook
- pt_BR: Discord Webhook
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/discord/tools/discord_webhook.py b/api/core/tools/provider/builtin/discord/tools/discord_webhook.py
deleted file mode 100644
index c1834a1a265be2..00000000000000
--- a/api/core/tools/provider/builtin/discord/tools/discord_webhook.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DiscordWebhookTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Incoming Webhooks
- API Document:
- https://discord.com/developers/docs/resources/webhook#execute-webhook
- """
-
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- webhook_url = tool_parameters.get("webhook_url", "")
- if not webhook_url.startswith("https://discord.com/api/webhooks/"):
- return self.create_text_message(
- f"Invalid parameter webhook_url ${webhook_url}, \
- not a valid Discord webhook URL"
- )
-
- headers = {
- "Content-Type": "application/json",
- }
- payload = {
- "username": tool_parameters.get("username") or user_id,
- "content": content,
- "avatar_url": tool_parameters.get("avatar_url") or None,
- }
-
- try:
- res = httpx.post(webhook_url, headers=headers, json=payload)
- if res.is_success:
- return self.create_text_message("Text message was sent successfully")
- else:
- return self.create_text_message(
- f"Failed to send the text message, \
- status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to send message through webhook. {}".format(e))
diff --git a/api/core/tools/provider/builtin/discord/tools/discord_webhook.yaml b/api/core/tools/provider/builtin/discord/tools/discord_webhook.yaml
deleted file mode 100644
index 6847b973cabd19..00000000000000
--- a/api/core/tools/provider/builtin/discord/tools/discord_webhook.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-identity:
- name: discord_webhook
- author: Ice Yao
- label:
- en_US: Incoming Webhook to send message
- zh_Hans: 通过入站Webhook发送消息
- pt_BR: Incoming Webhook to send message
- icon: icon.svg
-description:
- human:
- en_US: Sending a message on Discord via the Incoming Webhook
- zh_Hans: 通过入站Webhook在Discord上发送消息
- pt_BR: Sending a message on Discord via the Incoming Webhook
- llm: A tool for sending messages to a chat on Discord.
-parameters:
- - name: webhook_url
- type: string
- required: true
- label:
- en_US: Discord Incoming Webhook url
- zh_Hans: Discord入站Webhook的url
- pt_BR: Discord Incoming Webhook url
- human_description:
- en_US: Discord Incoming Webhook url
- zh_Hans: Discord入站Webhook的url
- pt_BR: Discord Incoming Webhook url
- form: form
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- pt_BR: content
- human_description:
- en_US: Content to sent to the channel or person.
- zh_Hans: 消息内容文本
- pt_BR: Content to sent to the channel or person.
- llm_description: Content of the message
- form: llm
- - name: username
- type: string
- required: false
- label:
- en_US: Discord Webhook Username
- zh_Hans: Discord Webhook用户名
- pt_BR: Discord Webhook Username
- human_description:
- en_US: Discord Webhook Username
- zh_Hans: Discord Webhook用户名
- pt_BR: Discord Webhook Username
- llm_description: Discord Webhook Username
- form: llm
- - name: avatar_url
- type: string
- required: false
- label:
- en_US: Discord Webhook Avatar
- zh_Hans: Discord Webhook头像
- pt_BR: Discord Webhook Avatar
- human_description:
- en_US: Discord Webhook Avatar URL
- zh_Hans: Discord Webhook头像地址
- pt_BR: Discord Webhook Avatar URL
- form: form
diff --git a/api/core/tools/provider/builtin/duckduckgo/_assets/icon.svg b/api/core/tools/provider/builtin/duckduckgo/_assets/icon.svg
deleted file mode 100644
index a816a6b49ebb78..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py b/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py
deleted file mode 100644
index 8269167127b8e5..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.duckduckgo.tools.ddgo_search import DuckDuckGoSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class DuckDuckGoProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- DuckDuckGoSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "John Doe",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.yaml b/api/core/tools/provider/builtin/duckduckgo/duckduckgo.yaml
deleted file mode 100644
index f3faa060455772..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-identity:
- author: Yash Parmar
- name: duckduckgo
- label:
- en_US: DuckDuckGo
- zh_Hans: DuckDuckGo
- description:
- en_US: A privacy-focused search engine.
- zh_Hans: 一个注重隐私的搜索引擎。
- icon: icon.svg
- tags:
- - search
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py
deleted file mode 100644
index 8bdd638f4a01d1..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from duckduckgo_search import DDGS
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DuckDuckGoAITool(BuiltinTool):
- """
- Tool for performing a search using DuckDuckGo search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- query_dict = {
- "keywords": tool_parameters.get("query"),
- "model": tool_parameters.get("model"),
- }
- response = DDGS().chat(**query_dict)
- return self.create_text_message(text=response)
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml
deleted file mode 100644
index dd049d3b5a13d2..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-identity:
- name: ddgo_ai
- author: hjlarry
- label:
- en_US: DuckDuckGo AI Chat
- zh_Hans: DuckDuckGo AI聊天
-description:
- human:
- en_US: Use the anonymous private chat provided by DuckDuckGo.
- zh_Hans: 使用DuckDuckGo提供的匿名私密聊天。
- llm: Use the anonymous private chat provided by DuckDuckGo.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Chat Content
- zh_Hans: 聊天内容
- human_description:
- en_US: The chat content.
- zh_Hans: 要聊天的内容。
- llm_description: Key words for chat
- form: llm
- - name: model
- type: select
- required: true
- options:
- - value: gpt-4o-mini
- label:
- en_US: GPT-4o-mini
- - value: claude-3-haiku
- label:
- en_US: Claude 3
- - value: llama-3-70b
- label:
- en_US: Llama 3
- - value: mixtral-8x7b
- label:
- en_US: Mixtral
- default: gpt-4o-mini
- label:
- en_US: Choose Model
- zh_Hans: 选择模型
- human_description:
- en_US: used to select the model for AI chat.
- zh_Hans: 用于选择使用AI聊天的模型
- form: form
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py
deleted file mode 100644
index b3c630878f3c62..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import Any
-
-from duckduckgo_search import DDGS
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DuckDuckGoImageSearchTool(BuiltinTool):
- """
- Tool for performing an image search using DuckDuckGo search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]:
- query_dict = {
- "keywords": tool_parameters.get("query"),
- "timelimit": tool_parameters.get("timelimit"),
- "size": tool_parameters.get("size"),
- "max_results": tool_parameters.get("max_results"),
- }
-
- # Add query_prefix handling
- query_prefix = tool_parameters.get("query_prefix", "").strip()
- final_query = f"{query_prefix} {query_dict['keywords']}".strip()
- query_dict["keywords"] = final_query
-
- response = DDGS().images(**query_dict)
- markdown_result = "\n\n"
- json_result = []
- for res in response:
- markdown_result += f" or ''})"
- json_result.append(self.create_json_message(res))
- return [self.create_text_message(markdown_result)] + json_result
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml
deleted file mode 100644
index a543d1e218b578..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml
+++ /dev/null
@@ -1,99 +0,0 @@
-identity:
- name: ddgo_img
- author: hjlarry
- label:
- en_US: DuckDuckGo Image Search
- zh_Hans: DuckDuckGo 图片搜索
-description:
- human:
- en_US: Perform image searches on DuckDuckGo and get results.
- zh_Hans: 在 DuckDuckGo 上进行图片搜索并获取结果。
- llm: Perform image searches on DuckDuckGo and get results.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- human_description:
- en_US: The search query.
- zh_Hans: 搜索查询语句。
- llm_description: Key words for searching
- form: llm
- - name: max_results
- type: number
- required: true
- default: 3
- label:
- en_US: Max results
- zh_Hans: 最大结果数量
- human_description:
- en_US: The max results.
- zh_Hans: 最大结果数量
- form: form
- - name: timelimit
- type: select
- required: false
- options:
- - value: Day
- label:
- en_US: current day
- zh_Hans: 当天
- - value: Week
- label:
- en_US: current week
- zh_Hans: 本周
- - value: Month
- label:
- en_US: current month
- zh_Hans: 当月
- - value: Year
- label:
- en_US: current year
- zh_Hans: 今年
- label:
- en_US: Result time limit
- zh_Hans: 结果时间限制
- human_description:
- en_US: Use when querying results within a specific time range only.
- zh_Hans: 只查询一定时间范围内的结果时使用
- form: form
- - name: size
- type: select
- required: false
- options:
- - value: Small
- label:
- en_US: small
- zh_Hans: 小
- - value: Medium
- label:
- en_US: medium
- zh_Hans: 中
- - value: Large
- label:
- en_US: large
- zh_Hans: 大
- - value: Wallpaper
- label:
- en_US: xl
- zh_Hans: 超大
- label:
- en_US: image size
- zh_Hans: 图片大小
- human_description:
- en_US: The size of the image to be searched.
- zh_Hans: 要搜索的图片的大小
- form: form
- - name: query_prefix
- label:
- en_US: Query Prefix
- zh_Hans: 查询前缀
- type: string
- required: false
- default: ""
- form: form
- human_description:
- en_US: Specific Search e.g. "site:unsplash.com"
- zh_Hans: 定向搜索 e.g. "site:unsplash.com"
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py
deleted file mode 100644
index 11da6f5cf76580..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from typing import Any
-
-from duckduckgo_search import DDGS
-
-from core.model_runtime.entities.message_entities import SystemPromptMessage
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SUMMARY_PROMPT = """
-User's query:
-{query}
-
-Here are the news results:
-{content}
-
-Please summarize the news in a few sentences.
-"""
-
-
-class DuckDuckGoNewsSearchTool(BuiltinTool):
- """
- Tool for performing a news search using DuckDuckGo search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- query_dict = {
- "keywords": tool_parameters.get("query"),
- "timelimit": tool_parameters.get("timelimit"),
- "max_results": tool_parameters.get("max_results"),
- "safesearch": "moderate",
- "region": "wt-wt",
- }
-
- # Add query_prefix handling
- query_prefix = tool_parameters.get("query_prefix", "").strip()
- final_query = f"{query_prefix} {query_dict['keywords']}".strip()
- query_dict["keywords"] = final_query
-
- try:
- response = list(DDGS().news(**query_dict))
- if not response:
- return [self.create_text_message("No news found matching your criteria.")]
- except Exception as e:
- return [self.create_text_message(f"Error searching news: {str(e)}")]
-
- require_summary = tool_parameters.get("require_summary", False)
-
- if require_summary:
- results = "\n".join([f"{res.get('title')}: {res.get('body')}" for res in response])
- results = self.summary_results(user_id=user_id, content=results, query=query_dict["keywords"])
- return self.create_text_message(text=results)
-
- # Create rich markdown content for each news item
- markdown_result = "\n\n"
- json_result = []
-
- for res in response:
- markdown_result += f"### {res.get('title', 'Untitled')}\n\n"
- if res.get("date"):
- markdown_result += f"**Date:** {res.get('date')}\n\n"
- if res.get("body"):
- markdown_result += f"{res.get('body')}\n\n"
- if res.get("source"):
- markdown_result += f"*Source: {res.get('source')}*\n\n"
- if res.get("image"):
- markdown_result += f"})\n\n"
- markdown_result += f"[Read more]({res.get('url', '')})\n\n---\n\n"
-
- json_result.append(
- self.create_json_message(
- {
- "title": res.get("title", ""),
- "date": res.get("date", ""),
- "body": res.get("body", ""),
- "url": res.get("url", ""),
- "image": res.get("image", ""),
- "source": res.get("source", ""),
- }
- )
- )
-
- return [self.create_text_message(markdown_result)] + json_result
-
- def summary_results(self, user_id: str, content: str, query: str) -> str:
- prompt = SUMMARY_PROMPT.format(query=query, content=content)
- summary = self.invoke_model(
- user_id=user_id,
- prompt_messages=[
- SystemPromptMessage(content=prompt),
- ],
- stop=[],
- )
- return summary.message.content
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml
deleted file mode 100644
index 6e181e0f41c22f..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml
+++ /dev/null
@@ -1,82 +0,0 @@
-identity:
- name: ddgo_news
- author: Assistant
- label:
- en_US: DuckDuckGo News Search
- zh_Hans: DuckDuckGo 新闻搜索
-description:
- human:
- en_US: Perform news searches on DuckDuckGo and get results.
- zh_Hans: 在 DuckDuckGo 上进行新闻搜索并获取结果。
- llm: Perform news searches on DuckDuckGo and get results.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query String
- zh_Hans: 查询语句
- human_description:
- en_US: Search Query.
- zh_Hans: 搜索查询语句。
- llm_description: Key words for searching
- form: llm
- - name: max_results
- type: number
- required: true
- default: 5
- label:
- en_US: Max Results
- zh_Hans: 最大结果数量
- human_description:
- en_US: The Max Results
- zh_Hans: 最大结果数量
- form: form
- - name: timelimit
- type: select
- required: false
- options:
- - value: Day
- label:
- en_US: Current Day
- zh_Hans: 当天
- - value: Week
- label:
- en_US: Current Week
- zh_Hans: 本周
- - value: Month
- label:
- en_US: Current Month
- zh_Hans: 当月
- - value: Year
- label:
- en_US: Current Year
- zh_Hans: 今年
- label:
- en_US: Result Time Limit
- zh_Hans: 结果时间限制
- human_description:
- en_US: Use when querying results within a specific time range only.
- zh_Hans: 只查询一定时间范围内的结果时使用
- form: form
- - name: require_summary
- type: boolean
- default: false
- label:
- en_US: Require Summary
- zh_Hans: 是否总结
- human_description:
- en_US: Whether to pass the news results to llm for summarization.
- zh_Hans: 是否需要将新闻结果传给大模型总结
- form: form
- - name: query_prefix
- label:
- en_US: Query Prefix
- zh_Hans: 查询前缀
- type: string
- required: false
- default: ""
- form: form
- human_description:
- en_US: Specific Search e.g. "site:msn.com"
- zh_Hans: 定向搜索 e.g. "site:msn.com"
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py
deleted file mode 100644
index 3cd35d16a6f460..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from typing import Any
-
-from duckduckgo_search import DDGS
-
-from core.model_runtime.entities.message_entities import SystemPromptMessage
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SUMMARY_PROMPT = """
-User's query:
-{query}
-
-Here is the search engine result:
-{content}
-
-Please summarize the result in a few sentences.
-"""
-
-
-class DuckDuckGoSearchTool(BuiltinTool):
- """
- Tool for performing a search using DuckDuckGo search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- query = tool_parameters.get("query")
- max_results = tool_parameters.get("max_results", 5)
- require_summary = tool_parameters.get("require_summary", False)
-
- # Add query_prefix handling
- query_prefix = tool_parameters.get("query_prefix", "").strip()
- final_query = f"{query_prefix} {query}".strip()
-
- response = DDGS().text(final_query, max_results=max_results)
- if require_summary:
- results = "\n".join([res.get("body") for res in response])
- results = self.summary_results(user_id=user_id, content=results, query=query)
- return self.create_text_message(text=results)
- return [self.create_json_message(res) for res in response]
-
- def summary_results(self, user_id: str, content: str, query: str) -> str:
- prompt = SUMMARY_PROMPT.format(query=query, content=content)
- summary = self.invoke_model(
- user_id=user_id,
- prompt_messages=[
- SystemPromptMessage(content=prompt),
- ],
- stop=[],
- )
- return summary.message.content
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml
deleted file mode 100644
index 54e27d9905da12..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-identity:
- name: ddgo_search
- author: Yash Parmar
- label:
- en_US: DuckDuckGo Search
- zh_Hans: DuckDuckGo 搜索
-description:
- human:
- en_US: Perform searches on DuckDuckGo and get results.
- zh_Hans: 在 DuckDuckGo 上进行搜索并获取结果。
- llm: Perform searches on DuckDuckGo and get results.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- human_description:
- en_US: The search query.
- zh_Hans: 搜索查询语句。
- llm_description: Key words for searching
- form: llm
- - name: max_results
- type: number
- required: true
- default: 5
- label:
- en_US: Max results
- zh_Hans: 最大结果数量
- form: form
- - name: require_summary
- type: boolean
- default: false
- label:
- en_US: Require Summary
- zh_Hans: 是否总结
- human_description:
- en_US: Whether to pass the search results to llm for summarization.
- zh_Hans: 是否需要将搜索结果传给大模型总结
- form: form
- - name: query_prefix
- label:
- en_US: Query Prefix
- zh_Hans: 查询前缀
- type: string
- required: false
- default: ""
- form: form
- human_description:
- en_US: Specific Search e.g. "site:wikipedia.org"
- zh_Hans: 定向搜索 e.g. "site:wikipedia.org"
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py
deleted file mode 100644
index 396ce21b183afc..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from duckduckgo_search import DDGS
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DuckDuckGoTranslateTool(BuiltinTool):
- """
- Tool for performing a search using DuckDuckGo search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- query_dict = {
- "keywords": tool_parameters.get("query"),
- "to": tool_parameters.get("translate_to"),
- }
- response = DDGS().translate(**query_dict)[0].get("translated", "Unable to translate!")
- return self.create_text_message(text=response)
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml
deleted file mode 100644
index 78b5d0b02275b2..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml
+++ /dev/null
@@ -1,51 +0,0 @@
-identity:
- name: ddgo_translate
- author: hjlarry
- label:
- en_US: DuckDuckGo Translate
- zh_Hans: DuckDuckGo 翻译
-description:
- human:
- en_US: Use DuckDuckGo's translation feature.
- zh_Hans: 使用DuckDuckGo的翻译功能。
- llm: Use DuckDuckGo's translation feature.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Translate Content
- zh_Hans: 翻译内容
- human_description:
- en_US: The translate content.
- zh_Hans: 要翻译的内容。
- llm_description: Key words for translate
- form: llm
- - name: translate_to
- type: select
- required: true
- options:
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: zh-Hans
- label:
- en_US: Simplified Chinese
- zh_Hans: 简体中文
- - value: zh-Hant
- label:
- en_US: Traditional Chinese
- zh_Hans: 繁体中文
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- default: en
- label:
- en_US: Choose Language
- zh_Hans: 选择语言
- human_description:
- en_US: select the language to translate.
- zh_Hans: 选择要翻译的语言
- form: form
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py
deleted file mode 100644
index 1eef0b1ba23d42..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from typing import Any, ClassVar
-
-from duckduckgo_search import DDGS
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DuckDuckGoVideoSearchTool(BuiltinTool):
- """
- Tool for performing a video search using DuckDuckGo search engine.
- """
-
- IFRAME_TEMPLATE: ClassVar[str] = """
-
-
-
"""
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]:
- query_dict = {
- "keywords": tool_parameters.get("query"), # LLM's query
- "region": tool_parameters.get("region", "wt-wt"),
- "safesearch": tool_parameters.get("safesearch", "moderate"),
- "timelimit": tool_parameters.get("timelimit"),
- "resolution": tool_parameters.get("resolution"),
- "duration": tool_parameters.get("duration"),
- "license_videos": tool_parameters.get("license_videos"),
- "max_results": tool_parameters.get("max_results"),
- }
-
- # Remove None values to use API defaults
- query_dict = {k: v for k, v in query_dict.items() if v is not None}
-
- # Get proxy URL from parameters
- proxy_url = tool_parameters.get("proxy_url", "").strip()
-
- query_prefix = tool_parameters.get("query_prefix", "").strip()
- final_query = f"{query_prefix} {query_dict['keywords']}".strip()
-
- # Update the keywords in query_dict with the final_query
- query_dict["keywords"] = final_query
-
- response = DDGS().videos(**query_dict)
-
- # Create HTML result with embedded iframes
- markdown_result = "\n\n"
- json_result = []
-
- for res in response:
- title = res.get("title", "")
- embed_html = res.get("embed_html", "")
- description = res.get("description", "")
- content_url = res.get("content", "")
- transcript_url = None
-
- # Handle TED.com videos
- if "ted.com/talks" in content_url:
- # Create transcript URL
- transcript_url = f"{content_url}/transcript"
- # Create embed URL
- embed_url = content_url.replace("www.ted.com", "embed.ted.com")
- if proxy_url:
- embed_url = f"{proxy_url}{embed_url}"
- embed_html = self.IFRAME_TEMPLATE.format(src=embed_url)
-
- # Original YouTube/other platform handling
- elif embed_html:
- embed_url = res.get("embed_url", "")
- if proxy_url and embed_url:
- embed_url = f"{proxy_url}{embed_url}"
- embed_html = self.IFRAME_TEMPLATE.format(src=embed_url)
-
- markdown_result += f"{title}\n\n"
- markdown_result += f"{embed_html}\n\n"
- if description:
- markdown_result += f"{description}\n\n"
- markdown_result += "---\n\n"
-
- # Add transcript_url to the JSON result if available
- result_dict = res.copy()
- if transcript_url:
- result_dict["transcript_url"] = transcript_url
- json_result.append(self.create_json_message(result_dict))
-
- return [self.create_text_message(markdown_result)] + json_result
diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml
deleted file mode 100644
index d846244e3dfcbd..00000000000000
--- a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml
+++ /dev/null
@@ -1,108 +0,0 @@
-identity:
- name: ddgo_video
- author: Tao Wang
- label:
- en_US: DuckDuckGo Video Search
- zh_Hans: DuckDuckGo 视频搜索
-description:
- human:
- en_US: Search and embedded videos.
- zh_Hans: 搜索并嵌入视频
- llm: Search videos on duckduckgo and embed videos in iframe
-parameters:
- - name: query
- label:
- en_US: Query String
- zh_Hans: 查询语句
- type: string
- required: true
- human_description:
- en_US: Search Query
- zh_Hans: 搜索查询语句
- llm_description: Key words for searching
- form: llm
- - name: max_results
- label:
- en_US: Max Results
- zh_Hans: 最大结果数量
- type: number
- required: true
- default: 3
- minimum: 1
- maximum: 10
- human_description:
- en_US: The max results (1-10)
- zh_Hans: 最大结果数量(1-10)
- form: form
- - name: timelimit
- label:
- en_US: Result Time Limit
- zh_Hans: 结果时间限制
- type: select
- required: false
- options:
- - value: Day
- label:
- en_US: Current Day
- zh_Hans: 当天
- - value: Week
- label:
- en_US: Current Week
- zh_Hans: 本周
- - value: Month
- label:
- en_US: Current Month
- zh_Hans: 当月
- - value: Year
- label:
- en_US: Current Year
- zh_Hans: 今年
- human_description:
- en_US: Query results within a specific time range only
- zh_Hans: 只查询一定时间范围内的结果时使用
- form: form
- - name: duration
- label:
- en_US: Video Duration
- zh_Hans: 视频时长
- type: select
- required: false
- options:
- - value: short
- label:
- en_US: Short (<4 minutes)
- zh_Hans: 短视频(<4分钟)
- - value: medium
- label:
- en_US: Medium (4-20 minutes)
- zh_Hans: 中等(4-20分钟)
- - value: long
- label:
- en_US: Long (>20 minutes)
- zh_Hans: 长视频(>20分钟)
- human_description:
- en_US: Filter videos by duration
- zh_Hans: 按时长筛选视频
- form: form
- - name: proxy_url
- label:
- en_US: Proxy URL
- zh_Hans: 视频代理地址
- type: string
- required: false
- default: ""
- human_description:
- en_US: Proxy URL
- zh_Hans: 视频代理地址
- form: form
- - name: query_prefix
- label:
- en_US: Query Prefix
- zh_Hans: 查询前缀
- type: string
- required: false
- default: ""
- form: form
- human_description:
- en_US: Specific Search e.g. "site:www.ted.com"
- zh_Hans: 定向搜索 e.g. "site:www.ted.com"
diff --git a/api/core/tools/provider/builtin/email/_assets/icon.svg b/api/core/tools/provider/builtin/email/_assets/icon.svg
deleted file mode 100644
index b34f333890bc1a..00000000000000
--- a/api/core/tools/provider/builtin/email/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/api/core/tools/provider/builtin/email/email.py b/api/core/tools/provider/builtin/email/email.py
deleted file mode 100644
index 182d8dac28efea..00000000000000
--- a/api/core/tools/provider/builtin/email/email.py
+++ /dev/null
@@ -1,7 +0,0 @@
-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
deleted file mode 100644
index bb1bb7f6f3e972..00000000000000
--- a/api/core/tools/provider/builtin/email/email.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-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
deleted file mode 100644
index 2012d8b1156fb5..00000000000000
--- a/api/core/tools/provider/builtin/email/tools/send.py
+++ /dev/null
@@ -1,53 +0,0 @@
-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(params: SendEmailToolParameters):
- timeout = 60
- msg = MIMEMultipart("alternative")
- msg["From"] = params.email_account
- msg["To"] = params.sender_to
- msg["Subject"] = params.subject
- msg.attach(MIMEText(params.email_content, "plain"))
- msg.attach(MIMEText(params.email_content, "html"))
-
- ctx = ssl.create_default_context()
-
- if params.encrypt_method.upper() == "SSL":
- try:
- with smtplib.SMTP_SSL(params.smtp_server, params.smtp_port, context=ctx, timeout=timeout) as server:
- server.login(params.email_account, params.email_password)
- server.sendmail(params.email_account, params.sender_to, msg.as_string())
- return True
- except Exception as e:
- logging.exception("send email failed")
- return False
- else: # NONE or TLS
- try:
- with smtplib.SMTP(params.smtp_server, params.smtp_port, timeout=timeout) as server:
- if params.encrypt_method.upper() == "TLS":
- server.starttls(context=ctx)
- server.login(params.email_account, params.email_password)
- server.sendmail(params.email_account, params.sender_to, msg.as_string())
- return True
- except Exception as e:
- logging.exception("send email failed")
- 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
deleted file mode 100644
index 33c040400ca14a..00000000000000
--- a/api/core/tools/provider/builtin/email/tools/send_mail.py
+++ /dev/null
@@ -1,66 +0,0 @@
-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
deleted file mode 100644
index f54880bf3e9e22..00000000000000
--- a/api/core/tools/provider/builtin/email/tools/send_mail.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-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
deleted file mode 100644
index 537dedb27d530f..00000000000000
--- a/api/core/tools/provider/builtin/email/tools/send_mail_batch.py
+++ /dev/null
@@ -1,75 +0,0 @@
-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
deleted file mode 100644
index 6e4aa785cb946e..00000000000000
--- a/api/core/tools/provider/builtin/email/tools/send_mail_batch.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-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
deleted file mode 100644
index bfb270774dd14c..00000000000000
--- a/api/core/tools/provider/builtin/fal/_assets/icon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/api/core/tools/provider/builtin/fal/fal.py b/api/core/tools/provider/builtin/fal/fal.py
deleted file mode 100644
index c68e2021331082..00000000000000
--- a/api/core/tools/provider/builtin/fal/fal.py
+++ /dev/null
@@ -1,20 +0,0 @@
-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
deleted file mode 100644
index 050a73f62660f6..00000000000000
--- a/api/core/tools/provider/builtin/fal/fal.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index 7b5f10a64d4cdd..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.py
+++ /dev/null
@@ -1,46 +0,0 @@
-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
deleted file mode 100644
index 237ee9937f925b..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.yaml
+++ /dev/null
@@ -1,147 +0,0 @@
-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
deleted file mode 100644
index 2fb1565e7cd1b2..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.py
+++ /dev/null
@@ -1,47 +0,0 @@
-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
deleted file mode 100644
index d518e5192935b9..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.yaml
+++ /dev/null
@@ -1,162 +0,0 @@
-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
deleted file mode 100644
index b44d9fe752ed5a..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_dev.py
+++ /dev/null
@@ -1,47 +0,0 @@
-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
deleted file mode 100644
index 3b22af941fc60a..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_dev.yaml
+++ /dev/null
@@ -1,137 +0,0 @@
-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
deleted file mode 100644
index be60366155dbe3..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.py
+++ /dev/null
@@ -1,47 +0,0 @@
-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
deleted file mode 100644
index 6f8dbb3a54e9ec..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.yaml
+++ /dev/null
@@ -1,164 +0,0 @@
-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
deleted file mode 100644
index ba05a6207330b5..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/wizper.py
+++ /dev/null
@@ -1,56 +0,0 @@
-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,
- )
-
- json_message = self.create_json_message(result)
-
- text = result.get("text", "")
- text_message = self.create_text_message(text)
-
- return [json_message, text_message]
diff --git a/api/core/tools/provider/builtin/fal/tools/wizper.yaml b/api/core/tools/provider/builtin/fal/tools/wizper.yaml
deleted file mode 100644
index 5742efcc1b4002..00000000000000
--- a/api/core/tools/provider/builtin/fal/tools/wizper.yaml
+++ /dev/null
@@ -1,489 +0,0 @@
-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/feishu/_assets/icon.svg b/api/core/tools/provider/builtin/feishu/_assets/icon.svg
deleted file mode 100644
index bf3c202abf3ff6..00000000000000
--- a/api/core/tools/provider/builtin/feishu/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/api/core/tools/provider/builtin/feishu/feishu.py b/api/core/tools/provider/builtin/feishu/feishu.py
deleted file mode 100644
index 72a9333619988d..00000000000000
--- a/api/core/tools/provider/builtin/feishu/feishu.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin.feishu.tools.feishu_group_bot import FeishuGroupBotTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class FeishuProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- FeishuGroupBotTool()
diff --git a/api/core/tools/provider/builtin/feishu/feishu.yaml b/api/core/tools/provider/builtin/feishu/feishu.yaml
deleted file mode 100644
index a029c7edb8853b..00000000000000
--- a/api/core/tools/provider/builtin/feishu/feishu.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-identity:
- author: Arkii Sun
- name: feishu
- label:
- en_US: Feishu
- zh_Hans: 飞书
- pt_BR: Feishu
- description:
- en_US: Feishu group bot
- zh_Hans: 飞书群机器人
- pt_BR: Feishu group bot
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.py b/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.py
deleted file mode 100644
index e82da8ca534b96..00000000000000
--- a/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.uuid_utils import is_valid_uuid
-
-
-class FeishuGroupBotTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- API document: https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
- """
-
- url = "https://open.feishu.cn/open-apis/bot/v2/hook"
-
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- hook_key = tool_parameters.get("hook_key", "")
- if not is_valid_uuid(hook_key):
- return self.create_text_message(f"Invalid parameter hook_key ${hook_key}, not a valid UUID")
-
- msg_type = "text"
- api_url = f"{url}/{hook_key}"
- headers = {
- "Content-Type": "application/json",
- }
- params = {}
- payload = {
- "msg_type": msg_type,
- "content": {
- "text": content,
- },
- }
-
- try:
- res = httpx.post(api_url, headers=headers, params=params, json=payload)
- if res.is_success:
- return self.create_text_message("Text message sent successfully")
- else:
- return self.create_text_message(
- f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
diff --git a/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.yaml b/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.yaml
deleted file mode 100644
index 6c3f084e4dafe3..00000000000000
--- a/api/core/tools/provider/builtin/feishu/tools/feishu_group_bot.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- name: feishu_group_bot
- author: Arkii Sun
- label:
- en_US: Send Group Message
- zh_Hans: 发送群消息
- pt_BR: Send Group Message
- icon: icon.png
-description:
- human:
- en_US: Sending a group message on Feishu via the webhook of group bot
- zh_Hans: 通过飞书的群机器人webhook发送群消息
- pt_BR: Sending a group message on Feishu via the webhook of group bot
- llm: A tool for sending messages to a chat group on Feishu(飞书) .
-parameters:
- - name: hook_key
- type: secret-input
- required: true
- label:
- en_US: Feishu Group bot webhook key
- zh_Hans: 群机器人webhook的key
- pt_BR: Feishu Group bot webhook key
- human_description:
- en_US: Feishu Group bot webhook key
- zh_Hans: 群机器人webhook的key
- pt_BR: Feishu Group bot webhook key
- form: form
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- pt_BR: content
- human_description:
- en_US: Content to sent to the group.
- zh_Hans: 群消息文本
- pt_BR: Content to sent to the group.
- llm_description: Content of the message
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/_assets/icon.png b/api/core/tools/provider/builtin/feishu_base/_assets/icon.png
deleted file mode 100644
index 787427e7218058..00000000000000
Binary files a/api/core/tools/provider/builtin/feishu_base/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/feishu_base/feishu_base.py b/api/core/tools/provider/builtin/feishu_base/feishu_base.py
deleted file mode 100644
index f301ec5355d48f..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/feishu_base.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuBaseProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_base/feishu_base.yaml b/api/core/tools/provider/builtin/feishu_base/feishu_base.yaml
deleted file mode 100644
index 456dd8c88fc348..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/feishu_base.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_base
- label:
- en_US: Feishu Base
- zh_Hans: 飞书多维表格
- description:
- en_US: |
- Feishu base, requires the following permissions: bitable:app.
- zh_Hans: |
- 飞书多维表格,需要开通以下权限: bitable:app。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/add_records.py b/api/core/tools/provider/builtin/feishu_base/tools/add_records.py
deleted file mode 100644
index 905f8b78806d05..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/add_records.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class AddRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- records = tool_parameters.get("records")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.add_records(app_token, table_id, table_name, records, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/add_records.yaml b/api/core/tools/provider/builtin/feishu_base/tools/add_records.yaml
deleted file mode 100644
index f2a93490dc0c31..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/add_records.yaml
+++ /dev/null
@@ -1,91 +0,0 @@
-identity:
- name: add_records
- author: Doug Lea
- label:
- en_US: Add Records
- zh_Hans: 新增多条记录
-description:
- human:
- en_US: Add Multiple Records to Multidimensional Table
- zh_Hans: 在多维表格数据表中新增多条记录
- llm: A tool for adding multiple records to a multidimensional table. (在多维表格数据表中新增多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: records
- type: string
- required: true
- label:
- en_US: records
- zh_Hans: 记录列表
- human_description:
- en_US: |
- List of records to be added in this request. Example value: [{"multi-line-text":"text content","single_select":"option 1","date":1674206443000}]
- For supported field types, refer to the integration guide (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification). For data structures of different field types, refer to the data structure overview (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure).
- zh_Hans: |
- 本次请求将要新增的记录列表,示例值:[{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- llm_description: |
- 本次请求将要新增的记录列表,示例值:[{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/create_base.py b/api/core/tools/provider/builtin/feishu_base/tools/create_base.py
deleted file mode 100644
index f074acc5ff709e..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/create_base.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateBaseTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- name = tool_parameters.get("name")
- folder_token = tool_parameters.get("folder_token")
-
- res = client.create_base(name, folder_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/create_base.yaml b/api/core/tools/provider/builtin/feishu_base/tools/create_base.yaml
deleted file mode 100644
index 3ec91a90e7f0b6..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/create_base.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-identity:
- name: create_base
- author: Doug Lea
- label:
- en_US: Create Base
- zh_Hans: 创建多维表格
-description:
- human:
- en_US: Create Multidimensional Table in Specified Directory
- zh_Hans: 在指定目录下创建多维表格
- llm: A tool for creating a multidimensional table in a specified directory. (在指定目录下创建多维表格)
-parameters:
- - name: name
- type: string
- required: false
- label:
- en_US: name
- zh_Hans: 多维表格 App 名字
- human_description:
- en_US: |
- Name of the multidimensional table App. Example value: "A new multidimensional table".
- zh_Hans: 多维表格 App 名字,示例值:"一篇新的多维表格"。
- llm_description: 多维表格 App 名字,示例值:"一篇新的多维表格"。
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: folder_token
- zh_Hans: 多维表格 App 归属文件夹
- human_description:
- en_US: |
- Folder where the multidimensional table App belongs. Default is empty, meaning the table will be created in the root directory of the cloud space. Example values: Fa3sfoAgDlMZCcdcJy1cDFg8nJc or https://svi136aogf123.feishu.cn/drive/folder/Fa3sfoAgDlMZCcdcJy1cDFg8nJc.
- The folder_token must be an existing folder and supports inputting folder token or folder URL.
- zh_Hans: |
- 多维表格 App 归属文件夹。默认为空,表示多维表格将被创建在云空间根目录。示例值: Fa3sfoAgDlMZCcdcJy1cDFg8nJc 或者 https://svi136aogf123.feishu.cn/drive/folder/Fa3sfoAgDlMZCcdcJy1cDFg8nJc。
- folder_token 必须是已存在的文件夹,支持输入文件夹 token 或者文件夹 URL。
- llm_description: |
- 多维表格 App 归属文件夹。默认为空,表示多维表格将被创建在云空间根目录。示例值: Fa3sfoAgDlMZCcdcJy1cDFg8nJc 或者 https://svi136aogf123.feishu.cn/drive/folder/Fa3sfoAgDlMZCcdcJy1cDFg8nJc。
- folder_token 必须是已存在的文件夹,支持输入文件夹 token 或者文件夹 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/create_table.py b/api/core/tools/provider/builtin/feishu_base/tools/create_table.py
deleted file mode 100644
index 81f2617545969b..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/create_table.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateTableTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_name = tool_parameters.get("table_name")
- default_view_name = tool_parameters.get("default_view_name")
- fields = tool_parameters.get("fields")
-
- res = client.create_table(app_token, table_name, default_view_name, fields)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/create_table.yaml b/api/core/tools/provider/builtin/feishu_base/tools/create_table.yaml
deleted file mode 100644
index 8b1007b9a53166..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/create_table.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-identity:
- name: create_table
- author: Doug Lea
- label:
- en_US: Create Table
- zh_Hans: 新增数据表
-description:
- human:
- en_US: Add a Data Table to Multidimensional Table
- zh_Hans: 在多维表格中新增一个数据表
- llm: A tool for adding a data table to a multidimensional table. (在多维表格中新增一个数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_name
- type: string
- required: true
- label:
- en_US: Table Name
- zh_Hans: 数据表名称
- human_description:
- en_US: |
- The name of the data table, length range: 1 character to 100 characters.
- zh_Hans: 数据表名称,长度范围:1 字符 ~ 100 字符。
- llm_description: 数据表名称,长度范围:1 字符 ~ 100 字符。
- form: llm
-
- - name: default_view_name
- type: string
- required: false
- label:
- en_US: Default View Name
- zh_Hans: 默认表格视图的名称
- human_description:
- en_US: The name of the default table view, defaults to "Table" if not filled.
- zh_Hans: 默认表格视图的名称,不填则默认为"表格"。
- llm_description: 默认表格视图的名称,不填则默认为"表格"。
- form: llm
-
- - name: fields
- type: string
- required: true
- label:
- en_US: Initial Fields
- zh_Hans: 初始字段
- human_description:
- en_US: |
- Initial fields of the data table, format: [ { "field_name": "Multi-line Text","type": 1 },{ "field_name": "Number","type": 2 },{ "field_name": "Single Select","type": 3 },{ "field_name": "Multiple Select","type": 4 },{ "field_name": "Date","type": 5 } ]. For field details, refer to: https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- zh_Hans: 数据表的初始字段,格式为:[{"field_name":"多行文本","type":1},{"field_name":"数字","type":2},{"field_name":"单选","type":3},{"field_name":"多选","type":4},{"field_name":"日期","type":5}]。字段详情参考:https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- llm_description: 数据表的初始字段,格式为:[{"field_name":"多行文本","type":1},{"field_name":"数字","type":2},{"field_name":"单选","type":3},{"field_name":"多选","type":4},{"field_name":"日期","type":5}]。字段详情参考:https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/delete_records.py b/api/core/tools/provider/builtin/feishu_base/tools/delete_records.py
deleted file mode 100644
index c896a2c81b97f8..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/delete_records.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class DeleteRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- record_ids = tool_parameters.get("record_ids")
-
- res = client.delete_records(app_token, table_id, table_name, record_ids)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/delete_records.yaml b/api/core/tools/provider/builtin/feishu_base/tools/delete_records.yaml
deleted file mode 100644
index c30ebd630ce9d8..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/delete_records.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-identity:
- name: delete_records
- author: Doug Lea
- label:
- en_US: Delete Records
- zh_Hans: 删除多条记录
-description:
- human:
- en_US: Delete Multiple Records from Multidimensional Table
- zh_Hans: 删除多维表格数据表中的多条记录
- llm: A tool for deleting multiple records from a multidimensional table. (删除多维表格数据表中的多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: record_ids
- type: string
- required: true
- label:
- en_US: Record IDs
- zh_Hans: 记录 ID 列表
- human_description:
- en_US: |
- List of IDs for the records to be deleted, example value: ["recwNXzPQv"].
- zh_Hans: 删除的多条记录 ID 列表,示例值:["recwNXzPQv"]。
- llm_description: 删除的多条记录 ID 列表,示例值:["recwNXzPQv"]。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.py b/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.py
deleted file mode 100644
index f732a16da6f697..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class DeleteTablesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_ids = tool_parameters.get("table_ids")
- table_names = tool_parameters.get("table_names")
-
- res = client.delete_tables(app_token, table_ids, table_names)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.yaml b/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.yaml
deleted file mode 100644
index 498126eae53302..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/delete_tables.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-identity:
- name: delete_tables
- author: Doug Lea
- label:
- en_US: Delete Tables
- zh_Hans: 删除数据表
-description:
- human:
- en_US: Batch Delete Data Tables from Multidimensional Table
- zh_Hans: 批量删除多维表格中的数据表
- llm: A tool for batch deleting data tables from a multidimensional table. (批量删除多维表格中的数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_ids
- type: string
- required: false
- label:
- en_US: Table IDs
- zh_Hans: 数据表 ID
- human_description:
- en_US: |
- IDs of the tables to be deleted. Each operation supports deleting up to 50 tables. Example: ["tbl1TkhyTWDkSoZ3"]. Ensure that either table_ids or table_names is not empty.
- zh_Hans: 待删除的数据表的 ID,每次操作最多支持删除 50 个数据表。示例值:["tbl1TkhyTWDkSoZ3"]。请确保 table_ids 和 table_names 至少有一个不为空。
- llm_description: 待删除的数据表的 ID,每次操作最多支持删除 50 个数据表。示例值:["tbl1TkhyTWDkSoZ3"]。请确保 table_ids 和 table_names 至少有一个不为空。
- form: llm
-
- - name: table_names
- type: string
- required: false
- label:
- en_US: Table Names
- zh_Hans: 数据表名称
- human_description:
- en_US: |
- Names of the tables to be deleted. Each operation supports deleting up to 50 tables. Example: ["Table1", "Table2"]. Ensure that either table_names or table_ids is not empty.
- zh_Hans: 待删除的数据表的名称,每次操作最多支持删除 50 个数据表。示例值:["数据表1", "数据表2"]。请确保 table_names 和 table_ids 至少有一个不为空。
- llm_description: 待删除的数据表的名称,每次操作最多支持删除 50 个数据表。示例值:["数据表1", "数据表2"]。请确保 table_names 和 table_ids 至少有一个不为空。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.py b/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.py
deleted file mode 100644
index a74e9be288bc17..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetBaseInfoTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
-
- res = client.get_base_info(app_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.yaml b/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.yaml
deleted file mode 100644
index eb0e7a26c06a55..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/get_base_info.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: get_base_info
- author: Doug Lea
- label:
- en_US: Get Base Info
- zh_Hans: 获取多维表格元数据
-description:
- human:
- en_US: Get Metadata Information of Specified Multidimensional Table
- zh_Hans: 获取指定多维表格的元数据信息
- llm: A tool for getting metadata information of a specified multidimensional table. (获取指定多维表格的元数据信息)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/list_tables.py b/api/core/tools/provider/builtin/feishu_base/tools/list_tables.py
deleted file mode 100644
index c7768a496debce..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/list_tables.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ListTablesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- page_token = tool_parameters.get("page_token")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.list_tables(app_token, page_token, page_size)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/list_tables.yaml b/api/core/tools/provider/builtin/feishu_base/tools/list_tables.yaml
deleted file mode 100644
index 7571519039bd24..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/list_tables.yaml
+++ /dev/null
@@ -1,50 +0,0 @@
-identity:
- name: list_tables
- author: Doug Lea
- label:
- en_US: List Tables
- zh_Hans: 列出数据表
-description:
- human:
- en_US: Get All Data Tables under Multidimensional Table
- zh_Hans: 获取多维表格下的所有数据表
- llm: A tool for getting all data tables under a multidimensional table. (获取多维表格下的所有数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: |
- Page size, default value: 20, maximum value: 100.
- zh_Hans: 分页大小,默认值:20,最大值:100。
- llm_description: 分页大小,默认值:20,最大值:100。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: |
- Page token, leave empty for the first request to start from the beginning; a new page_token will be returned if there are more items in the paginated query results, which can be used for the next traversal. Example value: "tblsRc9GRRXKqhvW".
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/read_records.py b/api/core/tools/provider/builtin/feishu_base/tools/read_records.py
deleted file mode 100644
index 46f3df4ff040f3..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/read_records.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ReadRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- record_ids = tool_parameters.get("record_ids")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_records(app_token, table_id, table_name, record_ids, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/read_records.yaml b/api/core/tools/provider/builtin/feishu_base/tools/read_records.yaml
deleted file mode 100644
index 911e667cfc90ad..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/read_records.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-identity:
- name: read_records
- author: Doug Lea
- label:
- en_US: Read Records
- zh_Hans: 批量获取记录
-description:
- human:
- en_US: Batch Retrieve Records from Multidimensional Table
- zh_Hans: 批量获取多维表格数据表中的记录信息
- llm: A tool for batch retrieving records from a multidimensional table, supporting up to 100 records per call. (批量获取多维表格数据表中的记录信息,单次调用最多支持查询 100 条记录)
-
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: record_ids
- type: string
- required: true
- label:
- en_US: record_ids
- zh_Hans: 记录 ID 列表
- human_description:
- en_US: List of record IDs, which can be obtained by calling the "Query Records API".
- zh_Hans: 记录 ID 列表,可以通过调用"查询记录接口"获取。
- llm_description: 记录 ID 列表,可以通过调用"查询记录接口"获取。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/search_records.py b/api/core/tools/provider/builtin/feishu_base/tools/search_records.py
deleted file mode 100644
index d58b42b82029ce..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/search_records.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class SearchRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token", "")
- table_id = tool_parameters.get("table_id", "")
- table_name = tool_parameters.get("table_name", "")
- view_id = tool_parameters.get("view_id", "")
- field_names = tool_parameters.get("field_names", "")
- sort = tool_parameters.get("sort", "")
- filters = tool_parameters.get("filter", "")
- page_token = tool_parameters.get("page_token", "")
- automatic_fields = tool_parameters.get("automatic_fields", False)
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.search_record(
- app_token,
- table_id,
- table_name,
- view_id,
- field_names,
- sort,
- filters,
- page_token,
- automatic_fields,
- user_id_type,
- page_size,
- )
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/search_records.yaml b/api/core/tools/provider/builtin/feishu_base/tools/search_records.yaml
deleted file mode 100644
index decf76d53ed928..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/search_records.yaml
+++ /dev/null
@@ -1,163 +0,0 @@
-identity:
- name: search_records
- author: Doug Lea
- label:
- en_US: Search Records
- zh_Hans: 查询记录
-description:
- human:
- en_US: Query records in a multidimensional table, up to 500 rows per query.
- zh_Hans: 查询多维表格数据表中的记录,单次最多查询 500 行记录。
- llm: A tool for querying records in a multidimensional table, up to 500 rows per query. (查询多维表格数据表中的记录,单次最多查询 500 行记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: view_id
- type: string
- required: false
- label:
- en_US: view_id
- zh_Hans: 视图唯一标识
- human_description:
- en_US: |
- Unique identifier for a view in a multidimensional table. It can be found in the URL's query parameter with the key 'view'. For example: https://svi136aogf123.feishu.cn/base/KWC8bYsYXahYqGsTtqectNn9n3e?table=tblE8a2fmBIEflaE&view=vewlkAVpRx.
- zh_Hans: 多维表格中视图的唯一标识,可在多维表格的 URL 地址栏中找到,query 参数中 key 为 view 的部分。例如:https://svi136aogf123.feishu.cn/base/KWC8bYsYXahYqGsTtqectNn9n3e?table=tblE8a2fmBIEflaE&view=vewlkAVpRx。
- llm_description: 多维表格中视图的唯一标识,可在多维表格的 URL 地址栏中找到,query 参数中 key 为 view 的部分。例如:https://svi136aogf123.feishu.cn/base/KWC8bYsYXahYqGsTtqectNn9n3e?table=tblE8a2fmBIEflaE&view=vewlkAVpRx。
- form: llm
-
- - name: field_names
- type: string
- required: false
- label:
- en_US: field_names
- zh_Hans: 字段名称
- human_description:
- en_US: |
- Field names to specify which fields to include in the returned records. Example value: ["Field1", "Field2"].
- zh_Hans: 字段名称,用于指定本次查询返回记录中包含的字段。示例值:["字段1","字段2"]。
- llm_description: 字段名称,用于指定本次查询返回记录中包含的字段。示例值:["字段1","字段2"]。
- form: llm
-
- - name: sort
- type: string
- required: false
- label:
- en_US: sort
- zh_Hans: 排序条件
- human_description:
- en_US: |
- Sorting conditions, for example: [{"field_name":"Multiline Text","desc":true}].
- zh_Hans: 排序条件,例如:[{"field_name":"多行文本","desc":true}]。
- llm_description: 排序条件,例如:[{"field_name":"多行文本","desc":true}]。
- form: llm
-
- - name: filter
- type: string
- required: false
- label:
- en_US: filter
- zh_Hans: 筛选条件
- human_description:
- en_US: Object containing filter information. For details on how to fill in the filter, refer to the record filter parameter guide (https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide).
- zh_Hans: 包含条件筛选信息的对象。了解如何填写 filter,参考记录筛选参数填写指南(https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide)。
- llm_description: 包含条件筛选信息的对象。了解如何填写 filter,参考记录筛选参数填写指南(https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide)。
- form: llm
-
- - name: automatic_fields
- type: boolean
- required: false
- label:
- en_US: automatic_fields
- zh_Hans: automatic_fields
- human_description:
- en_US: Whether to return automatically calculated fields. Default is false, meaning they are not returned.
- zh_Hans: 是否返回自动计算的字段。默认为 false,表示不返回。
- llm_description: 是否返回自动计算的字段。默认为 false,表示不返回。
- form: form
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: |
- Page size, default value: 20, maximum value: 500.
- zh_Hans: 分页大小,默认值:20,最大值:500。
- llm_description: 分页大小,默认值:20,最大值:500。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: |
- Page token, leave empty for the first request to start from the beginning; a new page_token will be returned if there are more items in the paginated query results, which can be used for the next traversal. Example value: "tblsRc9GRRXKqhvW".
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/update_records.py b/api/core/tools/provider/builtin/feishu_base/tools/update_records.py
deleted file mode 100644
index 31cf8e18d85b8d..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/update_records.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class UpdateRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token", "")
- table_id = tool_parameters.get("table_id", "")
- table_name = tool_parameters.get("table_name", "")
- records = tool_parameters.get("records", "")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.update_records(app_token, table_id, table_name, records, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_base/tools/update_records.yaml b/api/core/tools/provider/builtin/feishu_base/tools/update_records.yaml
deleted file mode 100644
index 68117e71367892..00000000000000
--- a/api/core/tools/provider/builtin/feishu_base/tools/update_records.yaml
+++ /dev/null
@@ -1,91 +0,0 @@
-identity:
- name: update_records
- author: Doug Lea
- label:
- en_US: Update Records
- zh_Hans: 更新多条记录
-description:
- human:
- en_US: Update Multiple Records in Multidimensional Table
- zh_Hans: 更新多维表格数据表中的多条记录
- llm: A tool for updating multiple records in a multidimensional table. (更新多维表格数据表中的多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: records
- type: string
- required: true
- label:
- en_US: records
- zh_Hans: 记录列表
- human_description:
- en_US: |
- List of records to be updated in this request. Example value: [{"fields":{"multi-line-text":"text content","single_select":"option 1","date":1674206443000},"record_id":"recupK4f4RM5RX"}].
- For supported field types, refer to the integration guide (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification). For data structures of different field types, refer to the data structure overview (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure).
- zh_Hans: |
- 本次请求将要更新的记录列表,示例值:[{"fields":{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000},"record_id":"recupK4f4RM5RX"}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- llm_description: |
- 本次请求将要更新的记录列表,示例值:[{"fields":{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000},"record_id":"recupK4f4RM5RX"}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_calendar/_assets/icon.png b/api/core/tools/provider/builtin/feishu_calendar/_assets/icon.png
deleted file mode 100644
index 2a934747a98c66..00000000000000
Binary files a/api/core/tools/provider/builtin/feishu_calendar/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.py b/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.py
deleted file mode 100644
index a46a9fa9e80cab..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuCalendarProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.yaml b/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.yaml
deleted file mode 100644
index db5bab5c1081d9..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/feishu_calendar.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_calendar
- label:
- en_US: Feishu Calendar
- zh_Hans: 飞书日历
- description:
- en_US: |
- Feishu calendar, requires the following permissions: calendar:calendar:read、calendar:calendar、contact:user.id:readonly.
- zh_Hans: |
- 飞书日历,需要开通以下权限: calendar:calendar:read、calendar:calendar、contact:user.id:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py b/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py
deleted file mode 100644
index 80287feca176e1..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class AddEventAttendeesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id", "")
- attendee_phone_or_email = tool_parameters.get("attendee_phone_or_email", "")
- need_notification = tool_parameters.get("need_notification", True)
-
- res = client.add_event_attendees(event_id, attendee_phone_or_email, need_notification)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.yaml
deleted file mode 100644
index b7744499b07344..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-identity:
- name: add_event_attendees
- author: Doug Lea
- label:
- en_US: Add Event Attendees
- zh_Hans: 添加日程参会人
-description:
- human:
- en_US: Add Event Attendees
- zh_Hans: 添加日程参会人
- llm: A tool for adding attendees to events in Feishu. (在飞书中添加日程参会人)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, which will be returned when the event is created. For example: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0.
- zh_Hans: |
- 创建日程时会返回日程 ID。例如: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0。
- llm_description: |
- 日程 ID,创建日程时会返回日程 ID。例如: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否需要通知
- human_description:
- en_US: |
- Whether to send a Bot notification to attendees. true: send, false: do not send.
- zh_Hans: |
- 是否给参与人发送 Bot 通知,true: 发送,false: 不发送。
- llm_description: |
- 是否给参与人发送 Bot 通知,true: 发送,false: 不发送。
- form: form
-
- - name: attendee_phone_or_email
- type: string
- required: true
- label:
- en_US: Attendee Phone or Email
- zh_Hans: 参会人电话或邮箱
- human_description:
- en_US: The list of attendee emails or phone numbers, separated by commas.
- zh_Hans: 日程参会人邮箱或者手机号列表,使用逗号分隔。
- llm_description: 日程参会人邮箱或者手机号列表,使用逗号分隔。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.py b/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.py
deleted file mode 100644
index 8820bebdbed922..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- summary = tool_parameters.get("summary")
- description = tool_parameters.get("description")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- attendee_ability = tool_parameters.get("attendee_ability")
- need_notification = tool_parameters.get("need_notification", True)
- auto_record = tool_parameters.get("auto_record", False)
-
- res = client.create_event(
- summary, description, start_time, end_time, attendee_ability, need_notification, auto_record
- )
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.yaml
deleted file mode 100644
index f0784221ce7965..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/create_event.yaml
+++ /dev/null
@@ -1,119 +0,0 @@
-identity:
- name: create_event
- author: Doug Lea
- label:
- en_US: Create Event
- zh_Hans: 创建日程
-description:
- human:
- en_US: Create Event
- zh_Hans: 创建日程
- llm: A tool for creating events in Feishu.(创建飞书日程)
-parameters:
- - name: summary
- type: string
- required: false
- label:
- en_US: Summary
- zh_Hans: 日程标题
- human_description:
- en_US: The title of the event. If not filled, the event title will display (No Subject).
- zh_Hans: 日程标题,若不填则日程标题显示 (无主题)。
- llm_description: 日程标题,若不填则日程标题显示 (无主题)。
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Description
- zh_Hans: 日程描述
- human_description:
- en_US: The description of the event.
- zh_Hans: 日程描述。
- llm_description: 日程描述。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否发送通知
- human_description:
- en_US: |
- Whether to send a bot message when the event is created, true: send, false: do not send.
- zh_Hans: 创建日程时是否发送 bot 消息,true:发送,false:不发送。
- llm_description: 创建日程时是否发送 bot 消息,true:发送,false:不发送。
- form: form
-
- - name: start_time
- type: string
- required: true
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程开始时间,格式:2006-01-02 15:04:05。
- llm_description: 日程开始时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: true
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程结束时间,格式:2006-01-02 15:04:05。
- llm_description: 日程结束时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: attendee_ability
- type: select
- required: false
- options:
- - value: none
- label:
- en_US: none
- zh_Hans: 无
- - value: can_see_others
- label:
- en_US: can_see_others
- zh_Hans: 可以查看参与人列表
- - value: can_invite_others
- label:
- en_US: can_invite_others
- zh_Hans: 可以邀请其它参与人
- - value: can_modify_event
- label:
- en_US: can_modify_event
- zh_Hans: 可以编辑日程
- default: "none"
- label:
- en_US: attendee_ability
- zh_Hans: 参会人权限
- human_description:
- en_US: Attendee ability, optional values are none, can_see_others, can_invite_others, can_modify_event, with a default value of none.
- zh_Hans: 参会人权限,可选值有无、可以查看参与人列表、可以邀请其它参与人、可以编辑日程,默认值为无。
- llm_description: 参会人权限,可选值有无、可以查看参与人列表、可以邀请其它参与人、可以编辑日程,默认值为无。
- form: form
-
- - name: auto_record
- type: boolean
- required: false
- default: false
- label:
- en_US: Auto Record
- zh_Hans: 自动录制
- human_description:
- en_US: |
- Whether to enable automatic recording, true: enabled, automatically record when the meeting starts; false: not enabled.
- zh_Hans: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- llm_description: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py b/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py
deleted file mode 100644
index 02e9b445219ac8..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class DeleteEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id", "")
- need_notification = tool_parameters.get("need_notification", True)
-
- res = client.delete_event(event_id, need_notification)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.yaml
deleted file mode 100644
index 54fdb04acc3371..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-identity:
- name: delete_event
- author: Doug Lea
- label:
- en_US: Delete Event
- zh_Hans: 删除日程
-description:
- human:
- en_US: Delete Event
- zh_Hans: 删除日程
- llm: A tool for deleting events in Feishu.(在飞书中删除日程)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, for example: e8b9791c-39ae-4908-8ad8-66b13159b9fb_0.
- zh_Hans: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- llm_description: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否需要通知
- human_description:
- en_US: |
- Indicates whether to send bot notifications to event participants upon deletion. true: send, false: do not send.
- zh_Hans: 删除日程是否给日程参与人发送 bot 通知,true:发送,false:不发送。
- llm_description: 删除日程是否给日程参与人发送 bot 通知,true:发送,false:不发送。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py b/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py
deleted file mode 100644
index 4dafe4b3baf0cd..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetPrimaryCalendarTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.get_primary_calendar(user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.yaml
deleted file mode 100644
index 3440c85d4a9733..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-identity:
- name: get_primary_calendar
- author: Doug Lea
- label:
- en_US: Get Primary Calendar
- zh_Hans: 查询主日历信息
-description:
- human:
- en_US: Get Primary Calendar
- zh_Hans: 查询主日历信息
- llm: A tool for querying primary calendar information in Feishu.(在飞书中查询主日历信息)
-parameters:
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py b/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py
deleted file mode 100644
index 2e8ca968b3cc42..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ListEventsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- start_time = tool_parameters.get("start_time", "")
- end_time = tool_parameters.get("end_time", "")
- page_token = tool_parameters.get("page_token", "")
- page_size = tool_parameters.get("page_size", 50)
-
- res = client.list_events(start_time, end_time, page_token, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.yaml
deleted file mode 100644
index 5f0155a2465866..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.yaml
+++ /dev/null
@@ -1,62 +0,0 @@
-identity:
- name: list_events
- author: Doug Lea
- label:
- en_US: List Events
- zh_Hans: 获取日程列表
-description:
- human:
- en_US: List Events
- zh_Hans: 获取日程列表
- llm: A tool for listing events in Feishu.(在飞书中获取日程列表)
-parameters:
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time, defaults to 0:00 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- llm_description: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time, defaults to 23:59 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- llm_description: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 50
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 50, and the value range is [50,1000].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 50,取值范围为 [50,1000]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 50,取值范围为 [50,1000]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.py b/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.py
deleted file mode 100644
index dc365205a4cffa..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class SearchEventsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- query = tool_parameters.get("query")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- page_token = tool_parameters.get("page_token")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.search_events(query, start_time, end_time, page_token, user_id_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.yaml
deleted file mode 100644
index bd60a07b5b5341..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/search_events.yaml
+++ /dev/null
@@ -1,100 +0,0 @@
-identity:
- name: search_events
- author: Doug Lea
- label:
- en_US: Search Events
- zh_Hans: 搜索日程
-description:
- human:
- en_US: Search Events
- zh_Hans: 搜索日程
- llm: A tool for searching events in Feishu.(在飞书中搜索日程)
-parameters:
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 搜索关键字
- human_description:
- en_US: The search keyword used for fuzzy searching event names, with a maximum input of 200 characters.
- zh_Hans: 用于模糊查询日程名称的搜索关键字,最大输入 200 字符。
- llm_description: 用于模糊查询日程名称的搜索关键字,最大输入 200 字符。
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time, defaults to 0:00 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- llm_description: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time, defaults to 23:59 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- llm_description: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [10,100].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [10,100]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [10,100]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py b/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py
deleted file mode 100644
index b20eb6c31828e4..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class UpdateEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id", "")
- summary = tool_parameters.get("summary", "")
- description = tool_parameters.get("description", "")
- need_notification = tool_parameters.get("need_notification", True)
- start_time = tool_parameters.get("start_time", "")
- end_time = tool_parameters.get("end_time", "")
- auto_record = tool_parameters.get("auto_record", False)
-
- res = client.update_event(event_id, summary, description, need_notification, start_time, end_time, auto_record)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.yaml b/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.yaml
deleted file mode 100644
index 4d60dbf8c8e1b0..00000000000000
--- a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.yaml
+++ /dev/null
@@ -1,100 +0,0 @@
-identity:
- name: update_event
- author: Doug Lea
- label:
- en_US: Update Event
- zh_Hans: 更新日程
-description:
- human:
- en_US: Update Event
- zh_Hans: 更新日程
- llm: A tool for updating events in Feishu.(更新飞书中的日程)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, for example: e8b9791c-39ae-4908-8ad8-66b13159b9fb_0.
- zh_Hans: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- llm_description: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- form: llm
-
- - name: summary
- type: string
- required: false
- label:
- en_US: Summary
- zh_Hans: 日程标题
- human_description:
- en_US: The title of the event.
- zh_Hans: 日程标题。
- llm_description: 日程标题。
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Description
- zh_Hans: 日程描述
- human_description:
- en_US: The description of the event.
- zh_Hans: 日程描述。
- llm_description: 日程描述。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- label:
- en_US: Need Notification
- zh_Hans: 是否发送通知
- human_description:
- en_US: |
- Whether to send a bot message when the event is updated, true: send, false: do not send.
- zh_Hans: 更新日程时是否发送 bot 消息,true:发送,false:不发送。
- llm_description: 更新日程时是否发送 bot 消息,true:发送,false:不发送。
- form: form
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程开始时间,格式:2006-01-02 15:04:05。
- llm_description: 日程开始时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程结束时间,格式:2006-01-02 15:04:05。
- llm_description: 日程结束时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: auto_record
- type: boolean
- required: false
- label:
- en_US: Auto Record
- zh_Hans: 自动录制
- human_description:
- en_US: |
- Whether to enable automatic recording, true: enabled, automatically record when the meeting starts; false: not enabled.
- zh_Hans: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- llm_description: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_document/_assets/icon.svg b/api/core/tools/provider/builtin/feishu_document/_assets/icon.svg
deleted file mode 100644
index 5a0a6416b3db32..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/_assets/icon.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/feishu_document/feishu_document.py b/api/core/tools/provider/builtin/feishu_document/feishu_document.py
deleted file mode 100644
index 217ae52082b82c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/feishu_document.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuDocumentProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_document/feishu_document.yaml b/api/core/tools/provider/builtin/feishu_document/feishu_document.yaml
deleted file mode 100644
index 8f9afa6149445c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/feishu_document.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_document
- label:
- en_US: Lark Cloud Document
- zh_Hans: 飞书云文档
- description:
- en_US: |
- Lark cloud document, requires the following permissions: docx:document、drive:drive、docs:document.content:read.
- zh_Hans: |
- 飞书云文档,需要开通以下权限: docx:document、drive:drive、docs:document.content:read。
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/create_document.py b/api/core/tools/provider/builtin/feishu_document/tools/create_document.py
deleted file mode 100644
index 1533f594172878..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/create_document.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateDocumentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- title = tool_parameters.get("title", "")
- content = tool_parameters.get("content", "")
- folder_token = tool_parameters.get("folder_token", "")
-
- res = client.create_document(title, content, folder_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/create_document.yaml b/api/core/tools/provider/builtin/feishu_document/tools/create_document.yaml
deleted file mode 100644
index 85382e9d8e8d1f..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/create_document.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-identity:
- name: create_document
- author: Doug Lea
- label:
- en_US: Create Lark document
- zh_Hans: 创建飞书文档
-description:
- human:
- en_US: Create Lark document
- zh_Hans: 创建飞书文档,支持创建空文档和带内容的文档,支持 markdown 语法创建。应用需要开启机器人能力(https://open.feishu.cn/document/faq/trouble-shooting/how-to-enable-bot-ability)。
- llm: A tool for creating Feishu documents.
-parameters:
- - name: title
- type: string
- required: false
- label:
- en_US: Document title
- zh_Hans: 文档标题
- human_description:
- en_US: Document title, only supports plain text content.
- zh_Hans: 文档标题,只支持纯文本内容。
- llm_description: 文档标题,只支持纯文本内容,可以为空。
- form: llm
-
- - name: content
- type: string
- required: false
- label:
- en_US: Document content
- zh_Hans: 文档内容
- human_description:
- en_US: Document content, supports markdown syntax, can be empty.
- zh_Hans: 文档内容,支持 markdown 语法,可以为空。
- llm_description: 文档内容,支持 markdown 语法,可以为空。
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: folder_token
- zh_Hans: 文档所在文件夹的 Token
- human_description:
- en_US: |
- The token of the folder where the document is located. If it is not passed or is empty, it means the root directory. For Example: https://svi136aogf123.feishu.cn/drive/folder/JgR9fiG9AlPt8EdsSNpcGjIInbf
- zh_Hans: 文档所在文件夹的 Token,不传或传空表示根目录。例如:https://svi136aogf123.feishu.cn/drive/folder/JgR9fiG9AlPt8EdsSNpcGjIInbf。
- llm_description: 文档所在文件夹的 Token,不传或传空表示根目录。例如:https://svi136aogf123.feishu.cn/drive/folder/JgR9fiG9AlPt8EdsSNpcGjIInbf。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.py b/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.py
deleted file mode 100644
index e67a017facc8d4..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetDocumentRawContentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id")
- mode = tool_parameters.get("mode", "markdown")
- lang = tool_parameters.get("lang", "0")
-
- res = client.get_document_content(document_id, mode, lang)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.yaml b/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.yaml
deleted file mode 100644
index 15e827cde91ee6..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/get_document_content.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-identity:
- name: get_document_content
- author: Doug Lea
- label:
- en_US: Get Document Content
- zh_Hans: 获取飞书云文档的内容
-description:
- human:
- en_US: Get document content
- zh_Hans: 获取飞书云文档的内容
- llm: A tool for retrieving content from Feishu cloud documents.
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: 飞书文档的唯一标识
- human_description:
- en_US: Unique identifier for a Feishu document. You can also input the document's URL.
- zh_Hans: 飞书文档的唯一标识,支持输入文档的 URL。
- llm_description: 飞书文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: mode
- type: select
- required: false
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: text
- - value: markdown
- label:
- en_US: markdown
- zh_Hans: markdown
- default: "markdown"
- label:
- en_US: mode
- zh_Hans: 文档返回格式
- human_description:
- en_US: Format of the document return, optional values are text, markdown, can be empty, default is markdown.
- zh_Hans: 文档返回格式,可选值有 text、markdown,可以为空,默认值为 markdown。
- llm_description: 文档返回格式,可选值有 text、markdown,可以为空,默认值为 markdown。
- form: form
-
- - name: lang
- type: select
- required: false
- options:
- - value: "0"
- label:
- en_US: User's default name
- zh_Hans: 用户的默认名称
- - value: "1"
- label:
- en_US: User's English name
- zh_Hans: 用户的英文名称
- default: "0"
- label:
- en_US: lang
- zh_Hans: 指定@用户的语言
- human_description:
- en_US: |
- Specifies the language for MentionUser, optional values are [0, 1]. 0: User's default name, 1: User's English name, default is 0.
- zh_Hans: |
- 指定返回的 MentionUser,即@用户的语言,可选值有 [0,1]。0: 该用户的默认名称,1: 该用户的英文名称,默认值为 0。
- llm_description: |
- 指定返回的 MentionUser,即@用户的语言,可选值有 [0,1]。0: 该用户的默认名称,1: 该用户的英文名称,默认值为 0。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py b/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py
deleted file mode 100644
index 8ea68a2ed87855..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ListDocumentBlockTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- if not self.runtime or not self.runtime.credentials:
- raise ValueError("Runtime is not set")
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ValueError("app_id and app_secret are required")
- client = FeishuRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id", "")
- page_token = tool_parameters.get("page_token", "")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 500)
-
- res = client.list_document_blocks(document_id, page_token, user_id_type, page_size)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.yaml b/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.yaml
deleted file mode 100644
index d4fab96c1f9601..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-identity:
- name: list_document_blocks
- author: Doug Lea
- label:
- en_US: List Document Blocks
- zh_Hans: 获取飞书文档所有块
-description:
- human:
- en_US: List document blocks
- zh_Hans: 获取飞书文档所有块的富文本内容并分页返回
- llm: A tool to get all blocks of Feishu documents
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: 飞书文档的唯一标识
- human_description:
- en_US: Unique identifier for a Feishu document. You can also input the document's URL.
- zh_Hans: 飞书文档的唯一标识,支持输入文档的 URL。
- llm_description: 飞书文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 500
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: Paging size, the default and maximum value is 500.
- zh_Hans: 分页大小, 默认值和最大值为 500。
- llm_description: 分页大小, 表示一次请求最多返回多少条数据,默认值和最大值为 500。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: Pagination token used to navigate through query results, allowing retrieval of additional items in subsequent requests.
- zh_Hans: 分页标记,用于分页查询结果,以便下次遍历时获取更多项。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/write_document.py b/api/core/tools/provider/builtin/feishu_document/tools/write_document.py
deleted file mode 100644
index 59f08f53dc68de..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/write_document.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateDocumentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id")
- content = tool_parameters.get("content")
- position = tool_parameters.get("position", "end")
-
- res = client.write_document(document_id, content, position)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_document/tools/write_document.yaml b/api/core/tools/provider/builtin/feishu_document/tools/write_document.yaml
deleted file mode 100644
index de70f4e7726a28..00000000000000
--- a/api/core/tools/provider/builtin/feishu_document/tools/write_document.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-identity:
- name: write_document
- author: Doug Lea
- label:
- en_US: Write Document
- zh_Hans: 在飞书文档中新增内容
-description:
- human:
- en_US: Adding new content to Lark documents
- zh_Hans: 在飞书文档中新增内容
- llm: A tool for adding new content to Lark documents.
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: 飞书文档的唯一标识
- human_description:
- en_US: Unique identifier for a Feishu document. You can also input the document's URL.
- zh_Hans: 飞书文档的唯一标识,支持输入文档的 URL。
- llm_description: 飞书文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: content
- type: string
- required: true
- label:
- en_US: Plain text or Markdown content
- zh_Hans: 纯文本或 Markdown 内容
- human_description:
- en_US: Plain text or Markdown content. Note that embedded tables in the document should not have merged cells.
- zh_Hans: 纯文本或 Markdown 内容。注意文档的内嵌套表格不允许有单元格合并。
- llm_description: 纯文本或 Markdown 内容,注意文档的内嵌套表格不允许有单元格合并。
- form: llm
-
- - name: position
- type: select
- required: false
- options:
- - value: start
- label:
- en_US: document start
- zh_Hans: 文档开始
- - value: end
- label:
- en_US: document end
- zh_Hans: 文档结束
- default: "end"
- label:
- en_US: position
- zh_Hans: 内容添加位置
- human_description:
- en_US: Content insertion position, optional values are start, end. 'start' means adding content at the beginning of the document; 'end' means adding content at the end of the document. The default value is end.
- zh_Hans: 内容添加位置,可选值有 start、end。start 表示在文档开头添加内容;end 表示在文档结尾添加内容,默认值为 end。
- llm_description: 内容添加位置,可选值有 start、end。start 表示在文档开头添加内容;end 表示在文档结尾添加内容,默认值为 end。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_message/_assets/icon.svg b/api/core/tools/provider/builtin/feishu_message/_assets/icon.svg
deleted file mode 100644
index 222a1571f9bbbb..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/_assets/icon.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
diff --git a/api/core/tools/provider/builtin/feishu_message/feishu_message.py b/api/core/tools/provider/builtin/feishu_message/feishu_message.py
deleted file mode 100644
index a3b54737691c9c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/feishu_message.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuMessageProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_message/feishu_message.yaml b/api/core/tools/provider/builtin/feishu_message/feishu_message.yaml
deleted file mode 100644
index 56683ec1680f40..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/feishu_message.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_message
- label:
- en_US: Lark Message
- zh_Hans: 飞书消息
- description:
- en_US: |
- Lark message, requires the following permissions: im:message、im:message.group_msg.
- zh_Hans: |
- 飞书消息,需要开通以下权限: im:message、im:message.group_msg。
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.py b/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.py
deleted file mode 100644
index 7eb29230b2ceb0..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetChatMessagesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- container_id = tool_parameters.get("container_id")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- page_token = tool_parameters.get("page_token")
- sort_type = tool_parameters.get("sort_type", "ByCreateTimeAsc")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.get_chat_messages(container_id, start_time, end_time, page_token, sort_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.yaml b/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.yaml
deleted file mode 100644
index 984c9120e8cd96..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/get_chat_messages.yaml
+++ /dev/null
@@ -1,96 +0,0 @@
-identity:
- name: get_chat_messages
- author: Doug Lea
- label:
- en_US: Get Chat Messages
- zh_Hans: 获取指定单聊、群聊的消息历史
-description:
- human:
- en_US: Get Chat Messages
- zh_Hans: 获取指定单聊、群聊的消息历史
- llm: A tool for getting chat messages from specific one-on-one chats or group chats.(获取指定单聊、群聊的消息历史)
-parameters:
- - name: container_id
- type: string
- required: true
- label:
- en_US: Container Id
- zh_Hans: 群聊或单聊的 ID
- human_description:
- en_US: The ID of the group chat or single chat. Refer to the group ID description for how to obtain it. https://open.feishu.cn/document/server-docs/group/chat/chat-id-description
- zh_Hans: 群聊或单聊的 ID,获取方式参见群 ID 说明。https://open.feishu.cn/document/server-docs/group/chat/chat-id-description
- llm_description: 群聊或单聊的 ID,获取方式参见群 ID 说明。https://open.feishu.cn/document/server-docs/group/chat/chat-id-description
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 起始时间
- human_description:
- en_US: The start time for querying historical messages, formatted as "2006-01-02 15:04:05".
- zh_Hans: 待查询历史信息的起始时间,格式为 "2006-01-02 15:04:05"。
- llm_description: 待查询历史信息的起始时间,格式为 "2006-01-02 15:04:05"。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: The end time for querying historical messages, formatted as "2006-01-02 15:04:05".
- zh_Hans: 待查询历史信息的结束时间,格式为 "2006-01-02 15:04:05"。
- llm_description: 待查询历史信息的结束时间,格式为 "2006-01-02 15:04:05"。
- form: llm
-
- - name: sort_type
- type: select
- required: false
- options:
- - value: ByCreateTimeAsc
- label:
- en_US: ByCreateTimeAsc
- zh_Hans: ByCreateTimeAsc
- - value: ByCreateTimeDesc
- label:
- en_US: ByCreateTimeDesc
- zh_Hans: ByCreateTimeDesc
- default: "ByCreateTimeAsc"
- label:
- en_US: Sort Type
- zh_Hans: 排序方式
- human_description:
- en_US: |
- The message sorting method. Optional values are ByCreateTimeAsc: sorted in ascending order by message creation time; ByCreateTimeDesc: sorted in descending order by message creation time. The default value is ByCreateTimeAsc. Note: When using page_token for pagination requests, the sorting method (sort_type) is consistent with the first request and cannot be changed midway.
- zh_Hans: |
- 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- llm_description: 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [1,50].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.py b/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.py
deleted file mode 100644
index 3b14f46e0048a8..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetChatMessagesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- container_id = tool_parameters.get("container_id")
- page_token = tool_parameters.get("page_token")
- sort_type = tool_parameters.get("sort_type", "ByCreateTimeAsc")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.get_thread_messages(container_id, page_token, sort_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.yaml b/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.yaml
deleted file mode 100644
index 85a138292f6203..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/get_thread_messages.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: get_thread_messages
- author: Doug Lea
- label:
- en_US: Get Thread Messages
- zh_Hans: 获取指定话题的消息历史
-description:
- human:
- en_US: Get Thread Messages
- zh_Hans: 获取指定话题的消息历史
- llm: A tool for getting chat messages from specific threads.(获取指定话题的消息历史)
-parameters:
- - name: container_id
- type: string
- required: true
- label:
- en_US: Thread Id
- zh_Hans: 话题 ID
- human_description:
- en_US: The ID of the thread. Refer to the thread overview on how to obtain the thread_id. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- zh_Hans: 话题 ID,获取方式参见话题概述的如何获取 thread_id 章节。https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- llm_description: 话题 ID,获取方式参见话题概述的如何获取 thread_id 章节。https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- form: llm
-
- - name: sort_type
- type: select
- required: false
- options:
- - value: ByCreateTimeAsc
- label:
- en_US: ByCreateTimeAsc
- zh_Hans: ByCreateTimeAsc
- - value: ByCreateTimeDesc
- label:
- en_US: ByCreateTimeDesc
- zh_Hans: ByCreateTimeDesc
- default: "ByCreateTimeAsc"
- label:
- en_US: Sort Type
- zh_Hans: 排序方式
- human_description:
- en_US: |
- The message sorting method. Optional values are ByCreateTimeAsc: sorted in ascending order by message creation time; ByCreateTimeDesc: sorted in descending order by message creation time. The default value is ByCreateTimeAsc. Note: When using page_token for pagination requests, the sorting method (sort_type) is consistent with the first request and cannot be changed midway.
- zh_Hans: |
- 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- llm_description: 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [1,50].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.py b/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.py
deleted file mode 100644
index 1dd315d0e293a0..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class SendBotMessageTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- receive_id_type = tool_parameters.get("receive_id_type")
- receive_id = tool_parameters.get("receive_id")
- msg_type = tool_parameters.get("msg_type")
- content = tool_parameters.get("content")
-
- res = client.send_bot_message(receive_id_type, receive_id, msg_type, content)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.yaml b/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.yaml
deleted file mode 100644
index 4f7f65a8a74fc0..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/send_bot_message.yaml
+++ /dev/null
@@ -1,125 +0,0 @@
-identity:
- name: send_bot_message
- author: Doug Lea
- label:
- en_US: Send Bot Message
- zh_Hans: 发送飞书应用消息
-description:
- human:
- en_US: Send bot message
- zh_Hans: 发送飞书应用消息
- llm: A tool for sending Feishu application messages.
-parameters:
- - name: receive_id
- type: string
- required: true
- label:
- en_US: receive_id
- zh_Hans: 消息接收者的 ID
- human_description:
- en_US: The ID of the message receiver, the ID type is consistent with the value of the query parameter receive_id_type.
- zh_Hans: 消息接收者的 ID,ID 类型与查询参数 receive_id_type 的取值一致。
- llm_description: 消息接收者的 ID,ID 类型与查询参数 receive_id_type 的取值一致。
- form: llm
-
- - name: receive_id_type
- type: select
- required: true
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- - value: email
- label:
- en_US: email
- zh_Hans: email
- - value: chat_id
- label:
- en_US: chat_id
- zh_Hans: chat_id
- label:
- en_US: receive_id_type
- zh_Hans: 消息接收者的 ID 类型
- human_description:
- en_US: The ID type of the message receiver, optional values are open_id, union_id, user_id, email, chat_id, with a default value of open_id.
- zh_Hans: 消息接收者的 ID 类型,可选值有 open_id、union_id、user_id、email、chat_id,默认值为 open_id。
- llm_description: 消息接收者的 ID 类型,可选值有 open_id、union_id、user_id、email、chat_id,默认值为 open_id。
- form: form
-
- - name: msg_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: interactive
- label:
- en_US: interactive
- zh_Hans: 卡片
- - value: post
- label:
- en_US: post
- zh_Hans: 富文本
- - value: image
- label:
- en_US: image
- zh_Hans: 图片
- - value: file
- label:
- en_US: file
- zh_Hans: 文件
- - value: audio
- label:
- en_US: audio
- zh_Hans: 语音
- - value: media
- label:
- en_US: media
- zh_Hans: 视频
- - value: sticker
- label:
- en_US: sticker
- zh_Hans: 表情包
- - value: share_chat
- label:
- en_US: share_chat
- zh_Hans: 分享群名片
- - value: share_user
- label:
- en_US: share_user
- zh_Hans: 分享个人名片
- - value: system
- label:
- en_US: system
- zh_Hans: 系统消息
- label:
- en_US: msg_type
- zh_Hans: 消息类型
- human_description:
- en_US: Message type. Optional values are text, post, image, file, audio, media, sticker, interactive, share_chat, share_user, system. For detailed introduction of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息类型。可选值有:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user、system。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息类型。可选值有:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user、system。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: form
-
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- human_description:
- en_US: Message content, a JSON structure serialized string. The value of this parameter corresponds to msg_type. For example, if msg_type is text, this parameter needs to pass in text type content. To understand the format and usage limitations of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.py b/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.py
deleted file mode 100644
index 44e70e0a15b64d..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class SendWebhookMessageTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- webhook = tool_parameters.get("webhook")
- msg_type = tool_parameters.get("msg_type")
- content = tool_parameters.get("content")
-
- res = client.send_webhook_message(webhook, msg_type, content)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.yaml b/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.yaml
deleted file mode 100644
index eeeae8b29cd935..00000000000000
--- a/api/core/tools/provider/builtin/feishu_message/tools/send_webhook_message.yaml
+++ /dev/null
@@ -1,68 +0,0 @@
-identity:
- name: send_webhook_message
- author: Doug Lea
- label:
- en_US: Send Webhook Message
- zh_Hans: 使用自定义机器人发送飞书消息
-description:
- human:
- en_US: Send webhook message
- zh_Hans: 使用自定义机器人发送飞书消息
- llm: A tool for sending Lark messages using a custom robot.
-parameters:
- - name: webhook
- type: string
- required: true
- label:
- en_US: webhook
- zh_Hans: webhook
- human_description:
- en_US: |
- The address of the webhook, the format of the webhook address corresponding to the bot is as follows: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx. For details, please refer to: Feishu Custom Bot Usage Guide(https://open.larkoffice.com/document/client-docs/bot-v3/add-custom-bot)
- zh_Hans: |
- webhook 的地址,机器人对应的 webhook 地址格式如下: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx,详情可参考: 飞书自定义机器人使用指南(https://open.larkoffice.com/document/client-docs/bot-v3/add-custom-bot)
- llm_description: |
- webhook 的地址,机器人对应的 webhook 地址格式如下: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx,详情可参考: 飞书自定义机器人使用指南(https://open.larkoffice.com/document/client-docs/bot-v3/add-custom-bot)
- form: llm
-
- - name: msg_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: interactive
- label:
- en_US: interactive
- zh_Hans: 卡片
- - value: image
- label:
- en_US: image
- zh_Hans: 图片
- - value: share_chat
- label:
- en_US: share_chat
- zh_Hans: 分享群名片
- label:
- en_US: msg_type
- zh_Hans: 消息类型
- human_description:
- en_US: Message type. Optional values are text, image, interactive, share_chat. For detailed introduction of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息类型。可选值有:text、image、interactive、share_chat。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息类型。可选值有:text、image、interactive、share_chat。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: form
-
-
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- human_description:
- en_US: Message content, a JSON structure serialized string. The value of this parameter corresponds to msg_type. For example, if msg_type is text, this parameter needs to pass in text type content. To understand the format and usage limitations of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/_assets/icon.png b/api/core/tools/provider/builtin/feishu_spreadsheet/_assets/icon.png
deleted file mode 100644
index 258b361261d4e3..00000000000000
Binary files a/api/core/tools/provider/builtin/feishu_spreadsheet/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.py b/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.py
deleted file mode 100644
index a3b54737691c9c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuMessageProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.yaml
deleted file mode 100644
index 29e448d730f745..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/feishu_spreadsheet.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_spreadsheet
- label:
- en_US: Feishu Spreadsheet
- zh_Hans: 飞书电子表格
- description:
- en_US: |
- Feishu Spreadsheet, requires the following permissions: sheets:spreadsheet.
- zh_Hans: |
- 飞书电子表格,需要开通以下权限: sheets:spreadsheet。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.py
deleted file mode 100644
index 44d062f9bdded2..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class AddColsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- length = tool_parameters.get("length")
- values = tool_parameters.get("values")
-
- res = client.add_cols(spreadsheet_token, sheet_id, sheet_name, length, values)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.yaml
deleted file mode 100644
index b73335f405c20c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_cols.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: add_cols
- author: Doug Lea
- label:
- en_US: Add Cols
- zh_Hans: 新增多列至工作表最后
-description:
- human:
- en_US: Add Cols
- zh_Hans: 新增多列至工作表最后
- llm: A tool for adding multiple columns to the end of a spreadsheet. (新增多列至工作表最后)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: length
- type: number
- required: true
- label:
- en_US: length
- zh_Hans: 要增加的列数
- human_description:
- en_US: Number of columns to add, range (0-5000].
- zh_Hans: 要增加的列数,范围(0-5000]。
- llm_description: 要增加的列数,范围(0-5000]。
- form: form
-
- - name: values
- type: string
- required: false
- label:
- en_US: values
- zh_Hans: 新增列的单元格内容
- human_description:
- en_US: |
- Content of the new columns, array of objects in string format, each array represents a row of table data, format like: [ [ "ID","Name","Age" ],[ 1,"Zhang San",10 ],[ 2,"Li Si",11 ] ].
- zh_Hans: 新增列的单元格内容,数组对象字符串,每个数组一行表格数据,格式:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- llm_description: 新增列的单元格内容,数组对象字符串,每个数组一行表格数据,格式:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.py
deleted file mode 100644
index 3a85b7b46ccb93..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class AddRowsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- length = tool_parameters.get("length")
- values = tool_parameters.get("values")
-
- res = client.add_rows(spreadsheet_token, sheet_id, sheet_name, length, values)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.yaml
deleted file mode 100644
index 6bce305b9825ec..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/add_rows.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: add_rows
- author: Doug Lea
- label:
- en_US: Add Rows
- zh_Hans: 新增多行至工作表最后
-description:
- human:
- en_US: Add Rows
- zh_Hans: 新增多行至工作表最后
- llm: A tool for adding multiple rows to the end of a spreadsheet. (新增多行至工作表最后)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: length
- type: number
- required: true
- label:
- en_US: length
- zh_Hans: 要增加行数
- human_description:
- en_US: Number of rows to add, range (0-5000].
- zh_Hans: 要增加行数,范围(0-5000]。
- llm_description: 要增加行数,范围(0-5000]。
- form: form
-
- - name: values
- type: string
- required: false
- label:
- en_US: values
- zh_Hans: 新增行的表格内容
- human_description:
- en_US: |
- Content of the new rows, array of objects in string format, each array represents a row of table data, format like: [ [ "ID","Name","Age" ],[ 1,"Zhang San",10 ],[ 2,"Li Si",11 ] ].
- zh_Hans: 新增行的表格内容,数组对象字符串,每个数组一行表格数据,格式,如:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- llm_description: 新增行的表格内容,数组对象字符串,每个数组一行表格数据,格式,如:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.py
deleted file mode 100644
index 647364fab0a966..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateSpreadsheetTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- title = tool_parameters.get("title")
- folder_token = tool_parameters.get("folder_token")
-
- res = client.create_spreadsheet(title, folder_token)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.yaml
deleted file mode 100644
index 931310e63172d4..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/create_spreadsheet.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-identity:
- name: create_spreadsheet
- author: Doug Lea
- label:
- en_US: Create Spreadsheet
- zh_Hans: 创建电子表格
-description:
- human:
- en_US: Create Spreadsheet
- zh_Hans: 创建电子表格
- llm: A tool for creating spreadsheets. (创建电子表格)
-parameters:
- - name: title
- type: string
- required: false
- label:
- en_US: Spreadsheet Title
- zh_Hans: 电子表格标题
- human_description:
- en_US: The title of the spreadsheet
- zh_Hans: 电子表格的标题
- llm_description: 电子表格的标题
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: Folder Token
- zh_Hans: 文件夹 token
- human_description:
- en_US: The token of the folder, supports folder URL input, e.g., https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- zh_Hans: 文件夹 token,支持文件夹 URL 输入,如:https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- llm_description: 文件夹 token,支持文件夹 URL 输入,如:https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.py
deleted file mode 100644
index dda8c59daffabf..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetSpreadsheetTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.get_spreadsheet(spreadsheet_token, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.yaml
deleted file mode 100644
index c519938617ba8c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/get_spreadsheet.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-identity:
- name: get_spreadsheet
- author: Doug Lea
- label:
- en_US: Get Spreadsheet
- zh_Hans: 获取电子表格信息
-description:
- human:
- en_US: Get Spreadsheet
- zh_Hans: 获取电子表格信息
- llm: A tool for getting information from spreadsheets. (获取电子表格信息)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: Spreadsheet Token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 URL。
- llm_description: 电子表格 token,支持输入电子表格 URL。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.py
deleted file mode 100644
index 98497791c0fa1e..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ListSpreadsheetSheetsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
-
- res = client.list_spreadsheet_sheets(spreadsheet_token)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.yaml
deleted file mode 100644
index c6a7ef45d46589..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/list_spreadsheet_sheets.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: list_spreadsheet_sheets
- author: Doug Lea
- label:
- en_US: List Spreadsheet Sheets
- zh_Hans: 列出电子表格所有工作表
-description:
- human:
- en_US: List Spreadsheet Sheets
- zh_Hans: 列出电子表格所有工作表
- llm: A tool for listing all sheets in a spreadsheet. (列出电子表格所有工作表)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: Spreadsheet Token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 URL。
- llm_description: 电子表格 token,支持输入电子表格 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.py
deleted file mode 100644
index ebe3f619d091d1..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ReadColsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- start_col = tool_parameters.get("start_col")
- num_cols = tool_parameters.get("num_cols")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_cols(spreadsheet_token, sheet_id, sheet_name, start_col, num_cols, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.yaml
deleted file mode 100644
index 34da74592d5898..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_cols.yaml
+++ /dev/null
@@ -1,97 +0,0 @@
-identity:
- name: read_cols
- author: Doug Lea
- label:
- en_US: Read Cols
- zh_Hans: 读取工作表列数据
-description:
- human:
- en_US: Read Cols
- zh_Hans: 读取工作表列数据
- llm: A tool for reading column data from a spreadsheet. (读取工作表列数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_col
- type: number
- required: false
- label:
- en_US: start_col
- zh_Hans: 起始列号
- human_description:
- en_US: Starting column number, starting from 1.
- zh_Hans: 起始列号,从 1 开始。
- llm_description: 起始列号,从 1 开始。
- form: form
-
- - name: num_cols
- type: number
- required: true
- label:
- en_US: num_cols
- zh_Hans: 读取列数
- human_description:
- en_US: Number of columns to read.
- zh_Hans: 读取列数
- llm_description: 读取列数
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.py
deleted file mode 100644
index 86b91b104b7029..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ReadRowsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- start_row = tool_parameters.get("start_row")
- num_rows = tool_parameters.get("num_rows")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_rows(spreadsheet_token, sheet_id, sheet_name, start_row, num_rows, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.yaml
deleted file mode 100644
index 5dfa8d58354125..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_rows.yaml
+++ /dev/null
@@ -1,97 +0,0 @@
-identity:
- name: read_rows
- author: Doug Lea
- label:
- en_US: Read Rows
- zh_Hans: 读取工作表行数据
-description:
- human:
- en_US: Read Rows
- zh_Hans: 读取工作表行数据
- llm: A tool for reading row data from a spreadsheet. (读取工作表行数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_row
- type: number
- required: false
- label:
- en_US: start_row
- zh_Hans: 起始行号
- human_description:
- en_US: Starting row number, starting from 1.
- zh_Hans: 起始行号,从 1 开始。
- llm_description: 起始行号,从 1 开始。
- form: form
-
- - name: num_rows
- type: number
- required: true
- label:
- en_US: num_rows
- zh_Hans: 读取行数
- human_description:
- en_US: Number of rows to read.
- zh_Hans: 读取行数
- llm_description: 读取行数
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.py b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.py
deleted file mode 100644
index ddd607d87838f4..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class ReadTableTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- num_range = tool_parameters.get("num_range")
- query = tool_parameters.get("query")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_table(spreadsheet_token, sheet_id, sheet_name, num_range, query, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.yaml b/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.yaml
deleted file mode 100644
index 10534436d66e7a..00000000000000
--- a/api/core/tools/provider/builtin/feishu_spreadsheet/tools/read_table.yaml
+++ /dev/null
@@ -1,122 +0,0 @@
-identity:
- name: read_table
- author: Doug Lea
- label:
- en_US: Read Table
- zh_Hans: 自定义读取电子表格行列数据
-description:
- human:
- en_US: Read Table
- zh_Hans: 自定义读取电子表格行列数据
- llm: A tool for custom reading of row and column data from a spreadsheet. (自定义读取电子表格行列数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_row
- type: number
- required: false
- label:
- en_US: start_row
- zh_Hans: 起始行号
- human_description:
- en_US: Starting row number, starting from 1.
- zh_Hans: 起始行号,从 1 开始。
- llm_description: 起始行号,从 1 开始。
- form: form
-
- - name: num_rows
- type: number
- required: false
- label:
- en_US: num_rows
- zh_Hans: 读取行数
- human_description:
- en_US: Number of rows to read.
- zh_Hans: 读取行数
- llm_description: 读取行数
- form: form
-
- - name: range
- type: string
- required: false
- label:
- en_US: range
- zh_Hans: 取数范围
- human_description:
- en_US: |
- Data range, format like: A1:B2, can be empty when query=all.
- zh_Hans: 取数范围,格式如:A1:B2,query=all 时可为空。
- llm_description: 取数范围,格式如:A1:B2,query=all 时可为空。
- form: llm
-
- - name: query
- type: string
- required: false
- label:
- en_US: query
- zh_Hans: 查询
- human_description:
- en_US: Pass "all" to query all data in the table, but no more than 100 columns.
- zh_Hans: 传 all,表示查询表格所有数据,但最多查询 100 列数据。
- llm_description: 传 all,表示查询表格所有数据,但最多查询 100 列数据。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_task/_assets/icon.png b/api/core/tools/provider/builtin/feishu_task/_assets/icon.png
deleted file mode 100644
index 3485be0d0fbd85..00000000000000
Binary files a/api/core/tools/provider/builtin/feishu_task/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/feishu_task/feishu_task.py b/api/core/tools/provider/builtin/feishu_task/feishu_task.py
deleted file mode 100644
index 6df05968d8f176..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/feishu_task.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuTaskProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_task/feishu_task.yaml b/api/core/tools/provider/builtin/feishu_task/feishu_task.yaml
deleted file mode 100644
index 88736f79a02e87..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/feishu_task.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_task
- label:
- en_US: Feishu Task
- zh_Hans: 飞书任务
- description:
- en_US: |
- Feishu Task, requires the following permissions: task:task:write、contact:user.id:readonly.
- zh_Hans: |
- 飞书任务,需要开通以下权限: task:task:write、contact:user.id:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/add_members.py b/api/core/tools/provider/builtin/feishu_task/tools/add_members.py
deleted file mode 100644
index e58ed22e0f4797..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/add_members.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class AddMembersTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
- member_phone_or_email = tool_parameters.get("member_phone_or_email")
- member_role = tool_parameters.get("member_role", "follower")
-
- res = client.add_members(task_guid, member_phone_or_email, member_role)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/add_members.yaml b/api/core/tools/provider/builtin/feishu_task/tools/add_members.yaml
deleted file mode 100644
index 063c0f7f04956c..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/add_members.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-identity:
- name: add_members
- author: Doug Lea
- label:
- en_US: Add Members
- zh_Hans: 添加任务成员
-description:
- human:
- en_US: Add Members
- zh_Hans: 添加任务成员
- llm: A tool for adding members to a Feishu task.(添加任务成员)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The GUID of the task to be added, supports passing either the Task ID or the Task link URL. Example of Task ID: 8b5425ec-9f2a-43bd-a3ab-01912f50282b; Example of Task link URL: https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- zh_Hans: 要添加的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- llm_description: 要添加的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- form: llm
-
- - name: member_phone_or_email
- type: string
- required: true
- label:
- en_US: Task Member Phone Or Email
- zh_Hans: 任务成员的电话或邮箱
- human_description:
- en_US: A list of member emails or phone numbers, separated by commas.
- zh_Hans: 任务成员邮箱或者手机号列表,使用逗号分隔。
- llm_description: 任务成员邮箱或者手机号列表,使用逗号分隔。
- form: llm
-
- - name: member_role
- type: select
- required: true
- options:
- - value: assignee
- label:
- en_US: assignee
- zh_Hans: 负责人
- - value: follower
- label:
- en_US: follower
- zh_Hans: 关注人
- default: "follower"
- label:
- en_US: member_role
- zh_Hans: 成员的角色
- human_description:
- en_US: Member role, optional values are "assignee" (responsible person) and "follower" (observer), with a default value of "assignee".
- zh_Hans: 成员的角色,可选值有 "assignee"(负责人)和 "follower"(关注人),默认值为 "assignee"。
- llm_description: 成员的角色,可选值有 "assignee"(负责人)和 "follower"(关注人),默认值为 "assignee"。
- form: form
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/create_task.py b/api/core/tools/provider/builtin/feishu_task/tools/create_task.py
deleted file mode 100644
index 96cdcd71f6d2ec..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/create_task.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class CreateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- summary = tool_parameters.get("summary")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- completed_time = tool_parameters.get("completed_time")
- description = tool_parameters.get("description")
-
- res = client.create_task(summary, start_time, end_time, completed_time, description)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/create_task.yaml b/api/core/tools/provider/builtin/feishu_task/tools/create_task.yaml
deleted file mode 100644
index 7eb4af168bf740..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/create_task.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-identity:
- name: create_task
- author: Doug Lea
- label:
- en_US: Create Task
- zh_Hans: 创建飞书任务
-description:
- human:
- en_US: Create Feishu Task
- zh_Hans: 创建飞书任务
- llm: A tool for creating tasks in Feishu.(创建飞书任务)
-parameters:
- - name: summary
- type: string
- required: true
- label:
- en_US: Task Title
- zh_Hans: 任务标题
- human_description:
- en_US: The title of the task.
- zh_Hans: 任务标题
- llm_description: 任务标题
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Task Description
- zh_Hans: 任务备注
- human_description:
- en_US: The description or notes for the task.
- zh_Hans: 任务备注
- llm_description: 任务备注
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 任务开始时间
- human_description:
- en_US: |
- The start time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务开始时间,格式为:2006-01-02 15:04:05
- llm_description: 任务开始时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 任务结束时间
- human_description:
- en_US: |
- The end time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务结束时间,格式为:2006-01-02 15:04:05
- llm_description: 任务结束时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: completed_time
- type: string
- required: false
- label:
- en_US: Completed Time
- zh_Hans: 任务完成时间
- human_description:
- en_US: |
- The completion time of the task, in the format: 2006-01-02 15:04:05. Leave empty to create an incomplete task; fill in a specific time to create a completed task.
- zh_Hans: 任务完成时间,格式为:2006-01-02 15:04:05,不填写表示创建一个未完成任务;填写一个具体的时间表示创建一个已完成任务。
- llm_description: 任务完成时间,格式为:2006-01-02 15:04:05,不填写表示创建一个未完成任务;填写一个具体的时间表示创建一个已完成任务。
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/delete_task.py b/api/core/tools/provider/builtin/feishu_task/tools/delete_task.py
deleted file mode 100644
index dee036fee5203a..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/delete_task.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class UpdateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
-
- res = client.delete_task(task_guid)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/delete_task.yaml b/api/core/tools/provider/builtin/feishu_task/tools/delete_task.yaml
deleted file mode 100644
index d3f97413676624..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/delete_task.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-identity:
- name: delete_task
- author: Doug Lea
- label:
- en_US: Delete Task
- zh_Hans: 删除飞书任务
-description:
- human:
- en_US: Delete Task
- zh_Hans: 删除飞书任务
- llm: A tool for deleting tasks in Feishu.(删除飞书任务)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The GUID of the task to be deleted, supports passing either the Task ID or the Task link URL. Example of Task ID: 8b5425ec-9f2a-43bd-a3ab-01912f50282b; Example of Task link URL: https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- zh_Hans: 要删除的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- llm_description: 要删除的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.feishu-pre.net/client/todo/detail?guid=8c6bf822-e4da-449a-b82a-dc44020f9be9&suite_entity_num=t21587362
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/update_task.py b/api/core/tools/provider/builtin/feishu_task/tools/update_task.py
deleted file mode 100644
index 4a48cd283abf1d..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/update_task.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class UpdateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
- summary = tool_parameters.get("summary")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- completed_time = tool_parameters.get("completed_time")
- description = tool_parameters.get("description")
-
- res = client.update_task(task_guid, summary, start_time, end_time, completed_time, description)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_task/tools/update_task.yaml b/api/core/tools/provider/builtin/feishu_task/tools/update_task.yaml
deleted file mode 100644
index 83c9bcb1c443ac..00000000000000
--- a/api/core/tools/provider/builtin/feishu_task/tools/update_task.yaml
+++ /dev/null
@@ -1,89 +0,0 @@
-identity:
- name: update_task
- author: Doug Lea
- label:
- en_US: Update Task
- zh_Hans: 更新飞书任务
-description:
- human:
- en_US: Update Feishu Task
- zh_Hans: 更新飞书任务
- llm: A tool for updating tasks in Feishu.(更新飞书任务)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The task ID, supports inputting either the Task ID or the Task link URL. Example of Task ID: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64; Example of Task link URL: https://applink.feishu-pre.net/client/todo/detail?guid=42cad8a0-f8c8-4344-9be2-d1d7e8e91b64&suite_entity_num=t21700217
- zh_Hans: |
- 任务ID,支持传入任务 ID 和任务链接 URL。任务 ID 示例: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64;任务链接 URL 示例: https://applink.feishu-pre.net/client/todo/detail?guid=42cad8a0-f8c8-4344-9be2-d1d7e8e91b64&suite_entity_num=t21700217
- llm_description: |
- 任务ID,支持传入任务 ID 和任务链接 URL。任务 ID 示例: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64;任务链接 URL 示例: https://applink.feishu-pre.net/client/todo/detail?guid=42cad8a0-f8c8-4344-9be2-d1d7e8e91b64&suite_entity_num=t21700217
- form: llm
-
- - name: summary
- type: string
- required: true
- label:
- en_US: Task Title
- zh_Hans: 任务标题
- human_description:
- en_US: The title of the task.
- zh_Hans: 任务标题
- llm_description: 任务标题
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Task Description
- zh_Hans: 任务备注
- human_description:
- en_US: The description or notes for the task.
- zh_Hans: 任务备注
- llm_description: 任务备注
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 任务开始时间
- human_description:
- en_US: |
- The start time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务开始时间,格式为:2006-01-02 15:04:05
- llm_description: 任务开始时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 任务结束时间
- human_description:
- en_US: |
- The end time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务结束时间,格式为:2006-01-02 15:04:05
- llm_description: 任务结束时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: completed_time
- type: string
- required: false
- label:
- en_US: Completed Time
- zh_Hans: 任务完成时间
- human_description:
- en_US: |
- The completion time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务完成时间,格式为:2006-01-02 15:04:05
- llm_description: 任务完成时间,格式为:2006-01-02 15:04:05
- form: llm
diff --git a/api/core/tools/provider/builtin/feishu_wiki/_assets/icon.png b/api/core/tools/provider/builtin/feishu_wiki/_assets/icon.png
deleted file mode 100644
index 878672c9ae5a51..00000000000000
Binary files a/api/core/tools/provider/builtin/feishu_wiki/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.py b/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.py
deleted file mode 100644
index 6c5fccb1a31d0d..00000000000000
--- a/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.feishu_api_utils import auth
-
-
-class FeishuWikiProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- auth(credentials)
diff --git a/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.yaml b/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.yaml
deleted file mode 100644
index 1fb5f71cbc5169..00000000000000
--- a/api/core/tools/provider/builtin/feishu_wiki/feishu_wiki.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: feishu_wiki
- label:
- en_US: Feishu Wiki
- zh_Hans: 飞书知识库
- description:
- en_US: |
- Feishu Wiki, requires the following permissions: wiki:wiki:readonly.
- zh_Hans: |
- 飞书知识库,需要开通以下权限: wiki:wiki:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your feishu app id
- zh_Hans: 请输入你的飞书 app id
- help:
- en_US: Get your app_id and app_secret from Feishu
- zh_Hans: 从飞书获取您的 app_id 和 app_secret
- url: https://open.larkoffice.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的飞书 app secret
diff --git a/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.py b/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.py
deleted file mode 100644
index 374b4c9a7d1492..00000000000000
--- a/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.feishu_api_utils import FeishuRequest
-
-
-class GetWikiNodesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = FeishuRequest(app_id, app_secret)
-
- space_id = tool_parameters.get("space_id")
- parent_node_token = tool_parameters.get("parent_node_token")
- page_token = tool_parameters.get("page_token")
- page_size = tool_parameters.get("page_size")
-
- res = client.get_wiki_nodes(space_id, parent_node_token, page_token, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.yaml b/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.yaml
deleted file mode 100644
index 74d51e7bcbc32a..00000000000000
--- a/api/core/tools/provider/builtin/feishu_wiki/tools/get_wiki_nodes.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-identity:
- name: get_wiki_nodes
- author: Doug Lea
- label:
- en_US: Get Wiki Nodes
- zh_Hans: 获取知识空间子节点列表
-description:
- human:
- en_US: |
- Get the list of child nodes in Wiki, make sure the app/bot is a member of the wiki space. See How to add an app as a wiki base administrator (member). https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa
- zh_Hans: |
- 获取知识库全部子节点列表,请确保应用/机器人为知识空间成员。参阅如何将应用添加为知识库管理员(成员)。https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa
- llm: A tool for getting all sub-nodes of a knowledge base.(获取知识空间子节点列表)
-parameters:
- - name: space_id
- type: string
- required: true
- label:
- en_US: Space Id
- zh_Hans: 知识空间 ID
- human_description:
- en_US: |
- The ID of the knowledge space. Supports space link URL, for example: https://svi136aogf123.feishu.cn/wiki/settings/7166950623940706332
- zh_Hans: 知识空间 ID,支持空间链接 URL,例如:https://svi136aogf123.feishu.cn/wiki/settings/7166950623940706332
- llm_description: 知识空间 ID,支持空间链接 URL,例如:https://svi136aogf123.feishu.cn/wiki/settings/7166950623940706332
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 10
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The size of each page, with a maximum value of 50.
- zh_Hans: 分页大小,最大值 50。
- llm_description: 分页大小,最大值 50。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave empty for the first request to start from the beginning; if the paginated query result has more items, a new page_token will be returned, which can be used to get the next set of results.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
-
- - name: parent_node_token
- type: string
- required: false
- label:
- en_US: Parent Node Token
- zh_Hans: 父节点 token
- human_description:
- en_US: The token of the parent node.
- zh_Hans: 父节点 token
- llm_description: 父节点 token
- form: llm
diff --git a/api/core/tools/provider/builtin/firecrawl/_assets/icon.svg b/api/core/tools/provider/builtin/firecrawl/_assets/icon.svg
deleted file mode 100644
index e1e5f54117b1be..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/_assets/icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
- 🔥
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/firecrawl/firecrawl.py b/api/core/tools/provider/builtin/firecrawl/firecrawl.py
deleted file mode 100644
index 01455d7206f185..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/firecrawl.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.firecrawl.tools.scrape import ScrapeTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class FirecrawlProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- # Example validation using the ScrapeTool, only scraping title for minimize content
- ScrapeTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
- user_id="", tool_parameters={"url": "https://google.com", "onlyIncludeTags": "title"}
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/firecrawl/firecrawl.yaml b/api/core/tools/provider/builtin/firecrawl/firecrawl.yaml
deleted file mode 100644
index a48b9d9f541eb3..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/firecrawl.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-identity:
- author: Richards Tu
- name: firecrawl
- label:
- en_US: Firecrawl
- zh_CN: Firecrawl
- description:
- en_US: Firecrawl API integration for web crawling and scraping.
- zh_Hans: Firecrawl API 集成,用于网页爬取和数据抓取。
- icon: icon.svg
- tags:
- - search
- - utilities
-credentials_for_provider:
- firecrawl_api_key:
- type: secret-input
- required: true
- label:
- en_US: Firecrawl API Key
- zh_Hans: Firecrawl API 密钥
- placeholder:
- en_US: Please input your Firecrawl API key
- zh_Hans: 请输入您的 Firecrawl API 密钥,如果是自托管版本,可以随意填写密钥
- help:
- en_US: Get your Firecrawl API key from your Firecrawl account settings.If you are using a self-hosted version, you may enter any key at your convenience.
- zh_Hans: 从您的 Firecrawl 账户设置中获取 Firecrawl API 密钥。如果是自托管版本,可以随意填写密钥。
- url: https://www.firecrawl.dev/account
- base_url:
- type: text-input
- required: false
- label:
- en_US: Firecrawl server's Base URL
- zh_Hans: Firecrawl服务器的API URL
- placeholder:
- en_US: https://api.firecrawl.dev
diff --git a/api/core/tools/provider/builtin/firecrawl/firecrawl_appx.py b/api/core/tools/provider/builtin/firecrawl/firecrawl_appx.py
deleted file mode 100644
index 14596bf93f493f..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/firecrawl_appx.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import json
-import logging
-import time
-from collections.abc import Mapping
-from typing import Any
-
-import requests
-from requests.exceptions import HTTPError
-
-logger = logging.getLogger(__name__)
-
-
-class FirecrawlApp:
- def __init__(self, api_key: str | None = None, base_url: str | None = None):
- self.api_key = api_key
- self.base_url = base_url or "https://api.firecrawl.dev"
- if not self.api_key:
- raise ValueError("API key is required")
-
- def _prepare_headers(self, idempotency_key: str | None = None):
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"}
- if idempotency_key:
- headers["Idempotency-Key"] = idempotency_key
- return headers
-
- def _request(
- self,
- method: str,
- url: str,
- data: Mapping[str, Any] | None = None,
- headers: Mapping[str, str] | None = None,
- retries: int = 3,
- backoff_factor: float = 0.3,
- ) -> Mapping[str, Any] | None:
- if not headers:
- headers = self._prepare_headers()
- for i in range(retries):
- try:
- response = requests.request(method, url, json=data, headers=headers)
- return response.json()
- except requests.exceptions.RequestException:
- if i < retries - 1:
- time.sleep(backoff_factor * (2**i))
- else:
- raise
- return None
-
- def scrape_url(self, url: str, **kwargs):
- endpoint = f"{self.base_url}/v1/scrape"
- data = {"url": url, **kwargs}
- logger.debug(f"Sent request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data)
- if response is None:
- raise HTTPError("Failed to scrape URL after multiple retries")
- return response
-
- def map(self, url: str, **kwargs):
- endpoint = f"{self.base_url}/v1/map"
- data = {"url": url, **kwargs}
- logger.debug(f"Sent request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data)
- if response is None:
- raise HTTPError("Failed to perform map after multiple retries")
- return response
-
- def crawl_url(
- self, url: str, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs
- ):
- endpoint = f"{self.base_url}/v1/crawl"
- headers = self._prepare_headers(idempotency_key)
- data = {"url": url, **kwargs}
- logger.debug(f"Sent request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data, headers)
- if response is None:
- raise HTTPError("Failed to initiate crawl after multiple retries")
- elif response.get("success") == False:
- raise HTTPError(f"Failed to crawl: {response.get('error')}")
- job_id: str = response["id"]
- if wait:
- return self._monitor_job_status(job_id=job_id, poll_interval=poll_interval)
- return response
-
- def check_crawl_status(self, job_id: str):
- endpoint = f"{self.base_url}/v1/crawl/{job_id}"
- response = self._request("GET", endpoint)
- if response is None:
- raise HTTPError(f"Failed to check status for job {job_id} after multiple retries")
- return response
-
- def cancel_crawl_job(self, job_id: str):
- endpoint = f"{self.base_url}/v1/crawl/{job_id}"
- response = self._request("DELETE", endpoint)
- if response is None:
- raise HTTPError(f"Failed to cancel job {job_id} after multiple retries")
- return response
-
- def _monitor_job_status(self, job_id: str, poll_interval: int):
- while True:
- status = self.check_crawl_status(job_id)
- if status["status"] == "completed":
- return status
- elif status["status"] == "failed":
- raise HTTPError(f"Job {job_id} failed: {status['error']}")
- time.sleep(poll_interval)
-
-
-def get_array_params(tool_parameters: dict[str, Any], key):
- param = tool_parameters.get(key)
- if param:
- return param.split(",")
-
-
-def get_json_params(tool_parameters: dict[str, Any], key):
- param = tool_parameters.get(key)
- if param:
- try:
- # support both single quotes and double quotes
- param = param.replace("'", '"')
- param = json.loads(param)
- except Exception:
- raise ValueError(f"Invalid {key} format.")
- return param
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/crawl.py b/api/core/tools/provider/builtin/firecrawl/tools/crawl.py
deleted file mode 100644
index 15ab510c6c889c..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/crawl.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.firecrawl.firecrawl_appx import FirecrawlApp, get_array_params, get_json_params
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CrawlTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- """
- the api doc:
- https://docs.firecrawl.dev/api-reference/endpoint/crawl
- """
- app = FirecrawlApp(
- api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
- )
-
- scrapeOptions = {}
- payload = {}
-
- wait_for_results = tool_parameters.get("wait_for_results", True)
-
- payload["excludePaths"] = get_array_params(tool_parameters, "excludePaths")
- payload["includePaths"] = get_array_params(tool_parameters, "includePaths")
- payload["maxDepth"] = tool_parameters.get("maxDepth")
- payload["ignoreSitemap"] = tool_parameters.get("ignoreSitemap", False)
- payload["limit"] = tool_parameters.get("limit", 5)
- payload["allowBackwardLinks"] = tool_parameters.get("allowBackwardLinks", False)
- payload["allowExternalLinks"] = tool_parameters.get("allowExternalLinks", False)
- payload["webhook"] = tool_parameters.get("webhook")
-
- scrapeOptions["formats"] = get_array_params(tool_parameters, "formats")
- scrapeOptions["headers"] = get_json_params(tool_parameters, "headers")
- scrapeOptions["includeTags"] = get_array_params(tool_parameters, "includeTags")
- scrapeOptions["excludeTags"] = get_array_params(tool_parameters, "excludeTags")
- scrapeOptions["onlyMainContent"] = tool_parameters.get("onlyMainContent", False)
- scrapeOptions["waitFor"] = tool_parameters.get("waitFor", 0)
- scrapeOptions = {k: v for k, v in scrapeOptions.items() if v not in (None, "")}
- payload["scrapeOptions"] = scrapeOptions or None
-
- payload = {k: v for k, v in payload.items() if v not in (None, "")}
-
- crawl_result = app.crawl_url(url=tool_parameters["url"], wait=wait_for_results, **payload)
-
- return self.create_json_message(crawl_result)
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/crawl.yaml b/api/core/tools/provider/builtin/firecrawl/tools/crawl.yaml
deleted file mode 100644
index 0d7dbcac20ea16..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/crawl.yaml
+++ /dev/null
@@ -1,200 +0,0 @@
-identity:
- name: crawl
- author: Richards Tu
- label:
- en_US: Crawl
- zh_Hans: 深度爬取
-description:
- human:
- en_US: Recursively search through a urls subdomains, and gather the content.
- zh_Hans: 递归爬取一个网址的子域名,并收集内容。
- llm: This tool initiates a web crawl to extract data from a specified URL. It allows configuring crawler options such as including or excluding URL patterns, generating alt text for images using LLMs (paid plan required), limiting the maximum number of pages to crawl, and returning only the main content of the page. The tool can return either a list of crawled documents or a list of URLs based on the provided options.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: Start URL
- zh_Hans: 起始URL
- human_description:
- en_US: The base URL to start crawling from.
- zh_Hans: 要爬取网站的起始URL。
- llm_description: The URL of the website that needs to be crawled. This is a required parameter.
- form: llm
- - name: wait_for_results
- type: boolean
- default: true
- label:
- en_US: Wait For Results
- zh_Hans: 等待爬取结果
- human_description:
- en_US: If you choose not to wait, it will directly return a job ID. You can use this job ID to check the crawling results or cancel the crawling task, which is usually very useful for a large-scale crawling task.
- zh_Hans: 如果选择不等待,则会直接返回一个job_id,可以通过job_id查询爬取结果或取消爬取任务,这通常对于一个大型爬取任务来说非常有用。
- form: form
-############## Payload #######################
- - name: excludePaths
- type: string
- label:
- en_US: URL patterns to exclude
- zh_Hans: 要排除的URL模式
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Pages matching these patterns will be skipped. Example: blog/*, about/*
- zh_Hans: 匹配这些模式的页面将被跳过。示例:blog/*, about/*
- form: form
- - name: includePaths
- type: string
- required: false
- label:
- en_US: URL patterns to include
- zh_Hans: 要包含的URL模式
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Only pages matching these patterns will be crawled. Example: blog/*, about/*
- zh_Hans: 只有与这些模式匹配的页面才会被爬取。示例:blog/*, about/*
- form: form
- - name: maxDepth
- type: number
- label:
- en_US: Maximum crawl depth
- zh_Hans: 爬取深度
- human_description:
- en_US: Maximum depth to crawl relative to the entered URL. A maxDepth of 0 scrapes only the entered URL. A maxDepth of 1 scrapes the entered URL and all pages one level deep. A maxDepth of 2 scrapes the entered URL and all pages up to two levels deep. Higher values follow the same pattern.
- zh_Hans: 相对于输入的URL,爬取的最大深度。maxDepth为0时,仅抓取输入的URL。maxDepth为1时,抓取输入的URL以及所有一级深层页面。maxDepth为2时,抓取输入的URL以及所有两级深层页面。更高值遵循相同模式。
- form: form
- min: 0
- default: 2
- - name: ignoreSitemap
- type: boolean
- default: true
- label:
- en_US: ignore Sitemap
- zh_Hans: 忽略站点地图
- human_description:
- en_US: Ignore the website sitemap when crawling.
- zh_Hans: 爬取时忽略网站站点地图。
- form: form
- - name: limit
- type: number
- required: false
- label:
- en_US: Maximum pages to crawl
- zh_Hans: 最大爬取页面数
- human_description:
- en_US: Specify the maximum number of pages to crawl. The crawler will stop after reaching this limit.
- zh_Hans: 指定要爬取的最大页面数。爬虫将在达到此限制后停止。
- form: form
- min: 1
- default: 5
- - name: allowBackwardLinks
- type: boolean
- default: false
- label:
- en_US: allow Backward Crawling
- zh_Hans: 允许向后爬取
- human_description:
- en_US: Enables the crawler to navigate from a specific URL to previously linked pages. For instance, from 'example.com/product/123' back to 'example.com/product'
- zh_Hans: 使爬虫能够从特定URL导航到之前链接的页面。例如,从'example.com/product/123'返回到'example.com/product'
- form: form
- - name: allowExternalLinks
- type: boolean
- default: false
- label:
- en_US: allow External Content Links
- zh_Hans: 允许爬取外链
- human_description:
- en_US: Allows the crawler to follow links to external websites.
- zh_Hans:
- form: form
- - name: webhook
- type: string
- label:
- en_US: webhook
- human_description:
- en_US: |
- The URL to send the webhook to. This will trigger for crawl started (crawl.started) ,every page crawled (crawl.page) and when the crawl is completed (crawl.completed or crawl.failed). The response will be the same as the /scrape endpoint.
- zh_Hans: 发送Webhook的URL。这将在开始爬取(crawl.started)、每爬取一个页面(crawl.page)以及爬取完成(crawl.completed或crawl.failed)时触发。响应将与/scrape端点相同。
- form: form
-############## Scrape Options #######################
- - name: formats
- type: string
- label:
- en_US: Formats
- zh_Hans: 结果的格式
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Formats to include in the output. Available options: markdown, html, rawHtml, links, screenshot
- zh_Hans: |
- 输出中应包含的格式。可以填入: markdown, html, rawHtml, links, screenshot
- form: form
- - name: headers
- type: string
- label:
- en_US: headers
- zh_Hans: 请求头
- human_description:
- en_US: |
- Headers to send with the request. Can be used to send cookies, user-agent, etc. Example: {"cookies": "testcookies"}
- zh_Hans: |
- 随请求发送的头部。可以用来发送cookies、用户代理等。示例:{"cookies": "testcookies"}
- placeholder:
- en_US: Please enter an object that can be serialized in JSON
- zh_Hans: 请输入可以json序列化的对象
- form: form
- - name: includeTags
- type: string
- label:
- en_US: Include Tags
- zh_Hans: 仅抓取这些标签
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: script, .ad, #footer
- zh_Hans: |
- 仅在最终输出中包含HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script, .ad, #footer
- form: form
- - name: excludeTags
- type: string
- label:
- en_US: Exclude Tags
- zh_Hans: 要移除这些标签
- human_description:
- en_US: |
- Tags, classes and ids to remove from the page. Use comma separated values. Example: script, .ad, #footer
- zh_Hans: |
- 要在最终输出中移除HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script, .ad, #footer
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- form: form
- - name: onlyMainContent
- type: boolean
- default: false
- label:
- en_US: only Main Content
- zh_Hans: 仅抓取主要内容
- human_description:
- en_US: Only return the main content of the page excluding headers, navs, footers, etc.
- zh_Hans: 只返回页面的主要内容,不包括头部、导航栏、尾部等。
- form: form
- - name: waitFor
- type: number
- min: 0
- label:
- en_US: wait For
- zh_Hans: 等待时间
- human_description:
- en_US: Wait x amount of milliseconds for the page to load to fetch content.
- zh_Hans: 等待x毫秒以使页面加载并获取内容。
- form: form
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.py b/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.py
deleted file mode 100644
index 0d2486c7ca4426..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.firecrawl.firecrawl_appx import FirecrawlApp
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CrawlJobTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app = FirecrawlApp(
- api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
- )
- operation = tool_parameters.get("operation", "get")
- if operation == "get":
- result = app.check_crawl_status(job_id=tool_parameters["job_id"])
- elif operation == "cancel":
- result = app.cancel_crawl_job(job_id=tool_parameters["job_id"])
- else:
- raise ValueError(f"Invalid operation: {operation}")
-
- return self.create_json_message(result)
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.yaml b/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.yaml
deleted file mode 100644
index 78008e4ad4d8a6..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/crawl_job.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-identity:
- name: crawl_job
- author: hjlarry
- label:
- en_US: Crawl Job
- zh_Hans: 爬取任务处理
-description:
- human:
- en_US: Retrieve the scraping results based on the job ID, or cancel the scraping task.
- zh_Hans: 根据爬取任务ID获取爬取结果,或者取消爬取任务
- llm: Retrieve the scraping results based on the job ID, or cancel the scraping task.
-parameters:
- - name: job_id
- type: string
- required: true
- label:
- en_US: Job ID
- human_description:
- en_US: Set wait_for_results to false in the Crawl tool can get the job ID.
- zh_Hans: 在深度爬取工具中将等待爬取结果设置为否可以获取Job ID。
- llm_description: Set wait_for_results to false in the Crawl tool can get the job ID.
- form: llm
- - name: operation
- type: select
- required: true
- options:
- - value: get
- label:
- en_US: get crawl status
- - value: cancel
- label:
- en_US: cancel crawl job
- label:
- en_US: operation
- zh_Hans: 操作
- llm_description: choose the operation to perform. `get` is for getting the crawl status, `cancel` is for cancelling the crawl job.
- form: llm
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/map.py b/api/core/tools/provider/builtin/firecrawl/tools/map.py
deleted file mode 100644
index bdfb5faeb8e2c9..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/map.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.firecrawl.firecrawl_appx import FirecrawlApp
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class MapTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- """
- the api doc:
- https://docs.firecrawl.dev/api-reference/endpoint/map
- """
- app = FirecrawlApp(
- api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
- )
- payload = {}
- payload["search"] = tool_parameters.get("search")
- payload["ignoreSitemap"] = tool_parameters.get("ignoreSitemap", True)
- payload["includeSubdomains"] = tool_parameters.get("includeSubdomains", False)
- payload["limit"] = tool_parameters.get("limit", 5000)
-
- map_result = app.map(url=tool_parameters["url"], **payload)
-
- return self.create_json_message(map_result)
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/map.yaml b/api/core/tools/provider/builtin/firecrawl/tools/map.yaml
deleted file mode 100644
index 9913756983370a..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/map.yaml
+++ /dev/null
@@ -1,59 +0,0 @@
-identity:
- name: map
- author: hjlarry
- label:
- en_US: Map
- zh_Hans: 地图式快爬
-description:
- human:
- en_US: Input a website and get all the urls on the website - extremly fast
- zh_Hans: 输入一个网站,快速获取网站上的所有网址。
- llm: Input a website and get all the urls on the website - extremly fast
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: Start URL
- zh_Hans: 起始URL
- human_description:
- en_US: The base URL to start crawling from.
- zh_Hans: 要爬取网站的起始URL。
- llm_description: The URL of the website that needs to be crawled. This is a required parameter.
- form: llm
- - name: search
- type: string
- label:
- en_US: search
- zh_Hans: 搜索查询
- human_description:
- en_US: Search query to use for mapping. During the Alpha phase, the 'smart' part of the search functionality is limited to 100 search results. However, if map finds more results, there is no limit applied.
- zh_Hans: 用于映射的搜索查询。在Alpha阶段,搜索功能的“智能”部分限制为最多100个搜索结果。然而,如果地图找到了更多结果,则不施加任何限制。
- llm_description: Search query to use for mapping. During the Alpha phase, the 'smart' part of the search functionality is limited to 100 search results. However, if map finds more results, there is no limit applied.
- form: llm
-############## Page Options #######################
- - name: ignoreSitemap
- type: boolean
- default: true
- label:
- en_US: ignore Sitemap
- zh_Hans: 忽略站点地图
- human_description:
- en_US: Ignore the website sitemap when crawling.
- zh_Hans: 爬取时忽略网站站点地图。
- form: form
- - name: includeSubdomains
- type: boolean
- default: false
- label:
- en_US: include Subdomains
- zh_Hans: 包含子域名
- form: form
- - name: limit
- type: number
- min: 0
- default: 5000
- label:
- en_US: Maximum results
- zh_Hans: 最大结果数量
- form: form
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/scrape.py b/api/core/tools/provider/builtin/firecrawl/tools/scrape.py
deleted file mode 100644
index f00a9b31ce8c2c..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/scrape.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.firecrawl.firecrawl_appx import FirecrawlApp, get_array_params, get_json_params
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class ScrapeTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]:
- """
- the api doc:
- https://docs.firecrawl.dev/api-reference/endpoint/scrape
- """
- app = FirecrawlApp(
- api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
- )
-
- payload = {}
- extract = {}
-
- payload["formats"] = get_array_params(tool_parameters, "formats")
- payload["onlyMainContent"] = tool_parameters.get("onlyMainContent", True)
- payload["includeTags"] = get_array_params(tool_parameters, "includeTags")
- payload["excludeTags"] = get_array_params(tool_parameters, "excludeTags")
- payload["headers"] = get_json_params(tool_parameters, "headers")
- payload["waitFor"] = tool_parameters.get("waitFor", 0)
- payload["timeout"] = tool_parameters.get("timeout", 30000)
-
- extract["schema"] = get_json_params(tool_parameters, "schema")
- extract["systemPrompt"] = tool_parameters.get("systemPrompt")
- extract["prompt"] = tool_parameters.get("prompt")
- extract = {k: v for k, v in extract.items() if v not in (None, "")}
- payload["extract"] = extract or None
-
- payload = {k: v for k, v in payload.items() if v not in (None, "")}
-
- crawl_result = app.scrape_url(url=tool_parameters["url"], **payload)
- markdown_result = crawl_result.get("data", {}).get("markdown", "")
- return [self.create_text_message(markdown_result), self.create_json_message(crawl_result)]
diff --git a/api/core/tools/provider/builtin/firecrawl/tools/scrape.yaml b/api/core/tools/provider/builtin/firecrawl/tools/scrape.yaml
deleted file mode 100644
index 8f1f1348a459ca..00000000000000
--- a/api/core/tools/provider/builtin/firecrawl/tools/scrape.yaml
+++ /dev/null
@@ -1,152 +0,0 @@
-identity:
- name: scrape
- author: ahasasjeb
- label:
- en_US: Scrape
- zh_Hans: 单页面抓取
-description:
- human:
- en_US: Turn any url into clean data.
- zh_Hans: 将任何网址转换为干净的数据。
- llm: This tool is designed to scrape URL and output the content in Markdown format.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: URL to scrape
- zh_Hans: 要抓取的URL
- human_description:
- en_US: The URL of the website to scrape and extract data from.
- zh_Hans: 要抓取并提取数据的网站URL。
- llm_description: The URL of the website that needs to be crawled. This is a required parameter.
- form: llm
-############## Payload #######################
- - name: formats
- type: string
- label:
- en_US: Formats
- zh_Hans: 结果的格式
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Formats to include in the output. Available options: markdown, html, rawHtml, links, screenshot, extract, screenshot@fullPage
- zh_Hans: |
- 输出中应包含的格式。可以填入: markdown, html, rawHtml, links, screenshot, extract, screenshot@fullPage
- form: form
- - name: onlyMainContent
- type: boolean
- default: false
- label:
- en_US: only Main Content
- zh_Hans: 仅抓取主要内容
- human_description:
- en_US: Only return the main content of the page excluding headers, navs, footers, etc.
- zh_Hans: 只返回页面的主要内容,不包括头部、导航栏、尾部等。
- form: form
- - name: includeTags
- type: string
- label:
- en_US: Include Tags
- zh_Hans: 仅抓取这些标签
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- human_description:
- en_US: |
- Only include tags, classes and ids from the page in the final output. Use comma separated values. Example: script, .ad, #footer
- zh_Hans: |
- 仅在最终输出中包含HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script, .ad, #footer
- form: form
- - name: excludeTags
- type: string
- label:
- en_US: Exclude Tags
- zh_Hans: 要移除这些标签
- human_description:
- en_US: |
- Tags, classes and ids to remove from the page. Use comma separated values. Example: script, .ad, #footer
- zh_Hans: |
- 要在最终输出中移除HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script, .ad, #footer
- placeholder:
- en_US: Use commas to separate multiple tags
- zh_Hans: 多个标签时使用半角逗号分隔
- form: form
- - name: headers
- type: string
- label:
- en_US: headers
- zh_Hans: 请求头
- human_description:
- en_US: |
- Headers to send with the request. Can be used to send cookies, user-agent, etc. Example: {"cookies": "testcookies"}
- zh_Hans: |
- 随请求发送的头部。可以用来发送cookies、用户代理等。示例:{"cookies": "testcookies"}
- placeholder:
- en_US: Please enter an object that can be serialized in JSON
- zh_Hans: 请输入可以json序列化的对象
- form: form
- - name: waitFor
- type: number
- min: 0
- default: 0
- label:
- en_US: wait For
- zh_Hans: 等待时间
- human_description:
- en_US: Wait x amount of milliseconds for the page to load to fetch content.
- zh_Hans: 等待x毫秒以使页面加载并获取内容。
- form: form
- - name: timeout
- type: number
- min: 0
- default: 30000
- label:
- en_US: Timeout
- human_description:
- en_US: Timeout in milliseconds for the request.
- zh_Hans: 请求的超时时间(以毫秒为单位)。
- form: form
-############## Extractor Options #######################
- - name: schema
- type: string
- label:
- en_US: Extractor Schema
- zh_Hans: 提取时的结构
- placeholder:
- en_US: Please enter an object that can be serialized in JSON
- zh_Hans: 请输入可以json序列化的对象
- human_description:
- en_US: |
- The schema for the data to be extracted. Example: {
- "type": "object",
- "properties": {"company_mission": {"type": "string"}},
- "required": ["company_mission"]
- }
- zh_Hans: |
- 使用该结构去提取,示例:{
- "type": "object",
- "properties": {"company_mission": {"type": "string"}},
- "required": ["company_mission"]
- }
- form: form
- - name: systemPrompt
- type: string
- label:
- en_US: Extractor System Prompt
- zh_Hans: 提取时的系统提示词
- human_description:
- en_US: The system prompt to use for the extraction.
- zh_Hans: 用于提取的系统提示。
- form: form
- - name: prompt
- type: string
- label:
- en_US: Extractor Prompt
- zh_Hans: 提取时的提示词
- human_description:
- en_US: The prompt to use for the extraction without a schema.
- zh_Hans: 用于无schema时提取的提示词
- form: form
diff --git a/api/core/tools/provider/builtin/gaode/_assets/icon.svg b/api/core/tools/provider/builtin/gaode/_assets/icon.svg
deleted file mode 100644
index 0f5729e17aea8d..00000000000000
--- a/api/core/tools/provider/builtin/gaode/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/gaode/gaode.py b/api/core/tools/provider/builtin/gaode/gaode.py
deleted file mode 100644
index 49a8e537fb9070..00000000000000
--- a/api/core/tools/provider/builtin/gaode/gaode.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import urllib.parse
-
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GaodeProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- if "api_key" not in credentials or not credentials.get("api_key"):
- raise ToolProviderCredentialValidationError("Gaode API key is required.")
-
- try:
- response = requests.get(
- url="https://restapi.amap.com/v3/geocode/geo?address={address}&key={apikey}".format(
- address=urllib.parse.quote("广东省广州市天河区广州塔"), apikey=credentials.get("api_key")
- )
- )
- if response.status_code == 200 and (response.json()).get("info") == "OK":
- pass
- else:
- raise ToolProviderCredentialValidationError((response.json()).get("info"))
- except Exception as e:
- raise ToolProviderCredentialValidationError("Gaode API Key is invalid. {}".format(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/gaode/gaode.yaml b/api/core/tools/provider/builtin/gaode/gaode.yaml
deleted file mode 100644
index 2eb3b161a29915..00000000000000
--- a/api/core/tools/provider/builtin/gaode/gaode.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-identity:
- author: CharlieWei
- name: gaode
- label:
- en_US: Autonavi
- zh_Hans: 高德
- pt_BR: Autonavi
- description:
- en_US: Autonavi Open Platform service toolkit.
- zh_Hans: 高德开放平台服务工具包。
- pt_BR: Kit de ferramentas de serviço Autonavi Open Platform.
- icon: icon.svg
- tags:
- - utilities
- - productivity
- - travel
- - weather
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- zh_Hans: API Key
- pt_BR: Fogo a chave
- placeholder:
- en_US: Please enter your Autonavi API Key
- zh_Hans: 请输入你的高德开放平台 API Key
- pt_BR: Insira sua chave de API Autonavi
- help:
- en_US: Get your API Key from Autonavi
- zh_Hans: 从高德获取您的 API Key
- pt_BR: Obtenha sua chave de API do Autonavi
- url: https://console.amap.com/dev/key/app
diff --git a/api/core/tools/provider/builtin/gaode/tools/gaode_weather.py b/api/core/tools/provider/builtin/gaode/tools/gaode_weather.py
deleted file mode 100644
index 4642415e6dd394..00000000000000
--- a/api/core/tools/provider/builtin/gaode/tools/gaode_weather.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GaodeRepositoriesTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- city = tool_parameters.get("city", "")
- if not city:
- return self.create_text_message("Please tell me your city")
-
- if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
- return self.create_text_message("Gaode API key is required.")
-
- try:
- s = requests.session()
- api_domain = "https://restapi.amap.com/v3"
- city_response = s.request(
- method="GET",
- headers={"Content-Type": "application/json; charset=utf-8"},
- url="{url}/config/district?keywords={keywords}&subdistrict=0&extensions=base&key={apikey}".format(
- url=api_domain, keywords=city, apikey=self.runtime.credentials.get("api_key")
- ),
- )
- City_data = city_response.json()
- if city_response.status_code == 200 and City_data.get("info") == "OK":
- if len(City_data.get("districts")) > 0:
- CityCode = City_data["districts"][0]["adcode"]
- weatherInfo_response = s.request(
- method="GET",
- url="{url}/weather/weatherInfo?city={citycode}&extensions=all&key={apikey}&output=json".format(
- url=api_domain, citycode=CityCode, apikey=self.runtime.credentials.get("api_key")
- ),
- )
- weatherInfo_data = weatherInfo_response.json()
- if weatherInfo_response.status_code == 200 and weatherInfo_data.get("info") == "OK":
- contents = []
- if len(weatherInfo_data.get("forecasts")) > 0:
- for item in weatherInfo_data["forecasts"][0]["casts"]:
- content = {}
- content["date"] = item.get("date")
- content["week"] = item.get("week")
- content["dayweather"] = item.get("dayweather")
- content["daytemp_float"] = item.get("daytemp_float")
- content["daywind"] = item.get("daywind")
- content["nightweather"] = item.get("nightweather")
- content["nighttemp_float"] = item.get("nighttemp_float")
- contents.append(content)
- s.close()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False))
- )
- s.close()
- return self.create_text_message(f"No weather information for {city} was found.")
- except Exception as e:
- return self.create_text_message("Gaode API Key and Api Version is invalid. {}".format(e))
diff --git a/api/core/tools/provider/builtin/gaode/tools/gaode_weather.yaml b/api/core/tools/provider/builtin/gaode/tools/gaode_weather.yaml
deleted file mode 100644
index e41851e188edee..00000000000000
--- a/api/core/tools/provider/builtin/gaode/tools/gaode_weather.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-identity:
- name: gaode_weather
- author: CharlieWei
- label:
- en_US: Weather Forecast
- zh_Hans: 天气预报
- pt_BR: Previsão do tempo
- icon: icon.svg
-description:
- human:
- en_US: Weather forecast inquiry
- zh_Hans: 天气预报查询。
- pt_BR: Inquérito sobre previsão meteorológica.
- llm: A tool when you want to ask about the weather or weather-related question.
-parameters:
- - name: city
- type: string
- required: true
- label:
- en_US: city
- zh_Hans: 城市
- pt_BR: cidade
- human_description:
- en_US: Target city for weather forecast query.
- zh_Hans: 天气预报查询的目标城市。
- pt_BR: Cidade de destino para consulta de previsão do tempo.
- llm_description: If you don't know you can extract the city name from the question or you can reply:Please tell me your city. You have to extract the Chinese city name from the question.
- form: llm
diff --git a/api/core/tools/provider/builtin/getimgai/_assets/icon.svg b/api/core/tools/provider/builtin/getimgai/_assets/icon.svg
deleted file mode 100644
index 6b2513386da458..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/getimgai/getimgai.py b/api/core/tools/provider/builtin/getimgai/getimgai.py
deleted file mode 100644
index bbd07d120fd0ea..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/getimgai.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.getimgai.tools.text2image import Text2ImageTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GetImgAIProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- # Example validation using the text2image tool
- Text2ImageTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
- user_id="",
- tool_parameters={
- "prompt": "A fire egg",
- "response_format": "url",
- "style": "photorealism",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/getimgai/getimgai.yaml b/api/core/tools/provider/builtin/getimgai/getimgai.yaml
deleted file mode 100644
index c9db0a9e22a6c4..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/getimgai.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-identity:
- author: Matri Qi
- name: getimgai
- label:
- en_US: getimg.ai
- zh_CN: getimg.ai
- description:
- en_US: GetImg API integration for image generation and scraping.
- icon: icon.svg
- tags:
- - image
-credentials_for_provider:
- getimg_api_key:
- type: secret-input
- required: true
- label:
- en_US: getimg.ai API Key
- placeholder:
- en_US: Please input your getimg.ai API key
- help:
- en_US: Get your getimg.ai API key from your getimg.ai account settings. If you are using a self-hosted version, you may enter any key at your convenience.
- url: https://dashboard.getimg.ai/api-keys
- base_url:
- type: text-input
- required: false
- label:
- en_US: getimg.ai server's Base URL
- placeholder:
- en_US: https://api.getimg.ai/v1
diff --git a/api/core/tools/provider/builtin/getimgai/getimgai_appx.py b/api/core/tools/provider/builtin/getimgai/getimgai_appx.py
deleted file mode 100644
index 0e95a5f654505f..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/getimgai_appx.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import logging
-import time
-from collections.abc import Mapping
-from typing import Any
-
-import requests
-from requests.exceptions import HTTPError
-
-logger = logging.getLogger(__name__)
-
-
-class GetImgAIApp:
- def __init__(self, api_key: str | None = None, base_url: str | None = None):
- self.api_key = api_key
- self.base_url = base_url or "https://api.getimg.ai/v1"
- if not self.api_key:
- raise ValueError("API key is required")
-
- def _prepare_headers(self):
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"}
- return headers
-
- def _request(
- self,
- method: str,
- url: str,
- data: Mapping[str, Any] | None = None,
- headers: Mapping[str, str] | None = None,
- retries: int = 3,
- backoff_factor: float = 0.3,
- ) -> Mapping[str, Any] | None:
- for i in range(retries):
- try:
- response = requests.request(method, url, json=data, headers=headers)
- response.raise_for_status()
- return response.json()
- except requests.exceptions.RequestException as e:
- if i < retries - 1 and isinstance(e, HTTPError) and e.response.status_code >= 500:
- time.sleep(backoff_factor * (2**i))
- else:
- raise
- return None
-
- def text2image(self, mode: str, **kwargs):
- data = kwargs["params"]
- if not data.get("prompt"):
- raise ValueError("Prompt is required")
-
- endpoint = f"{self.base_url}/{mode}/text-to-image"
- headers = self._prepare_headers()
- logger.debug(f"Send request to {endpoint=} body={data}")
- response = self._request("POST", endpoint, data, headers)
- if response is None:
- raise HTTPError("Failed to initiate getimg.ai after multiple retries")
- return response
diff --git a/api/core/tools/provider/builtin/getimgai/tools/text2image.py b/api/core/tools/provider/builtin/getimgai/tools/text2image.py
deleted file mode 100644
index c556749552c8ef..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/tools/text2image.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import json
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.getimgai.getimgai_appx import GetImgAIApp
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class Text2ImageTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- app = GetImgAIApp(
- api_key=self.runtime.credentials["getimg_api_key"], base_url=self.runtime.credentials["base_url"]
- )
-
- options = {
- "style": tool_parameters.get("style"),
- "prompt": tool_parameters.get("prompt"),
- "aspect_ratio": tool_parameters.get("aspect_ratio"),
- "output_format": tool_parameters.get("output_format", "jpeg"),
- "response_format": tool_parameters.get("response_format", "url"),
- "width": tool_parameters.get("width"),
- "height": tool_parameters.get("height"),
- "steps": tool_parameters.get("steps"),
- "negative_prompt": tool_parameters.get("negative_prompt"),
- "prompt_2": tool_parameters.get("prompt_2"),
- }
- options = {k: v for k, v in options.items() if v}
-
- text2image_result = app.text2image(mode=tool_parameters.get("mode", "essential-v2"), params=options, wait=True)
-
- if not isinstance(text2image_result, str):
- text2image_result = json.dumps(text2image_result, ensure_ascii=False, indent=4)
-
- if not text2image_result:
- return self.create_text_message("getimg.ai request failed.")
-
- return self.create_text_message(text2image_result)
diff --git a/api/core/tools/provider/builtin/getimgai/tools/text2image.yaml b/api/core/tools/provider/builtin/getimgai/tools/text2image.yaml
deleted file mode 100644
index d972186f56d6a6..00000000000000
--- a/api/core/tools/provider/builtin/getimgai/tools/text2image.yaml
+++ /dev/null
@@ -1,167 +0,0 @@
-identity:
- name: text2image
- author: Matri Qi
- label:
- en_US: text2image
- icon: icon.svg
-description:
- human:
- en_US: Generate image via getimg.ai.
- llm: This tool is used to generate image from prompt or image via https://getimg.ai.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: prompt
- human_description:
- en_US: The text prompt used to generate the image. The getimg.aier will generate an image based on this prompt.
- llm_description: this prompt text will be used to generate image.
- form: llm
- - name: mode
- type: select
- required: false
- label:
- en_US: mode
- human_description:
- en_US: The getimg.ai mode to use. The mode determines the endpoint used to generate the image.
- form: form
- options:
- - value: "essential-v2"
- label:
- en_US: essential-v2
- - value: stable-diffusion-xl
- label:
- en_US: stable-diffusion-xl
- - value: stable-diffusion
- label:
- en_US: stable-diffusion
- - value: latent-consistency
- label:
- en_US: latent-consistency
- - name: style
- type: select
- required: false
- label:
- en_US: style
- human_description:
- en_US: The style preset to use. The style preset guides the generation towards a particular style. It's just efficient for `Essential V2` mode.
- form: form
- options:
- - value: photorealism
- label:
- en_US: photorealism
- - value: anime
- label:
- en_US: anime
- - value: art
- label:
- en_US: art
- - name: aspect_ratio
- type: select
- required: false
- label:
- en_US: "aspect ratio"
- human_description:
- en_US: The aspect ratio of the generated image. It's just efficient for `Essential V2` mode.
- form: form
- options:
- - value: "1:1"
- label:
- en_US: "1:1"
- - value: "4:5"
- label:
- en_US: "4:5"
- - value: "5:4"
- label:
- en_US: "5:4"
- - value: "2:3"
- label:
- en_US: "2:3"
- - value: "3:2"
- label:
- en_US: "3:2"
- - value: "4:7"
- label:
- en_US: "4:7"
- - value: "7:4"
- label:
- en_US: "7:4"
- - name: output_format
- type: select
- required: false
- label:
- en_US: "output format"
- human_description:
- en_US: The file format of the generated image.
- form: form
- options:
- - value: jpeg
- label:
- en_US: jpeg
- - value: png
- label:
- en_US: png
- - name: response_format
- type: select
- required: false
- label:
- en_US: "response format"
- human_description:
- en_US: The format in which the generated images are returned. Must be one of url or b64. URLs are only valid for 1 hour after the image has been generated.
- form: form
- options:
- - value: url
- label:
- en_US: url
- - value: b64
- label:
- en_US: b64
- - name: model
- type: string
- required: false
- label:
- en_US: model
- human_description:
- en_US: Model ID supported by this pipeline and family. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode.
- form: form
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: negative prompt
- human_description:
- en_US: Text input that will not guide the image generation. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode.
- form: form
- - name: prompt_2
- type: string
- required: false
- label:
- en_US: prompt2
- human_description:
- en_US: Prompt sent to second tokenizer and text encoder. If not defined, prompt is used in both text-encoders. It's just efficient for `Stable Diffusion XL` mode.
- form: form
- - name: width
- type: number
- required: false
- label:
- en_US: width
- human_description:
- en_US: he width of the generated image in pixels. Width needs to be multiple of 64.
- form: form
- - name: height
- type: number
- required: false
- label:
- en_US: height
- human_description:
- en_US: he height of the generated image in pixels. Height needs to be multiple of 64.
- form: form
- - name: steps
- type: number
- required: false
- label:
- en_US: steps
- human_description:
- en_US: The number of denoising steps. More steps usually can produce higher quality images, but take more time to generate. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode.
- form: form
diff --git a/api/core/tools/provider/builtin/gitee_ai/_assets/icon.svg b/api/core/tools/provider/builtin/gitee_ai/_assets/icon.svg
deleted file mode 100644
index 6dd75d1a6b5b44..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/_assets/icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.py b/api/core/tools/provider/builtin/gitee_ai/gitee_ai.py
deleted file mode 100644
index 151cafec14b2b7..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GiteeAIProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- url = "https://ai.gitee.com/api/base/account/me"
- headers = {
- "accept": "application/json",
- "authorization": f"Bearer {credentials.get('api_key')}",
- }
-
- response = requests.get(url, headers=headers)
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("GiteeAI API key is invalid")
diff --git a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml b/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml
deleted file mode 100644
index d0475665dd7ac7..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-identity:
- author: Gitee AI
- name: gitee_ai
- label:
- en_US: Gitee AI
- zh_Hans: Gitee AI
- description:
- en_US: Quickly experience large models and explore the leading AI open source world
- zh_Hans: 快速体验大模型,领先探索 AI 开源世界
- icon: icon.svg
- tags:
- - image
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- placeholder:
- zh_Hans: 在此输入您的 API Key
- en_US: Enter your API Key
- url: https://ai.gitee.com/dashboard/settings/tokens
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/embedding.py b/api/core/tools/provider/builtin/gitee_ai/tools/embedding.py
deleted file mode 100644
index ab03759c1966ad..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/embedding.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GiteeAIToolEmbedding(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "content-type": "application/json",
- "authorization": f"Bearer {self.runtime.credentials['api_key']}",
- }
-
- payload = {"inputs": tool_parameters.get("inputs")}
- model = tool_parameters.get("model", "bge-m3")
- url = f"https://ai.gitee.com/api/serverless/{model}/embeddings"
- response = requests.post(url, json=payload, headers=headers)
- if response.status_code != 200:
- return self.create_text_message(f"Got Error Response:{response.text}")
-
- return [self.create_text_message(response.content.decode("utf-8"))]
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/embedding.yaml b/api/core/tools/provider/builtin/gitee_ai/tools/embedding.yaml
deleted file mode 100644
index 53e569d731d072..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/embedding.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-identity:
- name: embedding
- author: gitee_ai
- label:
- en_US: embedding
- icon: icon.svg
-description:
- human:
- en_US: Generate word embeddings using Serverless-supported models (compatible with OpenAI)
- llm: This tool is used to generate word embeddings from text input.
-parameters:
- - name: model
- type: string
- required: true
- in: path
- description:
- en_US: Supported Embedding (compatible with OpenAI) interface models
- enum:
- - bge-m3
- - bge-large-zh-v1.5
- - bge-small-zh-v1.5
- label:
- en_US: Service Model
- zh_Hans: 服务模型
- default: bge-m3
- form: form
- - name: inputs
- type: string
- required: true
- label:
- en_US: Input Text
- zh_Hans: 输入文本
- human_description:
- en_US: The text input used to generate embeddings.
- zh_Hans: 用于生成词向量的输入文本。
- llm_description: This text input will be used to generate embeddings.
- form: llm
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.py b/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.py
deleted file mode 100644
index e3558ce69915be..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GiteeAIToolRiskControl(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "content-type": "application/json",
- "authorization": f"Bearer {self.runtime.credentials['api_key']}",
- }
-
- inputs = [{"type": "text", "text": tool_parameters.get("input-text")}]
- model = tool_parameters.get("model", "Security-semantic-filtering")
- payload = {"model": model, "input": inputs}
- url = "https://ai.gitee.com/v1/moderations"
- response = requests.post(url, json=payload, headers=headers)
- if response.status_code != 200:
- return self.create_text_message(f"Got Error Response:{response.text}")
-
- return [self.create_text_message(response.content.decode("utf-8"))]
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.yaml b/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.yaml
deleted file mode 100644
index 6e7229dc1c54d5..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/risk-control.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- name: risk control
- author: gitee_ai
- label:
- en_US: risk control identification
- zh_Hans: 风控识别
- icon: icon.svg
-description:
- human:
- en_US: Ensuring the protection and compliance of sensitive information through the filtering and analysis of data semantics
- zh_Hans: 通过对数据语义的过滤和分析,确保敏感信息的保护和合规性
- llm: This tool is used to risk control identification.
-parameters:
- - name: model
- type: string
- required: true
- default: Security-semantic-filtering
- label:
- en_US: Service Model
- zh_Hans: 服务模型
- form: form
- - name: input-text
- type: string
- required: true
- label:
- en_US: Input Text
- zh_Hans: 输入文本
- human_description:
- en_US: The text input for filtering and analysis.
- zh_Hans: 用于分析过滤的文本
- llm_description: The text input for filtering and analysis.
- form: llm
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.py b/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.py
deleted file mode 100644
index bb0b2c915b333c..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GiteeAIToolText2Image(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "content-type": "application/json",
- "authorization": f"Bearer {self.runtime.credentials['api_key']}",
- }
-
- payload = {
- "inputs": tool_parameters.get("inputs"),
- "width": tool_parameters.get("width", "720"),
- "height": tool_parameters.get("height", "720"),
- }
- model = tool_parameters.get("model", "Kolors")
- url = f"https://ai.gitee.com/api/serverless/{model}/text-to-image"
-
- response = requests.post(url, json=payload, headers=headers)
- if response.status_code != 200:
- return self.create_text_message(f"Got Error Response:{response.text}")
-
- # The returned image is base64 and needs to be mark as an image
- result = [self.create_blob_message(blob=response.content, meta={"mime_type": "image/jpeg"})]
-
- return result
diff --git a/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.yaml b/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.yaml
deleted file mode 100644
index 5e03f9abe9dfe4..00000000000000
--- a/api/core/tools/provider/builtin/gitee_ai/tools/text-to-image.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: text to image
- author: gitee_ai
- label:
- en_US: text to image
- icon: icon.svg
-description:
- human:
- en_US: generate images using a variety of popular models
- llm: This tool is used to generate image from text.
-parameters:
- - name: model
- type: select
- required: true
- options:
- - value: flux-1-schnell
- label:
- en_US: flux-1-schnell
- - value: Kolors
- label:
- en_US: Kolors
- - value: stable-diffusion-3-medium
- label:
- en_US: stable-diffusion-3-medium
- - value: stable-diffusion-xl-base-1.0
- label:
- en_US: stable-diffusion-xl-base-1.0
- - value: stable-diffusion-v1-4
- label:
- en_US: stable-diffusion-v1-4
- default: Kolors
- label:
- en_US: Choose Image Model
- zh_Hans: 选择生成图片的模型
- form: form
- - name: inputs
- type: string
- required: true
- label:
- en_US: Input Text
- zh_Hans: 输入文本
- human_description:
- en_US: The text input used to generate the image.
- zh_Hans: 用于生成图片的输入文本。
- llm_description: This text input will be used to generate image.
- form: llm
- - name: width
- type: number
- required: true
- default: 720
- min: 1
- max: 1024
- label:
- en_US: Image Width
- zh_Hans: 图片宽度
- human_description:
- en_US: The width of the generated image.
- zh_Hans: 生成图片的宽度。
- form: form
- - name: height
- type: number
- required: true
- default: 720
- min: 1
- max: 1024
- label:
- en_US: Image Height
- zh_Hans: 图片高度
- human_description:
- en_US: The height of the generated image.
- zh_Hans: 生成图片的高度。
- form: form
diff --git a/api/core/tools/provider/builtin/github/_assets/icon.svg b/api/core/tools/provider/builtin/github/_assets/icon.svg
deleted file mode 100644
index d56adb2c2f9955..00000000000000
--- a/api/core/tools/provider/builtin/github/_assets/icon.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- github [#142]
- Created with Sketch.
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/github/github.py b/api/core/tools/provider/builtin/github/github.py
deleted file mode 100644
index 87a34ac3e806ea..00000000000000
--- a/api/core/tools/provider/builtin/github/github.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GithubProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- if "access_tokens" not in credentials or not credentials.get("access_tokens"):
- raise ToolProviderCredentialValidationError("Github API Access Tokens is required.")
- if "api_version" not in credentials or not credentials.get("api_version"):
- api_version = "2022-11-28"
- else:
- api_version = credentials.get("api_version")
-
- try:
- headers = {
- "Content-Type": "application/vnd.github+json",
- "Authorization": f"Bearer {credentials.get('access_tokens')}",
- "X-GitHub-Api-Version": api_version,
- }
-
- response = requests.get(
- url="https://api.github.com/search/users?q={account}".format(account="charli117"), headers=headers
- )
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError((response.json()).get("message"))
- except Exception as e:
- raise ToolProviderCredentialValidationError("Github API Key and Api Version is invalid. {}".format(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/github/github.yaml b/api/core/tools/provider/builtin/github/github.yaml
deleted file mode 100644
index c3d85fc3f69cf7..00000000000000
--- a/api/core/tools/provider/builtin/github/github.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-identity:
- author: CharlieWei
- name: github
- label:
- en_US: Github
- zh_Hans: Github
- pt_BR: Github
- description:
- en_US: GitHub is an online software source code hosting service.
- zh_Hans: GitHub是一个在线软件源代码托管服务平台。
- pt_BR: GitHub é uma plataforma online para serviços de hospedagem de código fonte de software.
- icon: icon.svg
- tags:
- - utilities
-credentials_for_provider:
- access_tokens:
- type: secret-input
- required: true
- label:
- en_US: Access Tokens
- zh_Hans: Access Tokens
- pt_BR: Tokens de acesso
- placeholder:
- en_US: Please input your Github Access Tokens
- zh_Hans: 请输入你的 Github Access Tokens
- pt_BR: Insira seus Tokens de Acesso do Github
- help:
- en_US: Get your Access Tokens from Github
- zh_Hans: 从 Github 获取您的 Access Tokens
- pt_BR: Obtenha sua chave da API do Google no Google
- url: https://github.com/settings/tokens?type=beta
- api_version:
- type: text-input
- required: false
- default: '2022-11-28'
- label:
- en_US: API Version
- zh_Hans: API Version
- pt_BR: Versão da API
- placeholder:
- en_US: Please input your Github API Version
- zh_Hans: 请输入你的 Github API Version
- pt_BR: Insira sua versão da API do Github
- help:
- en_US: Get your API Version from Github
- zh_Hans: 从 Github 获取您的 API Version
- pt_BR: Obtenha sua versão da API do Github
- url: https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28
diff --git a/api/core/tools/provider/builtin/github/tools/github_repositories.py b/api/core/tools/provider/builtin/github/tools/github_repositories.py
deleted file mode 100644
index 32f9922e651785..00000000000000
--- a/api/core/tools/provider/builtin/github/tools/github_repositories.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import json
-from datetime import datetime
-from typing import Any, Union
-from urllib.parse import quote
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GithubRepositoriesTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- top_n = tool_parameters.get("top_n", 5)
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Please input symbol")
-
- if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
- return self.create_text_message("Github API Access Tokens is required.")
- if "api_version" not in self.runtime.credentials or not self.runtime.credentials.get("api_version"):
- api_version = "2022-11-28"
- else:
- api_version = self.runtime.credentials.get("api_version")
-
- try:
- headers = {
- "Content-Type": "application/vnd.github+json",
- "Authorization": f"Bearer {self.runtime.credentials.get('access_tokens')}",
- "X-GitHub-Api-Version": api_version,
- }
- s = requests.session()
- api_domain = "https://api.github.com"
- response = s.request(
- method="GET",
- headers=headers,
- url=f"{api_domain}/search/repositories?q={quote(query)}&sort=stars&per_page={top_n}&order=desc",
- )
- response_data = response.json()
- if response.status_code == 200 and isinstance(response_data.get("items"), list):
- contents = []
- if len(response_data.get("items")) > 0:
- for item in response_data.get("items"):
- content = {}
- updated_at_object = datetime.strptime(item["updated_at"], "%Y-%m-%dT%H:%M:%SZ")
- content["owner"] = item["owner"]["login"]
- content["name"] = item["name"]
- content["description"] = (
- item["description"][:100] + "..." if len(item["description"]) > 100 else item["description"]
- )
- content["url"] = item["html_url"]
- content["star"] = item["watchers"]
- content["forks"] = item["forks"]
- content["updated"] = updated_at_object.strftime("%Y-%m-%d")
- contents.append(content)
- s.close()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False))
- )
- else:
- return self.create_text_message(f"No items related to {query} were found.")
- else:
- return self.create_text_message((response.json()).get("message"))
- except Exception as e:
- return self.create_text_message("Github API Key and Api Version is invalid. {}".format(e))
diff --git a/api/core/tools/provider/builtin/github/tools/github_repositories.yaml b/api/core/tools/provider/builtin/github/tools/github_repositories.yaml
deleted file mode 100644
index c170aee797fe4d..00000000000000
--- a/api/core/tools/provider/builtin/github/tools/github_repositories.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-identity:
- name: github_repositories
- author: CharlieWei
- label:
- en_US: Search Repositories
- zh_Hans: 仓库搜索
- pt_BR: Pesquisar Repositórios
- icon: icon.svg
-description:
- human:
- en_US: Search the Github repository to retrieve the open source projects you need
- zh_Hans: 搜索Github仓库,检索你需要的开源项目。
- pt_BR: Pesquise o repositório do Github para recuperar os projetos de código aberto necessários.
- llm: A tool when you wants to search for popular warehouses or open source projects for any keyword. format query condition like "keywords+language:js", language can be other dev languages.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: query
- zh_Hans: 关键字
- pt_BR: consulta
- human_description:
- en_US: You want to find the project development language, keywords, For example. Find 10 Python developed PDF document parsing projects.
- zh_Hans: 你想要找的项目开发语言、关键字,如:找10个Python开发的PDF文档解析项目。
- pt_BR: Você deseja encontrar a linguagem de desenvolvimento do projeto, palavras-chave, Por exemplo. Encontre 10 projetos de análise de documentos PDF desenvolvidos em Python.
- llm_description: The query of you want to search, format query condition like "keywords+language:js", language can be other dev languages.
- form: llm
- - name: top_n
- type: number
- default: 5
- required: true
- label:
- en_US: Top N
- zh_Hans: Top N
- pt_BR: Topo N
- human_description:
- en_US: Number of records returned by sorting based on stars. 5 is returned by default.
- zh_Hans: 基于stars排序返回的记录数, 默认返回5条。
- pt_BR: Número de registros retornados por classificação com base em estrelas. 5 é retornado por padrão.
- llm_description: Extract the first N records from the returned result.
- form: llm
diff --git a/api/core/tools/provider/builtin/gitlab/_assets/gitlab.svg b/api/core/tools/provider/builtin/gitlab/_assets/gitlab.svg
deleted file mode 100644
index 07734077d5d300..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/_assets/gitlab.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/gitlab/gitlab.py b/api/core/tools/provider/builtin/gitlab/gitlab.py
deleted file mode 100644
index 9bd4a0bd52ea64..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/gitlab.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GitlabProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- if "access_tokens" not in credentials or not credentials.get("access_tokens"):
- raise ToolProviderCredentialValidationError("Gitlab Access Tokens is required.")
-
- if "site_url" not in credentials or not credentials.get("site_url"):
- site_url = "https://gitlab.com"
- else:
- site_url = credentials.get("site_url")
-
- try:
- headers = {
- "Content-Type": "application/vnd.text+json",
- "Authorization": f"Bearer {credentials.get('access_tokens')}",
- }
-
- response = requests.get(url=f"{site_url}/api/v4/user", headers=headers)
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError((response.json()).get("message"))
- except Exception as e:
- raise ToolProviderCredentialValidationError("Gitlab Access Tokens is invalid. {}".format(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/gitlab/gitlab.yaml b/api/core/tools/provider/builtin/gitlab/gitlab.yaml
deleted file mode 100644
index 22d7ebf73ac2aa..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/gitlab.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-identity:
- author: Leo.Wang
- name: gitlab
- label:
- en_US: GitLab
- zh_Hans: GitLab
- description:
- en_US: GitLab plugin, API v4 only.
- zh_Hans: 用于获取GitLab内容的插件,目前仅支持 API v4。
- icon: gitlab.svg
-credentials_for_provider:
- access_tokens:
- type: secret-input
- required: true
- label:
- en_US: GitLab access token
- zh_Hans: GitLab access token
- placeholder:
- en_US: Please input your GitLab access token
- zh_Hans: 请输入你的 GitLab access token
- help:
- en_US: Get your GitLab access token from GitLab
- zh_Hans: 从 GitLab 获取您的 access token
- url: https://docs.gitlab.com/16.9/ee/api/oauth2.html
- site_url:
- type: text-input
- required: false
- default: 'https://gitlab.com'
- label:
- en_US: GitLab site url
- zh_Hans: GitLab site url
- placeholder:
- en_US: Please input your GitLab site url
- zh_Hans: 请输入你的 GitLab site url
- help:
- en_US: Find your GitLab url
- zh_Hans: 找到你的 GitLab url
- url: https://gitlab.com/help
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.py b/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.py
deleted file mode 100644
index 716da7c8c110f3..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.py
+++ /dev/null
@@ -1,134 +0,0 @@
-import json
-import urllib.parse
-from datetime import datetime, timedelta
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GitlabCommitsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- branch = tool_parameters.get("branch", "")
- repository = tool_parameters.get("repository", "")
- employee = tool_parameters.get("employee", "")
- start_time = tool_parameters.get("start_time", "")
- end_time = tool_parameters.get("end_time", "")
- change_type = tool_parameters.get("change_type", "all")
-
- if not repository:
- return self.create_text_message("Either repository is required")
-
- if not start_time:
- start_time = (datetime.utcnow() - timedelta(days=1)).isoformat()
- if not end_time:
- end_time = datetime.utcnow().isoformat()
-
- access_token = self.runtime.credentials.get("access_tokens")
- site_url = self.runtime.credentials.get("site_url")
-
- if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
- return self.create_text_message("Gitlab API Access Tokens is required.")
- if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
- site_url = "https://gitlab.com"
-
- # Get commit content
- result = self.fetch_commits(
- site_url, access_token, repository, branch, employee, start_time, end_time, change_type, is_repository=True
- )
-
- return [self.create_json_message(item) for item in result]
-
- def fetch_commits(
- self,
- site_url: str,
- access_token: str,
- repository: str,
- branch: str,
- employee: str,
- start_time: str,
- end_time: str,
- change_type: str,
- is_repository: bool,
- ) -> list[dict[str, Any]]:
- domain = site_url
- headers = {"PRIVATE-TOKEN": access_token}
- results = []
-
- try:
- # URL encode the repository path
- encoded_repository = urllib.parse.quote(repository, safe="")
- commits_url = f"{domain}/api/v4/projects/{encoded_repository}/repository/commits"
-
- # Fetch commits for the repository
- params = {"since": start_time, "until": end_time}
- if branch:
- params["ref_name"] = branch
- if employee:
- params["author"] = employee
-
- commits_response = requests.get(commits_url, headers=headers, params=params)
- commits_response.raise_for_status()
- commits = commits_response.json()
-
- for commit in commits:
- commit_sha = commit["id"]
- author_name = commit["author_name"]
-
- diff_url = f"{domain}/api/v4/projects/{encoded_repository}/repository/commits/{commit_sha}/diff"
-
- diff_response = requests.get(diff_url, headers=headers)
- diff_response.raise_for_status()
- diffs = diff_response.json()
-
- for diff in diffs:
- # Calculate code lines of changes
- added_lines = diff["diff"].count("\n+")
- removed_lines = diff["diff"].count("\n-")
- total_changes = added_lines + removed_lines
-
- if change_type == "new":
- if added_lines > 1:
- final_code = "".join(
- [
- line[1:]
- for line in diff["diff"].split("\n")
- if line.startswith("+") and not line.startswith("+++")
- ]
- )
- results.append(
- {
- "diff_url": diff_url,
- "commit_sha": commit_sha,
- "author_name": author_name,
- "diff": final_code,
- }
- )
- else:
- if total_changes > 1:
- final_code = "".join(
- [
- line[1:]
- for line in diff["diff"].split("\n")
- if (line.startswith("+") or line.startswith("-"))
- and not line.startswith("+++")
- and not line.startswith("---")
- ]
- )
- final_code_escaped = json.dumps(final_code)[1:-1] # Escape the final code
- results.append(
- {
- "diff_url": diff_url,
- "commit_sha": commit_sha,
- "author_name": author_name,
- "diff": final_code_escaped,
- }
- )
- except requests.RequestException as e:
- print(f"Error fetching data from GitLab: {e}")
-
- return results
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.yaml b/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.yaml
deleted file mode 100644
index 2ff5fb570ecc42..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_commits.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
-identity:
- name: gitlab_commits
- author: Leo.Wang
- label:
- en_US: GitLab Commits
- zh_Hans: GitLab 提交内容查询
-description:
- human:
- en_US: A tool for query GitLab commits, Input should be a exists username or project.
- zh_Hans: 一个用于查询 GitLab 代码提交内容的工具,输入的内容应该是一个已存在的用户名或者项目名。
- llm: A tool for query GitLab commits, Input should be a exists username or project.
-parameters:
- - name: username
- type: string
- required: false
- label:
- en_US: username
- zh_Hans: 员工用户名
- human_description:
- en_US: username
- zh_Hans: 员工用户名
- llm_description: User name for GitLab
- form: llm
- - name: repository
- type: string
- required: true
- label:
- en_US: repository
- zh_Hans: 仓库路径
- human_description:
- en_US: repository
- zh_Hans: 仓库路径,以namespace/project_name的形式。
- llm_description: Repository path for GitLab, like namespace/project_name.
- form: llm
- - name: branch
- type: string
- required: false
- label:
- en_US: branch
- zh_Hans: 分支名
- human_description:
- en_US: branch
- zh_Hans: 分支名
- llm_description: branch for GitLab
- form: llm
- - name: start_time
- type: string
- required: false
- label:
- en_US: start_time
- zh_Hans: 开始时间
- human_description:
- en_US: start_time
- zh_Hans: 开始时间
- llm_description: Start time for GitLab
- form: llm
- - name: end_time
- type: string
- required: false
- label:
- en_US: end_time
- zh_Hans: 结束时间
- human_description:
- en_US: end_time
- zh_Hans: 结束时间
- llm_description: End time for GitLab
- form: llm
- - name: change_type
- type: select
- required: false
- options:
- - value: all
- label:
- en_US: all
- zh_Hans: 所有
- - value: new
- label:
- en_US: new
- zh_Hans: 新增
- default: all
- label:
- en_US: change_type
- zh_Hans: 变更类型
- human_description:
- en_US: change_type
- zh_Hans: 变更类型
- llm_description: Content change type for GitLab
- form: llm
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.py b/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.py
deleted file mode 100644
index 0ac9e2777dfe2c..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import urllib.parse
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GitlabFilesTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- repository = tool_parameters.get("repository", "")
- project = tool_parameters.get("project", "")
- branch = tool_parameters.get("branch", "")
- path = tool_parameters.get("path", "")
- file_path = tool_parameters.get("file_path", "")
-
- if not repository and not project:
- return self.create_text_message("Either repository or project is required")
- if not branch:
- return self.create_text_message("Branch is required")
- if not path and not file_path:
- return self.create_text_message("Either path or file_path is required")
-
- access_token = self.runtime.credentials.get("access_tokens")
- headers = {"PRIVATE-TOKEN": access_token}
- site_url = self.runtime.credentials.get("site_url")
-
- if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
- return self.create_text_message("Gitlab API Access Tokens is required.")
- if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
- site_url = "https://gitlab.com"
-
- if repository:
- # URL encode the repository path
- identifier = urllib.parse.quote(repository, safe="")
- else:
- identifier = self.get_project_id(site_url, access_token, project)
- if not identifier:
- raise Exception(f"Project '{project}' not found.)")
-
- # Get file content
- if path:
- results = self.fetch_files(site_url, headers, identifier, branch, path)
- return [self.create_json_message(item) for item in results]
- else:
- result = self.fetch_file(site_url, headers, identifier, branch, file_path)
- return [self.create_json_message(result)]
-
- @staticmethod
- def fetch_file(
- site_url: str,
- headers: dict[str, str],
- identifier: str,
- branch: str,
- path: str,
- ) -> dict[str, Any]:
- encoded_file_path = urllib.parse.quote(path, safe="")
- file_url = f"{site_url}/api/v4/projects/{identifier}/repository/files/{encoded_file_path}/raw?ref={branch}"
-
- file_response = requests.get(file_url, headers=headers)
- file_response.raise_for_status()
- file_content = file_response.text
- return {"path": path, "branch": branch, "content": file_content}
-
- def fetch_files(
- self, site_url: str, headers: dict[str, str], identifier: str, branch: str, path: str
- ) -> list[dict[str, Any]]:
- results = []
-
- try:
- tree_url = f"{site_url}/api/v4/projects/{identifier}/repository/tree?path={path}&ref={branch}"
- response = requests.get(tree_url, headers=headers)
- response.raise_for_status()
- items = response.json()
-
- for item in items:
- item_path = item["path"]
- if item["type"] == "tree": # It's a directory
- results.extend(self.fetch_files(site_url, headers, identifier, branch, item_path))
- else: # It's a file
- result = self.fetch_file(site_url, headers, identifier, branch, item_path)
- results.append(result)
- except requests.RequestException as e:
- print(f"Error fetching data from GitLab: {e}")
-
- return results
-
- def get_project_id(self, site_url: str, access_token: str, project_name: str) -> Union[str, None]:
- headers = {"PRIVATE-TOKEN": access_token}
- try:
- url = f"{site_url}/api/v4/projects?search={project_name}"
- response = requests.get(url, headers=headers)
- response.raise_for_status()
- projects = response.json()
- for project in projects:
- if project["name"] == project_name:
- return project["id"]
- except requests.RequestException as e:
- print(f"Error fetching project ID from GitLab: {e}")
- return None
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.yaml b/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.yaml
deleted file mode 100644
index 3371f62fa8d98c..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_files.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-identity:
- name: gitlab_files
- author: Leo.Wang
- label:
- en_US: GitLab Files
- zh_Hans: GitLab 文件获取
-description:
- human:
- en_US: A tool for query GitLab files, Input should be branch and a exists file or directory path.
- zh_Hans: 一个用于查询 GitLab 文件的工具,输入的内容应该是分支和一个已存在文件或者文件夹路径。
- llm: A tool for query GitLab files, Input should be a exists file or directory path.
-parameters:
- - name: repository
- type: string
- required: false
- label:
- en_US: repository
- zh_Hans: 仓库路径
- human_description:
- en_US: repository
- zh_Hans: 仓库路径,以namespace/project_name的形式。
- llm_description: Repository path for GitLab, like namespace/project_name.
- form: llm
- - name: project
- type: string
- required: false
- label:
- en_US: project
- zh_Hans: 项目
- human_description:
- en_US: project
- zh_Hans: 项目(和仓库路径二选一,都填写以仓库路径优先)
- llm_description: Project for GitLab
- form: llm
- - name: branch
- type: string
- required: true
- label:
- en_US: branch
- zh_Hans: 分支
- human_description:
- en_US: branch
- zh_Hans: 分支
- llm_description: Branch for GitLab
- form: llm
- - name: path
- type: string
- label:
- en_US: path
- zh_Hans: 文件夹
- human_description:
- en_US: path
- zh_Hans: 文件夹
- llm_description: Dir path for GitLab
- form: llm
- - name: file_path
- type: string
- label:
- en_US: file_path
- zh_Hans: 文件路径
- human_description:
- en_US: file_path
- zh_Hans: 文件路径(和文件夹二选一,都填写以文件夹优先)
- llm_description: File path for GitLab
- form: llm
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.py b/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.py
deleted file mode 100644
index ef99fa82e9d9d6..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import urllib.parse
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GitlabMergeRequestsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- repository = tool_parameters.get("repository", "")
- branch = tool_parameters.get("branch", "")
- start_time = tool_parameters.get("start_time", "")
- end_time = tool_parameters.get("end_time", "")
- state = tool_parameters.get("state", "opened") # Default to "opened"
-
- if not repository:
- return self.create_text_message("Repository is required")
-
- access_token = self.runtime.credentials.get("access_tokens")
- site_url = self.runtime.credentials.get("site_url")
-
- if not access_token:
- return self.create_text_message("Gitlab API Access Tokens is required.")
- if not site_url:
- site_url = "https://gitlab.com"
-
- # Get merge requests
- result = self.get_merge_requests(site_url, access_token, repository, branch, start_time, end_time, state)
-
- return [self.create_json_message(item) for item in result]
-
- def get_merge_requests(
- self, site_url: str, access_token: str, repository: str, branch: str, start_time: str, end_time: str, state: str
- ) -> list[dict[str, Any]]:
- domain = site_url
- headers = {"PRIVATE-TOKEN": access_token}
- results = []
-
- try:
- # URL encode the repository path
- encoded_repository = urllib.parse.quote(repository, safe="")
- merge_requests_url = f"{domain}/api/v4/projects/{encoded_repository}/merge_requests"
- params = {"state": state}
-
- # Add time filters if provided
- if start_time:
- params["created_after"] = start_time
- if end_time:
- params["created_before"] = end_time
-
- response = requests.get(merge_requests_url, headers=headers, params=params)
- response.raise_for_status()
- merge_requests = response.json()
-
- for mr in merge_requests:
- # Filter by target branch
- if branch and mr["target_branch"] != branch:
- continue
-
- results.append(
- {
- "id": mr["id"],
- "title": mr["title"],
- "author": mr["author"]["name"],
- "web_url": mr["web_url"],
- "target_branch": mr["target_branch"],
- "created_at": mr["created_at"],
- "state": mr["state"],
- }
- )
- except requests.RequestException as e:
- print(f"Error fetching merge requests from GitLab: {e}")
-
- return results
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.yaml b/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.yaml
deleted file mode 100644
index 81adb3db7d932d..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_mergerequests.yaml
+++ /dev/null
@@ -1,77 +0,0 @@
-identity:
- name: gitlab_mergerequests
- author: Leo.Wang
- label:
- en_US: GitLab Merge Requests
- zh_Hans: GitLab 合并请求查询
-description:
- human:
- en_US: A tool for query GitLab merge requests, Input should be a exists repository or branch.
- zh_Hans: 一个用于查询 GitLab 代码合并请求的工具,输入的内容应该是一个已存在的仓库名或者分支。
- llm: A tool for query GitLab merge requests, Input should be a exists repository or branch.
-parameters:
- - name: repository
- type: string
- required: false
- label:
- en_US: repository
- zh_Hans: 仓库路径
- human_description:
- en_US: repository
- zh_Hans: 仓库路径,以namespace/project_name的形式。
- llm_description: Repository path for GitLab, like namespace/project_name.
- form: llm
- - name: branch
- type: string
- required: false
- label:
- en_US: branch
- zh_Hans: 分支名
- human_description:
- en_US: branch
- zh_Hans: 分支名
- llm_description: branch for GitLab
- form: llm
- - name: start_time
- type: string
- required: false
- label:
- en_US: start_time
- zh_Hans: 开始时间
- human_description:
- en_US: start_time
- zh_Hans: 开始时间
- llm_description: Start time for GitLab
- form: llm
- - name: end_time
- type: string
- required: false
- label:
- en_US: end_time
- zh_Hans: 结束时间
- human_description:
- en_US: end_time
- zh_Hans: 结束时间
- llm_description: End time for GitLab
- form: llm
- - name: state
- type: select
- required: false
- options:
- - value: opened
- label:
- en_US: opened
- zh_Hans: 打开
- - value: closed
- label:
- en_US: closed
- zh_Hans: 关闭
- default: opened
- label:
- en_US: state
- zh_Hans: 变更状态
- human_description:
- en_US: state
- zh_Hans: 变更状态
- llm_description: Merge request state type for GitLab
- form: llm
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.py b/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.py
deleted file mode 100644
index ea0c028b4f3d07..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import urllib.parse
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GitlabProjectsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- project_name = tool_parameters.get("project_name", "")
- page = tool_parameters.get("page", 1)
- page_size = tool_parameters.get("page_size", 20)
-
- access_token = self.runtime.credentials.get("access_tokens")
- site_url = self.runtime.credentials.get("site_url")
-
- if not access_token:
- return self.create_text_message("Gitlab API Access Tokens is required.")
- if not site_url:
- site_url = "https://gitlab.com"
-
- # Get project content
- result = self.fetch_projects(site_url, access_token, project_name, page, page_size)
-
- return [self.create_json_message(item) for item in result]
-
- def fetch_projects(
- self,
- site_url: str,
- access_token: str,
- project_name: str,
- page: str,
- page_size: str,
- ) -> list[dict[str, Any]]:
- domain = site_url
- headers = {"PRIVATE-TOKEN": access_token}
- results = []
-
- try:
- if project_name:
- # URL encode the project name for the search query
- encoded_project_name = urllib.parse.quote(project_name, safe="")
- projects_url = (
- f"{domain}/api/v4/projects?search={encoded_project_name}&page={page}&per_page={page_size}"
- )
- else:
- projects_url = f"{domain}/api/v4/projects?page={page}&per_page={page_size}"
-
- response = requests.get(projects_url, headers=headers)
- response.raise_for_status()
- projects = response.json()
-
- for project in projects:
- # Filter projects by exact name match if necessary
- if project_name and project["name"].lower() == project_name.lower():
- results.append(
- {
- "id": project["id"],
- "name": project["name"],
- "description": project.get("description", ""),
- "web_url": project["web_url"],
- }
- )
- elif not project_name:
- # If no specific project name is provided, add all projects
- results.append(
- {
- "id": project["id"],
- "name": project["name"],
- "description": project.get("description", ""),
- "web_url": project["web_url"],
- }
- )
- except requests.RequestException as e:
- print(f"Error fetching data from GitLab: {e}")
-
- return results
diff --git a/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.yaml b/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.yaml
deleted file mode 100644
index 5fe098e1f7a647..00000000000000
--- a/api/core/tools/provider/builtin/gitlab/tools/gitlab_projects.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-identity:
- name: gitlab_projects
- author: Leo.Wang
- label:
- en_US: GitLab Projects
- zh_Hans: GitLab 项目列表查询
-description:
- human:
- en_US: A tool for query GitLab projects, Input should be a project name.
- zh_Hans: 一个用于查询 GitLab 项目列表的工具,输入的内容应该是一个项目名称。
- llm: A tool for query GitLab projects, Input should be a project name.
-parameters:
- - name: project_name
- type: string
- required: false
- label:
- en_US: project_name
- zh_Hans: 项目名称
- human_description:
- en_US: project_name
- zh_Hans: 项目名称
- llm_description: Project name for GitLab
- form: llm
- - name: page
- type: string
- required: false
- label:
- en_US: page
- zh_Hans: 页码
- human_description:
- en_US: page
- zh_Hans: 页码
- llm_description: Page index for GitLab
- form: llm
- - name: page_size
- type: string
- required: false
- label:
- en_US: page_size
- zh_Hans: 每页数量
- human_description:
- en_US: page_size
- zh_Hans: 每页数量
- llm_description: Page size for GitLab
- form: llm
diff --git a/api/core/tools/provider/builtin/google/_assets/icon.svg b/api/core/tools/provider/builtin/google/_assets/icon.svg
deleted file mode 100644
index bebbf52d3a23a4..00000000000000
--- a/api/core/tools/provider/builtin/google/_assets/icon.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/google/google.py b/api/core/tools/provider/builtin/google/google.py
deleted file mode 100644
index 6b5395f9d3e5b8..00000000000000
--- a/api/core/tools/provider/builtin/google/google.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.google.tools.google_search import GoogleSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GoogleProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- GoogleSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"query": "test", "result_type": "link"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/google/google.yaml b/api/core/tools/provider/builtin/google/google.yaml
deleted file mode 100644
index afb4d5b2145ba6..00000000000000
--- a/api/core/tools/provider/builtin/google/google.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- author: Dify
- name: google
- label:
- en_US: Google
- zh_Hans: Google
- pt_BR: Google
- description:
- en_US: Google
- zh_Hans: GoogleSearch
- pt_BR: Google
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- serpapi_api_key:
- type: secret-input
- required: true
- label:
- en_US: SerpApi API key
- zh_Hans: SerpApi API key
- pt_BR: SerpApi API key
- placeholder:
- en_US: Please input your SerpApi API key
- zh_Hans: 请输入你的 SerpApi API key
- pt_BR: Please input your SerpApi API key
- help:
- en_US: Get your SerpApi API key from SerpApi
- zh_Hans: 从 SerpApi 获取您的 SerpApi API key
- pt_BR: Get your SerpApi API key from SerpApi
- url: https://serpapi.com/manage-api-key
diff --git a/api/core/tools/provider/builtin/google/tools/google_search.py b/api/core/tools/provider/builtin/google/tools/google_search.py
deleted file mode 100644
index a9f65925d86f94..00000000000000
--- a/api/core/tools/provider/builtin/google/tools/google_search.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SERP_API_URL = "https://serpapi.com/search"
-
-
-class GoogleSearchTool(BuiltinTool):
- def _parse_response(self, response: dict) -> dict:
- result = {}
- if "knowledge_graph" in response:
- result["title"] = response["knowledge_graph"].get("title", "")
- result["description"] = response["knowledge_graph"].get("description", "")
- if "organic_results" in response:
- result["organic_results"] = [
- {"title": item.get("title", ""), "link": item.get("link", ""), "snippet": item.get("snippet", "")}
- for item in response["organic_results"]
- ]
- return result
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- params = {
- "api_key": self.runtime.credentials["serpapi_api_key"],
- "q": tool_parameters["query"],
- "engine": "google",
- "google_domain": "google.com",
- "gl": "us",
- "hl": "en",
- }
- response = requests.get(url=SERP_API_URL, params=params)
- response.raise_for_status()
- valuable_res = self._parse_response(response.json())
- return self.create_json_message(valuable_res)
diff --git a/api/core/tools/provider/builtin/google/tools/google_search.yaml b/api/core/tools/provider/builtin/google/tools/google_search.yaml
deleted file mode 100644
index 72db3839eb022a..00000000000000
--- a/api/core/tools/provider/builtin/google/tools/google_search.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: google_search
- author: Dify
- label:
- en_US: GoogleSearch
- zh_Hans: 谷歌搜索
- pt_BR: GoogleSearch
-description:
- human:
- en_US: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
- zh_Hans: 一个用于执行 Google SERP 搜索并提取片段和网页的工具。输入应该是一个搜索查询。
- pt_BR: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
- llm: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: used for searching
- zh_Hans: 用于搜索网页内容
- pt_BR: used for searching
- llm_description: key words for searching
- form: llm
diff --git a/api/core/tools/provider/builtin/google_translate/_assets/icon.svg b/api/core/tools/provider/builtin/google_translate/_assets/icon.svg
deleted file mode 100644
index de69a9c5e58316..00000000000000
--- a/api/core/tools/provider/builtin/google_translate/_assets/icon.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/google_translate/google_translate.py b/api/core/tools/provider/builtin/google_translate/google_translate.py
deleted file mode 100644
index ea53aa4eeb906f..00000000000000
--- a/api/core/tools/provider/builtin/google_translate/google_translate.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.google_translate.tools.translate import GoogleTranslate
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class JsonExtractProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- GoogleTranslate().invoke(user_id="", tool_parameters={"content": "这是一段测试文本", "dest": "en"})
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/google_translate/google_translate.yaml b/api/core/tools/provider/builtin/google_translate/google_translate.yaml
deleted file mode 100644
index 8bc821a3d5e9fa..00000000000000
--- a/api/core/tools/provider/builtin/google_translate/google_translate.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-identity:
- author: Ron Liu
- name: google_translate
- label:
- en_US: Google Translate
- zh_Hans: 谷歌翻译
- description:
- en_US: Translate text using Google
- zh_Hans: 使用 Google 进行翻译
- icon: icon.svg
- tags:
- - utilities
diff --git a/api/core/tools/provider/builtin/google_translate/tools/translate.py b/api/core/tools/provider/builtin/google_translate/tools/translate.py
deleted file mode 100644
index ea3f2077d5d485..00000000000000
--- a/api/core/tools/provider/builtin/google_translate/tools/translate.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GoogleTranslate(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- dest = tool_parameters.get("dest", "")
- if not dest:
- return self.create_text_message("Invalid parameter destination language")
-
- try:
- result = self._translate(content, dest)
- return self.create_text_message(str(result))
- except Exception:
- return self.create_text_message("Translation service error, please check the network")
-
- def _translate(self, content: str, dest: str) -> str:
- try:
- url = "https://translate.googleapis.com/translate_a/single"
- params = {"client": "gtx", "sl": "auto", "tl": dest, "dt": "t", "q": content}
-
- headers = {
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
- " Chrome/91.0.4472.124 Safari/537.36"
- }
-
- response_json = requests.get(url, params=params, headers=headers).json()
- result = response_json[0]
- translated_text = "".join([item[0] for item in result if item[0]])
- return str(translated_text)
- except Exception as e:
- return str(e)
diff --git a/api/core/tools/provider/builtin/google_translate/tools/translate.yaml b/api/core/tools/provider/builtin/google_translate/tools/translate.yaml
deleted file mode 100644
index a4189cd7439ad7..00000000000000
--- a/api/core/tools/provider/builtin/google_translate/tools/translate.yaml
+++ /dev/null
@@ -1,215 +0,0 @@
-identity:
- name: translate
- author: Ron Liu
- label:
- en_US: Translate
- zh_Hans: 翻译
-description:
- human:
- en_US: A tool for Google Translate
- zh_Hans: Google 翻译
- llm: A tool for Google Translate
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: Text content
- zh_Hans: 文本内容
- human_description:
- en_US: Text content
- zh_Hans: 需要翻译的文本内容
- llm_description: Text content
- form: llm
- - name: dest
- type: select
- required: true
- label:
- en_US: destination language
- zh_Hans: 目标语言
- human_description:
- en_US: The destination language you want to translate.
- zh_Hans: 你想翻译的目标语言
- default: en
- form: form
- options:
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
diff --git a/api/core/tools/provider/builtin/hap/_assets/icon.svg b/api/core/tools/provider/builtin/hap/_assets/icon.svg
deleted file mode 100644
index 0fa6f0886fdfdb..00000000000000
--- a/api/core/tools/provider/builtin/hap/_assets/icon.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/hap/hap.py b/api/core/tools/provider/builtin/hap/hap.py
deleted file mode 100644
index cbdf9504659568..00000000000000
--- a/api/core/tools/provider/builtin/hap/hap.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class HapProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- pass
diff --git a/api/core/tools/provider/builtin/hap/hap.yaml b/api/core/tools/provider/builtin/hap/hap.yaml
deleted file mode 100644
index 25b473cf9dd211..00000000000000
--- a/api/core/tools/provider/builtin/hap/hap.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: Mingdao
- name: hap
- label:
- en_US: HAP
- zh_Hans: HAP
- pt_BR: HAP
- description:
- en_US: "Hyper application platform that is particularly friendly to AI"
- zh_Hans: "对 AI 特别友好的超级应用平台"
- pt_BR: "Plataforma de aplicação hiper que é particularmente amigável à IA"
- icon: icon.svg
- tags:
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.py b/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.py
deleted file mode 100644
index 597adc91db9768..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import json
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class AddWorksheetRecordTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
- record_data = tool_parameters.get("record_data", "")
- if not record_data:
- return self.create_text_message("Invalid parameter Record Row Data")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not host.startswith(("http://", "https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url = f"{host}/v2/open/worksheet/addRow"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
-
- try:
- payload["controls"] = json.loads(record_data)
- res = httpx.post(url, headers=headers, json=payload, timeout=60)
- res.raise_for_status()
- res_json = res.json()
- if res_json.get("error_code") != 1:
- return self.create_text_message(f"Failed to add the new record. {res_json['error_msg']}")
- return self.create_text_message(f"New record added successfully. The record ID is {res_json['data']}.")
- except httpx.RequestError as e:
- return self.create_text_message(f"Failed to add the new record, request error: {e}")
- except json.JSONDecodeError as e:
- return self.create_text_message(f"Failed to parse JSON response: {e}")
- except Exception as e:
- return self.create_text_message(f"Failed to add the new record, unexpected error: {e}")
diff --git a/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.yaml b/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.yaml
deleted file mode 100644
index add7742cd74db1..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/add_worksheet_record.yaml
+++ /dev/null
@@ -1,78 +0,0 @@
-identity:
- name: add_worksheet_record
- author: Ryan Tian
- label:
- en_US: Add Worksheet Record
- zh_Hans: 新增一条工作表记录
-description:
- human:
- en_US: Adds a new record to the specified worksheet
- zh_Hans: 向指定的工作表新增一条记录数据
- llm: A tool to append a new data entry into a specified worksheet.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the specified worksheet
- zh_Hans: 要获取字段信息的工作表 ID
- llm_description: The ID of the specified worksheet which to get the fields information.
- form: llm
-
- - name: record_data
- type: string
- required: true
- label:
- en_US: Record Row Data
- zh_Hans: 记录数据
- human_description:
- en_US: The fields with data of the specified record
- zh_Hans: 要新增的记录数据,JSON 对象数组格式。数组元素属性:controlId-字段ID,value-字段值
- llm_description: |
- The fields with data of the specified record which to be created. It is in the format of an array of JSON objects, and the structure is defined as follows:
- ```
- type RowData = {
- controlId: string; // Field ID to be updated
- value: string; // Field value to be updated
- }[];
- ```
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.py b/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.py
deleted file mode 100644
index 5d42af4c490598..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DeleteWorksheetRecordTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
- row_id = tool_parameters.get("row_id", "")
- if not row_id:
- return self.create_text_message("Invalid parameter Record Row ID")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not host.startswith(("http://", "https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url = f"{host}/v2/open/worksheet/deleteRow"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
-
- try:
- res = httpx.post(url, headers=headers, json=payload, timeout=30)
- res.raise_for_status()
- res_json = res.json()
- if res_json.get("error_code") != 1:
- return self.create_text_message(f"Failed to delete the record. {res_json['error_msg']}")
- return self.create_text_message("Successfully deleted the record.")
- except httpx.RequestError as e:
- return self.create_text_message(f"Failed to delete the record, request error: {e}")
- except Exception as e:
- return self.create_text_message(f"Failed to delete the record, unexpected error: {e}")
diff --git a/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.yaml b/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.yaml
deleted file mode 100644
index 7c0c2a6439003f..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/delete_worksheet_record.yaml
+++ /dev/null
@@ -1,71 +0,0 @@
-identity:
- name: delete_worksheet_record
- author: Ryan Tian
- label:
- en_US: Delete Worksheet Record
- zh_Hans: 删除指定的一条工作表记录
-description:
- human:
- en_US: Deletes a single record from a worksheet based on the specified record row ID
- zh_Hans: 根据指定的记录ID删除一条工作表记录数据
- llm: A tool to remove a particular record from a worksheet by specifying its unique record identifier.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the specified worksheet
- zh_Hans: 要获取字段信息的工作表 ID
- llm_description: The ID of the specified worksheet which to get the fields information.
- form: llm
-
- - name: row_id
- type: string
- required: true
- label:
- en_US: Record Row ID
- zh_Hans: 记录 ID
- human_description:
- en_US: The row ID of the specified record
- zh_Hans: 要删除的记录 ID
- llm_description: The row ID of the specified record which to be deleted.
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.py b/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.py
deleted file mode 100644
index 6887b8b4e99df6..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import json
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetWorksheetFieldsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not host.startswith(("http://", "https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url = f"{host}/v2/open/worksheet/getWorksheetInfo"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
-
- try:
- res = httpx.post(url, headers=headers, json=payload, timeout=60)
- res.raise_for_status()
- res_json = res.json()
- if res_json.get("error_code") != 1:
- return self.create_text_message(f"Failed to get the worksheet information. {res_json['error_msg']}")
-
- fields_json, fields_table = self.get_controls(res_json["data"]["controls"])
- result_type = tool_parameters.get("result_type", "table")
- return self.create_text_message(
- text=json.dumps(fields_json, ensure_ascii=False) if result_type == "json" else fields_table
- )
- except httpx.RequestError as e:
- return self.create_text_message(f"Failed to get the worksheet information, request error: {e}")
- except json.JSONDecodeError as e:
- return self.create_text_message(f"Failed to parse JSON response: {e}")
- except Exception as e:
- return self.create_text_message(f"Failed to get the worksheet information, unexpected error: {e}")
-
- def get_field_type_by_id(self, field_type_id: int) -> str:
- field_type_map = {
- 2: "Text",
- 3: "Text-Phone",
- 4: "Text-Phone",
- 5: "Text-Email",
- 6: "Number",
- 7: "Text",
- 8: "Number",
- 9: "Option-Single Choice",
- 10: "Option-Multiple Choices",
- 11: "Option-Single Choice",
- 15: "Date",
- 16: "Date",
- 24: "Option-Region",
- 25: "Text",
- 26: "Option-Member",
- 27: "Option-Department",
- 28: "Number",
- 29: "Option-Linked Record",
- 30: "Unknown Type",
- 31: "Number",
- 32: "Text",
- 33: "Text",
- 35: "Option-Linked Record",
- 36: "Number-Yes1/No0",
- 37: "Number",
- 38: "Date",
- 40: "Location",
- 41: "Text",
- 46: "Time",
- 48: "Option-Organizational Role",
- 50: "Text",
- 51: "Query Record",
- }
- return field_type_map.get(field_type_id, "")
-
- def get_controls(self, controls: list) -> dict:
- fields = []
- fields_list = ["|fieldId|fieldName|fieldType|fieldTypeId|description|options|", "|" + "---|" * 6]
- for control in controls:
- if control["type"] in self._get_ignore_types():
- continue
- field_type_id = control["type"]
- field_type = self.get_field_type_by_id(control["type"])
- if field_type_id == 30:
- source_type = control["sourceControl"]["type"]
- if source_type in self._get_ignore_types():
- continue
- else:
- field_type_id = source_type
- field_type = self.get_field_type_by_id(source_type)
- field = {
- "id": control["controlId"],
- "name": control["controlName"],
- "type": field_type,
- "typeId": field_type_id,
- "description": control["remark"].replace("\n", " ").replace("\t", " "),
- "options": self._extract_options(control),
- }
- fields.append(field)
- fields_list.append(
- f"|{field['id']}|{field['name']}|{field['type']}|{field['typeId']}|{field['description']}"
- f"|{field['options'] or ''}|"
- )
-
- fields.append(
- {
- "id": "ctime",
- "name": "Created Time",
- "type": self.get_field_type_by_id(16),
- "typeId": 16,
- "description": "",
- "options": [],
- }
- )
- fields_list.append("|ctime|Created Time|Date|16|||")
- return fields, "\n".join(fields_list)
-
- def _extract_options(self, control: dict) -> list:
- options = []
- if control["type"] in {9, 10, 11}:
- options.extend([{"key": opt["key"], "value": opt["value"]} for opt in control.get("options", [])])
- elif control["type"] in {28, 36}:
- itemnames = control["advancedSetting"].get("itemnames")
- if itemnames and itemnames.startswith("[{"):
- try:
- options = json.loads(itemnames)
- except json.JSONDecodeError:
- pass
- elif control["type"] == 30:
- source_type = control["sourceControl"]["type"]
- if source_type not in self._get_ignore_types():
- options.extend([{"key": opt["key"], "value": opt["value"]} for opt in control.get("options", [])])
- return options
-
- def _get_ignore_types(self):
- return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
diff --git a/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.yaml b/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.yaml
deleted file mode 100644
index f0d4973e8549f7..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/get_worksheet_fields.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-identity:
- name: get_worksheet_fields
- author: Ryan Tian
- label:
- en_US: Get Worksheet Fields
- zh_Hans: 获取工作表字段结构
-description:
- human:
- en_US: Get fields information of the worksheet
- zh_Hans: 获取指定工作表的所有字段结构信息
- llm: A tool to get fields information of the specific worksheet.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the specified worksheet
- zh_Hans: 要获取字段信息的工作表 ID
- llm_description: The ID of the specified worksheet which to get the fields information.
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
-
- - name: result_type
- type: select
- required: true
- options:
- - value: table
- label:
- en_US: table text
- zh_Hans: 表格文本
- - value: json
- label:
- en_US: json text
- zh_Hans: JSON文本
- default: table
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, table styled text or json text
- zh_Hans: 用于选择结果类型,使用表格格式文本还是JSON格式文本
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.py b/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.py
deleted file mode 100644
index 26d7116869b6d9..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.py
+++ /dev/null
@@ -1,137 +0,0 @@
-import json
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetWorksheetPivotDataTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
- x_column_fields = tool_parameters.get("x_column_fields", "")
- if not x_column_fields or not x_column_fields.startswith("["):
- return self.create_text_message("Invalid parameter Column Fields")
- y_row_fields = tool_parameters.get("y_row_fields", "")
- if y_row_fields and not y_row_fields.strip().startswith("["):
- return self.create_text_message("Invalid parameter Row Fields")
- elif not y_row_fields:
- y_row_fields = "[]"
- value_fields = tool_parameters.get("value_fields", "")
- if not value_fields or not value_fields.strip().startswith("["):
- return self.create_text_message("Invalid parameter Value Fields")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not host.startswith(("http://", "https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url = f"{host}/report/getPivotData"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "options": {"showTotal": True}}
-
- try:
- x_column_fields = json.loads(x_column_fields)
- payload["columns"] = x_column_fields
- y_row_fields = json.loads(y_row_fields)
- if y_row_fields:
- payload["rows"] = y_row_fields
- value_fields = json.loads(value_fields)
- payload["values"] = value_fields
- sort_fields = tool_parameters.get("sort_fields", "")
- if not sort_fields:
- sort_fields = "[]"
- sort_fields = json.loads(sort_fields)
- if sort_fields:
- payload["options"]["sort"] = sort_fields
- res = httpx.post(url, headers=headers, json=payload, timeout=60)
- res.raise_for_status()
- res_json = res.json()
- if res_json.get("status") != 1:
- return self.create_text_message(f"Failed to get the worksheet pivot data. {res_json['msg']}")
-
- pivot_json = self.generate_pivot_json(res_json["data"])
- pivot_table = self.generate_pivot_table(res_json["data"])
- result_type = tool_parameters.get("result_type", "")
- text = pivot_table if result_type == "table" else json.dumps(pivot_json, ensure_ascii=False)
- return self.create_text_message(text)
- except httpx.RequestError as e:
- return self.create_text_message(f"Failed to get the worksheet pivot data, request error: {e}")
- except json.JSONDecodeError as e:
- return self.create_text_message(f"Failed to parse JSON response: {e}")
- except Exception as e:
- return self.create_text_message(f"Failed to get the worksheet pivot data, unexpected error: {e}")
-
- def generate_pivot_table(self, data: dict[str, Any]) -> str:
- columns = data["metadata"]["columns"]
- rows = data["metadata"]["rows"]
- values = data["metadata"]["values"]
-
- rows_data = data["data"]
-
- header = (
- ([row["displayName"] for row in rows] if rows else [])
- + [column["displayName"] for column in columns]
- + [value["displayName"] for value in values]
- )
- line = (["---"] * len(rows) if rows else []) + ["---"] * len(columns) + ["--:"] * len(values)
-
- table = [header, line]
- for row in rows_data:
- row_data = [self.replace_pipe(row["rows"][r["controlId"]]) for r in rows] if rows else []
- row_data.extend([self.replace_pipe(row["columns"][column["controlId"]]) for column in columns])
- row_data.extend([self.replace_pipe(str(row["values"][value["controlId"]])) for value in values])
- table.append(row_data)
-
- return "\n".join([("|" + "|".join(row) + "|") for row in table])
-
- def replace_pipe(self, text: str) -> str:
- return text.replace("|", "▏").replace("\n", " ")
-
- def generate_pivot_json(self, data: dict[str, Any]) -> dict:
- fields = {
- "x-axis": [
- {"fieldId": column["controlId"], "fieldName": column["displayName"]}
- for column in data["metadata"]["columns"]
- ],
- "y-axis": [
- {"fieldId": row["controlId"], "fieldName": row["displayName"]} for row in data["metadata"]["rows"]
- ]
- if data["metadata"]["rows"]
- else [],
- "values": [
- {"fieldId": value["controlId"], "fieldName": value["displayName"]}
- for value in data["metadata"]["values"]
- ],
- }
- # fields = ([
- # {"fieldId": row["controlId"], "fieldName": row["displayName"]}
- # for row in data["metadata"]["rows"]
- # ] if data["metadata"]["rows"] else []) + [
- # {"fieldId": column["controlId"], "fieldName": column["displayName"]}
- # for column in data["metadata"]["columns"]
- # ] + [
- # {"fieldId": value["controlId"], "fieldName": value["displayName"]}
- # for value in data["metadata"]["values"]
- # ]
- rows = []
- for row in data["data"]:
- row_data = row["rows"] or {}
- row_data.update(row["columns"])
- row_data.update(row["values"])
- rows.append(row_data)
- return {"fields": fields, "rows": rows, "summary": data["metadata"]["totalRow"]}
diff --git a/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.yaml b/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.yaml
deleted file mode 100644
index cf8c57b26208a9..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/get_worksheet_pivot_data.yaml
+++ /dev/null
@@ -1,248 +0,0 @@
-identity:
- name: get_worksheet_pivot_data
- author: Ryan Tian
- label:
- en_US: Get Worksheet Pivot Data
- zh_Hans: 获取工作表统计透视数据
-description:
- human:
- en_US: Retrieve statistical pivot table data from a specified worksheet
- zh_Hans: 从指定的工作表中检索统计透视表数据
- llm: A tool for extracting statistical pivot table data from a specific worksheet, providing summarized information for analysis and reporting purposes.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the specified worksheet
- zh_Hans: 要获取字段信息的工作表 ID
- llm_description: The ID of the specified worksheet which to get the fields information.
- form: llm
-
- - name: x_column_fields
- type: string
- required: true
- label:
- en_US: Columns (X-axis)
- zh_Hans: 统计列字段(X轴)
- human_description:
- en_US: The column fields that make up the pivot table's X-axis groups or other dimensions for the X-axis in pivot charts
- zh_Hans: 组成透视表的统计列或者统计图表的X轴分组及X轴其它维度。JSON 对象数组格式,数组元素属性:controlId-列ID,displayName-显示名称,particleSize(可选)-字段类型是日期或者地区时,通过此参数设置统计维度(日期时间:1-日,2-周,3-月;地区:1-全国,2-省,3-市)
- llm_description: |
- This parameter allows you to specify the columns that make up the pivot table's X-axis groups or other dimensions for the X-axis in pivot charts. It is formatted as a JSON array, with its structure defined as follows:
- ```
- type XColumnFields = { // X-axis or column object array
- controlId: string; // fieldId
- displayName: string; // displayName
- particleSize?: number; // field type is date or area, set the statistical dimension (date time: 1-day, 2-week, 3-month; area: 1-nation, 2-province, 3-city)
- }[];
- ```
- form: llm
-
- - name: y_row_fields
- type: string
- required: false
- label:
- en_US: Rows (Y-axis)
- zh_Hans: 统计行字段(Y轴)
- human_description:
- en_US: The row fields that make up the pivot table's Y-axis groups or other dimensions for the Y-axis in pivot charts
- zh_Hans: 组成透视表的统计行或者统计图表的Y轴分组及Y轴其它维度。JSON 对象数组格式,数组元素属性:controlId-列ID,displayName-显示名称,particleSize(可选)-字段类型是日期或者地区时,通过此参数设置统计维度(日期时间:1-日,2-周,3-月;地区:1-全国,2-省,3-市)
- llm_description: |
- This parameter allows you to specify the rows that make up the pivot table's Y-axis groups or other dimensions for the Y-axis in pivot charts. It is formatted as a JSON array, with its structure defined as follows:
- ```
- type YRowFields = { // Y-axis or row object array
- controlId: string; // fieldId
- displayName: string; // displayName
- particleSize?: number; // field type is date or area, set the statistical dimension (date time: 1-day, 2-week, 3-month; area: 1-nation, 2-province, 3-city)
- }[];
- ```
- form: llm
-
- - name: value_fields
- type: string
- required: true
- label:
- en_US: Aggregated Values
- zh_Hans: 统计值字段
- human_description:
- en_US: The aggregated value fields in the pivot table
- zh_Hans: 透视表中经过聚合计算后的统计值字段。JSON 对象数组格式,数组元素属性:controlId-列ID,displayName-显示名称,aggregation-聚合方式(SUM,AVG,MIN,MAX,COUNT)
- llm_description: |
- This parameter allows you to specify the aggregated value fields in the pivot table. It is formatted as a JSON array, with its structure defined as follows:
- ```
- type ValueFields = { // aggregated value object array
- controlId: string; // fieldId
- displayName: string; // displayName
- aggregation: string; // aggregation method, e.g.: SUM, AVG, MIN, MAX, COUNT
- }[];
- ```
- form: llm
-
- - name: filters
- type: string
- required: false
- label:
- en_US: Filter Set
- zh_Hans: 筛选器组合
- human_description:
- en_US: A combination of filters applied to query records, formatted as a JSON array. See the application's API documentation for details on its structure and usage.
- zh_Hans: 查询记录的筛选条件组合,格式为 JSON 数组,可以从应用 API 文档中了解参数结构详情
- llm_description: |
- This parameter allows you to specify a set of conditions that records must meet to be included in the result set. It is formatted as a JSON array, with its structure defined as follows:
- ```
- type Filters = { // filter object array
- controlId: string; // fieldId
- dataType: number; // fieldTypeId
- spliceType: number; // condition concatenation method, 1: And, 2: Or
- filterType: number; // expression type, refer to the for enumerable values
- values?: string[]; // values in the condition, for option-type fields, multiple values can be passed
- value?: string; // value in the condition, a single value can be passed according to the field type
- dateRange?: number; // date range, mandatory when filterType is 17 or 18, refer to the for enumerable values
- minValue?: string; // minimum value for custom range
- maxValue?: string; // maximum value for custom range
- isAsc?: boolean; // ascending order, false: descending, true: ascending
- }[];
- ```
- For option-type fields, if this option field has `options`, then you need to get the corresponding `key` value from the `options` in the current field information via `value`, and pass it into `values` in array format. Do not use the `options` value of other fields as input conditions.
-
- ### FilterTypeEnum Reference
- ```
- Enum Value, Enum Character, Description
- 1, Like, Contains
- 2, Eq, Is (Equal)
- 3, Start, Starts With
- 4, End, Ends With
- 5, NotLike, Does Not Contain
- 6, Ne, Is Not (Not Equal)
- 7, IsEmpty, Empty
- 8, HasValue, Not Empty
- 11, Between, Within Range
- 12, NotBetween, Outside Range
- 13, Gt, Greater Than
- 14, Gte, Greater Than or Equal To
- 15, Lt, Less Than
- 16, Lte, Less Than or Equal To
- 17, DateEnum, Date Is
- 18, NotDateEnum, Date Is Not
- 21, MySelf, Owned by Me
- 22, UnRead, Unread
- 23, Sub, Owned by Subordinate
- 24, RCEq, Associated Field Is
- 25, RCNe, Associated Field Is Not
- 26, ArrEq, Array Equals
- 27, ArrNe, Array Does Not Equal
- 31, DateBetween, Date Within Range (can only be used with minValue and maxValue)
- 32, DateNotBetween, Date Not Within Range (can only be used with minValue and maxValue)
- 33, DateGt, Date Later Than
- 34, DateGte, Date Later Than or Equal To
- 35, DateLt, Date Earlier Than
- 36, DateLte, Date Earlier Than or Equal To
- ```
-
- ### DateRangeEnum Reference
- ```
- Enum Value, Enum Character, Description
- 1, Today, Today
- 2, Yesterday, Yesterday
- 3, Tomorrow, Tomorrow
- 4, ThisWeek, This Week
- 5, LastWeek, Last Week
- 6, NextWeek, Next Week
- 7, ThisMonth, This Month
- 8, LastMonth, Last Month
- 9, NextMonth, Next Month
- 12, ThisQuarter, This Quarter
- 13, LastQuarter, Last Quarter
- 14, NextQuarter, Next Quarter
- 15, ThisYear, This Year
- 16, LastYear, Last Year
- 17, NextYear, Next Year
- 18, Customize, Custom
- 21, Last7Day, Past 7 Days
- 22, Last14Day, Past 14 Days
- 23, Last30Day, Past 30 Days
- 31, Next7Day, Next 7 Days
- 32, Next14Day, Next 14 Days
- 33, Next33Day, Next 33 Days
- ```
- form: llm
-
- - name: sort_fields
- type: string
- required: false
- label:
- en_US: Sort Fields
- zh_Hans: 排序字段
- human_description:
- en_US: The fields to used for sorting
- zh_Hans: 用于确定排序的字段,不超过3个
- llm_description: |
- This optional parameter specifies the unique identifier of the fields that will be used to sort the results. It is in the format of an array of JSON objects, and its structure is defined as follows:
- ```
- type SortByFields = {
- controlId: string; // Field ID used for sorting
- isAsc: boolean; // Sorting direction, true indicates ascending order, false indicates descending order
- }[];
- ```
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
-
- - name: result_type
- type: select
- required: true
- options:
- - value: table
- label:
- en_US: table text
- zh_Hans: 表格文本
- - value: json
- label:
- en_US: json text
- zh_Hans: JSON文本
- default: table
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, table styled text or json text
- zh_Hans: 用于选择结果类型,使用表格格式文本还是JSON格式文本
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.py b/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.py
deleted file mode 100644
index 9e43d5c532e5b3..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.py
+++ /dev/null
@@ -1,231 +0,0 @@
-import json
-import re
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class ListWorksheetRecordsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
-
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
-
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not (host.startswith("http://") or host.startswith("https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url_fields = f"{host}/v2/open/worksheet/getWorksheetInfo"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
-
- field_ids = tool_parameters.get("field_ids", "")
-
- try:
- res = httpx.post(url_fields, headers=headers, json=payload, timeout=30)
- res_json = res.json()
- if res.is_success:
- if res_json["error_code"] != 1:
- return self.create_text_message(
- "Failed to get the worksheet information. {}".format(res_json["error_msg"])
- )
- else:
- worksheet_name = res_json["data"]["name"]
- fields, schema, table_header = self.get_schema(res_json["data"]["controls"], field_ids)
- else:
- return self.create_text_message(
- f"Failed to get the worksheet information, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message(
- "Failed to get the worksheet information, something went wrong: {}".format(e)
- )
-
- if field_ids:
- payload["controls"] = [v.strip() for v in field_ids.split(",")] if field_ids else []
- filters = tool_parameters.get("filters", "")
- if filters:
- payload["filters"] = json.loads(filters)
- sort_id = tool_parameters.get("sort_id", "")
- sort_is_asc = tool_parameters.get("sort_is_asc", False)
- if sort_id:
- payload["sortId"] = sort_id
- payload["isAsc"] = sort_is_asc
- limit = tool_parameters.get("limit", 50)
- payload["pageSize"] = limit
- page_index = tool_parameters.get("page_index", 1)
- payload["pageIndex"] = page_index
- payload["useControlId"] = True
- payload["listType"] = 1
-
- url = f"{host}/v2/open/worksheet/getFilterRows"
- try:
- res = httpx.post(url, headers=headers, json=payload, timeout=90)
- res_json = res.json()
- if res.is_success:
- if res_json["error_code"] != 1:
- return self.create_text_message("Failed to get the records. {}".format(res_json["error_msg"]))
- else:
- result = {
- "fields": fields,
- "rows": [],
- "total": res_json.get("data", {}).get("total"),
- "payload": {
- key: payload[key]
- for key in [
- "worksheetId",
- "controls",
- "filters",
- "sortId",
- "isAsc",
- "pageSize",
- "pageIndex",
- ]
- if key in payload
- },
- }
- rows = res_json.get("data", {}).get("rows", [])
- result_type = tool_parameters.get("result_type", "")
- if not result_type:
- result_type = "table"
- if result_type == "json":
- for row in rows:
- result["rows"].append(self.get_row_field_value(row, schema))
- return self.create_text_message(json.dumps(result, ensure_ascii=False))
- else:
- result_text = f'Found {result["total"]} rows in worksheet "{worksheet_name}".'
- if result["total"] > 0:
- result_text += (
- f" The following are {min(limit, result['total'])}"
- f" pieces of data presented in a table format:\n\n{table_header}"
- )
- for row in rows:
- result_values = []
- for f in fields:
- result_values.append(
- self.handle_value_type(row[f["fieldId"]], schema[f["fieldId"]])
- )
- result_text += "\n|" + "|".join(result_values) + "|"
- return self.create_text_message(result_text)
- else:
- return self.create_text_message(
- f"Failed to get the records, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to get the records, something went wrong: {}".format(e))
-
- def get_row_field_value(self, row: dict, schema: dict):
- row_value = {"rowid": row["rowid"]}
- for field in schema:
- row_value[field] = self.handle_value_type(row[field], schema[field])
- return row_value
-
- def get_schema(self, controls: list, fieldids: str):
- allow_fields = {v.strip() for v in fieldids.split(",")} if fieldids else set()
- fields = []
- schema = {}
- field_names = []
- for control in controls:
- control_type_id = self.get_real_type_id(control)
- if (control_type_id in self._get_ignore_types()) or (
- allow_fields and control["controlId"] not in allow_fields
- ):
- continue
- else:
- fields.append({"fieldId": control["controlId"], "fieldName": control["controlName"]})
- schema[control["controlId"]] = {"typeId": control_type_id, "options": self.set_option(control)}
- field_names.append(control["controlName"])
- if not allow_fields or ("ctime" in allow_fields):
- fields.append({"fieldId": "ctime", "fieldName": "Created Time"})
- schema["ctime"] = {"typeId": 16, "options": {}}
- field_names.append("Created Time")
- fields.append({"fieldId": "rowid", "fieldName": "Record Row ID"})
- schema["rowid"] = {"typeId": 2, "options": {}}
- field_names.append("Record Row ID")
- return fields, schema, "|" + "|".join(field_names) + "|\n|" + "---|" * len(field_names)
-
- def get_real_type_id(self, control: dict) -> int:
- return control["sourceControlType"] if control["type"] == 30 else control["type"]
-
- def set_option(self, control: dict) -> dict:
- options = {}
- if control.get("options"):
- options = {option["key"]: option["value"] for option in control["options"]}
- elif control.get("advancedSetting", {}).get("itemnames"):
- try:
- itemnames = json.loads(control["advancedSetting"]["itemnames"])
- options = {item["key"]: item["value"] for item in itemnames}
- except json.JSONDecodeError:
- pass
- return options
-
- def _get_ignore_types(self):
- return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
-
- def handle_value_type(self, value, field):
- type_id = field.get("typeId")
- if type_id == 10:
- value = value if isinstance(value, str) else "、".join(value)
- elif type_id in {28, 36}:
- value = field.get("options", {}).get(value, value)
- elif type_id in {26, 27, 48, 14}:
- value = self.process_value(value)
- elif type_id in {35, 29}:
- value = self.parse_cascade_or_associated(field, value)
- elif type_id == 40:
- value = self.parse_location(value)
- return self.rich_text_to_plain_text(value) if value else ""
-
- def process_value(self, value):
- if isinstance(value, str):
- if value.startswith('[{"accountId"'):
- value = json.loads(value)
- value = ", ".join([item["fullname"] for item in value])
- elif value.startswith('[{"departmentId"'):
- value = json.loads(value)
- value = "、".join([item["departmentName"] for item in value])
- elif value.startswith('[{"organizeId"'):
- value = json.loads(value)
- value = "、".join([item["organizeName"] for item in value])
- elif value.startswith('[{"file_id"') or value == "[]":
- value = ""
- elif hasattr(value, "accountId"):
- value = value["fullname"]
- return value
-
- def parse_cascade_or_associated(self, field, value):
- if (field["typeId"] == 35 and value.startswith("[")) or (field["typeId"] == 29 and value.startswith("[{")):
- value = json.loads(value)
- value = value[0]["name"] if len(value) > 0 else ""
- else:
- value = ""
- return value
-
- def parse_location(self, value):
- if len(value) > 10:
- parsed_value = json.loads(value)
- value = parsed_value.get("address", "")
- else:
- value = ""
- return value
-
- def rich_text_to_plain_text(self, rich_text):
- text = re.sub(r"<[^>]+>", "", rich_text) if "<" in rich_text else rich_text
- return text.replace("|", "▏").replace("\n", " ")
diff --git a/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.yaml b/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.yaml
deleted file mode 100644
index 3c37746b921d05..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/list_worksheet_records.yaml
+++ /dev/null
@@ -1,226 +0,0 @@
-identity:
- name: list_worksheet_records
- author: Ryan Tian
- label:
- en_US: List Worksheet Records
- zh_Hans: 查询工作表记录数据
-description:
- human:
- en_US: List records from the worksheet
- zh_Hans: 查询工作表的记录列表数据,一次最多1000行,可分页获取
- llm: A tool to retrieve record data from the specific worksheet.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the worksheet from which to retrieve record data
- zh_Hans: 要获取记录数据的工作表 ID
- llm_description: This parameter specifies the ID of the worksheet where the records are stored.
- form: llm
-
- - name: field_ids
- type: string
- required: false
- label:
- en_US: Field IDs
- zh_Hans: 字段 ID 列表
- human_description:
- en_US: A comma-separated list of field IDs whose data to retrieve. If not provided, all fields' data will be fetched
- zh_Hans: 要获取记录数据的字段 ID,多个 ID 间用英文逗号隔开,不传此参数则将获取所有字段的数据
- llm_description: This optional parameter lets you specify a comma-separated list of field IDs. Unless the user explicitly requests to output the specified field in the question, this parameter should usually be omitted. If this parameter is omitted, the API will return data for all fields by default. When provided, only the data associated with these fields will be included in the response.
- form: llm
-
- - name: filters
- type: string
- required: false
- label:
- en_US: Filter Set
- zh_Hans: 筛选器组合
- human_description:
- en_US: A combination of filters applied to query records, formatted as a JSON array. See the application's API documentation for details on its structure and usage.
- zh_Hans: 查询记录的筛选条件组合,格式为 JSON 数组,可以从应用 API 文档中了解参数结构详情
- llm_description: |
- This parameter allows you to specify a set of conditions that records must meet to be included in the result set. It is formatted as a JSON array, with its structure defined as follows:
- ```
- type Filters = { // filter object array
- controlId: string; // fieldId
- dataType: number; // fieldTypeId
- spliceType: number; // condition concatenation method, 1: And, 2: Or
- filterType: number; // expression type, refer to the for enumerable values
- values?: string[]; // values in the condition, for option-type fields, multiple values can be passed
- value?: string; // value in the condition, a single value can be passed according to the field type
- dateRange?: number; // date range, mandatory when filterType is 17 or 18, refer to the for enumerable values
- minValue?: string; // minimum value for custom range
- maxValue?: string; // maximum value for custom range
- isAsc?: boolean; // ascending order, false: descending, true: ascending
- }[];
- ```
- For option-type fields, if this option field has `options`, then you need to get the corresponding `key` value from the `options` in the current field information via `value`, and pass it into `values` in array format. Do not use the `options` value of other fields as input conditions.
-
- ### FilterTypeEnum Reference
- ```
- Enum Value, Enum Character, Description
- 1, Like, Contains(Include)
- 2, Eq, Is (Equal)
- 3, Start, Starts With
- 4, End, Ends With
- 5, NotLike, Does Not Contain(Not Include)
- 6, Ne, Is Not (Not Equal)
- 7, IsEmpty, Empty
- 8, HasValue, Not Empty
- 11, Between, Within Range(Belong to)
- 12, NotBetween, Outside Range(Not belong to)
- 13, Gt, Greater Than
- 14, Gte, Greater Than or Equal To
- 15, Lt, Less Than
- 16, Lte, Less Than or Equal To
- 17, DateEnum, Date Is
- 18, NotDateEnum, Date Is Not
- 24, RCEq, Associated Field Is
- 25, RCNe, Associated Field Is Not
- 26, ArrEq, Array Equals
- 27, ArrNe, Array Does Not Equal
- 31, DateBetween, Date Within Range (can only be used with minValue and maxValue)
- 32, DateNotBetween, Date Not Within Range (can only be used with minValue and maxValue)
- 33, DateGt, Date Later Than
- 34, DateGte, Date Later Than or Equal To
- 35, DateLt, Date Earlier Than
- 36, DateLte, Date Earlier Than or Equal To
- ```
-
- ### DateRangeEnum Reference
- ```
- Enum Value, Enum Character, Description
- 1, Today, Today
- 2, Yesterday, Yesterday
- 3, Tomorrow, Tomorrow
- 4, ThisWeek, This Week
- 5, LastWeek, Last Week
- 6, NextWeek, Next Week
- 7, ThisMonth, This Month
- 8, LastMonth, Last Month
- 9, NextMonth, Next Month
- 12, ThisQuarter, This Quarter
- 13, LastQuarter, Last Quarter
- 14, NextQuarter, Next Quarter
- 15, ThisYear, This Year
- 16, LastYear, Last Year
- 17, NextYear, Next Year
- 18, Customize, Custom
- 21, Last7Day, Past 7 Days
- 22, Last14Day, Past 14 Days
- 23, Last30Day, Past 30 Days
- 31, Next7Day, Next 7 Days
- 32, Next14Day, Next 14 Days
- 33, Next33Day, Next 33 Days
- ```
- form: llm
-
- - name: sort_id
- type: string
- required: false
- label:
- en_US: Sort Field ID
- zh_Hans: 排序字段 ID
- human_description:
- en_US: The ID of the field used for sorting
- zh_Hans: 用以排序的字段 ID
- llm_description: This optional parameter specifies the unique identifier of the field that will be used to sort the results. It should be set to the ID of an existing field within your data structure.
- form: llm
-
- - name: sort_is_asc
- type: boolean
- required: false
- label:
- en_US: Ascending Order
- zh_Hans: 是否升序排列
- human_description:
- en_US: Determines whether the sorting is in ascending (true) or descending (false) order
- zh_Hans: 排序字段的排序方式:true-升序,false-降序
- llm_description: This optional parameter controls the direction of the sort. If set to true, the results will be sorted in ascending order; if false, they will be sorted in descending order.
- form: llm
-
- - name: limit
- type: number
- required: false
- label:
- en_US: Record Limit
- zh_Hans: 记录数量限制
- human_description:
- en_US: The maximum number of records to retrieve
- zh_Hans: 要获取的记录数量限制条数
- llm_description: This optional parameter allows you to specify the maximum number of records that should be returned in the result set. When retrieving paginated record data, this parameter indicates the number of rows to fetch per page, and must be used in conjunction with the `page_index` parameter.
- form: llm
-
- - name: page_index
- type: number
- required: false
- label:
- en_US: Page Index
- zh_Hans: 页码
- human_description:
- en_US: The page number when paginating through a list of records
- zh_Hans: 分页读取记录列表时的页码
- llm_description: This parameter is used when you need to paginate through a large set of records. The default value is 1, which refers to the first page. When it is used, the meaning of the `limit` parameter becomes the number of records per page.
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
-
- - name: result_type
- type: select
- required: true
- options:
- - value: table
- label:
- en_US: table text
- zh_Hans: 表格文本
- - value: json
- label:
- en_US: json text
- zh_Hans: JSON文本
- default: table
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, table styled text or json text
- zh_Hans: 用于选择结果类型,使用表格格式文本还是JSON格式文本
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/list_worksheets.py b/api/core/tools/provider/builtin/hap/tools/list_worksheets.py
deleted file mode 100644
index 4e852c0028497c..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/list_worksheets.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import json
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class ListWorksheetsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not (host.startswith("http://") or host.startswith("https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
- url = f"{host}/v1/open/app/get"
-
- result_type = tool_parameters.get("result_type", "")
- if not result_type:
- result_type = "table"
-
- headers = {"Content-Type": "application/json"}
- params = {
- "appKey": appkey,
- "sign": sign,
- }
- try:
- res = httpx.get(url, headers=headers, params=params, timeout=30)
- res_json = res.json()
- if res.is_success:
- if res_json["error_code"] != 1:
- return self.create_text_message(
- "Failed to access the application. {}".format(res_json["error_msg"])
- )
- else:
- if result_type == "json":
- worksheets = []
- for section in res_json["data"]["sections"]:
- worksheets.extend(self._extract_worksheets(section, result_type))
- return self.create_text_message(text=json.dumps(worksheets, ensure_ascii=False))
- else:
- worksheets = "|worksheetId|worksheetName|description|\n|---|---|---|"
- for section in res_json["data"]["sections"]:
- worksheets += self._extract_worksheets(section, result_type)
- return self.create_text_message(worksheets)
-
- else:
- return self.create_text_message(
- f"Failed to list worksheets, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to list worksheets, something went wrong: {}".format(e))
-
- def _extract_worksheets(self, section, type):
- items = []
- tables = ""
- for item in section.get("items", []):
- if item.get("type") == 0 and ("notes" not in item or item.get("notes") != "NO"):
- if type == "json":
- filtered_item = {"id": item["id"], "name": item["name"], "notes": item.get("notes", "")}
- items.append(filtered_item)
- else:
- tables += f"\n|{item['id']}|{item['name']}|{item.get('notes', '')}|"
-
- for child_section in section.get("childSections", []):
- if type == "json":
- items.extend(self._extract_worksheets(child_section, "json"))
- else:
- tables += self._extract_worksheets(child_section, "table")
-
- return items if type == "json" else tables
diff --git a/api/core/tools/provider/builtin/hap/tools/list_worksheets.yaml b/api/core/tools/provider/builtin/hap/tools/list_worksheets.yaml
deleted file mode 100644
index 935b72a89564cd..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/list_worksheets.yaml
+++ /dev/null
@@ -1,68 +0,0 @@
-identity:
- name: list_worksheets
- author: Ryan Tian
- label:
- en_US: List Worksheets
- zh_Hans: 获取应用下所有工作表
-description:
- human:
- en_US: List worksheets within an application
- zh_Hans: 获取应用下的所有工作表和说明信息
- llm: A tool to list worksheets info within an application, imported parameter is AppKey and Sign of the application.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
-
- - name: result_type
- type: select
- required: true
- options:
- - value: table
- label:
- en_US: table text
- zh_Hans: 表格文本
- - value: json
- label:
- en_US: json text
- zh_Hans: JSON文本
- default: table
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, table styled text or json text
- zh_Hans: 用于选择结果类型,使用表格格式文本还是JSON格式文本
- form: form
diff --git a/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.py b/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.py
deleted file mode 100644
index 971f3d37f6dfbf..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import json
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class UpdateWorksheetRecordTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- appkey = tool_parameters.get("appkey", "")
- if not appkey:
- return self.create_text_message("Invalid parameter App Key")
- sign = tool_parameters.get("sign", "")
- if not sign:
- return self.create_text_message("Invalid parameter Sign")
- worksheet_id = tool_parameters.get("worksheet_id", "")
- if not worksheet_id:
- return self.create_text_message("Invalid parameter Worksheet ID")
- row_id = tool_parameters.get("row_id", "")
- if not row_id:
- return self.create_text_message("Invalid parameter Record Row ID")
- record_data = tool_parameters.get("record_data", "")
- if not record_data:
- return self.create_text_message("Invalid parameter Record Row Data")
-
- host = tool_parameters.get("host", "")
- if not host:
- host = "https://api.mingdao.com"
- elif not host.startswith(("http://", "https://")):
- return self.create_text_message("Invalid parameter Host Address")
- else:
- host = f"{host.removesuffix('/')}/api"
-
- url = f"{host}/v2/open/worksheet/editRow"
- headers = {"Content-Type": "application/json"}
- payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
-
- try:
- payload["controls"] = json.loads(record_data)
- res = httpx.post(url, headers=headers, json=payload, timeout=60)
- res.raise_for_status()
- res_json = res.json()
- if res_json.get("error_code") != 1:
- return self.create_text_message(f"Failed to update the record. {res_json['error_msg']}")
- return self.create_text_message("Record updated successfully.")
- except httpx.RequestError as e:
- return self.create_text_message(f"Failed to update the record, request error: {e}")
- except json.JSONDecodeError as e:
- return self.create_text_message(f"Failed to parse JSON response: {e}")
- except Exception as e:
- return self.create_text_message(f"Failed to update the record, unexpected error: {e}")
diff --git a/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.yaml b/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.yaml
deleted file mode 100644
index fe1f8f671a4e2f..00000000000000
--- a/api/core/tools/provider/builtin/hap/tools/update_worksheet_record.yaml
+++ /dev/null
@@ -1,90 +0,0 @@
-identity:
- name: update_worksheet_record
- author: Ryan Tian
- label:
- en_US: Update Worksheet Record
- zh_Hans: 更新指定的一条工作表记录
-description:
- human:
- en_US: Updates a single record in a worksheet based on the specified record row ID
- zh_Hans: 根据指定的记录ID更新一条工作表记录数据
- llm: A tool to modify existing information within a particular record of a worksheet by referencing its unique identifier.
-parameters:
- - name: appkey
- type: secret-input
- required: true
- label:
- en_US: App Key
- zh_Hans: App Key
- human_description:
- en_US: The AppKey parameter for the HAP application, typically found in the application's API documentation.
- zh_Hans: HAP 应用的 AppKey 参数,可以从应用 API 文档中查找到
- llm_description: the AppKey parameter for the HAP application
- form: form
-
- - name: sign
- type: secret-input
- required: true
- label:
- en_US: Sign
- zh_Hans: Sign
- human_description:
- en_US: The Sign parameter for the HAP application
- zh_Hans: HAP 应用的 Sign 参数
- llm_description: the Sign parameter for the HAP application
- form: form
-
- - name: worksheet_id
- type: string
- required: true
- label:
- en_US: Worksheet ID
- zh_Hans: 工作表 ID
- human_description:
- en_US: The ID of the specified worksheet
- zh_Hans: 要获取字段信息的工作表 ID
- llm_description: The ID of the specified worksheet which to get the fields information.
- form: llm
-
- - name: row_id
- type: string
- required: true
- label:
- en_US: Record Row ID
- zh_Hans: 记录 ID
- human_description:
- en_US: The row ID of the specified record
- zh_Hans: 要更新的记录 ID
- llm_description: The row ID of the specified record which to be updated.
- form: llm
-
- - name: record_data
- type: string
- required: true
- label:
- en_US: Record Row Data
- zh_Hans: 记录数据
- human_description:
- en_US: The fields with data of the specified record
- zh_Hans: 要更新的记录数据,JSON 对象数组格式。数组元素属性:controlId-字段ID,value-字段值
- llm_description: |
- The fields with data of the specified record which to be updated. It is in the format of an array of JSON objects, and the structure is defined as follows:
- ```
- type RowData = {
- controlId: string; // Field ID to be updated
- value: string; // Field value to be updated
- }[];
- ```
- form: llm
-
- - name: host
- type: string
- required: false
- label:
- en_US: Host Address
- zh_Hans: 服务器地址
- human_description:
- en_US: The address for the privately deployed HAP server.
- zh_Hans: 私有部署 HAP 服务器地址,公有云无需填写
- llm_description: the address for the privately deployed HAP server.
- form: form
diff --git a/api/core/tools/provider/builtin/jina/_assets/icon.svg b/api/core/tools/provider/builtin/jina/_assets/icon.svg
deleted file mode 100644
index 2e1b00fa52e43c..00000000000000
--- a/api/core/tools/provider/builtin/jina/_assets/icon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/api/core/tools/provider/builtin/jina/jina.py b/api/core/tools/provider/builtin/jina/jina.py
deleted file mode 100644
index 154e15db016dd1..00000000000000
--- a/api/core/tools/provider/builtin/jina/jina.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import json
-from typing import Any
-
-from core.tools.entities.values import ToolLabelEnum
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.jina.tools.jina_reader import JinaReaderTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GoogleProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- if credentials["api_key"] is None:
- credentials["api_key"] = ""
- else:
- result = (
- JinaReaderTool()
- .fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- )
- .invoke(
- user_id="",
- tool_parameters={
- "url": "https://example.com",
- },
- )[0]
- )
-
- message = json.loads(result.message)
- if message["code"] != 200:
- raise ToolProviderCredentialValidationError(message["message"])
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
-
- def _get_tool_labels(self) -> list[ToolLabelEnum]:
- return [ToolLabelEnum.SEARCH, ToolLabelEnum.PRODUCTIVITY]
diff --git a/api/core/tools/provider/builtin/jina/jina.yaml b/api/core/tools/provider/builtin/jina/jina.yaml
deleted file mode 100644
index af3ca23ffaff46..00000000000000
--- a/api/core/tools/provider/builtin/jina/jina.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- author: Dify
- name: jina
- label:
- en_US: Jina AI
- zh_Hans: Jina AI
- pt_BR: Jina AI
- description:
- en_US: Your Search Foundation, Supercharged!
- zh_Hans: 您的搜索底座,从此不同!
- pt_BR: Your Search Foundation, Supercharged!
- icon: icon.svg
- tags:
- - search
- - productivity
-credentials_for_provider:
- api_key:
- type: secret-input
- required: false
- label:
- en_US: API Key (leave empty if you don't have one)
- zh_Hans: API 密钥(可留空)
- pt_BR: Chave API (deixe vazio se você não tiver uma)
- placeholder:
- en_US: Please enter your Jina AI API key
- zh_Hans: 请输入你的 Jina AI API 密钥
- pt_BR: Por favor, insira sua chave de API do Jina AI
- help:
- en_US: Get your Jina AI API key from Jina AI (optional, but you can get a higher rate)
- zh_Hans: 从 Jina AI 获取您的 Jina AI API 密钥(非必须,能得到更高的速率)
- pt_BR: Obtenha sua chave de API do Jina AI na Jina AI (opcional, mas você pode obter uma taxa mais alta)
- url: https://jina.ai
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_reader.py b/api/core/tools/provider/builtin/jina/tools/jina_reader.py
deleted file mode 100644
index 756b7272248146..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_reader.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import json
-from typing import Any, Union
-
-from yarl import URL
-
-from core.helper import ssrf_proxy
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JinaReaderTool(BuiltinTool):
- _jina_reader_endpoint = "https://r.jina.ai/"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- url = tool_parameters["url"]
-
- headers = {"Accept": "application/json"}
-
- if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
- headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
-
- request_params = tool_parameters.get("request_params")
- if request_params is not None and request_params != "":
- try:
- request_params = json.loads(request_params)
- if not isinstance(request_params, dict):
- raise ValueError("request_params must be a JSON object")
- except (json.JSONDecodeError, ValueError) as e:
- raise ValueError(f"Invalid request_params: {e}")
-
- target_selector = tool_parameters.get("target_selector")
- if target_selector is not None and target_selector != "":
- headers["X-Target-Selector"] = target_selector
-
- wait_for_selector = tool_parameters.get("wait_for_selector")
- if wait_for_selector is not None and wait_for_selector != "":
- headers["X-Wait-For-Selector"] = wait_for_selector
-
- remove_selector = tool_parameters.get("remove_selector")
- if remove_selector is not None and remove_selector != "":
- headers["X-Remove-Selector"] = remove_selector
-
- if tool_parameters.get("retain_images", False):
- headers["X-Retain-Images"] = "true"
-
- if tool_parameters.get("image_caption", False):
- headers["X-With-Generated-Alt"] = "true"
-
- if tool_parameters.get("gather_all_links_at_the_end", False):
- headers["X-With-Links-Summary"] = "true"
-
- if tool_parameters.get("gather_all_images_at_the_end", False):
- headers["X-With-Images-Summary"] = "true"
-
- proxy_server = tool_parameters.get("proxy_server")
- if proxy_server is not None and proxy_server != "":
- headers["X-Proxy-Url"] = proxy_server
-
- if tool_parameters.get("no_cache", False):
- headers["X-No-Cache"] = "true"
-
- if tool_parameters.get("with_iframe", False):
- headers["X-With-Iframe"] = "true"
-
- if tool_parameters.get("with_shadow_dom", False):
- headers["X-With-Shadow-Dom"] = "true"
-
- max_retries = tool_parameters.get("max_retries", 3)
- response = ssrf_proxy.get(
- str(URL(self._jina_reader_endpoint + url)),
- headers=headers,
- params=request_params,
- timeout=(10, 60),
- max_retries=max_retries,
- )
-
- if tool_parameters.get("summary", False):
- return self.create_text_message(self.summary(user_id, response.text))
-
- return self.create_text_message(response.text)
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_reader.yaml b/api/core/tools/provider/builtin/jina/tools/jina_reader.yaml
deleted file mode 100644
index 012a8c7688cb57..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_reader.yaml
+++ /dev/null
@@ -1,221 +0,0 @@
-identity:
- name: jina_reader
- author: Dify
- label:
- en_US: Fetch Single Page
- zh_Hans: 获取单页面
- pt_BR: Fetch Single Page
-description:
- human:
- en_US: Fetch the target URL (can be a PDF) and convert it into a LLM-friendly markdown.
- zh_Hans: 获取目标网址(可以是 PDF),并将其转换为适合大模型处理的 Markdown 格式。
- pt_BR: Busque a URL de destino (que pode ser um PDF) e converta em um Markdown LLM-friendly.
- llm: A tool for scraping webpages. Input should be a URL.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: URL
- zh_Hans: 网址
- pt_BR: URL
- human_description:
- en_US: Web link
- zh_Hans: 网页链接
- pt_BR: URL da web
- llm_description: url para scraping
- form: llm
- - name: request_params
- type: string
- required: false
- label:
- en_US: Request params
- zh_Hans: 请求参数
- pt_BR: Parâmetros de solicitação
- human_description:
- en_US: |
- request parameters, format: {"key1": "value1", "key2": "value2"}
- zh_Hans: |
- 请求参数,格式:{"key1": "value1", "key2": "value2"}
- pt_BR: |
- parâmetros de solicitação, formato: {"key1": "value1", "key2": "value2"}
- llm_description: request parameters
- form: llm
- - name: target_selector
- type: string
- required: false
- label:
- en_US: Target selector
- zh_Hans: 目标选择器
- pt_BR: Seletor de destino
- human_description:
- en_US: css selector for scraping specific elements
- zh_Hans: css 选择器用于抓取特定元素
- pt_BR: css selector para scraping de elementos específicos
- llm_description: css selector of the target element to scrape
- form: form
- - name: wait_for_selector
- type: string
- required: false
- label:
- en_US: Wait for selector
- zh_Hans: 等待选择器
- pt_BR: Aguardar por seletor
- human_description:
- en_US: css selector for waiting for specific elements
- zh_Hans: css 选择器用于等待特定元素
- pt_BR: css selector para aguardar elementos específicos
- llm_description: css selector of the target element to wait for
- form: form
- - name: remove_selector
- type: string
- required: false
- label:
- en_US: Excluded Selector
- zh_Hans: 排除选择器
- pt_BR: Seletor Excluído
- human_description:
- en_US: css selector for remove for specific elements
- zh_Hans: css 选择器用于排除特定元素
- pt_BR: seletor CSS para remover elementos específicos
- llm_description: css selector of the target element to remove for
- form: form
- - name: retain_images
- type: boolean
- required: false
- default: false
- label:
- en_US: Remove All Images
- zh_Hans: 删除所有图片
- pt_BR: Remover todas as imagens
- human_description:
- en_US: Removes all images from the response.
- zh_Hans: 从响应中删除所有图片。
- pt_BR: Remove todas as imagens da resposta.
- llm_description: Remove all images
- form: form
- - name: image_caption
- type: boolean
- required: false
- default: false
- label:
- en_US: Image caption
- zh_Hans: 图片说明
- pt_BR: Legenda da imagem
- human_description:
- en_US: "Captions all images at the specified URL, adding 'Image [idx]: [caption]' as an alt tag for those without one. This allows downstream LLMs to interact with the images in activities such as reasoning and summarizing."
- zh_Hans: "为指定 URL 上的所有图像添加标题,为没有标题的图像添加“Image [idx]: [caption]”作为 alt 标签,以支持下游模型的图像交互。"
- pt_BR: "Adiciona legendas a todas as imagens na URL especificada, adicionando 'Imagem [idx]: [legenda]' como uma tag alt para aquelas que não têm uma. Isso permite que os modelos LLM inferiores interajam com as imagens em atividades como raciocínio e resumo."
- llm_description: Captions all images at the specified URL
- form: form
- - name: gather_all_links_at_the_end
- type: boolean
- required: false
- default: false
- label:
- en_US: Gather all links at the end
- zh_Hans: 将所有链接集中到最后
- pt_BR: Coletar todos os links ao final
- human_description:
- en_US: A "Buttons & Links" section will be created at the end. This helps the downstream LLMs or web agents navigating the page or take further actions.
- zh_Hans: 末尾将添加“按钮和链接”部分,方便下游模型或网络代理做页面导航或执行进一步操作。
- pt_BR: Um "Botões & Links" section will be created at the end. This helps the downstream LLMs or web agents navigating the page or take further actions.
- llm_description: Gather all links at the end
- form: form
- - name: gather_all_images_at_the_end
- type: boolean
- required: false
- default: false
- label:
- en_US: Gather all images at the end
- zh_Hans: 将所有图片集中到最后
- pt_BR: Coletar todas as imagens ao final
- human_description:
- en_US: An "Images" section will be created at the end. This gives the downstream LLMs an overview of all visuals on the page, which may improve reasoning.
- zh_Hans: 末尾会新增“图片”部分,方便下游模型全面了解页面的视觉内容,提升推理效果。
- pt_BR: Um "Imagens" section will be created at the end. This gives the downstream LLMs an overview of all visuals on the page, which may improve reasoning.
- llm_description: Gather all images at the end
- form: form
- - name: proxy_server
- type: string
- required: false
- label:
- en_US: Proxy server
- zh_Hans: 代理服务器
- pt_BR: Servidor de proxy
- human_description:
- en_US: Use proxy to access URLs
- zh_Hans: 利用代理访问 URL
- pt_BR: Use proxy to access URLs
- llm_description: Use proxy to access URLs
- form: form
- - name: no_cache
- type: boolean
- required: false
- default: false
- label:
- en_US: Bypass the Cache
- zh_Hans: 绕过缓存
- pt_BR: Ignorar o cache
- human_description:
- en_US: Bypass the Cache
- zh_Hans: 是否绕过缓存
- pt_BR: Ignorar o cache
- llm_description: bypass the cache
- form: form
- - name: with_iframe
- type: boolean
- required: false
- default: false
- label:
- en_US: Enable iframe extraction
- zh_Hans: 启用 iframe 提取
- pt_BR: Habilitar extração de iframe
- human_description:
- en_US: Extract and process content of all embedded iframes in the DOM tree.
- zh_Hans: 提取并处理 DOM 树中所有嵌入 iframe 的内容。
- pt_BR: Extrair e processar o conteúdo de todos os iframes incorporados na árvore DOM.
- llm_description: Extract content from embedded iframes
- form: form
- - name: with_shadow_dom
- type: boolean
- required: false
- default: false
- label:
- en_US: Enable Shadow DOM extraction
- zh_Hans: 启用 Shadow DOM 提取
- pt_BR: Habilitar extração de Shadow DOM
- human_description:
- en_US: Traverse all Shadow DOM roots in the document and extract content.
- zh_Hans: 遍历文档中所有 Shadow DOM 根并提取内容。
- pt_BR: Percorra todas as raízes do Shadow DOM no documento e extraia o conteúdo.
- llm_description: Extract content from Shadow DOM roots
- form: form
- - name: summary
- type: boolean
- required: false
- default: false
- label:
- en_US: Enable summary
- zh_Hans: 是否启用摘要
- pt_BR: Habilitar resumo
- human_description:
- en_US: Enable summary for the output
- zh_Hans: 为输出启用摘要
- pt_BR: Habilitar resumo para a saída
- llm_description: enable summary
- form: form
- - name: max_retries
- type: number
- required: false
- default: 3
- label:
- en_US: Retry
- zh_Hans: 重试
- pt_BR: Repetir
- human_description:
- en_US: Number of times to retry the request if it fails
- zh_Hans: 请求失败时重试的次数
- pt_BR: Número de vezes para repetir a solicitação se falhar
- llm_description: Number of times to retry the request if it fails
- form: form
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_search.py b/api/core/tools/provider/builtin/jina/tools/jina_search.py
deleted file mode 100644
index 30af6de7831e59..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_search.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Any, Union
-
-from yarl import URL
-
-from core.helper import ssrf_proxy
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JinaSearchTool(BuiltinTool):
- _jina_search_endpoint = "https://s.jina.ai/"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- query = tool_parameters["query"]
-
- headers = {"Accept": "application/json"}
-
- if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
- headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
-
- if tool_parameters.get("image_caption", False):
- headers["X-With-Generated-Alt"] = "true"
-
- if tool_parameters.get("gather_all_links_at_the_end", False):
- headers["X-With-Links-Summary"] = "true"
-
- if tool_parameters.get("gather_all_images_at_the_end", False):
- headers["X-With-Images-Summary"] = "true"
-
- proxy_server = tool_parameters.get("proxy_server")
- if proxy_server is not None and proxy_server != "":
- headers["X-Proxy-Url"] = proxy_server
-
- if tool_parameters.get("no_cache", False):
- headers["X-No-Cache"] = "true"
-
- max_retries = tool_parameters.get("max_retries", 3)
- response = ssrf_proxy.get(
- str(URL(self._jina_search_endpoint + query)), headers=headers, timeout=(10, 60), max_retries=max_retries
- )
-
- return self.create_text_message(response.text)
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_search.yaml b/api/core/tools/provider/builtin/jina/tools/jina_search.yaml
deleted file mode 100644
index e58c639e5690d0..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_search.yaml
+++ /dev/null
@@ -1,110 +0,0 @@
-identity:
- name: jina_search
- author: Dify
- label:
- en_US: Search the web
- zh_Hans: 联网搜索
- pt_BR: Search the web
-description:
- human:
- en_US: Search on the public web of a given query and return the top results as LLM-friendly markdown.
- zh_Hans: 针对给定的查询在互联网上进行搜索,并以适合大模型处理的 Markdown 格式返回最相关的结果。
- pt_BR: Procurar na web pública de uma consulta fornecida e retornar os melhores resultados como markdown para LLMs.
- llm: A tool for searching results on the web for grounding. Input should be a simple question.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Question (Query)
- zh_Hans: 查询
- pt_BR: Pergunta (Consulta)
- human_description:
- en_US: used to find information on the web
- zh_Hans: 在网络上搜索信息
- pt_BR: Usado para encontrar informações na web
- llm_description: Pergunta simples para fazer na web
- form: llm
- - name: image_caption
- type: boolean
- required: false
- default: false
- label:
- en_US: Image caption
- zh_Hans: 图片说明
- pt_BR: Legenda da imagem
- human_description:
- en_US: "Captions all images at the specified URL, adding 'Image [idx]: [caption]' as an alt tag for those without one. This allows downstream LLMs to interact with the images in activities such as reasoning and summarizing."
- zh_Hans: "为指定 URL 上的所有图像添加标题,为没有标题的图像添加“Image [idx]: [caption]”作为 alt 标签,以支持下游模型的图像交互。"
- pt_BR: "Captions all images at the specified URL, adding 'Image [idx]: [caption]' as an alt tag for those without one. This allows downstream LLMs to interact with the images in activities such as reasoning and summarizing."
- llm_description: Captions all images at the specified URL
- form: form
- - name: gather_all_links_at_the_end
- type: boolean
- required: false
- default: false
- label:
- en_US: Gather all links at the end
- zh_Hans: 将所有链接集中到最后
- pt_BR: Coletar todos os links ao final
- human_description:
- en_US: A "Buttons & Links" section will be created at the end. This helps the downstream LLMs or web agents navigating the page or take further actions.
- zh_Hans: 末尾将添加“按钮和链接”部分,汇总页面上的所有链接。方便下游模型或网络代理做页面导航或执行进一步操作。
- pt_BR: Um "Botão & Links" seção será criada no final. Isso ajuda os LLMs ou agentes da web navegando pela página ou executar ações adicionais.
- llm_description: Gather all links at the end
- form: form
- - name: gather_all_images_at_the_end
- type: boolean
- required: false
- default: false
- label:
- en_US: Gather all images at the end
- zh_Hans: 将所有图片集中到最后
- pt_BR: Coletar todas as imagens ao final
- human_description:
- en_US: An "Images" section will be created at the end. This gives the downstream LLMs an overview of all visuals on the page, which may improve reasoning.
- zh_Hans: 末尾会新增“图片”部分,汇总页面上的所有图片。方便下游模型概览页面的视觉内容,提升推理效果。
- pt_BR: Um "Imagens" seção será criada no final. Isso fornece uma visão geral de todas as imagens na página para os LLMs, que pode melhorar a razão.
- llm_description: Gather all images at the end
- form: form
- - name: proxy_server
- type: string
- required: false
- label:
- en_US: Proxy server
- zh_Hans: 代理服务器
- pt_BR: Servidor de proxy
- human_description:
- en_US: Use proxy to access URLs
- zh_Hans: 利用代理访问 URL
- pt_BR: Usar proxy para acessar URLs
- llm_description: Use proxy to access URLs
- form: form
- - name: no_cache
- type: boolean
- required: false
- default: false
- label:
- en_US: Bypass the Cache
- zh_Hans: 是否绕过缓存
- pt_BR: Ignorar o cache
- human_description:
- en_US: Bypass the Cache
- zh_Hans: 是否绕过缓存
- pt_BR: Ignorar o cache
- llm_description: bypass the cache
- form: form
- - name: max_retries
- type: number
- required: false
- default: 3
- label:
- en_US: Retry
- zh_Hans: 重试
- pt_BR: Repetir
- human_description:
- en_US: Number of times to retry the request if it fails
- zh_Hans: 请求失败时重试的次数
- pt_BR: Número de vezes para repetir a solicitação se falhar
- llm_description: Number of times to retry the request if it fails
- form: form
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.py b/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.py
deleted file mode 100644
index 06dabcc9c2a74e..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any
-
-from core.helper import ssrf_proxy
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JinaTokenizerTool(BuiltinTool):
- _jina_tokenizer_endpoint = "https://tokenize.jina.ai/"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> ToolInvokeMessage:
- content = tool_parameters["content"]
- body = {"content": content}
-
- headers = {"Content-Type": "application/json"}
-
- if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
- headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
-
- if tool_parameters.get("return_chunks", False):
- body["return_chunks"] = True
-
- if tool_parameters.get("return_tokens", False):
- body["return_tokens"] = True
-
- if tokenizer := tool_parameters.get("tokenizer"):
- body["tokenizer"] = tokenizer
-
- response = ssrf_proxy.post(
- self._jina_tokenizer_endpoint,
- headers=headers,
- json=body,
- )
-
- return self.create_json_message(response.json())
diff --git a/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.yaml b/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.yaml
deleted file mode 100644
index 74885cdf9a7048..00000000000000
--- a/api/core/tools/provider/builtin/jina/tools/jina_tokenizer.yaml
+++ /dev/null
@@ -1,78 +0,0 @@
-identity:
- name: jina_tokenizer
- author: hjlarry
- label:
- en_US: Segment
- zh_Hans: 切分器
- pt_BR: Segment
-description:
- human:
- en_US: Split long text into chunks and do tokenization.
- zh_Hans: 将长文本拆分成小段落,并做分词处理。
- pt_BR: Dividir o texto longo em pedaços e fazer tokenização.
- llm: Free API to tokenize text and segment long text into chunks.
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: Content
- zh_Hans: 内容
- pt_BR: Conteúdo
- llm_description: the content which need to tokenize or segment
- form: llm
- - name: return_tokens
- type: boolean
- required: false
- label:
- en_US: Return the tokens
- zh_Hans: 是否返回tokens
- pt_BR: Retornar os tokens
- human_description:
- en_US: Return the tokens and their corresponding ids in the response.
- zh_Hans: 返回tokens及其对应的ids。
- pt_BR: Retornar os tokens e seus respectivos ids na resposta.
- form: form
- - name: return_chunks
- type: boolean
- label:
- en_US: Return the chunks
- zh_Hans: 是否分块
- pt_BR: Retornar os chunks
- human_description:
- en_US: Chunking the input into semantically meaningful segments while handling a wide variety of text types and edge cases based on common structural cues.
- zh_Hans: 将输入文本分块为语义有意义的片段,同时基于常见的结构线索处理各种文本类型和特殊情况。
- pt_BR: Dividir o texto de entrada em segmentos semanticamente significativos, enquanto lida com uma ampla variedade de tipos de texto e casos de borda com base em pistas estruturais comuns.
- form: form
- - name: tokenizer
- type: select
- options:
- - value: cl100k_base
- label:
- en_US: cl100k_base
- - value: o200k_base
- label:
- en_US: o200k_base
- - value: p50k_base
- label:
- en_US: p50k_base
- - value: r50k_base
- label:
- en_US: r50k_base
- - value: p50k_edit
- label:
- en_US: p50k_edit
- - value: gpt2
- label:
- en_US: gpt2
- label:
- en_US: Tokenizer
- human_description:
- en_US: |
- · cl100k_base --- gpt-4, gpt-3.5-turbo, gpt-3.5
- · o200k_base --- gpt-4o, gpt-4o-mini
- · p50k_base --- text-davinci-003, text-davinci-002
- · r50k_base --- text-davinci-001, text-curie-001
- · p50k_edit --- text-davinci-edit-001, code-davinci-edit-001
- · gpt2 --- gpt-2
- form: form
diff --git a/api/core/tools/provider/builtin/json_process/_assets/icon.svg b/api/core/tools/provider/builtin/json_process/_assets/icon.svg
deleted file mode 100644
index b123983836962a..00000000000000
--- a/api/core/tools/provider/builtin/json_process/_assets/icon.svg
+++ /dev/null
@@ -1,358 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/json_process/json_process.py b/api/core/tools/provider/builtin/json_process/json_process.py
deleted file mode 100644
index 10746210b5c652..00000000000000
--- a/api/core/tools/provider/builtin/json_process/json_process.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.json_process.tools.parse import JSONParseTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class JsonExtractProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- JSONParseTool().invoke(
- user_id="",
- tool_parameters={"content": '{"name": "John", "age": 30, "city": "New York"}', "json_filter": "$.name"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/json_process/json_process.yaml b/api/core/tools/provider/builtin/json_process/json_process.yaml
deleted file mode 100644
index c7896bbea7a69f..00000000000000
--- a/api/core/tools/provider/builtin/json_process/json_process.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-identity:
- author: Mingwei Zhang
- name: json_process
- label:
- en_US: JSON Process
- zh_Hans: JSON 处理
- pt_BR: JSON Process
- description:
- en_US: Tools for processing JSON content using jsonpath_ng
- zh_Hans: 利用 jsonpath_ng 处理 JSON 内容的工具
- pt_BR: Tools for processing JSON content using jsonpath_ng
- icon: icon.svg
- tags:
- - utilities
diff --git a/api/core/tools/provider/builtin/json_process/tools/delete.py b/api/core/tools/provider/builtin/json_process/tools/delete.py
deleted file mode 100644
index 06f6cacd5d6126..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/delete.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import json
-from typing import Any, Union
-
-from jsonpath_ng import parse # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JSONDeleteTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the JSON delete tool
- """
- # Get content
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- # Get query
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Invalid parameter query")
-
- ensure_ascii = tool_parameters.get("ensure_ascii", True)
- try:
- result = self._delete(content, query, ensure_ascii)
- return self.create_text_message(str(result))
- except Exception as e:
- return self.create_text_message(f"Failed to delete JSON content: {str(e)}")
-
- def _delete(self, origin_json: str, query: str, ensure_ascii: bool) -> str:
- try:
- input_data = json.loads(origin_json)
- expr = parse("$." + query.lstrip("$.")) # Ensure query path starts with $
-
- matches = expr.find(input_data)
-
- if not matches:
- return json.dumps(input_data, ensure_ascii=ensure_ascii) # No changes if no matches found
-
- for match in matches:
- if isinstance(match.context.value, dict):
- # Delete key from dictionary
- del match.context.value[match.path.fields[-1]]
- elif isinstance(match.context.value, list):
- # Remove item from list
- match.context.value.remove(match.value)
- else:
- # For other cases, we might want to set to None or remove the parent key
- parent = match.context.parent
- if parent:
- del parent.value[match.path.fields[-1]]
-
- return json.dumps(input_data, ensure_ascii=ensure_ascii)
- except Exception as e:
- raise Exception(f"Delete operation failed: {str(e)}")
diff --git a/api/core/tools/provider/builtin/json_process/tools/delete.yaml b/api/core/tools/provider/builtin/json_process/tools/delete.yaml
deleted file mode 100644
index 4d390e40d17232..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/delete.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-identity:
- name: json_delete
- author: Mingwei Zhang
- label:
- en_US: JSON Delete
- zh_Hans: JSON 删除
- pt_BR: JSON Delete
-description:
- human:
- en_US: A tool for deleting JSON content
- zh_Hans: 一个删除 JSON 内容的工具
- pt_BR: A tool for deleting JSON content
- llm: A tool for deleting JSON content
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: JSON content
- zh_Hans: JSON 内容
- pt_BR: JSON content
- human_description:
- en_US: JSON content to be processed
- zh_Hans: 待处理的 JSON 内容
- pt_BR: JSON content to be processed
- llm_description: JSON content to be processed
- form: llm
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 查询
- pt_BR: Query
- human_description:
- en_US: JSONPath query to locate the element to delete
- zh_Hans: 用于定位要删除元素的 JSONPath 查询
- pt_BR: JSONPath query to locate the element to delete
- llm_description: JSONPath query to locate the element to delete
- form: llm
- - name: ensure_ascii
- type: boolean
- default: true
- label:
- en_US: Ensure ASCII
- zh_Hans: 确保 ASCII
- pt_BR: Ensure ASCII
- human_description:
- en_US: Ensure the JSON output is ASCII encoded
- zh_Hans: 确保输出的 JSON 是 ASCII 编码
- pt_BR: Ensure the JSON output is ASCII encoded
- form: form
diff --git a/api/core/tools/provider/builtin/json_process/tools/insert.py b/api/core/tools/provider/builtin/json_process/tools/insert.py
deleted file mode 100644
index e825329a6d8f61..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/insert.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import json
-from typing import Any, Union
-
-from jsonpath_ng import parse # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JSONParseTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get content
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- # get query
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Invalid parameter query")
-
- # get new value
- new_value = tool_parameters.get("new_value", "")
- if not new_value:
- return self.create_text_message("Invalid parameter new_value")
-
- # get insert position
- index = tool_parameters.get("index")
-
- # get create path
- create_path = tool_parameters.get("create_path", False)
-
- # get value decode.
- # if true, it will be decoded to an dict
- value_decode = tool_parameters.get("value_decode", False)
-
- ensure_ascii = tool_parameters.get("ensure_ascii", True)
- try:
- result = self._insert(content, query, new_value, ensure_ascii, value_decode, index, create_path)
- return self.create_text_message(str(result))
- except Exception:
- return self.create_text_message("Failed to insert JSON content")
-
- def _insert(
- self, origin_json, query, new_value, ensure_ascii: bool, value_decode: bool, index=None, create_path=False
- ):
- try:
- input_data = json.loads(origin_json)
- expr = parse(query)
- if value_decode is True:
- try:
- new_value = json.loads(new_value)
- except json.JSONDecodeError:
- return "Cannot decode new value to json object"
-
- matches = expr.find(input_data)
-
- if not matches and create_path:
- # create new path
- path_parts = query.strip("$").strip(".").split(".")
- current = input_data
- for i, part in enumerate(path_parts):
- if "[" in part and "]" in part:
- # process array index
- array_name, index = part.split("[")
- index = int(index.rstrip("]"))
- if array_name not in current:
- current[array_name] = []
- while len(current[array_name]) <= index:
- current[array_name].append({})
- current = current[array_name][index]
- else:
- if i == len(path_parts) - 1:
- current[part] = new_value
- elif part not in current:
- current[part] = {}
- current = current[part]
- else:
- for match in matches:
- if isinstance(match.value, dict):
- # insert new value into dict
- if isinstance(new_value, dict):
- match.value.update(new_value)
- else:
- raise ValueError("Cannot insert non-dict value into dict")
- elif isinstance(match.value, list):
- # insert new value into list
- if index is None:
- match.value.append(new_value)
- else:
- match.value.insert(int(index), new_value)
- else:
- # replace old value with new value
- match.full_path.update(input_data, new_value)
-
- return json.dumps(input_data, ensure_ascii=ensure_ascii)
- except Exception as e:
- return str(e)
diff --git a/api/core/tools/provider/builtin/json_process/tools/insert.yaml b/api/core/tools/provider/builtin/json_process/tools/insert.yaml
deleted file mode 100644
index 21b51312dab6b3..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/insert.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-identity:
- name: json_insert
- author: Mingwei Zhang
- label:
- en_US: JSON Insert
- zh_Hans: JSON 插入
- pt_BR: JSON Insert
-description:
- human:
- en_US: A tool for inserting JSON content
- zh_Hans: 一个插入 JSON 内容的工具
- pt_BR: A tool for inserting JSON content
- llm: A tool for inserting JSON content
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: JSON content
- zh_Hans: JSON 内容
- pt_BR: JSON content
- human_description:
- en_US: JSON content
- zh_Hans: JSON 内容
- pt_BR: JSON content
- llm_description: JSON content to be processed
- form: llm
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 查询
- pt_BR: Query
- human_description:
- en_US: Object to insert
- zh_Hans: 待插入的对象
- pt_BR: Object to insert
- llm_description: JSONPath query to locate the element to insert
- form: llm
- - name: new_value
- type: string
- required: true
- label:
- en_US: New Value
- zh_Hans: 新值
- pt_BR: New Value
- human_description:
- en_US: New Value
- zh_Hans: 插入的新值
- pt_BR: New Value
- llm_description: New Value to insert
- form: llm
- - name: value_decode
- type: boolean
- default: false
- label:
- en_US: Decode Value
- zh_Hans: 解码值
- pt_BR: Decode Value
- human_description:
- en_US: Whether to decode the value to a JSON object
- zh_Hans: 是否将值解码为 JSON 对象
- pt_BR: Whether to decode the value to a JSON object
- form: form
- - name: create_path
- type: select
- required: true
- default: "False"
- label:
- en_US: Whether to create a path
- zh_Hans: 是否创建路径
- pt_BR: Whether to create a path
- human_description:
- en_US: Whether to create a path when the path does not exist
- zh_Hans: 查询路径不存在时是否创建路径
- pt_BR: Whether to create a path when the path does not exist
- options:
- - value: "True"
- label:
- en_US: "Yes"
- zh_Hans: 是
- pt_BR: "Yes"
- - value: "False"
- label:
- en_US: "No"
- zh_Hans: 否
- pt_BR: "No"
- form: form
- - name: ensure_ascii
- type: boolean
- default: true
- label:
- en_US: Ensure ASCII
- zh_Hans: 确保 ASCII
- pt_BR: Ensure ASCII
- human_description:
- en_US: Ensure the JSON output is ASCII encoded
- zh_Hans: 确保输出的 JSON 是 ASCII 编码
- pt_BR: Ensure the JSON output is ASCII encoded
- form: form
diff --git a/api/core/tools/provider/builtin/json_process/tools/parse.py b/api/core/tools/provider/builtin/json_process/tools/parse.py
deleted file mode 100644
index 193017ba9a7c53..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/parse.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import json
-from typing import Any, Union
-
-from jsonpath_ng import parse # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JSONParseTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get content
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- # get json filter
- json_filter = tool_parameters.get("json_filter", "")
- if not json_filter:
- return self.create_text_message("Invalid parameter json_filter")
-
- ensure_ascii = tool_parameters.get("ensure_ascii", True)
- try:
- result = self._extract(content, json_filter, ensure_ascii)
- return self.create_text_message(str(result))
- except Exception:
- return self.create_text_message("Failed to extract JSON content")
-
- # Extract data from JSON content
- def _extract(self, content: str, json_filter: str, ensure_ascii: bool) -> str:
- try:
- input_data = json.loads(content)
- expr = parse(json_filter)
- result = [match.value for match in expr.find(input_data)]
-
- if not result:
- return ""
-
- if len(result) == 1:
- result = result[0]
-
- if isinstance(result, dict | list):
- return json.dumps(result, ensure_ascii=ensure_ascii)
- elif isinstance(result, str | int | float | bool) or result is None:
- return str(result)
- else:
- return repr(result)
- except Exception as e:
- return str(e)
diff --git a/api/core/tools/provider/builtin/json_process/tools/parse.yaml b/api/core/tools/provider/builtin/json_process/tools/parse.yaml
deleted file mode 100644
index c35f4eac0775ad..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/parse.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-identity:
- name: parse
- author: Mingwei Zhang
- label:
- en_US: JSON Parse
- zh_Hans: JSON 解析
- pt_BR: JSON Parse
-description:
- human:
- en_US: A tool for extracting JSON objects
- zh_Hans: 一个解析JSON对象的工具
- pt_BR: A tool for extracting JSON objects
- llm: A tool for extracting JSON objects
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: JSON data
- zh_Hans: JSON数据
- pt_BR: JSON data
- human_description:
- en_US: JSON data
- zh_Hans: JSON数据
- pt_BR: JSON数据
- llm_description: JSON data to be processed
- form: llm
- - name: json_filter
- type: string
- required: true
- label:
- en_US: JSON filter
- zh_Hans: JSON解析对象
- pt_BR: JSON filter
- human_description:
- en_US: JSON fields to be parsed
- zh_Hans: 需要解析的 JSON 字段
- pt_BR: JSON fields to be parsed
- llm_description: JSON fields to be parsed
- form: llm
- - name: ensure_ascii
- type: boolean
- default: true
- label:
- en_US: Ensure ASCII
- zh_Hans: 确保 ASCII
- pt_BR: Ensure ASCII
- human_description:
- en_US: Ensure the JSON output is ASCII encoded
- zh_Hans: 确保输出的 JSON 是 ASCII 编码
- pt_BR: Ensure the JSON output is ASCII encoded
- form: form
diff --git a/api/core/tools/provider/builtin/json_process/tools/replace.py b/api/core/tools/provider/builtin/json_process/tools/replace.py
deleted file mode 100644
index feca0d8a7c2783..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/replace.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import json
-from typing import Any, Union
-
-from jsonpath_ng import parse # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class JSONReplaceTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get content
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- # get query
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Invalid parameter query")
-
- # get replace value
- replace_value = tool_parameters.get("replace_value", "")
- if not replace_value:
- return self.create_text_message("Invalid parameter replace_value")
-
- # get replace model
- replace_model = tool_parameters.get("replace_model", "")
- if not replace_model:
- return self.create_text_message("Invalid parameter replace_model")
-
- # get value decode.
- # if true, it will be decoded to an dict
- value_decode = tool_parameters.get("value_decode", False)
-
- ensure_ascii = tool_parameters.get("ensure_ascii", True)
- try:
- if replace_model == "pattern":
- # get replace pattern
- replace_pattern = tool_parameters.get("replace_pattern", "")
- if not replace_pattern:
- return self.create_text_message("Invalid parameter replace_pattern")
- result = self._replace_pattern(
- content, query, replace_pattern, replace_value, ensure_ascii, value_decode
- )
- elif replace_model == "key":
- result = self._replace_key(content, query, replace_value, ensure_ascii)
- elif replace_model == "value":
- result = self._replace_value(content, query, replace_value, ensure_ascii, value_decode)
- return self.create_text_message(str(result))
- except Exception:
- return self.create_text_message("Failed to replace JSON content")
-
- # Replace pattern
- def _replace_pattern(
- self, content: str, query: str, replace_pattern: str, replace_value: str, ensure_ascii: bool, value_decode: bool
- ) -> str:
- try:
- input_data = json.loads(content)
- expr = parse(query)
-
- matches = expr.find(input_data)
-
- for match in matches:
- new_value = match.value.replace(replace_pattern, replace_value)
- if value_decode is True:
- try:
- new_value = json.loads(new_value)
- except json.JSONDecodeError:
- return "Cannot decode replace value to json object"
-
- match.full_path.update(input_data, new_value)
-
- return json.dumps(input_data, ensure_ascii=ensure_ascii)
- except Exception as e:
- return str(e)
-
- # Replace key
- def _replace_key(self, content: str, query: str, replace_value: str, ensure_ascii: bool) -> str:
- try:
- input_data = json.loads(content)
- expr = parse(query)
-
- matches = expr.find(input_data)
-
- for match in matches:
- parent = match.context.value
- if isinstance(parent, dict):
- old_key = match.path.fields[0]
- if old_key in parent:
- value = parent.pop(old_key)
- parent[replace_value] = value
- elif isinstance(parent, list):
- for item in parent:
- if isinstance(item, dict) and old_key in item:
- value = item.pop(old_key)
- item[replace_value] = value
- return json.dumps(input_data, ensure_ascii=ensure_ascii)
- except Exception as e:
- return str(e)
-
- # Replace value
- def _replace_value(
- self, content: str, query: str, replace_value: str, ensure_ascii: bool, value_decode: bool
- ) -> str:
- try:
- input_data = json.loads(content)
- expr = parse(query)
- if value_decode is True:
- try:
- replace_value = json.loads(replace_value)
- except json.JSONDecodeError:
- return "Cannot decode replace value to json object"
-
- matches = expr.find(input_data)
-
- for match in matches:
- match.full_path.update(input_data, replace_value)
-
- return json.dumps(input_data, ensure_ascii=ensure_ascii)
- except Exception as e:
- return str(e)
diff --git a/api/core/tools/provider/builtin/json_process/tools/replace.yaml b/api/core/tools/provider/builtin/json_process/tools/replace.yaml
deleted file mode 100644
index ae238b1fbcd05e..00000000000000
--- a/api/core/tools/provider/builtin/json_process/tools/replace.yaml
+++ /dev/null
@@ -1,119 +0,0 @@
-identity:
- name: json_replace
- author: Mingwei Zhang
- label:
- en_US: JSON Replace
- zh_Hans: JSON 替换
- pt_BR: JSON Replace
-description:
- human:
- en_US: A tool for replacing JSON content
- zh_Hans: 一个替换 JSON 内容的工具
- pt_BR: A tool for replacing JSON content
- llm: A tool for replacing JSON content
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: JSON content
- zh_Hans: JSON 内容
- pt_BR: JSON content
- human_description:
- en_US: JSON content
- zh_Hans: JSON 内容
- pt_BR: JSON content
- llm_description: JSON content to be processed
- form: llm
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 查询
- pt_BR: Query
- human_description:
- en_US: Query
- zh_Hans: 查询
- pt_BR: Query
- llm_description: JSONPath query to locate the element to replace
- form: llm
- - name: replace_pattern
- type: string
- required: false
- label:
- en_US: String to be replaced
- zh_Hans: 待替换字符串
- pt_BR: String to be replaced
- human_description:
- en_US: String to be replaced
- zh_Hans: 待替换字符串
- pt_BR: String to be replaced
- llm_description: String to be replaced
- form: llm
- - name: replace_value
- type: string
- required: true
- label:
- en_US: Replace Value
- zh_Hans: 替换值
- pt_BR: Replace Value
- human_description:
- en_US: New Value
- zh_Hans: 新值
- pt_BR: New Value
- llm_description: New Value to replace
- form: llm
- - name: value_decode
- type: boolean
- default: false
- label:
- en_US: Decode Value
- zh_Hans: 解码值
- pt_BR: Decode Value
- human_description:
- en_US: Whether to decode the value to a JSON object (Does not apply to replace key)
- zh_Hans: 是否将值解码为 JSON 对象 (不适用于键替换)
- pt_BR: Whether to decode the value to a JSON object (Does not apply to replace key)
- form: form
- - name: replace_model
- type: select
- required: true
- default: pattern
- label:
- en_US: Replace Model
- zh_Hans: 替换模式
- pt_BR: Replace Model
- human_description:
- en_US: Replace Model
- zh_Hans: 替换模式
- pt_BR: Replace Model
- options:
- - value: key
- label:
- en_US: replace key
- zh_Hans: 键替换
- pt_BR: replace key
- - value: value
- label:
- en_US: replace value
- zh_Hans: 值替换
- pt_BR: replace value
- - value: pattern
- label:
- en_US: replace string
- zh_Hans: 字符串替换
- pt_BR: replace string
- form: form
- - name: ensure_ascii
- type: boolean
- default: true
- label:
- en_US: Ensure ASCII
- zh_Hans: 确保 ASCII
- pt_BR: Ensure ASCII
- human_description:
- en_US: Ensure the JSON output is ASCII encoded
- zh_Hans: 确保输出的 JSON 是 ASCII 编码
- pt_BR: Ensure the JSON output is ASCII encoded
- form: form
diff --git a/api/core/tools/provider/builtin/judge0ce/_assets/icon.svg b/api/core/tools/provider/builtin/judge0ce/_assets/icon.svg
deleted file mode 100644
index 3e7e33da6e8b25..00000000000000
--- a/api/core/tools/provider/builtin/judge0ce/_assets/icon.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/judge0ce/judge0ce.py b/api/core/tools/provider/builtin/judge0ce/judge0ce.py
deleted file mode 100644
index 50db74dd9ebced..00000000000000
--- a/api/core/tools/provider/builtin/judge0ce/judge0ce.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.judge0ce.tools.executeCode import ExecuteCodeTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class Judge0CEProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- ExecuteCodeTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "source_code": "print('hello world')",
- "language_id": 71,
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/judge0ce/judge0ce.yaml b/api/core/tools/provider/builtin/judge0ce/judge0ce.yaml
deleted file mode 100644
index 9ff8aaac6debc6..00000000000000
--- a/api/core/tools/provider/builtin/judge0ce/judge0ce.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- author: Richards Tu
- name: judge0ce
- label:
- en_US: Judge0 CE
- zh_Hans: Judge0 CE
- pt_BR: Judge0 CE
- description:
- en_US: Judge0 CE is an open-source code execution system. Support various languages, including C, C++, Java, Python, Ruby, etc.
- zh_Hans: Judge0 CE 是一个开源的代码执行系统。支持多种语言,包括 C、C++、Java、Python、Ruby 等。
- pt_BR: Judge0 CE é um sistema de execução de código de código aberto. Suporta várias linguagens, incluindo C, C++, Java, Python, Ruby, etc.
- icon: icon.svg
- tags:
- - utilities
- - other
-credentials_for_provider:
- X-RapidAPI-Key:
- type: secret-input
- required: true
- label:
- en_US: RapidAPI Key
- zh_Hans: RapidAPI Key
- pt_BR: RapidAPI Key
- help:
- en_US: RapidAPI Key is required to access the Judge0 CE API.
- zh_Hans: RapidAPI Key 是访问 Judge0 CE API 所必需的。
- pt_BR: RapidAPI Key é necessário para acessar a API do Judge0 CE.
- placeholder:
- en_US: Enter your RapidAPI Key
- zh_Hans: 输入你的 RapidAPI Key
- pt_BR: Insira sua RapidAPI Key
- url: https://rapidapi.com/judge0-official/api/judge0-ce
diff --git a/api/core/tools/provider/builtin/judge0ce/tools/executeCode.py b/api/core/tools/provider/builtin/judge0ce/tools/executeCode.py
deleted file mode 100644
index b8d654ff639575..00000000000000
--- a/api/core/tools/provider/builtin/judge0ce/tools/executeCode.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-from httpx import post
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class ExecuteCodeTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- api_key = self.runtime.credentials["X-RapidAPI-Key"]
-
- url = "https://judge0-ce.p.rapidapi.com/submissions"
-
- querystring = {"base64_encoded": "false", "fields": "*"}
-
- headers = {
- "Content-Type": "application/json",
- "X-RapidAPI-Key": api_key,
- "X-RapidAPI-Host": "judge0-ce.p.rapidapi.com",
- }
-
- payload = {
- "language_id": tool_parameters["language_id"],
- "source_code": tool_parameters["source_code"],
- "stdin": tool_parameters.get("stdin", ""),
- "expected_output": tool_parameters.get("expected_output", ""),
- "additional_files": tool_parameters.get("additional_files", ""),
- }
-
- response = post(url, data=json.dumps(payload), headers=headers, params=querystring)
-
- if response.status_code != 201:
- raise Exception(response.text)
-
- token = response.json()["token"]
-
- url = f"https://judge0-ce.p.rapidapi.com/submissions/{token}"
- headers = {"X-RapidAPI-Key": api_key}
-
- response = requests.get(url, headers=headers)
- if response.status_code == 200:
- result = response.json()
- return self.create_text_message(
- text=f"stdout: {result.get('stdout', '')}\n"
- f"stderr: {result.get('stderr', '')}\n"
- f"compile_output: {result.get('compile_output', '')}\n"
- f"message: {result.get('message', '')}\n"
- f"status: {result['status']['description']}\n"
- f"time: {result.get('time', '')} seconds\n"
- f"memory: {result.get('memory', '')} bytes"
- )
- else:
- return self.create_text_message(text=f"Error retrieving submission details: {response.text}")
diff --git a/api/core/tools/provider/builtin/judge0ce/tools/executeCode.yaml b/api/core/tools/provider/builtin/judge0ce/tools/executeCode.yaml
deleted file mode 100644
index a8c0776f40185e..00000000000000
--- a/api/core/tools/provider/builtin/judge0ce/tools/executeCode.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
-identity:
- name: submitCodeExecutionTask
- author: Richards Tu
- label:
- en_US: Submit Code Execution Task to Judge0 CE and get execution result.
- zh_Hans: 提交代码执行任务到 Judge0 CE 并获取执行结果。
-description:
- human:
- en_US: A tool for executing code and getting the result.
- zh_Hans: 一个用于执行代码并获取结果的工具。
- llm: This tool is used for executing code and getting the result.
-parameters:
- - name: source_code
- type: string
- required: true
- label:
- en_US: Source Code
- zh_Hans: 源代码
- human_description:
- en_US: The source code to be executed.
- zh_Hans: 要执行的源代码。
- llm_description: The source code to be executed.
- form: llm
- - name: language_id
- type: number
- required: true
- label:
- en_US: Language ID
- zh_Hans: 语言 ID
- human_description:
- en_US: The ID of the language in which the source code is written.
- zh_Hans: 源代码所使用的语言的 ID。
- llm_description: The ID of the language in which the source code is written. For example, 50 for C++, 71 for Python, etc.
- form: llm
- - name: stdin
- type: string
- required: false
- label:
- en_US: Standard Input
- zh_Hans: 标准输入
- human_description:
- en_US: The standard input to be provided to the program.
- zh_Hans: 提供给程序的标准输入。
- llm_description: The standard input to be provided to the program. Optional.
- form: llm
- - name: expected_output
- type: string
- required: false
- label:
- en_US: Expected Output
- zh_Hans: 期望输出
- human_description:
- en_US: The expected output of the program. Used for comparison in some scenarios.
- zh_Hans: 程序的期望输出。在某些场景下用于比较。
- llm_description: The expected output of the program. Used for comparison in some scenarios. Optional.
- form: llm
- - name: additional_files
- type: string
- required: false
- label:
- en_US: Additional Files
- zh_Hans: 附加文件
- human_description:
- en_US: Base64 encoded additional files for the submission.
- zh_Hans: 提交的 Base64 编码的附加文件。
- llm_description: Base64 encoded additional files for the submission. Optional.
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/_assets/icon.png b/api/core/tools/provider/builtin/lark_base/_assets/icon.png
deleted file mode 100644
index 036e586772ef50..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_base/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_base/lark_base.py b/api/core/tools/provider/builtin/lark_base/lark_base.py
deleted file mode 100644
index de9b3683119844..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/lark_base.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkBaseProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_base/lark_base.yaml b/api/core/tools/provider/builtin/lark_base/lark_base.yaml
deleted file mode 100644
index 200b2e22cfa558..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/lark_base.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_base
- label:
- en_US: Lark Base
- zh_Hans: Lark 多维表格
- description:
- en_US: |
- Lark base, requires the following permissions: bitable:app.
- zh_Hans: |
- Lark 多维表格,需要开通以下权限: bitable:app。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_base/tools/add_records.py b/api/core/tools/provider/builtin/lark_base/tools/add_records.py
deleted file mode 100644
index c46898062a8cc2..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/add_records.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class AddRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- records = tool_parameters.get("records")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.add_records(app_token, table_id, table_name, records, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/add_records.yaml b/api/core/tools/provider/builtin/lark_base/tools/add_records.yaml
deleted file mode 100644
index f2a93490dc0c31..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/add_records.yaml
+++ /dev/null
@@ -1,91 +0,0 @@
-identity:
- name: add_records
- author: Doug Lea
- label:
- en_US: Add Records
- zh_Hans: 新增多条记录
-description:
- human:
- en_US: Add Multiple Records to Multidimensional Table
- zh_Hans: 在多维表格数据表中新增多条记录
- llm: A tool for adding multiple records to a multidimensional table. (在多维表格数据表中新增多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: records
- type: string
- required: true
- label:
- en_US: records
- zh_Hans: 记录列表
- human_description:
- en_US: |
- List of records to be added in this request. Example value: [{"multi-line-text":"text content","single_select":"option 1","date":1674206443000}]
- For supported field types, refer to the integration guide (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification). For data structures of different field types, refer to the data structure overview (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure).
- zh_Hans: |
- 本次请求将要新增的记录列表,示例值:[{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- llm_description: |
- 本次请求将要新增的记录列表,示例值:[{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_base/tools/create_base.py b/api/core/tools/provider/builtin/lark_base/tools/create_base.py
deleted file mode 100644
index a857c6ced6f94b..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/create_base.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateBaseTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- name = tool_parameters.get("name")
- folder_token = tool_parameters.get("folder_token")
-
- res = client.create_base(name, folder_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/create_base.yaml b/api/core/tools/provider/builtin/lark_base/tools/create_base.yaml
deleted file mode 100644
index e622edf3362ba4..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/create_base.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-identity:
- name: create_base
- author: Doug Lea
- label:
- en_US: Create Base
- zh_Hans: 创建多维表格
-description:
- human:
- en_US: Create Multidimensional Table in Specified Directory
- zh_Hans: 在指定目录下创建多维表格
- llm: A tool for creating a multidimensional table in a specified directory. (在指定目录下创建多维表格)
-parameters:
- - name: name
- type: string
- required: false
- label:
- en_US: name
- zh_Hans: 多维表格 App 名字
- human_description:
- en_US: |
- Name of the multidimensional table App. Example value: "A new multidimensional table".
- zh_Hans: 多维表格 App 名字,示例值:"一篇新的多维表格"。
- llm_description: 多维表格 App 名字,示例值:"一篇新的多维表格"。
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: folder_token
- zh_Hans: 多维表格 App 归属文件夹
- human_description:
- en_US: |
- Folder where the multidimensional table App belongs. Default is empty, meaning the table will be created in the root directory of the cloud space. Example values: Lf8uf6BoAlWkUfdGtpMjUV0PpZd or https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd.
- The folder_token must be an existing folder and supports inputting folder token or folder URL.
- zh_Hans: |
- 多维表格 App 归属文件夹。默认为空,表示多维表格将被创建在云空间根目录。示例值: Lf8uf6BoAlWkUfdGtpMjUV0PpZd 或者 https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd。
- folder_token 必须是已存在的文件夹,支持输入文件夹 token 或者文件夹 URL。
- llm_description: |
- 多维表格 App 归属文件夹。默认为空,表示多维表格将被创建在云空间根目录。示例值: Lf8uf6BoAlWkUfdGtpMjUV0PpZd 或者 https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd。
- folder_token 必须是已存在的文件夹,支持输入文件夹 token 或者文件夹 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/create_table.py b/api/core/tools/provider/builtin/lark_base/tools/create_table.py
deleted file mode 100644
index aff7e715b73a73..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/create_table.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateTableTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_name = tool_parameters.get("table_name")
- default_view_name = tool_parameters.get("default_view_name")
- fields = tool_parameters.get("fields")
-
- res = client.create_table(app_token, table_name, default_view_name, fields)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/create_table.yaml b/api/core/tools/provider/builtin/lark_base/tools/create_table.yaml
deleted file mode 100644
index 8b1007b9a53166..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/create_table.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-identity:
- name: create_table
- author: Doug Lea
- label:
- en_US: Create Table
- zh_Hans: 新增数据表
-description:
- human:
- en_US: Add a Data Table to Multidimensional Table
- zh_Hans: 在多维表格中新增一个数据表
- llm: A tool for adding a data table to a multidimensional table. (在多维表格中新增一个数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_name
- type: string
- required: true
- label:
- en_US: Table Name
- zh_Hans: 数据表名称
- human_description:
- en_US: |
- The name of the data table, length range: 1 character to 100 characters.
- zh_Hans: 数据表名称,长度范围:1 字符 ~ 100 字符。
- llm_description: 数据表名称,长度范围:1 字符 ~ 100 字符。
- form: llm
-
- - name: default_view_name
- type: string
- required: false
- label:
- en_US: Default View Name
- zh_Hans: 默认表格视图的名称
- human_description:
- en_US: The name of the default table view, defaults to "Table" if not filled.
- zh_Hans: 默认表格视图的名称,不填则默认为"表格"。
- llm_description: 默认表格视图的名称,不填则默认为"表格"。
- form: llm
-
- - name: fields
- type: string
- required: true
- label:
- en_US: Initial Fields
- zh_Hans: 初始字段
- human_description:
- en_US: |
- Initial fields of the data table, format: [ { "field_name": "Multi-line Text","type": 1 },{ "field_name": "Number","type": 2 },{ "field_name": "Single Select","type": 3 },{ "field_name": "Multiple Select","type": 4 },{ "field_name": "Date","type": 5 } ]. For field details, refer to: https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- zh_Hans: 数据表的初始字段,格式为:[{"field_name":"多行文本","type":1},{"field_name":"数字","type":2},{"field_name":"单选","type":3},{"field_name":"多选","type":4},{"field_name":"日期","type":5}]。字段详情参考:https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- llm_description: 数据表的初始字段,格式为:[{"field_name":"多行文本","type":1},{"field_name":"数字","type":2},{"field_name":"单选","type":3},{"field_name":"多选","type":4},{"field_name":"日期","type":5}]。字段详情参考:https://open.larkoffice.com/document/server-docs/docs/bitable-v1/app-table-field/guide
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/delete_records.py b/api/core/tools/provider/builtin/lark_base/tools/delete_records.py
deleted file mode 100644
index 1b0a7470505e4d..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/delete_records.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class DeleteRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- record_ids = tool_parameters.get("record_ids")
-
- res = client.delete_records(app_token, table_id, table_name, record_ids)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/delete_records.yaml b/api/core/tools/provider/builtin/lark_base/tools/delete_records.yaml
deleted file mode 100644
index c30ebd630ce9d8..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/delete_records.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-identity:
- name: delete_records
- author: Doug Lea
- label:
- en_US: Delete Records
- zh_Hans: 删除多条记录
-description:
- human:
- en_US: Delete Multiple Records from Multidimensional Table
- zh_Hans: 删除多维表格数据表中的多条记录
- llm: A tool for deleting multiple records from a multidimensional table. (删除多维表格数据表中的多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: record_ids
- type: string
- required: true
- label:
- en_US: Record IDs
- zh_Hans: 记录 ID 列表
- human_description:
- en_US: |
- List of IDs for the records to be deleted, example value: ["recwNXzPQv"].
- zh_Hans: 删除的多条记录 ID 列表,示例值:["recwNXzPQv"]。
- llm_description: 删除的多条记录 ID 列表,示例值:["recwNXzPQv"]。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_base/tools/delete_tables.py b/api/core/tools/provider/builtin/lark_base/tools/delete_tables.py
deleted file mode 100644
index e0ecae2f175050..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/delete_tables.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class DeleteTablesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_ids = tool_parameters.get("table_ids")
- table_names = tool_parameters.get("table_names")
-
- res = client.delete_tables(app_token, table_ids, table_names)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/delete_tables.yaml b/api/core/tools/provider/builtin/lark_base/tools/delete_tables.yaml
deleted file mode 100644
index 498126eae53302..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/delete_tables.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-identity:
- name: delete_tables
- author: Doug Lea
- label:
- en_US: Delete Tables
- zh_Hans: 删除数据表
-description:
- human:
- en_US: Batch Delete Data Tables from Multidimensional Table
- zh_Hans: 批量删除多维表格中的数据表
- llm: A tool for batch deleting data tables from a multidimensional table. (批量删除多维表格中的数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_ids
- type: string
- required: false
- label:
- en_US: Table IDs
- zh_Hans: 数据表 ID
- human_description:
- en_US: |
- IDs of the tables to be deleted. Each operation supports deleting up to 50 tables. Example: ["tbl1TkhyTWDkSoZ3"]. Ensure that either table_ids or table_names is not empty.
- zh_Hans: 待删除的数据表的 ID,每次操作最多支持删除 50 个数据表。示例值:["tbl1TkhyTWDkSoZ3"]。请确保 table_ids 和 table_names 至少有一个不为空。
- llm_description: 待删除的数据表的 ID,每次操作最多支持删除 50 个数据表。示例值:["tbl1TkhyTWDkSoZ3"]。请确保 table_ids 和 table_names 至少有一个不为空。
- form: llm
-
- - name: table_names
- type: string
- required: false
- label:
- en_US: Table Names
- zh_Hans: 数据表名称
- human_description:
- en_US: |
- Names of the tables to be deleted. Each operation supports deleting up to 50 tables. Example: ["Table1", "Table2"]. Ensure that either table_names or table_ids is not empty.
- zh_Hans: 待删除的数据表的名称,每次操作最多支持删除 50 个数据表。示例值:["数据表1", "数据表2"]。请确保 table_names 和 table_ids 至少有一个不为空。
- llm_description: 待删除的数据表的名称,每次操作最多支持删除 50 个数据表。示例值:["数据表1", "数据表2"]。请确保 table_names 和 table_ids 至少有一个不为空。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/get_base_info.py b/api/core/tools/provider/builtin/lark_base/tools/get_base_info.py
deleted file mode 100644
index 2c23248b88765a..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/get_base_info.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetBaseInfoTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
-
- res = client.get_base_info(app_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/get_base_info.yaml b/api/core/tools/provider/builtin/lark_base/tools/get_base_info.yaml
deleted file mode 100644
index eb0e7a26c06a55..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/get_base_info.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: get_base_info
- author: Doug Lea
- label:
- en_US: Get Base Info
- zh_Hans: 获取多维表格元数据
-description:
- human:
- en_US: Get Metadata Information of Specified Multidimensional Table
- zh_Hans: 获取指定多维表格的元数据信息
- llm: A tool for getting metadata information of a specified multidimensional table. (获取指定多维表格的元数据信息)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/list_tables.py b/api/core/tools/provider/builtin/lark_base/tools/list_tables.py
deleted file mode 100644
index 55b706854b2735..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/list_tables.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ListTablesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- page_token = tool_parameters.get("page_token")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.list_tables(app_token, page_token, page_size)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/list_tables.yaml b/api/core/tools/provider/builtin/lark_base/tools/list_tables.yaml
deleted file mode 100644
index 7571519039bd24..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/list_tables.yaml
+++ /dev/null
@@ -1,50 +0,0 @@
-identity:
- name: list_tables
- author: Doug Lea
- label:
- en_US: List Tables
- zh_Hans: 列出数据表
-description:
- human:
- en_US: Get All Data Tables under Multidimensional Table
- zh_Hans: 获取多维表格下的所有数据表
- llm: A tool for getting all data tables under a multidimensional table. (获取多维表格下的所有数据表)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: |
- Page size, default value: 20, maximum value: 100.
- zh_Hans: 分页大小,默认值:20,最大值:100。
- llm_description: 分页大小,默认值:20,最大值:100。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: |
- Page token, leave empty for the first request to start from the beginning; a new page_token will be returned if there are more items in the paginated query results, which can be used for the next traversal. Example value: "tblsRc9GRRXKqhvW".
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/read_records.py b/api/core/tools/provider/builtin/lark_base/tools/read_records.py
deleted file mode 100644
index 5cf25aad848dfa..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/read_records.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ReadRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- record_ids = tool_parameters.get("record_ids")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_records(app_token, table_id, table_name, record_ids, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/read_records.yaml b/api/core/tools/provider/builtin/lark_base/tools/read_records.yaml
deleted file mode 100644
index 911e667cfc90ad..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/read_records.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-identity:
- name: read_records
- author: Doug Lea
- label:
- en_US: Read Records
- zh_Hans: 批量获取记录
-description:
- human:
- en_US: Batch Retrieve Records from Multidimensional Table
- zh_Hans: 批量获取多维表格数据表中的记录信息
- llm: A tool for batch retrieving records from a multidimensional table, supporting up to 100 records per call. (批量获取多维表格数据表中的记录信息,单次调用最多支持查询 100 条记录)
-
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: record_ids
- type: string
- required: true
- label:
- en_US: record_ids
- zh_Hans: 记录 ID 列表
- human_description:
- en_US: List of record IDs, which can be obtained by calling the "Query Records API".
- zh_Hans: 记录 ID 列表,可以通过调用"查询记录接口"获取。
- llm_description: 记录 ID 列表,可以通过调用"查询记录接口"获取。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_base/tools/search_records.py b/api/core/tools/provider/builtin/lark_base/tools/search_records.py
deleted file mode 100644
index 9b0abcf067951a..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/search_records.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class SearchRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- view_id = tool_parameters.get("view_id")
- field_names = tool_parameters.get("field_names")
- sort = tool_parameters.get("sort")
- filters = tool_parameters.get("filter")
- page_token = tool_parameters.get("page_token")
- automatic_fields = tool_parameters.get("automatic_fields", False)
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.search_record(
- app_token,
- table_id,
- table_name,
- view_id,
- field_names,
- sort,
- filters,
- page_token,
- automatic_fields,
- user_id_type,
- page_size,
- )
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/search_records.yaml b/api/core/tools/provider/builtin/lark_base/tools/search_records.yaml
deleted file mode 100644
index edd86ab9d69686..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/search_records.yaml
+++ /dev/null
@@ -1,163 +0,0 @@
-identity:
- name: search_records
- author: Doug Lea
- label:
- en_US: Search Records
- zh_Hans: 查询记录
-description:
- human:
- en_US: Query records in a multidimensional table, up to 500 rows per query.
- zh_Hans: 查询多维表格数据表中的记录,单次最多查询 500 行记录。
- llm: A tool for querying records in a multidimensional table, up to 500 rows per query. (查询多维表格数据表中的记录,单次最多查询 500 行记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: view_id
- type: string
- required: false
- label:
- en_US: view_id
- zh_Hans: 视图唯一标识
- human_description:
- en_US: |
- Unique identifier for a view in a multidimensional table. It can be found in the URL's query parameter with the key 'view'. For example: https://lark-japan.jp.larksuite.com/base/XXX0bfYEraW5OWsbhcFjEqj6pxh?table=tbl5I6jqwz8wBRMv&view=vewW5zXVEU.
- zh_Hans: 多维表格中视图的唯一标识,可在多维表格的 URL 地址栏中找到,query 参数中 key 为 view 的部分。例如:https://lark-japan.jp.larksuite.com/base/XXX0bfYEraW5OWsbhcFjEqj6pxh?table=tbl5I6jqwz8wBRMv&view=vewW5zXVEU。
- llm_description: 多维表格中视图的唯一标识,可在多维表格的 URL 地址栏中找到,query 参数中 key 为 view 的部分。例如:https://lark-japan.jp.larksuite.com/base/XXX0bfYEraW5OWsbhcFjEqj6pxh?table=tbl5I6jqwz8wBRMv&view=vewW5zXVEU。
- form: llm
-
- - name: field_names
- type: string
- required: false
- label:
- en_US: field_names
- zh_Hans: 字段名称
- human_description:
- en_US: |
- Field names to specify which fields to include in the returned records. Example value: ["Field1", "Field2"].
- zh_Hans: 字段名称,用于指定本次查询返回记录中包含的字段。示例值:["字段1","字段2"]。
- llm_description: 字段名称,用于指定本次查询返回记录中包含的字段。示例值:["字段1","字段2"]。
- form: llm
-
- - name: sort
- type: string
- required: false
- label:
- en_US: sort
- zh_Hans: 排序条件
- human_description:
- en_US: |
- Sorting conditions, for example: [{"field_name":"Multiline Text","desc":true}].
- zh_Hans: 排序条件,例如:[{"field_name":"多行文本","desc":true}]。
- llm_description: 排序条件,例如:[{"field_name":"多行文本","desc":true}]。
- form: llm
-
- - name: filter
- type: string
- required: false
- label:
- en_US: filter
- zh_Hans: 筛选条件
- human_description:
- en_US: Object containing filter information. For details on how to fill in the filter, refer to the record filter parameter guide (https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide).
- zh_Hans: 包含条件筛选信息的对象。了解如何填写 filter,参考记录筛选参数填写指南(https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide)。
- llm_description: 包含条件筛选信息的对象。了解如何填写 filter,参考记录筛选参数填写指南(https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide)。
- form: llm
-
- - name: automatic_fields
- type: boolean
- required: false
- label:
- en_US: automatic_fields
- zh_Hans: automatic_fields
- human_description:
- en_US: Whether to return automatically calculated fields. Default is false, meaning they are not returned.
- zh_Hans: 是否返回自动计算的字段。默认为 false,表示不返回。
- llm_description: 是否返回自动计算的字段。默认为 false,表示不返回。
- form: form
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: |
- Page size, default value: 20, maximum value: 500.
- zh_Hans: 分页大小,默认值:20,最大值:500。
- llm_description: 分页大小,默认值:20,最大值:500。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: |
- Page token, leave empty for the first request to start from the beginning; a new page_token will be returned if there are more items in the paginated query results, which can be used for the next traversal. Example value: "tblsRc9GRRXKqhvW".
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。示例值:"tblsRc9GRRXKqhvW"。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_base/tools/update_records.py b/api/core/tools/provider/builtin/lark_base/tools/update_records.py
deleted file mode 100644
index 7c263df2bb031c..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/update_records.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class UpdateRecordsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- app_token = tool_parameters.get("app_token")
- table_id = tool_parameters.get("table_id")
- table_name = tool_parameters.get("table_name")
- records = tool_parameters.get("records")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.update_records(app_token, table_id, table_name, records, user_id_type)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_base/tools/update_records.yaml b/api/core/tools/provider/builtin/lark_base/tools/update_records.yaml
deleted file mode 100644
index 68117e71367892..00000000000000
--- a/api/core/tools/provider/builtin/lark_base/tools/update_records.yaml
+++ /dev/null
@@ -1,91 +0,0 @@
-identity:
- name: update_records
- author: Doug Lea
- label:
- en_US: Update Records
- zh_Hans: 更新多条记录
-description:
- human:
- en_US: Update Multiple Records in Multidimensional Table
- zh_Hans: 更新多维表格数据表中的多条记录
- llm: A tool for updating multiple records in a multidimensional table. (更新多维表格数据表中的多条记录)
-parameters:
- - name: app_token
- type: string
- required: true
- label:
- en_US: app_token
- zh_Hans: app_token
- human_description:
- en_US: Unique identifier for the multidimensional table, supports inputting document URL.
- zh_Hans: 多维表格的唯一标识符,支持输入文档 URL。
- llm_description: 多维表格的唯一标识符,支持输入文档 URL。
- form: llm
-
- - name: table_id
- type: string
- required: false
- label:
- en_US: table_id
- zh_Hans: table_id
- human_description:
- en_US: Unique identifier for the multidimensional table data, either table_id or table_name must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的唯一标识符,table_id 和 table_name 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: table_name
- type: string
- required: false
- label:
- en_US: table_name
- zh_Hans: table_name
- human_description:
- en_US: Name of the multidimensional table data, either table_name or table_id must be provided, cannot be empty simultaneously.
- zh_Hans: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- llm_description: 多维表格数据表的名称,table_name 和 table_id 至少需要提供一个,不能同时为空。
- form: llm
-
- - name: records
- type: string
- required: true
- label:
- en_US: records
- zh_Hans: 记录列表
- human_description:
- en_US: |
- List of records to be updated in this request. Example value: [{"fields":{"multi-line-text":"text content","single_select":"option 1","date":1674206443000},"record_id":"recupK4f4RM5RX"}].
- For supported field types, refer to the integration guide (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification). For data structures of different field types, refer to the data structure overview (https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure).
- zh_Hans: |
- 本次请求将要更新的记录列表,示例值:[{"fields":{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000},"record_id":"recupK4f4RM5RX"}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- llm_description: |
- 本次请求将要更新的记录列表,示例值:[{"fields":{"多行文本":"文本内容","单选":"选项 1","日期":1674206443000},"record_id":"recupK4f4RM5RX"}]。
- 当前接口支持的字段类型请参考接入指南(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/notification),不同类型字段的数据结构请参考数据结构概述(https://open.larkoffice.com/document/server-docs/docs/bitable-v1/bitable-structure)。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_calendar/_assets/icon.png b/api/core/tools/provider/builtin/lark_calendar/_assets/icon.png
deleted file mode 100644
index 2a934747a98c66..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_calendar/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_calendar/lark_calendar.py b/api/core/tools/provider/builtin/lark_calendar/lark_calendar.py
deleted file mode 100644
index 871de69cc15b39..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/lark_calendar.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkCalendarProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_calendar/lark_calendar.yaml b/api/core/tools/provider/builtin/lark_calendar/lark_calendar.yaml
deleted file mode 100644
index 72c41e36c0ebd3..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/lark_calendar.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_calendar
- label:
- en_US: Lark Calendar
- zh_Hans: Lark 日历
- description:
- en_US: |
- Lark calendar, requires the following permissions: calendar:calendar:read、calendar:calendar、contact:user.id:readonly.
- zh_Hans: |
- Lark 日历,需要开通以下权限: calendar:calendar:read、calendar:calendar、contact:user.id:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.py b/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.py
deleted file mode 100644
index f5929893ddfe24..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class AddEventAttendeesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id")
- attendee_phone_or_email = tool_parameters.get("attendee_phone_or_email")
- need_notification = tool_parameters.get("need_notification", True)
-
- res = client.add_event_attendees(event_id, attendee_phone_or_email, need_notification)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.yaml
deleted file mode 100644
index 9d7a1319072d6f..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/add_event_attendees.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-identity:
- name: add_event_attendees
- author: Doug Lea
- label:
- en_US: Add Event Attendees
- zh_Hans: 添加日程参会人
-description:
- human:
- en_US: Add Event Attendees
- zh_Hans: 添加日程参会人
- llm: A tool for adding attendees to events in Lark. (在 Lark 中添加日程参会人)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, which will be returned when the event is created. For example: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0.
- zh_Hans: |
- 创建日程时会返回日程 ID。例如: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0。
- llm_description: |
- 日程 ID,创建日程时会返回日程 ID。例如: fb2a6406-26d6-4c8d-a487-6f0246c94d2f_0。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否需要通知
- human_description:
- en_US: |
- Whether to send a Bot notification to attendees. true: send, false: do not send.
- zh_Hans: |
- 是否给参与人发送 Bot 通知,true: 发送,false: 不发送。
- llm_description: |
- 是否给参与人发送 Bot 通知,true: 发送,false: 不发送。
- form: form
-
- - name: attendee_phone_or_email
- type: string
- required: true
- label:
- en_US: Attendee Phone or Email
- zh_Hans: 参会人电话或邮箱
- human_description:
- en_US: The list of attendee emails or phone numbers, separated by commas.
- zh_Hans: 日程参会人邮箱或者手机号列表,使用逗号分隔。
- llm_description: 日程参会人邮箱或者手机号列表,使用逗号分隔。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/create_event.py b/api/core/tools/provider/builtin/lark_calendar/tools/create_event.py
deleted file mode 100644
index 8a0726008c3f8b..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/create_event.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- summary = tool_parameters.get("summary")
- description = tool_parameters.get("description")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- attendee_ability = tool_parameters.get("attendee_ability")
- need_notification = tool_parameters.get("need_notification", True)
- auto_record = tool_parameters.get("auto_record", False)
-
- res = client.create_event(
- summary, description, start_time, end_time, attendee_ability, need_notification, auto_record
- )
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/create_event.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/create_event.yaml
deleted file mode 100644
index b738736e630fa5..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/create_event.yaml
+++ /dev/null
@@ -1,119 +0,0 @@
-identity:
- name: create_event
- author: Doug Lea
- label:
- en_US: Create Event
- zh_Hans: 创建日程
-description:
- human:
- en_US: Create Event
- zh_Hans: 创建日程
- llm: A tool for creating events in Lark.(创建 Lark 日程)
-parameters:
- - name: summary
- type: string
- required: false
- label:
- en_US: Summary
- zh_Hans: 日程标题
- human_description:
- en_US: The title of the event. If not filled, the event title will display (No Subject).
- zh_Hans: 日程标题,若不填则日程标题显示 (无主题)。
- llm_description: 日程标题,若不填则日程标题显示 (无主题)。
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Description
- zh_Hans: 日程描述
- human_description:
- en_US: The description of the event.
- zh_Hans: 日程描述。
- llm_description: 日程描述。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否发送通知
- human_description:
- en_US: |
- Whether to send a bot message when the event is created, true: send, false: do not send.
- zh_Hans: 创建日程时是否发送 bot 消息,true:发送,false:不发送。
- llm_description: 创建日程时是否发送 bot 消息,true:发送,false:不发送。
- form: form
-
- - name: start_time
- type: string
- required: true
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程开始时间,格式:2006-01-02 15:04:05。
- llm_description: 日程开始时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: true
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程结束时间,格式:2006-01-02 15:04:05。
- llm_description: 日程结束时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: attendee_ability
- type: select
- required: false
- options:
- - value: none
- label:
- en_US: none
- zh_Hans: 无
- - value: can_see_others
- label:
- en_US: can_see_others
- zh_Hans: 可以查看参与人列表
- - value: can_invite_others
- label:
- en_US: can_invite_others
- zh_Hans: 可以邀请其它参与人
- - value: can_modify_event
- label:
- en_US: can_modify_event
- zh_Hans: 可以编辑日程
- default: "none"
- label:
- en_US: attendee_ability
- zh_Hans: 参会人权限
- human_description:
- en_US: Attendee ability, optional values are none, can_see_others, can_invite_others, can_modify_event, with a default value of none.
- zh_Hans: 参会人权限,可选值有无、可以查看参与人列表、可以邀请其它参与人、可以编辑日程,默认值为无。
- llm_description: 参会人权限,可选值有无、可以查看参与人列表、可以邀请其它参与人、可以编辑日程,默认值为无。
- form: form
-
- - name: auto_record
- type: boolean
- required: false
- default: false
- label:
- en_US: Auto Record
- zh_Hans: 自动录制
- human_description:
- en_US: |
- Whether to enable automatic recording, true: enabled, automatically record when the meeting starts; false: not enabled.
- zh_Hans: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- llm_description: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.py b/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.py
deleted file mode 100644
index 0e4ceac5e5d070..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class DeleteEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id")
- need_notification = tool_parameters.get("need_notification", True)
-
- res = client.delete_event(event_id, need_notification)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.yaml
deleted file mode 100644
index cdd6d7e1bb024a..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/delete_event.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-identity:
- name: delete_event
- author: Doug Lea
- label:
- en_US: Delete Event
- zh_Hans: 删除日程
-description:
- human:
- en_US: Delete Event
- zh_Hans: 删除日程
- llm: A tool for deleting events in Lark.(在 Lark 中删除日程)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, for example: e8b9791c-39ae-4908-8ad8-66b13159b9fb_0.
- zh_Hans: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- llm_description: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- default: true
- label:
- en_US: Need Notification
- zh_Hans: 是否需要通知
- human_description:
- en_US: |
- Indicates whether to send bot notifications to event participants upon deletion. true: send, false: do not send.
- zh_Hans: 删除日程是否给日程参与人发送 bot 通知,true:发送,false:不发送。
- llm_description: 删除日程是否给日程参与人发送 bot 通知,true:发送,false:不发送。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.py b/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.py
deleted file mode 100644
index d315bf35f05d98..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetPrimaryCalendarTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.get_primary_calendar(user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.yaml
deleted file mode 100644
index fe615947700995..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/get_primary_calendar.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-identity:
- name: get_primary_calendar
- author: Doug Lea
- label:
- en_US: Get Primary Calendar
- zh_Hans: 查询主日历信息
-description:
- human:
- en_US: Get Primary Calendar
- zh_Hans: 查询主日历信息
- llm: A tool for querying primary calendar information in Lark.(在 Lark 中查询主日历信息)
-parameters:
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/list_events.py b/api/core/tools/provider/builtin/lark_calendar/tools/list_events.py
deleted file mode 100644
index d74cc049d34230..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/list_events.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ListEventsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- page_token = tool_parameters.get("page_token")
- page_size = tool_parameters.get("page_size")
-
- res = client.list_events(start_time, end_time, page_token, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/list_events.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/list_events.yaml
deleted file mode 100644
index cef332f5272e55..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/list_events.yaml
+++ /dev/null
@@ -1,62 +0,0 @@
-identity:
- name: list_events
- author: Doug Lea
- label:
- en_US: List Events
- zh_Hans: 获取日程列表
-description:
- human:
- en_US: List Events
- zh_Hans: 获取日程列表
- llm: A tool for listing events in Lark.(在 Lark 中获取日程列表)
-parameters:
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time, defaults to 0:00 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- llm_description: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time, defaults to 23:59 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- llm_description: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 50
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 50, and the value range is [50,1000].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 50,取值范围为 [50,1000]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 50,取值范围为 [50,1000]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/search_events.py b/api/core/tools/provider/builtin/lark_calendar/tools/search_events.py
deleted file mode 100644
index a20038e47dd430..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/search_events.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class SearchEventsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- query = tool_parameters.get("query")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- page_token = tool_parameters.get("page_token")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.search_events(query, start_time, end_time, page_token, user_id_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/search_events.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/search_events.yaml
deleted file mode 100644
index 4d4f8819c11e4d..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/search_events.yaml
+++ /dev/null
@@ -1,100 +0,0 @@
-identity:
- name: search_events
- author: Doug Lea
- label:
- en_US: Search Events
- zh_Hans: 搜索日程
-description:
- human:
- en_US: Search Events
- zh_Hans: 搜索日程
- llm: A tool for searching events in Lark.(在 Lark 中搜索日程)
-parameters:
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 搜索关键字
- human_description:
- en_US: The search keyword used for fuzzy searching event names, with a maximum input of 200 characters.
- zh_Hans: 用于模糊查询日程名称的搜索关键字,最大输入 200 字符。
- llm_description: 用于模糊查询日程名称的搜索关键字,最大输入 200 字符。
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time, defaults to 0:00 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- llm_description: 开始时间,不传值时默认当天 0 点时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time, defaults to 23:59 of the current day if not provided, format: 2006-01-02 15:04:05.
- zh_Hans: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- llm_description: 结束时间,不传值时默认当天 23:59 分时间,格式为:2006-01-02 15:04:05。
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [10,100].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [10,100]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [10,100]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/update_event.py b/api/core/tools/provider/builtin/lark_calendar/tools/update_event.py
deleted file mode 100644
index a04029377f6799..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/update_event.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class UpdateEventTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- event_id = tool_parameters.get("event_id")
- summary = tool_parameters.get("summary")
- description = tool_parameters.get("description")
- need_notification = tool_parameters.get("need_notification", True)
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- auto_record = tool_parameters.get("auto_record", False)
-
- res = client.update_event(event_id, summary, description, need_notification, start_time, end_time, auto_record)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_calendar/tools/update_event.yaml b/api/core/tools/provider/builtin/lark_calendar/tools/update_event.yaml
deleted file mode 100644
index b9992e5b03f944..00000000000000
--- a/api/core/tools/provider/builtin/lark_calendar/tools/update_event.yaml
+++ /dev/null
@@ -1,100 +0,0 @@
-identity:
- name: update_event
- author: Doug Lea
- label:
- en_US: Update Event
- zh_Hans: 更新日程
-description:
- human:
- en_US: Update Event
- zh_Hans: 更新日程
- llm: A tool for updating events in Lark.(更新 Lark 中的日程)
-parameters:
- - name: event_id
- type: string
- required: true
- label:
- en_US: Event ID
- zh_Hans: 日程 ID
- human_description:
- en_US: |
- The ID of the event, for example: e8b9791c-39ae-4908-8ad8-66b13159b9fb_0.
- zh_Hans: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- llm_description: 日程 ID,例如:e8b9791c-39ae-4908-8ad8-66b13159b9fb_0。
- form: llm
-
- - name: summary
- type: string
- required: false
- label:
- en_US: Summary
- zh_Hans: 日程标题
- human_description:
- en_US: The title of the event.
- zh_Hans: 日程标题。
- llm_description: 日程标题。
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Description
- zh_Hans: 日程描述
- human_description:
- en_US: The description of the event.
- zh_Hans: 日程描述。
- llm_description: 日程描述。
- form: llm
-
- - name: need_notification
- type: boolean
- required: false
- label:
- en_US: Need Notification
- zh_Hans: 是否发送通知
- human_description:
- en_US: |
- Whether to send a bot message when the event is updated, true: send, false: do not send.
- zh_Hans: 更新日程时是否发送 bot 消息,true:发送,false:不发送。
- llm_description: 更新日程时是否发送 bot 消息,true:发送,false:不发送。
- form: form
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 开始时间
- human_description:
- en_US: |
- The start time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程开始时间,格式:2006-01-02 15:04:05。
- llm_description: 日程开始时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: |
- The end time of the event, format: 2006-01-02 15:04:05.
- zh_Hans: 日程结束时间,格式:2006-01-02 15:04:05。
- llm_description: 日程结束时间,格式:2006-01-02 15:04:05。
- form: llm
-
- - name: auto_record
- type: boolean
- required: false
- label:
- en_US: Auto Record
- zh_Hans: 自动录制
- human_description:
- en_US: |
- Whether to enable automatic recording, true: enabled, automatically record when the meeting starts; false: not enabled.
- zh_Hans: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- llm_description: 是否开启自动录制,true:开启,会议开始后自动录制;false:不开启。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_document/_assets/icon.svg b/api/core/tools/provider/builtin/lark_document/_assets/icon.svg
deleted file mode 100644
index 5a0a6416b3db32..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/_assets/icon.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/lark_document/lark_document.py b/api/core/tools/provider/builtin/lark_document/lark_document.py
deleted file mode 100644
index b1283276028361..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/lark_document.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkDocumentProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_document/lark_document.yaml b/api/core/tools/provider/builtin/lark_document/lark_document.yaml
deleted file mode 100644
index 0cb4ae1d62d3f8..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/lark_document.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_document
- label:
- en_US: Lark Cloud Document
- zh_Hans: Lark 云文档
- description:
- en_US: |
- Lark cloud document, requires the following permissions: docx:document、drive:drive、docs:document.content:read.
- zh_Hans: |
- Lark 云文档,需要开通以下权限: docx:document、drive:drive、docs:document.content:read。
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_document/tools/create_document.py b/api/core/tools/provider/builtin/lark_document/tools/create_document.py
deleted file mode 100644
index 2b1dae0db5578c..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/create_document.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateDocumentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- title = tool_parameters.get("title")
- content = tool_parameters.get("content")
- folder_token = tool_parameters.get("folder_token")
-
- res = client.create_document(title, content, folder_token)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_document/tools/create_document.yaml b/api/core/tools/provider/builtin/lark_document/tools/create_document.yaml
deleted file mode 100644
index 37a1e23041c6c9..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/create_document.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-identity:
- name: create_document
- author: Doug Lea
- label:
- en_US: Create Lark document
- zh_Hans: 创建 Lark 文档
-description:
- human:
- en_US: Create Lark document
- zh_Hans: 创建 Lark 文档,支持创建空文档和带内容的文档,支持 markdown 语法创建。应用需要开启机器人能力(https://open.larksuite.com/document/faq/trouble-shooting/how-to-enable-bot-ability)。
- llm: A tool for creating Lark documents.
-parameters:
- - name: title
- type: string
- required: false
- label:
- en_US: Document title
- zh_Hans: 文档标题
- human_description:
- en_US: Document title, only supports plain text content.
- zh_Hans: 文档标题,只支持纯文本内容。
- llm_description: 文档标题,只支持纯文本内容,可以为空。
- form: llm
-
- - name: content
- type: string
- required: false
- label:
- en_US: Document content
- zh_Hans: 文档内容
- human_description:
- en_US: Document content, supports markdown syntax, can be empty.
- zh_Hans: 文档内容,支持 markdown 语法,可以为空。
- llm_description: 文档内容,支持 markdown 语法,可以为空。
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: folder_token
- zh_Hans: 文档所在文件夹的 Token
- human_description:
- en_US: |
- The token of the folder where the document is located. If it is not passed or is empty, it means the root directory. For Example: https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd
- zh_Hans: 文档所在文件夹的 Token,不传或传空表示根目录。例如:https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd。
- llm_description: 文档所在文件夹的 Token,不传或传空表示根目录。例如:https://lark-japan.jp.larksuite.com/drive/folder/Lf8uf6BoAlWkUfdGtpMjUV0PpZd。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_document/tools/get_document_content.py b/api/core/tools/provider/builtin/lark_document/tools/get_document_content.py
deleted file mode 100644
index d15211b57e7a76..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/get_document_content.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetDocumentRawContentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id")
- mode = tool_parameters.get("mode", "markdown")
- lang = tool_parameters.get("lang", "0")
-
- res = client.get_document_content(document_id, mode, lang)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_document/tools/get_document_content.yaml b/api/core/tools/provider/builtin/lark_document/tools/get_document_content.yaml
deleted file mode 100644
index fd6a033bfd6947..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/get_document_content.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-identity:
- name: get_document_content
- author: Doug Lea
- label:
- en_US: Get Lark Cloud Document Content
- zh_Hans: 获取 Lark 云文档的内容
-description:
- human:
- en_US: Get lark cloud document content
- zh_Hans: 获取 Lark 云文档的内容
- llm: A tool for retrieving content from Lark cloud documents.
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: Lark 文档的唯一标识
- human_description:
- en_US: Unique identifier for a Lark document. You can also input the document's URL.
- zh_Hans: Lark 文档的唯一标识,支持输入文档的 URL。
- llm_description: Lark 文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: mode
- type: select
- required: false
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: text
- - value: markdown
- label:
- en_US: markdown
- zh_Hans: markdown
- default: "markdown"
- label:
- en_US: mode
- zh_Hans: 文档返回格式
- human_description:
- en_US: Format of the document return, optional values are text, markdown, can be empty, default is markdown.
- zh_Hans: 文档返回格式,可选值有 text、markdown,可以为空,默认值为 markdown。
- llm_description: 文档返回格式,可选值有 text、markdown,可以为空,默认值为 markdown。
- form: form
-
- - name: lang
- type: select
- required: false
- options:
- - value: "0"
- label:
- en_US: User's default name
- zh_Hans: 用户的默认名称
- - value: "1"
- label:
- en_US: User's English name
- zh_Hans: 用户的英文名称
- default: "0"
- label:
- en_US: lang
- zh_Hans: 指定@用户的语言
- human_description:
- en_US: |
- Specifies the language for MentionUser, optional values are [0, 1]. 0: User's default name, 1: User's English name, default is 0.
- zh_Hans: |
- 指定返回的 MentionUser,即@用户的语言,可选值有 [0,1]。0: 该用户的默认名称,1: 该用户的英文名称,默认值为 0。
- llm_description: |
- 指定返回的 MentionUser,即@用户的语言,可选值有 [0,1]。0: 该用户的默认名称,1: 该用户的英文名称,默认值为 0。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.py b/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.py
deleted file mode 100644
index b96a87489e055e..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ListDocumentBlockTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id")
- page_token = tool_parameters.get("page_token", "")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
- page_size = tool_parameters.get("page_size", 500)
-
- res = client.list_document_blocks(document_id, page_token, user_id_type, page_size)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.yaml b/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.yaml
deleted file mode 100644
index 08b673e0ae3ddc..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/list_document_blocks.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-identity:
- name: list_document_blocks
- author: Doug Lea
- label:
- en_US: List Lark Document Blocks
- zh_Hans: 获取 Lark 文档所有块
-description:
- human:
- en_US: List lark document blocks
- zh_Hans: 获取 Lark 文档所有块的富文本内容并分页返回
- llm: A tool to get all blocks of Lark documents
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: Lark 文档的唯一标识
- human_description:
- en_US: Unique identifier for a Lark document. You can also input the document's URL.
- zh_Hans: Lark 文档的唯一标识,支持输入文档的 URL。
- llm_description: Lark 文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 500
- label:
- en_US: page_size
- zh_Hans: 分页大小
- human_description:
- en_US: Paging size, the default and maximum value is 500.
- zh_Hans: 分页大小, 默认值和最大值为 500。
- llm_description: 分页大小, 表示一次请求最多返回多少条数据,默认值和最大值为 500。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: page_token
- zh_Hans: 分页标记
- human_description:
- en_US: Pagination token used to navigate through query results, allowing retrieval of additional items in subsequent requests.
- zh_Hans: 分页标记,用于分页查询结果,以便下次遍历时获取更多项。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_document/tools/write_document.py b/api/core/tools/provider/builtin/lark_document/tools/write_document.py
deleted file mode 100644
index 888e0e39fce389..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/write_document.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateDocumentTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- document_id = tool_parameters.get("document_id")
- content = tool_parameters.get("content")
- position = tool_parameters.get("position", "end")
-
- res = client.write_document(document_id, content, position)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_document/tools/write_document.yaml b/api/core/tools/provider/builtin/lark_document/tools/write_document.yaml
deleted file mode 100644
index 9cdf034ed08230..00000000000000
--- a/api/core/tools/provider/builtin/lark_document/tools/write_document.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-identity:
- name: write_document
- author: Doug Lea
- label:
- en_US: Write Document
- zh_Hans: 在 Lark 文档中新增内容
-description:
- human:
- en_US: Adding new content to Lark documents
- zh_Hans: 在 Lark 文档中新增内容
- llm: A tool for adding new content to Lark documents.
-parameters:
- - name: document_id
- type: string
- required: true
- label:
- en_US: document_id
- zh_Hans: Lark 文档的唯一标识
- human_description:
- en_US: Unique identifier for a Lark document. You can also input the document's URL.
- zh_Hans: Lark 文档的唯一标识,支持输入文档的 URL。
- llm_description: Lark 文档的唯一标识,支持输入文档的 URL。
- form: llm
-
- - name: content
- type: string
- required: true
- label:
- en_US: Plain text or Markdown content
- zh_Hans: 纯文本或 Markdown 内容
- human_description:
- en_US: Plain text or Markdown content. Note that embedded tables in the document should not have merged cells.
- zh_Hans: 纯文本或 Markdown 内容。注意文档的内嵌套表格不允许有单元格合并。
- llm_description: 纯文本或 Markdown 内容,注意文档的内嵌套表格不允许有单元格合并。
- form: llm
-
- - name: position
- type: select
- required: false
- options:
- - value: start
- label:
- en_US: document start
- zh_Hans: 文档开始
- - value: end
- label:
- en_US: document end
- zh_Hans: 文档结束
- default: "end"
- label:
- en_US: position
- zh_Hans: 内容添加位置
- human_description:
- en_US: Content insertion position, optional values are start, end. 'start' means adding content at the beginning of the document; 'end' means adding content at the end of the document. The default value is end.
- zh_Hans: 内容添加位置,可选值有 start、end。start 表示在文档开头添加内容;end 表示在文档结尾添加内容,默认值为 end。
- llm_description: 内容添加位置,可选值有 start、end。start 表示在文档开头添加内容;end 表示在文档结尾添加内容,默认值为 end。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/_assets/icon.png b/api/core/tools/provider/builtin/lark_message_and_group/_assets/icon.png
deleted file mode 100644
index 0dfd58a9d512fd..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_message_and_group/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.py b/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.py
deleted file mode 100644
index de6997b0bf942f..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkMessageAndGroupProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.yaml b/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.yaml
deleted file mode 100644
index ad3fe0f3619098..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/lark_message_and_group.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_message_and_group
- label:
- en_US: Lark Message And Group
- zh_Hans: Lark 消息和群组
- description:
- en_US: |
- Lark message and group, requires the following permissions: im:message、im:message.group_msg.
- zh_Hans: |
- Lark 消息和群组,需要开通以下权限: im:message、im:message.group_msg。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.py b/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.py
deleted file mode 100644
index 118bac7ab7d720..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetChatMessagesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- container_id = tool_parameters.get("container_id")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- page_token = tool_parameters.get("page_token")
- sort_type = tool_parameters.get("sort_type", "ByCreateTimeAsc")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.get_chat_messages(container_id, start_time, end_time, page_token, sort_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.yaml b/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.yaml
deleted file mode 100644
index 965b45a5fbaec9..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_chat_messages.yaml
+++ /dev/null
@@ -1,96 +0,0 @@
-identity:
- name: get_chat_messages
- author: Doug Lea
- label:
- en_US: Get Chat Messages
- zh_Hans: 获取指定单聊、群聊的消息历史
-description:
- human:
- en_US: Get Chat Messages
- zh_Hans: 获取指定单聊、群聊的消息历史
- llm: A tool for getting chat messages from specific one-on-one chats or group chats.(获取指定单聊、群聊的消息历史)
-parameters:
- - name: container_id
- type: string
- required: true
- label:
- en_US: Container Id
- zh_Hans: 群聊或单聊的 ID
- human_description:
- en_US: The ID of the group chat or single chat. Refer to the group ID description for how to obtain it. https://open.larkoffice.com/document/server-docs/group/chat/chat-id-description
- zh_Hans: 群聊或单聊的 ID,获取方式参见群 ID 说明。https://open.larkoffice.com/document/server-docs/group/chat/chat-id-description
- llm_description: 群聊或单聊的 ID,获取方式参见群 ID 说明。https://open.larkoffice.com/document/server-docs/group/chat/chat-id-description
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 起始时间
- human_description:
- en_US: The start time for querying historical messages, formatted as "2006-01-02 15:04:05".
- zh_Hans: 待查询历史信息的起始时间,格式为 "2006-01-02 15:04:05"。
- llm_description: 待查询历史信息的起始时间,格式为 "2006-01-02 15:04:05"。
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 结束时间
- human_description:
- en_US: The end time for querying historical messages, formatted as "2006-01-02 15:04:05".
- zh_Hans: 待查询历史信息的结束时间,格式为 "2006-01-02 15:04:05"。
- llm_description: 待查询历史信息的结束时间,格式为 "2006-01-02 15:04:05"。
- form: llm
-
- - name: sort_type
- type: select
- required: false
- options:
- - value: ByCreateTimeAsc
- label:
- en_US: ByCreateTimeAsc
- zh_Hans: ByCreateTimeAsc
- - value: ByCreateTimeDesc
- label:
- en_US: ByCreateTimeDesc
- zh_Hans: ByCreateTimeDesc
- default: "ByCreateTimeAsc"
- label:
- en_US: Sort Type
- zh_Hans: 排序方式
- human_description:
- en_US: |
- The message sorting method. Optional values are ByCreateTimeAsc: sorted in ascending order by message creation time; ByCreateTimeDesc: sorted in descending order by message creation time. The default value is ByCreateTimeAsc. Note: When using page_token for pagination requests, the sorting method (sort_type) is consistent with the first request and cannot be changed midway.
- zh_Hans: |
- 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- llm_description: 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [1,50].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.py b/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.py
deleted file mode 100644
index 3509d9bbcfe437..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetChatMessagesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- container_id = tool_parameters.get("container_id")
- page_token = tool_parameters.get("page_token")
- sort_type = tool_parameters.get("sort_type", "ByCreateTimeAsc")
- page_size = tool_parameters.get("page_size", 20)
-
- res = client.get_thread_messages(container_id, page_token, sort_type, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.yaml b/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.yaml
deleted file mode 100644
index 5f7a4f0902523e..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/get_thread_messages.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: get_thread_messages
- author: Doug Lea
- label:
- en_US: Get Thread Messages
- zh_Hans: 获取指定话题的消息历史
-description:
- human:
- en_US: Get Thread Messages
- zh_Hans: 获取指定话题的消息历史
- llm: A tool for getting chat messages from specific threads.(获取指定话题的消息历史)
-parameters:
- - name: container_id
- type: string
- required: true
- label:
- en_US: Thread Id
- zh_Hans: 话题 ID
- human_description:
- en_US: The ID of the thread. Refer to the thread overview on how to obtain the thread_id. https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- zh_Hans: 话题 ID,获取方式参见话题概述的如何获取 thread_id 章节。https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- llm_description: 话题 ID,获取方式参见话题概述的如何获取 thread_id 章节。https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/thread-introduction
- form: llm
-
- - name: sort_type
- type: select
- required: false
- options:
- - value: ByCreateTimeAsc
- label:
- en_US: ByCreateTimeAsc
- zh_Hans: ByCreateTimeAsc
- - value: ByCreateTimeDesc
- label:
- en_US: ByCreateTimeDesc
- zh_Hans: ByCreateTimeDesc
- default: "ByCreateTimeAsc"
- label:
- en_US: Sort Type
- zh_Hans: 排序方式
- human_description:
- en_US: |
- The message sorting method. Optional values are ByCreateTimeAsc: sorted in ascending order by message creation time; ByCreateTimeDesc: sorted in descending order by message creation time. The default value is ByCreateTimeAsc. Note: When using page_token for pagination requests, the sorting method (sort_type) is consistent with the first request and cannot be changed midway.
- zh_Hans: |
- 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- llm_description: 消息排序方式,可选值有 ByCreateTimeAsc:按消息创建时间升序排列;ByCreateTimeDesc:按消息创建时间降序排列。默认值为:ByCreateTimeAsc。注意:使用 page_token 分页请求时,排序方式(sort_type)均与第一次请求一致,不支持中途改换排序方式。
- form: form
-
- - name: page_size
- type: number
- required: false
- default: 20
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The page size, i.e., the number of data entries returned in a single request. The default value is 20, and the value range is [1,50].
- zh_Hans: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- llm_description: 分页大小,即单次请求所返回的数据条目数。默认值为 20,取值范围为 [1,50]。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave it blank for the first request, indicating to start traversing from the beginning; when the pagination query result has more items, a new page_token will be returned simultaneously, which can be used to obtain the query result in the next traversal.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.py b/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.py
deleted file mode 100644
index b0a8df61e85f2e..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class SendBotMessageTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- receive_id_type = tool_parameters.get("receive_id_type")
- receive_id = tool_parameters.get("receive_id")
- msg_type = tool_parameters.get("msg_type")
- content = tool_parameters.get("content")
-
- res = client.send_bot_message(receive_id_type, receive_id, msg_type, content)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.yaml b/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.yaml
deleted file mode 100644
index b949c5e01694ce..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_bot_message.yaml
+++ /dev/null
@@ -1,125 +0,0 @@
-identity:
- name: send_bot_message
- author: Doug Lea
- label:
- en_US: Send Bot Message
- zh_Hans: 发送 Lark 应用消息
-description:
- human:
- en_US: Send bot message
- zh_Hans: 发送 Lark 应用消息
- llm: A tool for sending Lark application messages.
-parameters:
- - name: receive_id
- type: string
- required: true
- label:
- en_US: receive_id
- zh_Hans: 消息接收者的 ID
- human_description:
- en_US: The ID of the message receiver, the ID type is consistent with the value of the query parameter receive_id_type.
- zh_Hans: 消息接收者的 ID,ID 类型与查询参数 receive_id_type 的取值一致。
- llm_description: 消息接收者的 ID,ID 类型与查询参数 receive_id_type 的取值一致。
- form: llm
-
- - name: receive_id_type
- type: select
- required: true
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- - value: email
- label:
- en_US: email
- zh_Hans: email
- - value: chat_id
- label:
- en_US: chat_id
- zh_Hans: chat_id
- label:
- en_US: receive_id_type
- zh_Hans: 消息接收者的 ID 类型
- human_description:
- en_US: The ID type of the message receiver, optional values are open_id, union_id, user_id, email, chat_id, with a default value of open_id.
- zh_Hans: 消息接收者的 ID 类型,可选值有 open_id、union_id、user_id、email、chat_id,默认值为 open_id。
- llm_description: 消息接收者的 ID 类型,可选值有 open_id、union_id、user_id、email、chat_id,默认值为 open_id。
- form: form
-
- - name: msg_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: interactive
- label:
- en_US: interactive
- zh_Hans: 卡片
- - value: post
- label:
- en_US: post
- zh_Hans: 富文本
- - value: image
- label:
- en_US: image
- zh_Hans: 图片
- - value: file
- label:
- en_US: file
- zh_Hans: 文件
- - value: audio
- label:
- en_US: audio
- zh_Hans: 语音
- - value: media
- label:
- en_US: media
- zh_Hans: 视频
- - value: sticker
- label:
- en_US: sticker
- zh_Hans: 表情包
- - value: share_chat
- label:
- en_US: share_chat
- zh_Hans: 分享群名片
- - value: share_user
- label:
- en_US: share_user
- zh_Hans: 分享个人名片
- - value: system
- label:
- en_US: system
- zh_Hans: 系统消息
- label:
- en_US: msg_type
- zh_Hans: 消息类型
- human_description:
- en_US: Message type. Optional values are text, post, image, file, audio, media, sticker, interactive, share_chat, share_user, system. For detailed introduction of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息类型。可选值有:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user、system。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息类型。可选值有:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user、system。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: form
-
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- human_description:
- en_US: Message content, a JSON structure serialized string. The value of this parameter corresponds to msg_type. For example, if msg_type is text, this parameter needs to pass in text type content. To understand the format and usage limitations of different message types, refer to the message content(https://open.larksuite.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larksuite.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larksuite.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.py b/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.py
deleted file mode 100644
index 18a605079fc950..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class SendWebhookMessageTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- webhook = tool_parameters.get("webhook")
- msg_type = tool_parameters.get("msg_type")
- content = tool_parameters.get("content")
-
- res = client.send_webhook_message(webhook, msg_type, content)
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.yaml b/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.yaml
deleted file mode 100644
index ea13cae52ba997..00000000000000
--- a/api/core/tools/provider/builtin/lark_message_and_group/tools/send_webhook_message.yaml
+++ /dev/null
@@ -1,68 +0,0 @@
-identity:
- name: send_webhook_message
- author: Doug Lea
- label:
- en_US: Send Webhook Message
- zh_Hans: 使用自定义机器人发送 Lark 消息
-description:
- human:
- en_US: Send webhook message
- zh_Hans: 使用自定义机器人发送 Lark 消息
- llm: A tool for sending Lark messages using a custom robot.
-parameters:
- - name: webhook
- type: string
- required: true
- label:
- en_US: webhook
- zh_Hans: webhook
- human_description:
- en_US: |
- The address of the webhook, the format of the webhook address corresponding to the bot is as follows: https://open.larksuite.com/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx. For details, please refer to: Lark Custom Bot Usage Guide(https://open.larkoffice.com/document/client-docs/bot-v3/add-custom-bot)
- zh_Hans: |
- webhook 的地址,机器人对应的 webhook 地址格式如下: https://open.larksuite.com/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx,详情可参考: Lark 自定义机器人使用指南(https://open.larksuite.com/document/client-docs/bot-v3/add-custom-bot)
- llm_description: |
- webhook 的地址,机器人对应的 webhook 地址格式如下: https://open.larksuite.com/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx,详情可参考: Lark 自定义机器人使用指南(https://open.larksuite.com/document/client-docs/bot-v3/add-custom-bot)
- form: llm
-
- - name: msg_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: interactive
- label:
- en_US: interactive
- zh_Hans: 卡片
- - value: image
- label:
- en_US: image
- zh_Hans: 图片
- - value: share_chat
- label:
- en_US: share_chat
- zh_Hans: 分享群名片
- label:
- en_US: msg_type
- zh_Hans: 消息类型
- human_description:
- en_US: Message type. Optional values are text, image, interactive, share_chat. For detailed introduction of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息类型。可选值有:text、image、interactive、share_chat。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息类型。可选值有:text、image、interactive、share_chat。不同消息类型的详细介绍,参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: form
-
-
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- human_description:
- en_US: Message content, a JSON structure serialized string. The value of this parameter corresponds to msg_type. For example, if msg_type is text, this parameter needs to pass in text type content. To understand the format and usage limitations of different message types, refer to the message content(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json).
- zh_Hans: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- llm_description: 消息内容,JSON 结构序列化后的字符串。该参数的取值与 msg_type 对应,例如 msg_type 取值为 text,则该参数需要传入文本类型的内容。了解不同类型的消息内容格式、使用限制,可参见发送消息内容(https://open.larkoffice.com/document/server-docs/im-v1/message-content-description/create_json)。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/_assets/icon.png b/api/core/tools/provider/builtin/lark_spreadsheet/_assets/icon.png
deleted file mode 100644
index 258b361261d4e3..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_spreadsheet/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.py b/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.py
deleted file mode 100644
index c791363f21fbe1..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkMessageProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.yaml
deleted file mode 100644
index 030b5c9063227a..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/lark_spreadsheet.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_spreadsheet
- label:
- en_US: Lark Spreadsheet
- zh_Hans: Lark 电子表格
- description:
- en_US: |
- Lark Spreadsheet, requires the following permissions: sheets:spreadsheet.
- zh_Hans: |
- Lark 电子表格,需要开通以下权限: sheets:spreadsheet。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.py
deleted file mode 100644
index deeb5a1ecf6f7d..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class AddColsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- length = tool_parameters.get("length")
- values = tool_parameters.get("values")
-
- res = client.add_cols(spreadsheet_token, sheet_id, sheet_name, length, values)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.yaml
deleted file mode 100644
index b73335f405c20c..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_cols.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: add_cols
- author: Doug Lea
- label:
- en_US: Add Cols
- zh_Hans: 新增多列至工作表最后
-description:
- human:
- en_US: Add Cols
- zh_Hans: 新增多列至工作表最后
- llm: A tool for adding multiple columns to the end of a spreadsheet. (新增多列至工作表最后)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: length
- type: number
- required: true
- label:
- en_US: length
- zh_Hans: 要增加的列数
- human_description:
- en_US: Number of columns to add, range (0-5000].
- zh_Hans: 要增加的列数,范围(0-5000]。
- llm_description: 要增加的列数,范围(0-5000]。
- form: form
-
- - name: values
- type: string
- required: false
- label:
- en_US: values
- zh_Hans: 新增列的单元格内容
- human_description:
- en_US: |
- Content of the new columns, array of objects in string format, each array represents a row of table data, format like: [ [ "ID","Name","Age" ],[ 1,"Zhang San",10 ],[ 2,"Li Si",11 ] ].
- zh_Hans: 新增列的单元格内容,数组对象字符串,每个数组一行表格数据,格式:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- llm_description: 新增列的单元格内容,数组对象字符串,每个数组一行表格数据,格式:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.py
deleted file mode 100644
index f434b1c60341f3..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class AddRowsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- length = tool_parameters.get("length")
- values = tool_parameters.get("values")
-
- res = client.add_rows(spreadsheet_token, sheet_id, sheet_name, length, values)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.yaml
deleted file mode 100644
index 6bce305b9825ec..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/add_rows.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-identity:
- name: add_rows
- author: Doug Lea
- label:
- en_US: Add Rows
- zh_Hans: 新增多行至工作表最后
-description:
- human:
- en_US: Add Rows
- zh_Hans: 新增多行至工作表最后
- llm: A tool for adding multiple rows to the end of a spreadsheet. (新增多行至工作表最后)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: length
- type: number
- required: true
- label:
- en_US: length
- zh_Hans: 要增加行数
- human_description:
- en_US: Number of rows to add, range (0-5000].
- zh_Hans: 要增加行数,范围(0-5000]。
- llm_description: 要增加行数,范围(0-5000]。
- form: form
-
- - name: values
- type: string
- required: false
- label:
- en_US: values
- zh_Hans: 新增行的表格内容
- human_description:
- en_US: |
- Content of the new rows, array of objects in string format, each array represents a row of table data, format like: [ [ "ID","Name","Age" ],[ 1,"Zhang San",10 ],[ 2,"Li Si",11 ] ].
- zh_Hans: 新增行的表格内容,数组对象字符串,每个数组一行表格数据,格式,如:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- llm_description: 新增行的表格内容,数组对象字符串,每个数组一行表格数据,格式,如:[["编号","姓名","年龄"],[1,"张三",10],[2,"李四",11]]。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.py
deleted file mode 100644
index 74b20ac2c838f8..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateSpreadsheetTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- title = tool_parameters.get("title")
- folder_token = tool_parameters.get("folder_token")
-
- res = client.create_spreadsheet(title, folder_token)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.yaml
deleted file mode 100644
index 931310e63172d4..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/create_spreadsheet.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-identity:
- name: create_spreadsheet
- author: Doug Lea
- label:
- en_US: Create Spreadsheet
- zh_Hans: 创建电子表格
-description:
- human:
- en_US: Create Spreadsheet
- zh_Hans: 创建电子表格
- llm: A tool for creating spreadsheets. (创建电子表格)
-parameters:
- - name: title
- type: string
- required: false
- label:
- en_US: Spreadsheet Title
- zh_Hans: 电子表格标题
- human_description:
- en_US: The title of the spreadsheet
- zh_Hans: 电子表格的标题
- llm_description: 电子表格的标题
- form: llm
-
- - name: folder_token
- type: string
- required: false
- label:
- en_US: Folder Token
- zh_Hans: 文件夹 token
- human_description:
- en_US: The token of the folder, supports folder URL input, e.g., https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- zh_Hans: 文件夹 token,支持文件夹 URL 输入,如:https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- llm_description: 文件夹 token,支持文件夹 URL 输入,如:https://bytedance.larkoffice.com/drive/folder/CxHEf4DCSlNkL2dUTCJcPRgentg
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.py
deleted file mode 100644
index 0fe35b6dc645b8..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetSpreadsheetTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.get_spreadsheet(spreadsheet_token, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.yaml
deleted file mode 100644
index c519938617ba8c..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/get_spreadsheet.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-identity:
- name: get_spreadsheet
- author: Doug Lea
- label:
- en_US: Get Spreadsheet
- zh_Hans: 获取电子表格信息
-description:
- human:
- en_US: Get Spreadsheet
- zh_Hans: 获取电子表格信息
- llm: A tool for getting information from spreadsheets. (获取电子表格信息)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: Spreadsheet Token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 URL。
- llm_description: 电子表格 token,支持输入电子表格 URL。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.py
deleted file mode 100644
index e711c23780e5e3..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ListSpreadsheetSheetsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
-
- res = client.list_spreadsheet_sheets(spreadsheet_token)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.yaml
deleted file mode 100644
index c6a7ef45d46589..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/list_spreadsheet_sheets.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: list_spreadsheet_sheets
- author: Doug Lea
- label:
- en_US: List Spreadsheet Sheets
- zh_Hans: 列出电子表格所有工作表
-description:
- human:
- en_US: List Spreadsheet Sheets
- zh_Hans: 列出电子表格所有工作表
- llm: A tool for listing all sheets in a spreadsheet. (列出电子表格所有工作表)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: Spreadsheet Token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 URL。
- llm_description: 电子表格 token,支持输入电子表格 URL。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.py
deleted file mode 100644
index 1df289c1d71b01..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ReadColsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- start_col = tool_parameters.get("start_col")
- num_cols = tool_parameters.get("num_cols")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_cols(spreadsheet_token, sheet_id, sheet_name, start_col, num_cols, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.yaml
deleted file mode 100644
index 34da74592d5898..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_cols.yaml
+++ /dev/null
@@ -1,97 +0,0 @@
-identity:
- name: read_cols
- author: Doug Lea
- label:
- en_US: Read Cols
- zh_Hans: 读取工作表列数据
-description:
- human:
- en_US: Read Cols
- zh_Hans: 读取工作表列数据
- llm: A tool for reading column data from a spreadsheet. (读取工作表列数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_col
- type: number
- required: false
- label:
- en_US: start_col
- zh_Hans: 起始列号
- human_description:
- en_US: Starting column number, starting from 1.
- zh_Hans: 起始列号,从 1 开始。
- llm_description: 起始列号,从 1 开始。
- form: form
-
- - name: num_cols
- type: number
- required: true
- label:
- en_US: num_cols
- zh_Hans: 读取列数
- human_description:
- en_US: Number of columns to read.
- zh_Hans: 读取列数
- llm_description: 读取列数
- form: form
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.py
deleted file mode 100644
index 1cab38a4545269..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ReadRowsTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- start_row = tool_parameters.get("start_row")
- num_rows = tool_parameters.get("num_rows")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_rows(spreadsheet_token, sheet_id, sheet_name, start_row, num_rows, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.yaml
deleted file mode 100644
index 5dfa8d58354125..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_rows.yaml
+++ /dev/null
@@ -1,97 +0,0 @@
-identity:
- name: read_rows
- author: Doug Lea
- label:
- en_US: Read Rows
- zh_Hans: 读取工作表行数据
-description:
- human:
- en_US: Read Rows
- zh_Hans: 读取工作表行数据
- llm: A tool for reading row data from a spreadsheet. (读取工作表行数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_row
- type: number
- required: false
- label:
- en_US: start_row
- zh_Hans: 起始行号
- human_description:
- en_US: Starting row number, starting from 1.
- zh_Hans: 起始行号,从 1 开始。
- llm_description: 起始行号,从 1 开始。
- form: form
-
- - name: num_rows
- type: number
- required: true
- label:
- en_US: num_rows
- zh_Hans: 读取行数
- human_description:
- en_US: Number of rows to read.
- zh_Hans: 读取行数
- llm_description: 读取行数
- form: form
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.py b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.py
deleted file mode 100644
index 0f05249004ee20..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class ReadTableTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- spreadsheet_token = tool_parameters.get("spreadsheet_token")
- sheet_id = tool_parameters.get("sheet_id")
- sheet_name = tool_parameters.get("sheet_name")
- num_range = tool_parameters.get("num_range")
- query = tool_parameters.get("query")
- user_id_type = tool_parameters.get("user_id_type", "open_id")
-
- res = client.read_table(spreadsheet_token, sheet_id, sheet_name, num_range, query, user_id_type)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.yaml b/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.yaml
deleted file mode 100644
index 10534436d66e7a..00000000000000
--- a/api/core/tools/provider/builtin/lark_spreadsheet/tools/read_table.yaml
+++ /dev/null
@@ -1,122 +0,0 @@
-identity:
- name: read_table
- author: Doug Lea
- label:
- en_US: Read Table
- zh_Hans: 自定义读取电子表格行列数据
-description:
- human:
- en_US: Read Table
- zh_Hans: 自定义读取电子表格行列数据
- llm: A tool for custom reading of row and column data from a spreadsheet. (自定义读取电子表格行列数据)
-parameters:
- - name: spreadsheet_token
- type: string
- required: true
- label:
- en_US: spreadsheet_token
- zh_Hans: 电子表格 token
- human_description:
- en_US: Spreadsheet token, supports input of spreadsheet URL.
- zh_Hans: 电子表格 token,支持输入电子表格 url。
- llm_description: 电子表格 token,支持输入电子表格 url。
- form: llm
-
- - name: sheet_id
- type: string
- required: false
- label:
- en_US: sheet_id
- zh_Hans: 工作表 ID
- human_description:
- en_US: Sheet ID, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表 ID,与 sheet_name 二者其一必填。
- llm_description: 工作表 ID,与 sheet_name 二者其一必填。
- form: llm
-
- - name: sheet_name
- type: string
- required: false
- label:
- en_US: sheet_name
- zh_Hans: 工作表名称
- human_description:
- en_US: Sheet name, either sheet_id or sheet_name must be filled.
- zh_Hans: 工作表名称,与 sheet_id 二者其一必填。
- llm_description: 工作表名称,与 sheet_id 二者其一必填。
- form: llm
-
- - name: user_id_type
- type: select
- required: false
- options:
- - value: open_id
- label:
- en_US: open_id
- zh_Hans: open_id
- - value: union_id
- label:
- en_US: union_id
- zh_Hans: union_id
- - value: user_id
- label:
- en_US: user_id
- zh_Hans: user_id
- default: "open_id"
- label:
- en_US: user_id_type
- zh_Hans: 用户 ID 类型
- human_description:
- en_US: User ID type, optional values are open_id, union_id, user_id, with a default value of open_id.
- zh_Hans: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- llm_description: 用户 ID 类型,可选值有 open_id、union_id、user_id,默认值为 open_id。
- form: form
-
- - name: start_row
- type: number
- required: false
- label:
- en_US: start_row
- zh_Hans: 起始行号
- human_description:
- en_US: Starting row number, starting from 1.
- zh_Hans: 起始行号,从 1 开始。
- llm_description: 起始行号,从 1 开始。
- form: form
-
- - name: num_rows
- type: number
- required: false
- label:
- en_US: num_rows
- zh_Hans: 读取行数
- human_description:
- en_US: Number of rows to read.
- zh_Hans: 读取行数
- llm_description: 读取行数
- form: form
-
- - name: range
- type: string
- required: false
- label:
- en_US: range
- zh_Hans: 取数范围
- human_description:
- en_US: |
- Data range, format like: A1:B2, can be empty when query=all.
- zh_Hans: 取数范围,格式如:A1:B2,query=all 时可为空。
- llm_description: 取数范围,格式如:A1:B2,query=all 时可为空。
- form: llm
-
- - name: query
- type: string
- required: false
- label:
- en_US: query
- zh_Hans: 查询
- human_description:
- en_US: Pass "all" to query all data in the table, but no more than 100 columns.
- zh_Hans: 传 all,表示查询表格所有数据,但最多查询 100 列数据。
- llm_description: 传 all,表示查询表格所有数据,但最多查询 100 列数据。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_task/_assets/icon.png b/api/core/tools/provider/builtin/lark_task/_assets/icon.png
deleted file mode 100644
index 26ea6a2eefa5be..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_task/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_task/lark_task.py b/api/core/tools/provider/builtin/lark_task/lark_task.py
deleted file mode 100644
index 02cf009f017e61..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/lark_task.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkTaskProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_task/lark_task.yaml b/api/core/tools/provider/builtin/lark_task/lark_task.yaml
deleted file mode 100644
index ada068b0aab3ce..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/lark_task.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_task
- label:
- en_US: Lark Task
- zh_Hans: Lark 任务
- description:
- en_US: |
- Lark Task, requires the following permissions: task:task:write、contact:user.id:readonly.
- zh_Hans: |
- Lark 任务,需要开通以下权限: task:task:write、contact:user.id:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_task/tools/add_members.py b/api/core/tools/provider/builtin/lark_task/tools/add_members.py
deleted file mode 100644
index 9b8e4d68f394a8..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/add_members.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class AddMembersTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
- member_phone_or_email = tool_parameters.get("member_phone_or_email")
- member_role = tool_parameters.get("member_role", "follower")
-
- res = client.add_members(task_guid, member_phone_or_email, member_role)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_task/tools/add_members.yaml b/api/core/tools/provider/builtin/lark_task/tools/add_members.yaml
deleted file mode 100644
index 0b12172e0b85e7..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/add_members.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-identity:
- name: add_members
- author: Doug Lea
- label:
- en_US: Add Lark Members
- zh_Hans: 添加 Lark 任务成员
-description:
- human:
- en_US: Add Lark Members
- zh_Hans: 添加 Lark 任务成员
- llm: A tool for adding members to a Lark task.(添加 Lark 任务成员)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The GUID of the task to be added, supports passing either the Task ID or the Task link URL. Example of Task ID: 8b5425ec-9f2a-43bd-a3ab-01912f50282b; Example of Task link URL: https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- zh_Hans: 要添加的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- llm_description: 要添加的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- form: llm
-
- - name: member_phone_or_email
- type: string
- required: true
- label:
- en_US: Task Member Phone Or Email
- zh_Hans: 任务成员的电话或邮箱
- human_description:
- en_US: A list of member emails or phone numbers, separated by commas.
- zh_Hans: 任务成员邮箱或者手机号列表,使用逗号分隔。
- llm_description: 任务成员邮箱或者手机号列表,使用逗号分隔。
- form: llm
-
- - name: member_role
- type: select
- required: true
- options:
- - value: assignee
- label:
- en_US: assignee
- zh_Hans: 负责人
- - value: follower
- label:
- en_US: follower
- zh_Hans: 关注人
- default: "follower"
- label:
- en_US: member_role
- zh_Hans: 成员的角色
- human_description:
- en_US: Member role, optional values are "assignee" (responsible person) and "follower" (observer), with a default value of "assignee".
- zh_Hans: 成员的角色,可选值有 "assignee"(负责人)和 "follower"(关注人),默认值为 "assignee"。
- llm_description: 成员的角色,可选值有 "assignee"(负责人)和 "follower"(关注人),默认值为 "assignee"。
- form: form
diff --git a/api/core/tools/provider/builtin/lark_task/tools/create_task.py b/api/core/tools/provider/builtin/lark_task/tools/create_task.py
deleted file mode 100644
index ff37593fbe3a12..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/create_task.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class CreateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- summary = tool_parameters.get("summary")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- completed_time = tool_parameters.get("completed_time")
- description = tool_parameters.get("description")
-
- res = client.create_task(summary, start_time, end_time, completed_time, description)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_task/tools/create_task.yaml b/api/core/tools/provider/builtin/lark_task/tools/create_task.yaml
deleted file mode 100644
index 4303763a1dd406..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/create_task.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-identity:
- name: create_task
- author: Doug Lea
- label:
- en_US: Create Lark Task
- zh_Hans: 创建 Lark 任务
-description:
- human:
- en_US: Create Lark Task
- zh_Hans: 创建 Lark 任务
- llm: A tool for creating tasks in Lark.(创建 Lark 任务)
-parameters:
- - name: summary
- type: string
- required: true
- label:
- en_US: Task Title
- zh_Hans: 任务标题
- human_description:
- en_US: The title of the task.
- zh_Hans: 任务标题
- llm_description: 任务标题
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Task Description
- zh_Hans: 任务备注
- human_description:
- en_US: The description or notes for the task.
- zh_Hans: 任务备注
- llm_description: 任务备注
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 任务开始时间
- human_description:
- en_US: |
- The start time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务开始时间,格式为:2006-01-02 15:04:05
- llm_description: 任务开始时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 任务结束时间
- human_description:
- en_US: |
- The end time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务结束时间,格式为:2006-01-02 15:04:05
- llm_description: 任务结束时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: completed_time
- type: string
- required: false
- label:
- en_US: Completed Time
- zh_Hans: 任务完成时间
- human_description:
- en_US: |
- The completion time of the task, in the format: 2006-01-02 15:04:05. Leave empty to create an incomplete task; fill in a specific time to create a completed task.
- zh_Hans: 任务完成时间,格式为:2006-01-02 15:04:05,不填写表示创建一个未完成任务;填写一个具体的时间表示创建一个已完成任务。
- llm_description: 任务完成时间,格式为:2006-01-02 15:04:05,不填写表示创建一个未完成任务;填写一个具体的时间表示创建一个已完成任务。
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_task/tools/delete_task.py b/api/core/tools/provider/builtin/lark_task/tools/delete_task.py
deleted file mode 100644
index eca381be2c185e..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/delete_task.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class UpdateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
-
- res = client.delete_task(task_guid)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_task/tools/delete_task.yaml b/api/core/tools/provider/builtin/lark_task/tools/delete_task.yaml
deleted file mode 100644
index bc0154d9dc5c77..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/delete_task.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-identity:
- name: delete_task
- author: Doug Lea
- label:
- en_US: Delete Lark Task
- zh_Hans: 删除 Lark 任务
-description:
- human:
- en_US: Delete Lark Task
- zh_Hans: 删除 Lark 任务
- llm: A tool for deleting tasks in Lark.(删除 Lark 任务)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The GUID of the task to be deleted, supports passing either the Task ID or the Task link URL. Example of Task ID: 8b5425ec-9f2a-43bd-a3ab-01912f50282b; Example of Task link URL: https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- zh_Hans: 要删除的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- llm_description: 要删除的任务的 GUID,支持传任务 ID 和任务链接 URL。任务 ID 示例:8b5425ec-9f2a-43bd-a3ab-01912f50282b;任务链接 URL 示例:https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_task/tools/update_task.py b/api/core/tools/provider/builtin/lark_task/tools/update_task.py
deleted file mode 100644
index 0d3469c91a01bc..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/update_task.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class UpdateTaskTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- task_guid = tool_parameters.get("task_guid")
- summary = tool_parameters.get("summary")
- start_time = tool_parameters.get("start_time")
- end_time = tool_parameters.get("end_time")
- completed_time = tool_parameters.get("completed_time")
- description = tool_parameters.get("description")
-
- res = client.update_task(task_guid, summary, start_time, end_time, completed_time, description)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_task/tools/update_task.yaml b/api/core/tools/provider/builtin/lark_task/tools/update_task.yaml
deleted file mode 100644
index a98f037f211c9a..00000000000000
--- a/api/core/tools/provider/builtin/lark_task/tools/update_task.yaml
+++ /dev/null
@@ -1,89 +0,0 @@
-identity:
- name: update_task
- author: Doug Lea
- label:
- en_US: Update Lark Task
- zh_Hans: 更新 Lark 任务
-description:
- human:
- en_US: Update Lark Task
- zh_Hans: 更新 Lark 任务
- llm: A tool for updating tasks in Lark.(更新 Lark 任务)
-parameters:
- - name: task_guid
- type: string
- required: true
- label:
- en_US: Task GUID
- zh_Hans: 任务 GUID
- human_description:
- en_US: |
- The task ID, supports inputting either the Task ID or the Task link URL. Example of Task ID: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64; Example of Task link URL: https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- zh_Hans: |
- 任务ID,支持传入任务 ID 和任务链接 URL。任务 ID 示例: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64;任务链接 URL 示例: https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- llm_description: |
- 任务ID,支持传入任务 ID 和任务链接 URL。任务 ID 示例: 42cad8a0-f8c8-4344-9be2-d1d7e8e91b64;任务链接 URL 示例: https://applink.larksuite.com/client/todo/detail?guid=1b066afa-96de-406c-90a3-dfd30159a571&suite_entity_num=t100805
- form: llm
-
- - name: summary
- type: string
- required: true
- label:
- en_US: Task Title
- zh_Hans: 任务标题
- human_description:
- en_US: The title of the task.
- zh_Hans: 任务标题
- llm_description: 任务标题
- form: llm
-
- - name: description
- type: string
- required: false
- label:
- en_US: Task Description
- zh_Hans: 任务备注
- human_description:
- en_US: The description or notes for the task.
- zh_Hans: 任务备注
- llm_description: 任务备注
- form: llm
-
- - name: start_time
- type: string
- required: false
- label:
- en_US: Start Time
- zh_Hans: 任务开始时间
- human_description:
- en_US: |
- The start time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务开始时间,格式为:2006-01-02 15:04:05
- llm_description: 任务开始时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: end_time
- type: string
- required: false
- label:
- en_US: End Time
- zh_Hans: 任务结束时间
- human_description:
- en_US: |
- The end time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务结束时间,格式为:2006-01-02 15:04:05
- llm_description: 任务结束时间,格式为:2006-01-02 15:04:05
- form: llm
-
- - name: completed_time
- type: string
- required: false
- label:
- en_US: Completed Time
- zh_Hans: 任务完成时间
- human_description:
- en_US: |
- The completion time of the task, in the format: 2006-01-02 15:04:05
- zh_Hans: 任务完成时间,格式为:2006-01-02 15:04:05
- llm_description: 任务完成时间,格式为:2006-01-02 15:04:05
- form: llm
diff --git a/api/core/tools/provider/builtin/lark_wiki/_assets/icon.png b/api/core/tools/provider/builtin/lark_wiki/_assets/icon.png
deleted file mode 100644
index 47f6b8c30ea0cf..00000000000000
Binary files a/api/core/tools/provider/builtin/lark_wiki/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/lark_wiki/lark_wiki.py b/api/core/tools/provider/builtin/lark_wiki/lark_wiki.py
deleted file mode 100644
index e6941206ee7618..00000000000000
--- a/api/core/tools/provider/builtin/lark_wiki/lark_wiki.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.utils.lark_api_utils import lark_auth
-
-
-class LarkWikiProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- lark_auth(credentials)
diff --git a/api/core/tools/provider/builtin/lark_wiki/lark_wiki.yaml b/api/core/tools/provider/builtin/lark_wiki/lark_wiki.yaml
deleted file mode 100644
index 86bef000868d68..00000000000000
--- a/api/core/tools/provider/builtin/lark_wiki/lark_wiki.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- author: Doug Lea
- name: lark_wiki
- label:
- en_US: Lark Wiki
- zh_Hans: Lark 知识库
- description:
- en_US: |
- Lark Wiki, requires the following permissions: wiki:wiki:readonly.
- zh_Hans: |
- Lark 知识库,需要开通以下权限: wiki:wiki:readonly。
- icon: icon.png
- tags:
- - social
- - productivity
-credentials_for_provider:
- app_id:
- type: text-input
- required: true
- label:
- en_US: APP ID
- placeholder:
- en_US: Please input your Lark app id
- zh_Hans: 请输入你的 Lark app id
- help:
- en_US: Get your app_id and app_secret from Lark
- zh_Hans: 从 Lark 获取您的 app_id 和 app_secret
- url: https://open.larksuite.com/app
- app_secret:
- type: secret-input
- required: true
- label:
- en_US: APP Secret
- placeholder:
- en_US: Please input your app secret
- zh_Hans: 请输入你的 Lark app secret
diff --git a/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.py b/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.py
deleted file mode 100644
index a05f300755962f..00000000000000
--- a/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.lark_api_utils import LarkRequest
-
-
-class GetWikiNodesTool(BuiltinTool):
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
- app_id = self.runtime.credentials.get("app_id")
- app_secret = self.runtime.credentials.get("app_secret")
- client = LarkRequest(app_id, app_secret)
-
- space_id = tool_parameters.get("space_id")
- parent_node_token = tool_parameters.get("parent_node_token")
- page_token = tool_parameters.get("page_token")
- page_size = tool_parameters.get("page_size")
-
- res = client.get_wiki_nodes(space_id, parent_node_token, page_token, page_size)
-
- return self.create_json_message(res)
diff --git a/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.yaml b/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.yaml
deleted file mode 100644
index a8c242a2e909df..00000000000000
--- a/api/core/tools/provider/builtin/lark_wiki/tools/get_wiki_nodes.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-identity:
- name: get_wiki_nodes
- author: Doug Lea
- label:
- en_US: Get Wiki Nodes
- zh_Hans: 获取知识空间子节点列表
-description:
- human:
- en_US: |
- Get the list of child nodes in Wiki, make sure the app/bot is a member of the wiki space. See How to add an app as a wiki base administrator (member). https://open.larksuite.com/document/server-docs/docs/wiki-v2/wiki-qa
- zh_Hans: |
- 获取知识库全部子节点列表,请确保应用/机器人为知识空间成员。参阅如何将应用添加为知识库管理员(成员)。https://open.larksuite.com/document/server-docs/docs/wiki-v2/wiki-qa
- llm: A tool for getting all sub-nodes of a knowledge base.(获取知识空间子节点列表)
-parameters:
- - name: space_id
- type: string
- required: true
- label:
- en_US: Space Id
- zh_Hans: 知识空间 ID
- human_description:
- en_US: |
- The ID of the knowledge space. Supports space link URL, for example: https://lark-japan.jp.larksuite.com/wiki/settings/7431084851517718561
- zh_Hans: 知识空间 ID,支持空间链接 URL,例如:https://lark-japan.jp.larksuite.com/wiki/settings/7431084851517718561
- llm_description: 知识空间 ID,支持空间链接 URL,例如:https://lark-japan.jp.larksuite.com/wiki/settings/7431084851517718561
- form: llm
-
- - name: page_size
- type: number
- required: false
- default: 10
- label:
- en_US: Page Size
- zh_Hans: 分页大小
- human_description:
- en_US: The size of each page, with a maximum value of 50.
- zh_Hans: 分页大小,最大值 50。
- llm_description: 分页大小,最大值 50。
- form: form
-
- - name: page_token
- type: string
- required: false
- label:
- en_US: Page Token
- zh_Hans: 分页标记
- human_description:
- en_US: The pagination token. Leave empty for the first request to start from the beginning; if the paginated query result has more items, a new page_token will be returned, which can be used to get the next set of results.
- zh_Hans: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- llm_description: 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果。
- form: llm
-
- - name: parent_node_token
- type: string
- required: false
- label:
- en_US: Parent Node Token
- zh_Hans: 父节点 token
- human_description:
- en_US: The token of the parent node.
- zh_Hans: 父节点 token
- llm_description: 父节点 token
- form: llm
diff --git a/api/core/tools/provider/builtin/maths/_assets/icon.svg b/api/core/tools/provider/builtin/maths/_assets/icon.svg
deleted file mode 100644
index f94d1152113830..00000000000000
--- a/api/core/tools/provider/builtin/maths/_assets/icon.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/maths/maths.py b/api/core/tools/provider/builtin/maths/maths.py
deleted file mode 100644
index d4b449ec87a18a..00000000000000
--- a/api/core/tools/provider/builtin/maths/maths.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.maths.tools.eval_expression import EvaluateExpressionTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class MathsProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- EvaluateExpressionTool().invoke(
- user_id="",
- tool_parameters={
- "expression": "1+(2+3)*4",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/maths/maths.yaml b/api/core/tools/provider/builtin/maths/maths.yaml
deleted file mode 100644
index 35c2380e29a701..00000000000000
--- a/api/core/tools/provider/builtin/maths/maths.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: Bowen Liang
- name: maths
- label:
- en_US: Maths
- zh_Hans: 数学工具
- pt_BR: Maths
- description:
- en_US: A tool for maths.
- zh_Hans: 一个用于数学计算的工具。
- pt_BR: A tool for maths.
- icon: icon.svg
- tags:
- - utilities
- - productivity
diff --git a/api/core/tools/provider/builtin/maths/tools/eval_expression.py b/api/core/tools/provider/builtin/maths/tools/eval_expression.py
deleted file mode 100644
index d3a497d1cd5c54..00000000000000
--- a/api/core/tools/provider/builtin/maths/tools/eval_expression.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import logging
-from typing import Any, Union
-
-import numexpr as ne # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class EvaluateExpressionTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get expression
- expression = tool_parameters.get("expression", "").strip()
- if not expression:
- return self.create_text_message("Invalid expression")
-
- try:
- result = ne.evaluate(expression)
- result_str = str(result)
- except Exception as e:
- logging.exception(f"Error evaluating expression: {expression}")
- return self.create_text_message(f"Invalid expression: {expression}, error: {str(e)}")
- return self.create_text_message(f'The result of the expression "{expression}" is {result_str}')
diff --git a/api/core/tools/provider/builtin/maths/tools/eval_expression.yaml b/api/core/tools/provider/builtin/maths/tools/eval_expression.yaml
deleted file mode 100644
index c936a4293fbe72..00000000000000
--- a/api/core/tools/provider/builtin/maths/tools/eval_expression.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-identity:
- name: eval_expression
- author: Bowen Liang
- label:
- en_US: Evaluate Math Expression
- zh_Hans: 计算数学表达式
- pt_BR: Evaluate Math Expression
-description:
- human:
- en_US: A tool for evaluating an math expression, calculated locally with NumExpr.
- zh_Hans: 一个用于计算数学表达式的工具,表达式将通过NumExpr本地执行。
- pt_BR: A tool for evaluating an math expression, calculated locally with NumExpr.
- llm: A tool for evaluating an math expression.
-parameters:
- - name: expression
- type: string
- required: true
- label:
- en_US: Math Expression
- zh_Hans: 数学计算表达式
- pt_BR: Math Expression
- human_description:
- en_US: Math Expression
- zh_Hans: 数学计算表达式
- pt_BR: Math Expression
- form: llm
diff --git a/api/core/tools/provider/builtin/nominatim/_assets/icon.svg b/api/core/tools/provider/builtin/nominatim/_assets/icon.svg
deleted file mode 100644
index db5a4eb868c5e8..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/_assets/icon.svg
+++ /dev/null
@@ -1,277 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 01011001 00110101 10010011
- 01011001 00110101 10010011
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/nominatim/nominatim.py b/api/core/tools/provider/builtin/nominatim/nominatim.py
deleted file mode 100644
index 5a24bed7507eb6..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/nominatim.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.nominatim.tools.nominatim_search import NominatimSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class NominatimProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- result = (
- NominatimSearchTool()
- .fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- )
- .invoke(
- user_id="",
- tool_parameters={
- "query": "London",
- "limit": 1,
- },
- )
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/nominatim/nominatim.yaml b/api/core/tools/provider/builtin/nominatim/nominatim.yaml
deleted file mode 100644
index 7d014bd78c6a59..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/nominatim.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-identity:
- author: Charles Zhou
- name: nominatim
- label:
- en_US: Nominatim
- zh_Hans: Nominatim
- de_DE: Nominatim
- ja_JP: Nominatim
- description:
- en_US: Nominatim is a search engine for OpenStreetMap data
- zh_Hans: Nominatim是OpenStreetMap数据的搜索引擎
- de_DE: Nominatim ist eine Suchmaschine für OpenStreetMap-Daten
- ja_JP: NominatimはOpenStreetMapデータの検索エンジンです
- icon: icon.svg
- tags:
- - search
- - utilities
-credentials_for_provider:
- base_url:
- type: text-input
- required: false
- default: https://nominatim.openstreetmap.org
- label:
- en_US: Nominatim Base URL
- zh_Hans: Nominatim 基础 URL
- de_DE: Nominatim Basis-URL
- ja_JP: Nominatim ベースURL
- placeholder:
- en_US: "Enter your Nominatim instance URL (default:
- https://nominatim.openstreetmap.org)"
- zh_Hans: 输入您的Nominatim实例URL(默认:https://nominatim.openstreetmap.org)
- de_DE: "Geben Sie Ihre Nominatim-Instanz-URL ein (Standard:
- https://nominatim.openstreetmap.org)"
- ja_JP: NominatimインスタンスのURLを入力してください(デフォルト:https://nominatim.openstreetmap.org)
- help:
- en_US: The base URL for the Nominatim instance. Use the default for the public
- service or enter your self-hosted instance URL.
- zh_Hans: Nominatim实例的基础URL。使用默认值可访问公共服务,或输入您的自托管实例URL。
- de_DE: Die Basis-URL für die Nominatim-Instanz. Verwenden Sie den Standardwert
- für den öffentlichen Dienst oder geben Sie die URL Ihrer selbst
- gehosteten Instanz ein.
- ja_JP: NominatimインスタンスのベースURL。公共サービスにはデフォルトを使用するか、自己ホスティングインスタンスのURLを入力してください。
- url: https://nominatim.org/
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py
deleted file mode 100644
index ffa8ad0fcc02e0..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NominatimLookupTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- osm_ids = tool_parameters.get("osm_ids", "")
-
- if not osm_ids:
- return self.create_text_message("Please provide OSM IDs")
-
- params = {"osm_ids": osm_ids, "format": "json", "addressdetails": 1}
-
- return self._make_request(user_id, "lookup", params)
-
- def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage:
- base_url = self.runtime.credentials.get("base_url", "https://nominatim.openstreetmap.org")
-
- try:
- headers = {"User-Agent": "DifyNominatimTool/1.0"}
- s = requests.session()
- response = s.request(method="GET", headers=headers, url=f"{base_url}/{endpoint}", params=params)
- response_data = response.json()
-
- if response.status_code == 200:
- s.close()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))
- )
- else:
- return self.create_text_message(f"Error: {response.status_code} - {response.text}")
- except Exception as e:
- return self.create_text_message(f"An error occurred: {str(e)}")
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml
deleted file mode 100644
index 508c4dcd88ff15..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- name: nominatim_lookup
- author: Charles Zhou
- label:
- en_US: Nominatim OSM Lookup
- zh_Hans: Nominatim OSM 对象查找
- de_DE: Nominatim OSM-Objektsuche
- ja_JP: Nominatim OSM ルックアップ
-description:
- human:
- en_US: Look up OSM objects using their IDs with Nominatim
- zh_Hans: 使用Nominatim通过ID查找OSM对象
- de_DE: Suchen Sie OSM-Objekte anhand ihrer IDs mit Nominatim
- ja_JP: Nominatimを使用してIDでOSMオブジェクトを検索
- llm: A tool for looking up OpenStreetMap objects using their IDs with Nominatim.
-parameters:
- - name: osm_ids
- type: string
- required: true
- label:
- en_US: OSM IDs
- zh_Hans: OSM ID
- de_DE: OSM-IDs
- ja_JP: OSM ID
- human_description:
- en_US: Comma-separated list of OSM IDs to lookup (e.g., N123,W456,R789)
- zh_Hans: 要查找的OSM ID的逗号分隔列表(例如:N123,W456,R789)
- de_DE: Kommagetrennte Liste von OSM-IDs für die Suche (z.B. N123,W456,R789)
- ja_JP: 検索するOSM IDのカンマ区切りリスト(例:N123,W456,R789)
- llm_description: A comma-separated list of OSM IDs (prefixed with N, W, or R) for lookup.
- form: llm
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py
deleted file mode 100644
index f46691e1a3ebb4..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NominatimReverseTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- lat = tool_parameters.get("lat")
- lon = tool_parameters.get("lon")
-
- if lat is None or lon is None:
- return self.create_text_message("Please provide both latitude and longitude")
-
- params = {"lat": lat, "lon": lon, "format": "json", "addressdetails": 1}
-
- return self._make_request(user_id, "reverse", params)
-
- def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage:
- base_url = self.runtime.credentials.get("base_url", "https://nominatim.openstreetmap.org")
-
- try:
- headers = {"User-Agent": "DifyNominatimTool/1.0"}
- s = requests.session()
- response = s.request(method="GET", headers=headers, url=f"{base_url}/{endpoint}", params=params)
- response_data = response.json()
-
- if response.status_code == 200:
- s.close()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))
- )
- else:
- return self.create_text_message(f"Error: {response.status_code} - {response.text}")
- except Exception as e:
- return self.create_text_message(f"An error occurred: {str(e)}")
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml
deleted file mode 100644
index f1a2dd09fbc5d5..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-identity:
- name: nominatim_reverse
- author: Charles Zhou
- label:
- en_US: Nominatim Reverse Geocoding
- zh_Hans: Nominatim 反向地理编码
- de_DE: Nominatim Rückwärts-Geocodierung
- ja_JP: Nominatim リバースジオコーディング
-description:
- human:
- en_US: Convert coordinates to addresses using Nominatim
- zh_Hans: 使用Nominatim将坐标转换为地址
- de_DE: Konvertieren Sie Koordinaten in Adressen mit Nominatim
- ja_JP: Nominatimを使用して座標を住所に変換
- llm: A tool for reverse geocoding using Nominatim, which can convert latitude
- and longitude coordinates to an address.
-parameters:
- - name: lat
- type: number
- required: true
- label:
- en_US: Latitude
- zh_Hans: 纬度
- de_DE: Breitengrad
- ja_JP: 緯度
- human_description:
- en_US: Latitude coordinate for reverse geocoding
- zh_Hans: 用于反向地理编码的纬度坐标
- de_DE: Breitengrad-Koordinate für die Rückwärts-Geocodierung
- ja_JP: リバースジオコーディングの緯度座標
- llm_description: The latitude coordinate for reverse geocoding.
- form: llm
- - name: lon
- type: number
- required: true
- label:
- en_US: Longitude
- zh_Hans: 经度
- de_DE: Längengrad
- ja_JP: 経度
- human_description:
- en_US: Longitude coordinate for reverse geocoding
- zh_Hans: 用于反向地理编码的经度坐标
- de_DE: Längengrad-Koordinate für die Rückwärts-Geocodierung
- ja_JP: リバースジオコーディングの経度座標
- llm_description: The longitude coordinate for reverse geocoding.
- form: llm
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py
deleted file mode 100644
index 34851d86dcaa5f..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NominatimSearchTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- query = tool_parameters.get("query", "")
- limit = tool_parameters.get("limit", 10)
-
- if not query:
- return self.create_text_message("Please input a search query")
-
- params = {"q": query, "format": "json", "limit": limit, "addressdetails": 1}
-
- return self._make_request(user_id, "search", params)
-
- def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage:
- base_url = self.runtime.credentials.get("base_url", "https://nominatim.openstreetmap.org")
-
- try:
- headers = {"User-Agent": "DifyNominatimTool/1.0"}
- s = requests.session()
- response = s.request(method="GET", headers=headers, url=f"{base_url}/{endpoint}", params=params)
- response_data = response.json()
-
- if response.status_code == 200:
- s.close()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))
- )
- else:
- return self.create_text_message(f"Error: {response.status_code} - {response.text}")
- except Exception as e:
- return self.create_text_message(f"An error occurred: {str(e)}")
diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml
deleted file mode 100644
index e0c53c046a2a41..00000000000000
--- a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml
+++ /dev/null
@@ -1,51 +0,0 @@
-identity:
- name: nominatim_search
- author: Charles Zhou
- label:
- en_US: Nominatim Search
- zh_Hans: Nominatim 搜索
- de_DE: Nominatim Suche
- ja_JP: Nominatim 検索
-description:
- human:
- en_US: Search for locations using Nominatim
- zh_Hans: 使用Nominatim搜索位置
- de_DE: Suche nach Orten mit Nominatim
- ja_JP: Nominatimを使用して場所を検索
- llm: A tool for geocoding using Nominatim, which can search for locations based
- on addresses or place names.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Search Query
- zh_Hans: 搜索查询
- de_DE: Suchanfrage
- ja_JP: 検索クエリ
- human_description:
- en_US: Enter an address or place name to search for
- zh_Hans: 输入要搜索的地址或地名
- de_DE: Geben Sie eine Adresse oder einen Ortsnamen für die Suche ein
- ja_JP: 検索する住所または場所の名前を入力してください
- llm_description: The search query for Nominatim, which can be an address or place name.
- form: llm
- - name: limit
- type: number
- default: 10
- min: 1
- max: 40
- required: false
- label:
- en_US: Result Limit
- zh_Hans: 结果限制
- de_DE: Ergebnislimit
- ja_JP: 結果の制限
- human_description:
- en_US: "Maximum number of results to return (default: 10, max: 40)"
- zh_Hans: 要返回的最大结果数(默认:10,最大:40)
- de_DE: "Maximale Anzahl der zurückzugebenden Ergebnisse (Standard: 10, max: 40)"
- ja_JP: 返す結果の最大数(デフォルト:10、最大:40)
- llm_description: Limit the number of returned results. The default is 10, and
- the maximum is 40.
- form: form
diff --git a/api/core/tools/provider/builtin/novitaai/_assets/icon.ico b/api/core/tools/provider/builtin/novitaai/_assets/icon.ico
deleted file mode 100644
index e353ecf711cac1..00000000000000
Binary files a/api/core/tools/provider/builtin/novitaai/_assets/icon.ico and /dev/null differ
diff --git a/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py b/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py
deleted file mode 100644
index 6473c509e1f4c2..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from novita_client import ( # type: ignore
- Txt2ImgV3Embedding,
- Txt2ImgV3HiresFix,
- Txt2ImgV3LoRA,
- Txt2ImgV3Refiner,
- V3TaskImage,
-)
-
-
-class NovitaAiToolBase:
- def _extract_loras(self, loras_str: str):
- if not loras_str:
- return []
-
- loras_ori_list = loras_str.strip().split(";")
- result_list = []
- for lora_str in loras_ori_list:
- lora_info = lora_str.strip().split(",")
- lora = Txt2ImgV3LoRA(
- model_name=lora_info[0].strip(),
- strength=float(lora_info[1]),
- )
- result_list.append(lora)
-
- return result_list
-
- def _extract_embeddings(self, embeddings_str: str):
- if not embeddings_str:
- return []
-
- embeddings_ori_list = embeddings_str.strip().split(";")
- result_list = []
- for embedding_str in embeddings_ori_list:
- embedding = Txt2ImgV3Embedding(model_name=embedding_str.strip())
- result_list.append(embedding)
-
- return result_list
-
- def _extract_hires_fix(self, hires_fix_str: str):
- hires_fix_info = hires_fix_str.strip().split(",")
- if "upscaler" in hires_fix_info:
- hires_fix = Txt2ImgV3HiresFix(
- target_width=int(hires_fix_info[0]),
- target_height=int(hires_fix_info[1]),
- strength=float(hires_fix_info[2]),
- upscaler=hires_fix_info[3].strip(),
- )
- else:
- hires_fix = Txt2ImgV3HiresFix(
- target_width=int(hires_fix_info[0]),
- target_height=int(hires_fix_info[1]),
- strength=float(hires_fix_info[2]),
- )
-
- return hires_fix
-
- def _extract_refiner(self, switch_at: str):
- refiner = Txt2ImgV3Refiner(switch_at=float(switch_at))
- return refiner
-
- def _is_hit_nsfw_detection(self, image: V3TaskImage, confidence_threshold: float) -> bool:
- """
- is hit nsfw
- """
- if image.nsfw_detection_result is None:
- return False
- if image.nsfw_detection_result.valid and image.nsfw_detection_result.confidence >= confidence_threshold:
- return True
- return False
diff --git a/api/core/tools/provider/builtin/novitaai/novitaai.py b/api/core/tools/provider/builtin/novitaai/novitaai.py
deleted file mode 100644
index d5e32eff29373a..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/novitaai.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.novitaai.tools.novitaai_txt2img import NovitaAiTxt2ImgTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class NovitaAIProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- result = (
- NovitaAiTxt2ImgTool()
- .fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- )
- .invoke(
- user_id="",
- tool_parameters={
- "model_name": "cinenautXLATRUE_cinenautV10_392434.safetensors",
- "prompt": "a futuristic city with flying cars",
- "negative_prompt": "",
- "width": 128,
- "height": 128,
- "image_num": 1,
- "guidance_scale": 7.5,
- "seed": -1,
- "steps": 1,
- },
- )
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/novitaai/novitaai.yaml b/api/core/tools/provider/builtin/novitaai/novitaai.yaml
deleted file mode 100644
index 3eed8a889c1bd8..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/novitaai.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- author: Xiao Ley
- name: novitaai
- label:
- en_US: Novita AI
- zh_Hans: Novita AI
- pt_BR: Novita AI
- description:
- en_US: Innovative AI for Image Generation
- zh_Hans: 用于图像生成的创新人工智能。
- pt_BR: Innovative AI for Image Generation
- icon: icon.ico
- tags:
- - image
- - productivity
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- zh_Hans: API 密钥
- pt_BR: Chave API
- placeholder:
- en_US: Please enter your Novita AI API key
- zh_Hans: 请输入你的 Novita AI API 密钥
- pt_BR: Por favor, insira sua chave de API do Novita AI
- help:
- en_US: Get your Novita AI API key from Novita AI
- zh_Hans: 从 Novita AI 获取您的 Novita AI API 密钥
- pt_BR: Obtenha sua chave de API do Novita AI na Novita AI
- url: https://novita.ai
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py b/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py
deleted file mode 100644
index 097b234bd50640..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from base64 import b64decode
-from copy import deepcopy
-from typing import Any, Union
-
-from novita_client import ( # type: ignore
- NovitaClient,
-)
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NovitaAiCreateTileTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
- raise ToolProviderCredentialValidationError("Novita AI API Key is required.")
-
- api_key = self.runtime.credentials.get("api_key")
-
- client = NovitaClient(api_key=api_key)
- param = self._process_parameters(tool_parameters)
- client_result = client.create_tile(**param)
-
- results = []
- results.append(
- self.create_blob_message(
- blob=b64decode(client_result.image_file),
- meta={"mime_type": f"image/{client_result.image_type}"},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
-
- return results
-
- def _process_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
- """
- process parameters
- """
- res_parameters = deepcopy(parameters)
-
- # delete none and empty
- keys_to_delete = [k for k, v in res_parameters.items() if v is None or v == ""]
- for k in keys_to_delete:
- del res_parameters[k]
-
- return res_parameters
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.yaml b/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.yaml
deleted file mode 100644
index 8e5df5042937d3..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-identity:
- name: novitaai_createtile
- author: Xiao Ley
- label:
- en_US: Novita AI Create Tile
- zh_Hans: Novita AI 创建平铺图案
-description:
- human:
- en_US: This feature produces images designed for seamless tiling, ideal for creating continuous patterns in fabrics, wallpapers, and various textures.
- zh_Hans: 该功能生成设计用于无缝平铺的图像,非常适合用于制作连续图案的织物、壁纸和各种纹理。
- llm: A tool for create images designed for seamless tiling, ideal for creating continuous patterns in fabrics, wallpapers, and various textures.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: prompt
- zh_Hans: 提示
- human_description:
- en_US: Positive prompt word of the created tile, divided by `,`, Range [1, 512]. Only English input is allowed.
- zh_Hans: 生成平铺图案的正向提示,用 `,` 分隔,范围 [1, 512]。仅允许输入英文。
- llm_description: Image prompt of Novita AI, you should describe the image you want to generate as a list of words as possible as detailed, divided by `,`, Range [1, 512]. Only English input is allowed.
- form: llm
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: negative prompt
- zh_Hans: 负向提示
- human_description:
- en_US: Negtive prompt word of the created tile, divided by `,`, Range [1, 512]. Only English input is allowed.
- zh_Hans: 生成平铺图案的负向提示,用 `,` 分隔,范围 [1, 512]。仅允许输入英文。
- llm_description: Image negative prompt of Novita AI, divided by `,`, Range [1, 512]. Only English input is allowed.
- form: llm
- - name: width
- type: number
- default: 256
- min: 128
- max: 1024
- required: true
- label:
- en_US: width
- zh_Hans: 宽
- human_description:
- en_US: Image width, Range [128, 1024].
- zh_Hans: 图像宽度,范围 [128, 1024]
- form: form
- - name: height
- type: number
- default: 256
- min: 128
- max: 1024
- required: true
- label:
- en_US: height
- zh_Hans: 高
- human_description:
- en_US: Image height, Range [128, 1024].
- zh_Hans: 图像高度,范围 [128, 1024]
- form: form
- - name: response_image_type
- type: select
- default: jpeg
- required: false
- label:
- en_US: response image type
- zh_Hans: 响应图像类型
- human_description:
- en_US: Response image type, png or jpeg
- zh_Hans: 响应图像类型,png 或 jpeg
- form: form
- options:
- - value: jpeg
- label:
- en_US: jpeg
- zh_Hans: jpeg
- - value: png
- label:
- en_US: png
- zh_Hans: png
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.py b/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.py
deleted file mode 100644
index a200ee81231f00..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import json
-from copy import deepcopy
-from typing import Any, Union
-
-from pandas import DataFrame
-from yarl import URL
-
-from core.helper import ssrf_proxy
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NovitaAiModelQueryTool(BuiltinTool):
- _model_query_endpoint = "https://api.novita.ai/v3/model"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
- raise ToolProviderCredentialValidationError("Novita AI API Key is required.")
-
- api_key = self.runtime.credentials.get("api_key")
- headers = {"Content-Type": "application/json", "Authorization": "Bearer " + api_key}
- params = self._process_parameters(tool_parameters)
- result_type = params.get("result_type")
- del params["result_type"]
-
- models_data = self._query_models(
- models_data=[],
- headers=headers,
- params=params,
- recursive=result_type not in {"first sd_name", "first name sd_name pair"},
- )
-
- result_str = ""
- if result_type == "first sd_name":
- result_str = models_data[0]["sd_name_in_api"] if len(models_data) > 0 else ""
- elif result_type == "first name sd_name pair":
- result_str = (
- json.dumps({"name": models_data[0]["name"], "sd_name": models_data[0]["sd_name_in_api"]})
- if len(models_data) > 0
- else ""
- )
- elif result_type == "sd_name array":
- sd_name_array = [model["sd_name_in_api"] for model in models_data] if len(models_data) > 0 else []
- result_str = json.dumps(sd_name_array)
- elif result_type == "name array":
- name_array = [model["name"] for model in models_data] if len(models_data) > 0 else []
- result_str = json.dumps(name_array)
- elif result_type == "name sd_name pair array":
- name_sd_name_pair_array = (
- [{"name": model["name"], "sd_name": model["sd_name_in_api"]} for model in models_data]
- if len(models_data) > 0
- else []
- )
- result_str = json.dumps(name_sd_name_pair_array)
- elif result_type == "whole info array":
- result_str = json.dumps(models_data)
- else:
- raise NotImplementedError
-
- return self.create_text_message(result_str)
-
- def _query_models(
- self,
- models_data: list,
- headers: dict[str, Any],
- params: dict[str, Any],
- pagination_cursor: str = "",
- recursive: bool = True,
- ) -> list:
- """
- query models
- """
- inside_params = deepcopy(params)
-
- if pagination_cursor != "":
- inside_params["pagination.cursor"] = pagination_cursor
-
- response = ssrf_proxy.get(
- url=str(URL(self._model_query_endpoint)), headers=headers, params=params, timeout=(10, 60)
- )
-
- res_data = response.json()
-
- models_data.extend(res_data["models"])
-
- res_data_len = len(res_data["models"])
- if res_data_len == 0 or res_data_len < int(params["pagination.limit"]) or recursive is False:
- # deduplicate
- df = DataFrame.from_dict(models_data)
- df_unique = df.drop_duplicates(subset=["id"])
- models_data = df_unique.to_dict("records")
- return models_data
-
- return self._query_models(
- models_data=models_data,
- headers=headers,
- params=inside_params,
- pagination_cursor=res_data["pagination"]["next_cursor"],
- )
-
- def _process_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
- """
- process parameters
- """
- process_parameters = deepcopy(parameters)
- res_parameters = {}
-
- # delete none or empty
- keys_to_delete = [k for k, v in process_parameters.items() if v is None or v == ""]
- for k in keys_to_delete:
- del process_parameters[k]
-
- if "query" in process_parameters and process_parameters.get("query") != "unspecified":
- res_parameters["filter.query"] = process_parameters["query"]
-
- if "visibility" in process_parameters and process_parameters.get("visibility") != "unspecified":
- res_parameters["filter.visibility"] = process_parameters["visibility"]
-
- if "source" in process_parameters and process_parameters.get("source") != "unspecified":
- res_parameters["filter.source"] = process_parameters["source"]
-
- if "type" in process_parameters and process_parameters.get("type") != "unspecified":
- res_parameters["filter.types"] = process_parameters["type"]
-
- if "is_sdxl" in process_parameters:
- if process_parameters["is_sdxl"] == "true":
- res_parameters["filter.is_sdxl"] = True
- elif process_parameters["is_sdxl"] == "false":
- res_parameters["filter.is_sdxl"] = False
-
- res_parameters["result_type"] = process_parameters.get("result_type", "first sd_name")
-
- res_parameters["pagination.limit"] = (
- 1
- if res_parameters.get("result_type") == "first sd_name"
- or res_parameters.get("result_type") == "first name sd_name pair"
- else 100
- )
-
- return res_parameters
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.yaml b/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.yaml
deleted file mode 100644
index a14795e45e0e4f..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_modelquery.yaml
+++ /dev/null
@@ -1,175 +0,0 @@
-identity:
- name: novitaai_modelquery
- author: Xiao Ley
- label:
- en_US: Novita AI Model Query
- zh_Hans: Novita AI 模型查询
-description:
- human:
- en_US: Retrieve information on both public and private models. It allows users to access details such as model specifications, status, and usage guidelines, ensuring comprehensive insight into the available modeling resources.
- zh_Hans: 检索公开和私有模型信息。它允许用户访问模型规范、状态和使用指南等详细信息,确保了解可用的建模资源。
- llm: A tool for retrieve information on both public and private Novita AI models.
-parameters:
- - name: query
- type: string
- required: false
- label:
- en_US: query
- zh_Hans: 查询
- human_description:
- en_US: Seaching the content of sd_name, name, tags.
- zh_Hans: 搜索 sd_name、name、tags 中的内容
- llm_description: Enter the content to search
- form: llm
- - name: result_type
- type: select
- default: "first sd_name"
- required: true
- label:
- en_US: result format
- zh_Hans: 结果格式
- human_description:
- en_US: The format of result
- zh_Hans: 请求结果的格式
- form: form
- options:
- - value: "first sd_name"
- label:
- en_US: "first sd_name"
- zh_Hans: "第一个 sd_name"
- - value: "first name sd_name pair"
- label:
- en_US: "first name and sd_name pair: {name, sd_name}"
- zh_Hans: "第一个 name sd_name 组合:{name, sd_name}"
- - value: "sd_name array"
- label:
- en_US: "sd_name array: [sd_name]"
- zh_Hans: "sd_name 数组:[sd_name]"
- - value: "name array"
- label:
- en_US: "name array: [name]"
- zh_Hans: "name 数组:[name]"
- - value: "name sd_name pair array"
- label:
- en_US: "name and sd_name pair array: [{name, sd_name}]"
- zh_Hans: "name sd_name 组合数组:[{name, sd_name}]"
- - value: "whole info array"
- label:
- en_US: whole info array
- zh_Hans: 完整信息数组
- - name: visibility
- type: select
- default: unspecified
- required: false
- label:
- en_US: visibility
- zh_Hans: 可见性
- human_description:
- en_US: Whether the model is public or private
- zh_Hans: 模型是否公开或私有
- form: form
- options:
- - value: unspecified
- label:
- en_US: Unspecified
- zh_Hans: 未指定
- - value: public
- label:
- en_US: Public
- zh_Hans: 公开
- - value: private
- label:
- en_US: Private
- zh_Hans: 私有
- - name: source
- type: select
- default: unspecified
- required: false
- label:
- en_US: source
- zh_Hans: 来源
- human_description:
- en_US: Source of the model
- zh_Hans: 模型来源
- form: form
- options:
- - value: unspecified
- label:
- en_US: Unspecified
- zh_Hans: 未指定
- - value: civitai
- label:
- en_US: Civitai
- zh_Hans: Civitai
- - value: training
- label:
- en_US: Training
- zh_Hans: 训练
- - value: uploading
- label:
- en_US: Uploading
- zh_Hans: 上传
- - name: type
- type: select
- default: unspecified
- required: false
- label:
- en_US: type
- zh_Hans: 类型
- human_description:
- en_US: Specifies the type of models to include in the query.
- zh_Hans: 指定要查询的模型类型
- form: form
- options:
- - value: unspecified
- label:
- en_US: Unspecified
- zh_Hans: 未指定
- - value: checkpoint
- label:
- en_US: Checkpoint
- zh_Hans: Checkpoint
- - value: lora
- label:
- en_US: LoRA
- zh_Hans: LoRA
- - value: vae
- label:
- en_US: VAE
- zh_Hans: VAE
- - value: controlnet
- label:
- en_US: ControlNet
- zh_Hans: ControlNet
- - value: upscaler
- label:
- en_US: Upscaler
- zh_Hans: Upscaler
- - value: textualinversion
- label:
- en_US: Textual inversion
- zh_Hans: Textual Inversion
- - name: is_sdxl
- type: select
- default: unspecified
- required: false
- label:
- en_US: is sdxl
- zh_Hans: 是否是 SDXL
- human_description:
- en_US: Whether sdxl model or not. Setting this parameter to `true` includes only sdxl models in the query results, which are typically large-scale, high-performance models designed for extensive data processing tasks. Conversely, setting it to `false` excludes these models from the results. If left unspecified, the filter will not discriminate based on the sdxl classification, including all model types in the search results.
- zh_Hans: 是否是 SDXL 模型。设置此参数为 `是`,只查询 SDXL 模型,并包含大规模,高性能的模型。相反,设置为 `否`,将排除这些模型。如果未指定,将不会根据 SDXL 分类进行区分,包括查询结果中的所有模型类型。
- form: form
- options:
- - value: unspecified
- label:
- en_US: Unspecified
- zh_Hans: 未指定
- - value: "true"
- label:
- en_US: "True"
- zh_Hans: 是
- - value: "false"
- label:
- en_US: "False"
- zh_Hans: 否
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py b/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py
deleted file mode 100644
index 297a27abba667a..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from base64 import b64decode
-from copy import deepcopy
-from typing import Any, Union
-
-from novita_client import ( # type: ignore
- NovitaClient,
-)
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.novitaai._novita_tool_base import NovitaAiToolBase
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class NovitaAiTxt2ImgTool(BuiltinTool, NovitaAiToolBase):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
- raise ToolProviderCredentialValidationError("Novita AI API Key is required.")
-
- api_key = self.runtime.credentials.get("api_key")
-
- client = NovitaClient(api_key=api_key)
- param = self._process_parameters(tool_parameters)
- client_result = client.txt2img_v3(**param)
-
- results = []
- for image_encoded, image in zip(client_result.images_encoded, client_result.images):
- if self._is_hit_nsfw_detection(image, 0.8):
- results = self.create_text_message(text="NSFW detected!")
- break
-
- results.append(
- self.create_blob_message(
- blob=b64decode(image_encoded),
- meta={"mime_type": f"image/{image.image_type}"},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
-
- return results
-
- def _process_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
- """
- process parameters
- """
- res_parameters = deepcopy(parameters)
-
- # delete none and empty
- keys_to_delete = [k for k, v in res_parameters.items() if v is None or v == ""]
- for k in keys_to_delete:
- del res_parameters[k]
-
- if "clip_skip" in res_parameters and res_parameters.get("clip_skip") == 0:
- del res_parameters["clip_skip"]
-
- if "refiner_switch_at" in res_parameters and res_parameters.get("refiner_switch_at") == 0:
- del res_parameters["refiner_switch_at"]
-
- if "enabled_enterprise_plan" in res_parameters:
- res_parameters["enterprise_plan"] = {"enabled": res_parameters["enabled_enterprise_plan"]}
- del res_parameters["enabled_enterprise_plan"]
-
- if "nsfw_detection_level" in res_parameters:
- res_parameters["nsfw_detection_level"] = int(res_parameters["nsfw_detection_level"])
-
- # process loras
- if "loras" in res_parameters:
- res_parameters["loras"] = self._extract_loras(res_parameters.get("loras"))
-
- # process embeddings
- if "embeddings" in res_parameters:
- res_parameters["embeddings"] = self._extract_embeddings(res_parameters.get("embeddings"))
-
- # process hires_fix
- if "hires_fix" in res_parameters:
- res_parameters["hires_fix"] = self._extract_hires_fix(res_parameters.get("hires_fix"))
-
- # process refiner
- if "refiner_switch_at" in res_parameters:
- res_parameters["refiner"] = self._extract_refiner(res_parameters.get("refiner_switch_at"))
- del res_parameters["refiner_switch_at"]
-
- return res_parameters
diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.yaml b/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.yaml
deleted file mode 100644
index d625a643f915b1..00000000000000
--- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.yaml
+++ /dev/null
@@ -1,341 +0,0 @@
-identity:
- name: novitaai_txt2img
- author: Xiao Ley
- label:
- en_US: Novita AI Text to Image
- zh_Hans: Novita AI 文字转图像
-description:
- human:
- en_US: Generate images from text prompts using Stable Diffusion models
- zh_Hans: 通过 Stable Diffusion 模型根据文字提示生成图像
- llm: A tool for generate images from English text prompts.
-parameters:
- - name: model_name
- type: string
- required: true
- label:
- en_US: model name
- zh_Hans: 模块名字
- human_description:
- en_US: Specify the name of the model checkpoint. You can use the "Novita AI Model Query" tool to query the corresponding "sd_name" value (type select "Checkpoint").
- zh_Hans: 指定 Model Checkpoint 名称。可通过“Novita AI 模型请求”工具查询对应的“sd_name”值(类型选择“Checkpoint”)。
- form: form
- - name: prompt
- type: string
- required: true
- label:
- en_US: prompt
- zh_Hans: 提示
- human_description:
- en_US: Text input required to guide the image generation, divided by `,`, Range [1, 1024]. Only English input is allowed.
- zh_Hans: 生成图像的正向提示,用 `,` 分隔,范围 [1, 1024]。仅允许输入英文。
- llm_description: Image prompt of Novita AI, you should describe the image you want to generate as a list of words as possible as detailed, divided by `,`, Range [1, 1024]. Only English input is allowed.
- form: llm
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: negative prompt
- zh_Hans: 负向提示
- human_description:
- en_US: Text input that will not guide the image generation, divided by `,`, Range [1, 1024]. Only English input is allowed.
- zh_Hans: 生成图像的负向提示,用 `,` 分隔,范围 [1, 1024]。仅允许输入英文。
- llm_description: Image negative prompt of Novita AI, divided by `,`, Range [1, 1024]. Only English input is allowed.
- form: llm
- - name: width
- type: number
- default: 512
- min: 128
- max: 2048
- required: true
- label:
- en_US: width
- zh_Hans: 宽
- human_description:
- en_US: Image width, Range [128, 2048].
- zh_Hans: 图像宽度,范围 [128, 2048]
- form: form
- - name: height
- type: number
- default: 512
- min: 128
- max: 2048
- required: true
- label:
- en_US: height
- zh_Hans: 高
- human_description:
- en_US: Image height, Range [128, 2048].
- zh_Hans: 图像高度,范围 [128, 2048]
- form: form
- - name: image_num
- type: number
- default: 1
- min: 1
- max: 8
- required: true
- label:
- en_US: image num
- zh_Hans: 图片数
- human_description:
- en_US: Image num, Range [1, 8].
- zh_Hans: 图片数,范围 [1, 8]
- form: form
- - name: steps
- type: number
- default: 20
- min: 1
- max: 100
- required: true
- label:
- en_US: steps
- zh_Hans: 步数
- human_description:
- en_US: The number of denoising steps. More steps usually can produce higher quality images, but take more time to generate, Range [1, 100].
- zh_Hans: 生成步数。更多步数可能会产生更好的图像,但生成时间更长,范围 [1, 100]
- form: form
- - name: seed
- type: number
- default: -1
- required: true
- label:
- en_US: seed
- zh_Hans: 种子
- human_description:
- en_US: A seed is a number from which Stable Diffusion generates noise, which, makes generation deterministic. Using the same seed and set of parameters will produce identical image each time, minimum -1.
- zh_Hans: 种子是 Stable Diffusion 生成噪声的数字,它使生成具有确定性。使用相同的种子和参数设置将生成每次生成相同的图像,最小值 -1。
- form: form
- - name: clip_skip
- type: number
- min: 1
- max: 12
- required: false
- label:
- en_US: clip skip
- zh_Hans: 层跳过数
- human_description:
- en_US: This parameter indicates the number of layers to stop from the bottom during optimization, so clip_skip on 2 would mean, that in SD1.x model where the CLIP has 12 layers, you would stop at 10th layer, Range [1, 12], get reference at https://novita.ai/get-started/Misc.html#what-s-clip-skip.
- zh_Hans: 此参数表示优化过程中从底部停止的层数,因此 clip_skip 的值为 2,表示在 SD1.x 模型中,CLIP 有 12 层,你将停止在 10 层,范围 [1, 12],参考 https://novita.ai/get-started/Misc.html#what-s-clip-skip。
- form: form
- - name: guidance_scale
- type: number
- default: "7.5"
- min: 1.0
- max: 30.0
- required: true
- label:
- en_US: guidance scale
- zh_Hans: 提示词遵守程度
- human_description:
- en_US: This setting says how close the Stable Diffusion will listen to your prompt, higer guidance forces the model to better follow the prompt, but result in lower quality output.Range [1, 30].
- zh_Hans: 此设置表明 Stable Diffusion 如何听从您的提示,较高的 guidance_scale 会强制模型更好跟随提示,但结果会更低质量输出。范围 [1.0, 30.0]。
- form: form
- - name: sampler_name
- type: select
- required: true
- label:
- en_US: sampler name
- zh_Hans: 采样器名称
- human_description:
- en_US: This parameter determines the denoising algorithm employed during the sampling phase of Stable Diffusion. Get reference at https://novita.ai/get-started/Misc.htmll#what-is-samplers.
- zh_Hans: 此参数决定了在稳定扩散采样阶段使用的去噪算法。参考 https://novita.ai/get-started/Misc.htmll#what-is-samplers。
- form: form
- options:
- - value: "Euler a"
- label:
- en_US: Euler a
- zh_Hans: Euler a
- - value: "Euler"
- label:
- en_US: Euler
- zh_Hans: Euler
- - value: "LMS"
- label:
- en_US: LMS
- zh_Hans: LMS
- - value: "Heun"
- label:
- en_US: Heun
- zh_Hans: Heun
- - value: "DPM2"
- label:
- en_US: DPM2
- zh_Hans: DPM2
- - value: "DPM2 a"
- label:
- en_US: DPM2 a
- zh_Hans: DPM2 a
- - value: "DPM++ 2S a"
- label:
- en_US: DPM++ 2S a
- zh_Hans: DPM++ 2S a
- - value: "DPM++ 2M"
- label:
- en_US: DPM++ 2M
- zh_Hans: DPM++ 2M
- - value: "DPM++ SDE"
- label:
- en_US: DPM++ SDE
- zh_Hans: DPM++ SDE
- - value: "DPM fast"
- label:
- en_US: DPM fast
- zh_Hans: DPM fast
- - value: "DPM adaptive"
- label:
- en_US: DPM adaptive
- zh_Hans: DPM adaptive
- - value: "LMS Karras"
- label:
- en_US: LMS Karras
- zh_Hans: LMS Karras
- - value: "DPM2 Karras"
- label:
- en_US: DPM2 Karras
- zh_Hans: DPM2 Karras
- - value: "DPM2 a Karras"
- label:
- en_US: DPM2 a Karras
- zh_Hans: DPM2 a Karras
- - value: "DPM++ 2S a Karras"
- label:
- en_US: DPM++ 2S a Karras
- zh_Hans: DPM++ 2S a Karras
- - value: "DPM++ 2M Karras"
- label:
- en_US: DPM++ 2M Karras
- zh_Hans: DPM++ 2M Karras
- - value: "DPM++ SDE Karras"
- label:
- en_US: DPM++ SDE Karras
- zh_Hans: DPM++ SDE Karras
- - value: "DDIM"
- label:
- en_US: DDIM
- zh_Hans: DDIM
- - value: "PLMS"
- label:
- en_US: PLMS
- zh_Hans: PLMS
- - value: "UniPC"
- label:
- en_US: UniPC
- zh_Hans: UniPC
- - name: sd_vae
- type: string
- required: false
- label:
- en_US: sd vae
- zh_Hans: sd vae
- human_description:
- en_US: VAE(Variational Autoencoder), get reference at https://novita.ai/get-started/Misc.html#what-s-variational-autoencoders-vae. You can use the "Novita AI Model Query" tool to query the corresponding "sd_name" value (type select "VAE").
- zh_Hans: VAE(变分自编码器),参考 https://novita.ai/get-started/Misc.html#what-s-variational-autoencoders-vae。可通过“Novita AI 模型请求”工具查询对应的“sd_name”值(类型选择“VAE”)。
- form: form
- - name: loras
- type: string
- required: false
- label:
- en_US: loRAs
- zh_Hans: loRAs
- human_description:
- en_US: LoRA models. Currenlty supports up to 5 LoRAs. You can use the "Novita AI Model Query" tool to query the corresponding "sd_name" value (type select "LoRA"). Input template is ",;,;...". Such as"Film Grain style_331903,0.5;DoggystylePOV_9600,0.5"
- zh_Hans: LoRA 模型。目前仅支持 5 个 LoRA。可通过“Novita AI 模型请求”工具查询对应的“sd_name”值(类型选择“LoRA”)。输入模板:“,;,;...”,例如:“Film Grain style_331903,0.5;DoggystylePOV_9600,0.5”
- form: form
- - name: embeddings
- type: string
- required: false
- label:
- en_US: text embeddings
- zh_Hans: 文本嵌入
- human_description:
- en_US: Textual Inversion is a training method for personalizing models by learning new text embeddings from a few example images, currenlty supports up to 5 embeddings. You can use the "Novita AI Model Query" tool to query the corresponding "sd_name" value (type select "Text Inversion"). Input template is ";;...". Such as "EasyNegativeV2_75525;AS-YoungerV2"
- zh_Hans: 文本反转是一种通过从一些示例图像中学习新的文本嵌入来个性化模型的训练方法,目前仅支持 5 个嵌入。可通过“Novita AI 模型请求”工具查询对应的“sd_name”值(类型选择“Text Inversion”)。输入模板:“;;...”,例如:“EasyNegativeV2_75525;AS-YoungerV2”
- form: form
- - name: hires_fix
- type: string
- required: false
- label:
- en_US: hires fix
- zh_Hans: 高分辨率修复
- human_description:
- en_US: Use high resolution image fix. Input template is ",,,". Such as "1024,1024,0.8", "1024,1024,0.8,RealESRGAN_x4plus_anime_6B"
- zh_Hans: 使用高分辨率修复。输入模板 “,,,”。例如 “1024,1024,0.8”、“1024,1024,0.8,RealESRGAN_x4plus_anime_6B”
- form: form
- - name: refiner_switch_at
- type: number
- min: 0.0
- max: 1.0
- required: false
- label:
- en_US: refiner switch at
- zh_Hans: 重采样参与时刻
- human_description:
- en_US: This parameter in the context of a refiner allows you to set the extent to which the refiner alters the output of a model. When set to 0, the refiner has no effect; at 1, it's fully active. Intermediate values like 0.5 provide a balanced effect, where the refiner is moderately engaged, enhancing or adjusting the output without dominating the original model's characteristics. This setting is particularly useful for fine-tuning the output to achieve a desired balance between refinement and the original generative features, Range [0, 1.0]. Is not all models support refiners!
- zh_Hans: 此参数允许您设置重采样更改模型输出的程度。当设置为0时,重采样不起作用;1时,它处于完全活动状态。像0.5这样的中间值提供了一种平衡效果,其中重采样适度参与,增强或调整输出,而不会主导原始模型的特性。此设置对于微调输出特别有用,范围 [0, 1.0]。不是所有模型都支持重采样!
- form: form
- - name: response_image_type
- type: select
- default: jpeg
- required: false
- label:
- en_US: response image type
- zh_Hans: 响应图像类型
- human_description:
- en_US: Response image type, png or jpeg
- zh_Hans: 响应图像类型,png 或 jpeg
- form: form
- options:
- - value: jpeg
- label:
- en_US: jpeg
- zh_Hans: jpeg
- - value: png
- label:
- en_US: png
- zh_Hans: png
- - name: enabled_enterprise_plan
- type: boolean
- default: false
- required: false
- label:
- en_US: enterprise plan enabled
- zh_Hans: 企业版计划启用
- human_description:
- en_US: Enable enterprise plan
- zh_Hans: 启用企业版计划
- form: form
- - name: enable_nsfw_detection
- type: boolean
- default: false
- required: false
- label:
- en_US: enable nsfw detection
- zh_Hans: 启用 NSFW 检测
- human_description:
- en_US: Enable nsfw detection
- zh_Hans: 启用 NSFW 检测
- form: form
- - name: nsfw_detection_level
- type: select
- default: "2"
- required: false
- label:
- en_US: nsfw detection level
- zh_Hans: NSFW 检测级别
- human_description:
- en_US: Nsfw detection level, from low to high
- zh_Hans: NSFW 检测级别,越高越严格
- form: form
- options:
- - value: "0"
- label:
- en_US: low
- zh_Hans: 低
- - value: "1"
- label:
- en_US: middle
- zh_Hans: 中
- - value: "2"
- label:
- en_US: high
- zh_Hans: 高
diff --git a/api/core/tools/provider/builtin/onebot/_assets/icon.ico b/api/core/tools/provider/builtin/onebot/_assets/icon.ico
deleted file mode 100644
index 1b07e965b9910b..00000000000000
Binary files a/api/core/tools/provider/builtin/onebot/_assets/icon.ico and /dev/null differ
diff --git a/api/core/tools/provider/builtin/onebot/onebot.py b/api/core/tools/provider/builtin/onebot/onebot.py
deleted file mode 100644
index b8e5ed24d6b43f..00000000000000
--- a/api/core/tools/provider/builtin/onebot/onebot.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class OneBotProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- if not credentials.get("ob11_http_url"):
- raise ToolProviderCredentialValidationError("OneBot HTTP URL is required.")
diff --git a/api/core/tools/provider/builtin/onebot/onebot.yaml b/api/core/tools/provider/builtin/onebot/onebot.yaml
deleted file mode 100644
index 1922adc4de4d56..00000000000000
--- a/api/core/tools/provider/builtin/onebot/onebot.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-identity:
- author: RockChinQ
- name: onebot
- label:
- en_US: OneBot v11 Protocol
- zh_Hans: OneBot v11 协议
- description:
- en_US: Unofficial OneBot v11 Protocol Tool
- zh_Hans: 非官方 OneBot v11 协议工具
- icon: icon.ico
-credentials_for_provider:
- ob11_http_url:
- type: text-input
- required: true
- label:
- en_US: HTTP URL
- zh_Hans: HTTP URL
- description:
- en_US: Forward HTTP URL of OneBot v11
- zh_Hans: OneBot v11 正向 HTTP URL
- help:
- en_US: Fill this with the HTTP URL of your OneBot server
- zh_Hans: 请在你的 OneBot 协议端开启 正向 HTTP 并填写其 URL
- access_token:
- type: secret-input
- required: false
- label:
- en_US: Access Token
- zh_Hans: 访问令牌
- description:
- en_US: Access Token for OneBot v11 Protocol
- zh_Hans: OneBot 协议访问令牌
- help:
- en_US: Fill this if you set a access token in your OneBot server
- zh_Hans: 如果你在 OneBot 服务器中设置了 access token,请填写此项
diff --git a/api/core/tools/provider/builtin/onebot/tools/__init__.py b/api/core/tools/provider/builtin/onebot/tools/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/onebot/tools/send_group_msg.py b/api/core/tools/provider/builtin/onebot/tools/send_group_msg.py
deleted file mode 100644
index 9c95bbc2ae8d2d..00000000000000
--- a/api/core/tools/provider/builtin/onebot/tools/send_group_msg.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any, Union
-
-import requests
-from yarl import URL
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SendGroupMsg(BuiltinTool):
- """OneBot v11 Tool: Send Group Message"""
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- # Get parameters
- send_group_id = tool_parameters.get("group_id", "")
-
- message = tool_parameters.get("message", "")
- if not message:
- return self.create_json_message({"error": "Message is empty."})
-
- auto_escape = tool_parameters.get("auto_escape", False)
-
- try:
- url = URL(self.runtime.credentials["ob11_http_url"]) / "send_group_msg"
-
- resp = requests.post(
- url,
- json={"group_id": send_group_id, "message": message, "auto_escape": auto_escape},
- headers={"Authorization": "Bearer " + self.runtime.credentials["access_token"]},
- )
-
- if resp.status_code != 200:
- return self.create_json_message({"error": f"Failed to send group message: {resp.text}"})
-
- return self.create_json_message({"response": resp.json()})
- except Exception as e:
- return self.create_json_message({"error": f"Failed to send group message: {e}"})
diff --git a/api/core/tools/provider/builtin/onebot/tools/send_group_msg.yaml b/api/core/tools/provider/builtin/onebot/tools/send_group_msg.yaml
deleted file mode 100644
index 64beaa85457a3a..00000000000000
--- a/api/core/tools/provider/builtin/onebot/tools/send_group_msg.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-identity:
- name: send_group_msg
- author: RockChinQ
- label:
- en_US: Send Group Message
- zh_Hans: 发送群消息
-description:
- human:
- en_US: Send a message to a group
- zh_Hans: 发送消息到群聊
- llm: A tool for sending a message segment to a group
-parameters:
- - name: group_id
- type: number
- required: true
- label:
- en_US: Target Group ID
- zh_Hans: 目标群 ID
- human_description:
- en_US: The group ID of the target group
- zh_Hans: 目标群的群 ID
- llm_description: The group ID of the target group
- form: llm
- - name: message
- type: string
- required: true
- label:
- en_US: Message
- zh_Hans: 消息
- human_description:
- en_US: The message to send
- zh_Hans: 要发送的消息。支持 CQ码(需要同时设置 auto_escape 为 true)
- llm_description: The message to send
- form: llm
- - name: auto_escape
- type: boolean
- required: false
- default: false
- label:
- en_US: Auto Escape
- zh_Hans: 自动转义
- human_description:
- en_US: If true, the message will be treated as a CQ code for parsing, otherwise it will be treated as plain text for direct sending. Since Dify currently does not support passing Object-format message chains, developers can send complex message components through CQ codes.
- zh_Hans: 若为 true 则会把 message 视为 CQ 码解析,否则视为 纯文本 直接发送。由于 Dify 目前不支持传入 Object格式 的消息,故开发者可以通过 CQ 码来发送复杂消息组件。
- llm_description: If true, the message will be treated as a CQ code for parsing, otherwise it will be treated as plain text for direct sending.
- form: form
diff --git a/api/core/tools/provider/builtin/onebot/tools/send_private_msg.py b/api/core/tools/provider/builtin/onebot/tools/send_private_msg.py
deleted file mode 100644
index 1174c7f07d002f..00000000000000
--- a/api/core/tools/provider/builtin/onebot/tools/send_private_msg.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any, Union
-
-import requests
-from yarl import URL
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SendPrivateMsg(BuiltinTool):
- """OneBot v11 Tool: Send Private Message"""
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- # Get parameters
- send_user_id = tool_parameters.get("user_id", "")
-
- message = tool_parameters.get("message", "")
- if not message:
- return self.create_json_message({"error": "Message is empty."})
-
- auto_escape = tool_parameters.get("auto_escape", False)
-
- try:
- url = URL(self.runtime.credentials["ob11_http_url"]) / "send_private_msg"
-
- resp = requests.post(
- url,
- json={"user_id": send_user_id, "message": message, "auto_escape": auto_escape},
- headers={"Authorization": "Bearer " + self.runtime.credentials["access_token"]},
- )
-
- if resp.status_code != 200:
- return self.create_json_message({"error": f"Failed to send private message: {resp.text}"})
-
- return self.create_json_message({"response": resp.json()})
- except Exception as e:
- return self.create_json_message({"error": f"Failed to send private message: {e}"})
diff --git a/api/core/tools/provider/builtin/onebot/tools/send_private_msg.yaml b/api/core/tools/provider/builtin/onebot/tools/send_private_msg.yaml
deleted file mode 100644
index 8200ce4a83f4e2..00000000000000
--- a/api/core/tools/provider/builtin/onebot/tools/send_private_msg.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-identity:
- name: send_private_msg
- author: RockChinQ
- label:
- en_US: Send Private Message
- zh_Hans: 发送私聊消息
-description:
- human:
- en_US: Send a private message to a user
- zh_Hans: 发送私聊消息给用户
- llm: A tool for sending a message segment to a user in private chat
-parameters:
- - name: user_id
- type: number
- required: true
- label:
- en_US: Target User ID
- zh_Hans: 目标用户 ID
- human_description:
- en_US: The user ID of the target user
- zh_Hans: 目标用户的用户 ID
- llm_description: The user ID of the target user
- form: llm
- - name: message
- type: string
- required: true
- label:
- en_US: Message
- zh_Hans: 消息
- human_description:
- en_US: The message to send
- zh_Hans: 要发送的消息。支持 CQ码(需要同时设置 auto_escape 为 true)
- llm_description: The message to send
- form: llm
- - name: auto_escape
- type: boolean
- required: false
- default: false
- label:
- en_US: Auto Escape
- zh_Hans: 自动转义
- human_description:
- en_US: If true, the message will be treated as a CQ code for parsing, otherwise it will be treated as plain text for direct sending. Since Dify currently does not support passing Object-format message chains, developers can send complex message components through CQ codes.
- zh_Hans: 若为 true 则会把 message 视为 CQ 码解析,否则视为 纯文本 直接发送。由于 Dify 目前不支持传入 Object格式 的消息,故开发者可以通过 CQ 码来发送复杂消息组件。
- llm_description: If true, the message will be treated as a CQ code for parsing, otherwise it will be treated as plain text for direct sending.
- form: form
diff --git a/api/core/tools/provider/builtin/openweather/_assets/icon.svg b/api/core/tools/provider/builtin/openweather/_assets/icon.svg
deleted file mode 100644
index f06cd87e64c9d3..00000000000000
--- a/api/core/tools/provider/builtin/openweather/_assets/icon.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/openweather/openweather.py b/api/core/tools/provider/builtin/openweather/openweather.py
deleted file mode 100644
index 9e40249aba6b40..00000000000000
--- a/api/core/tools/provider/builtin/openweather/openweather.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-def query_weather(city="Beijing", units="metric", language="zh_cn", api_key=None):
- url = "https://api.openweathermap.org/data/2.5/weather"
- params = {"q": city, "appid": api_key, "units": units, "lang": language}
-
- return requests.get(url, params=params)
-
-
-class OpenweatherProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- if "api_key" not in credentials or not credentials.get("api_key"):
- raise ToolProviderCredentialValidationError("Open weather API key is required.")
- apikey = credentials.get("api_key")
- try:
- response = query_weather(api_key=apikey)
- if response.status_code == 200:
- pass
- else:
- raise ToolProviderCredentialValidationError((response.json()).get("info"))
- except Exception as e:
- raise ToolProviderCredentialValidationError("Open weather API Key is invalid. {}".format(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/openweather/openweather.yaml b/api/core/tools/provider/builtin/openweather/openweather.yaml
deleted file mode 100644
index d4b66f87f908c6..00000000000000
--- a/api/core/tools/provider/builtin/openweather/openweather.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- author: Onelevenvy
- name: openweather
- label:
- en_US: Open weather query
- zh_Hans: Open Weather
- pt_BR: Consulta de clima open weather
- description:
- en_US: Weather query toolkit based on Open Weather
- zh_Hans: 基于open weather的天气查询工具包
- pt_BR: Kit de consulta de clima baseado no Open Weather
- icon: icon.svg
- tags:
- - weather
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- zh_Hans: API Key
- pt_BR: Fogo a chave
- placeholder:
- en_US: Please enter your open weather API Key
- zh_Hans: 请输入你的open weather API Key
- pt_BR: Insira sua chave de API open weather
- help:
- en_US: Get your API Key from open weather
- zh_Hans: 从open weather获取您的 API Key
- pt_BR: Obtenha sua chave de API do open weather
- url: https://openweathermap.org
diff --git a/api/core/tools/provider/builtin/openweather/tools/weather.py b/api/core/tools/provider/builtin/openweather/tools/weather.py
deleted file mode 100644
index ed4ec487fa984a..00000000000000
--- a/api/core/tools/provider/builtin/openweather/tools/weather.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class OpenweatherTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- city = tool_parameters.get("city", "")
- if not city:
- return self.create_text_message("Please tell me your city")
- if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
- return self.create_text_message("OpenWeather API key is required.")
-
- units = tool_parameters.get("units", "metric")
- lang = tool_parameters.get("lang", "zh_cn")
- try:
- # request URL
- url = "https://api.openweathermap.org/data/2.5/weather"
-
- # request params
- params = {
- "q": city,
- "appid": self.runtime.credentials.get("api_key"),
- "units": units,
- "lang": lang,
- }
- response = requests.get(url, params=params)
-
- if response.status_code == 200:
- data = response.json()
- return self.create_text_message(
- self.summary(user_id=user_id, content=json.dumps(data, ensure_ascii=False))
- )
- else:
- error_message = {
- "error": f"failed:{response.status_code}",
- "data": response.text,
- }
- # return error
- return json.dumps(error_message)
-
- except Exception as e:
- return self.create_text_message("Openweather API Key is invalid. {}".format(e))
diff --git a/api/core/tools/provider/builtin/openweather/tools/weather.yaml b/api/core/tools/provider/builtin/openweather/tools/weather.yaml
deleted file mode 100644
index f2dae5c2df9c08..00000000000000
--- a/api/core/tools/provider/builtin/openweather/tools/weather.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-identity:
- name: weather
- author: Onelevenvy
- label:
- en_US: Open Weather Query
- zh_Hans: 天气查询
- pt_BR: Previsão do tempo
- icon: icon.svg
-description:
- human:
- en_US: Weather forecast inquiry
- zh_Hans: 天气查询
- pt_BR: Inquérito sobre previsão meteorológica
- llm: A tool when you want to ask about the weather or weather-related question
-parameters:
- - name: city
- type: string
- required: true
- label:
- en_US: city
- zh_Hans: 城市
- pt_BR: cidade
- human_description:
- en_US: Target city for weather forecast query
- zh_Hans: 天气预报查询的目标城市
- pt_BR: Cidade de destino para consulta de previsão do tempo
- llm_description: If you don't know you can extract the city name from the
- question or you can reply:Please tell me your city. You have to extract
- the Chinese city name from the question.If the input region is in Chinese
- characters for China, it should be replaced with the corresponding English
- name, such as '北京' for correct input is 'Beijing'
- form: llm
- - name: lang
- type: select
- required: true
- human_description:
- en_US: language
- zh_Hans: 语言
- pt_BR: language
- label:
- en_US: language
- zh_Hans: 语言
- pt_BR: language
- form: form
- options:
- - value: zh_cn
- label:
- en_US: cn
- zh_Hans: 中国
- pt_BR: cn
- - value: en_us
- label:
- en_US: usa
- zh_Hans: 美国
- pt_BR: usa
- default: zh_cn
- - name: units
- type: select
- required: true
- human_description:
- en_US: units for temperature
- zh_Hans: 温度单位
- pt_BR: units for temperature
- label:
- en_US: units
- zh_Hans: 单位
- pt_BR: units
- form: form
- options:
- - value: metric
- label:
- en_US: metric
- zh_Hans: ℃
- pt_BR: metric
- - value: imperial
- label:
- en_US: imperial
- zh_Hans: ℉
- pt_BR: imperial
- default: metric
diff --git a/api/core/tools/provider/builtin/perplexity/_assets/icon.svg b/api/core/tools/provider/builtin/perplexity/_assets/icon.svg
deleted file mode 100644
index c2974c142fc622..00000000000000
--- a/api/core/tools/provider/builtin/perplexity/_assets/icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/api/core/tools/provider/builtin/perplexity/perplexity.py b/api/core/tools/provider/builtin/perplexity/perplexity.py
deleted file mode 100644
index 80518853fb4a4b..00000000000000
--- a/api/core/tools/provider/builtin/perplexity/perplexity.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.perplexity.tools.perplexity_search import PERPLEXITY_API_URL
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class PerplexityProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- headers = {
- "Authorization": f"Bearer {credentials.get('perplexity_api_key')}",
- "Content-Type": "application/json",
- }
-
- payload = {
- "model": "llama-3.1-sonar-small-128k-online",
- "messages": [
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Hello"},
- ],
- "max_tokens": 5,
- "temperature": 0.1,
- "top_p": 0.9,
- "stream": False,
- }
-
- try:
- response = requests.post(PERPLEXITY_API_URL, json=payload, headers=headers)
- response.raise_for_status()
- except requests.RequestException as e:
- raise ToolProviderCredentialValidationError(f"Failed to validate Perplexity API key: {str(e)}")
-
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError(
- f"Perplexity API key is invalid. Status code: {response.status_code}"
- )
diff --git a/api/core/tools/provider/builtin/perplexity/perplexity.yaml b/api/core/tools/provider/builtin/perplexity/perplexity.yaml
deleted file mode 100644
index c0b504f300c45a..00000000000000
--- a/api/core/tools/provider/builtin/perplexity/perplexity.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-identity:
- author: Dify
- name: perplexity
- label:
- en_US: Perplexity
- zh_Hans: Perplexity
- description:
- en_US: Perplexity.AI
- zh_Hans: Perplexity.AI
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- perplexity_api_key:
- type: secret-input
- required: true
- label:
- en_US: Perplexity API key
- zh_Hans: Perplexity API key
- placeholder:
- en_US: Please input your Perplexity API key
- zh_Hans: 请输入你的 Perplexity API key
- help:
- en_US: Get your Perplexity API key from Perplexity
- zh_Hans: 从 Perplexity 获取您的 Perplexity API key
- url: https://www.perplexity.ai/settings/api
diff --git a/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.py b/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.py
deleted file mode 100644
index 5ed4b9ca993483..00000000000000
--- a/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-PERPLEXITY_API_URL = "https://api.perplexity.ai/chat/completions"
-
-
-class PerplexityAITool(BuiltinTool):
- def _parse_response(self, response: dict) -> dict:
- """Parse the response from Perplexity AI API"""
- if "choices" in response and len(response["choices"]) > 0:
- message = response["choices"][0]["message"]
- return {
- "content": message.get("content", ""),
- "role": message.get("role", ""),
- "citations": response.get("citations", []),
- }
- else:
- return {"content": "Unable to get a valid response", "role": "assistant", "citations": []}
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "Authorization": f"Bearer {self.runtime.credentials['perplexity_api_key']}",
- "Content-Type": "application/json",
- }
-
- payload = {
- "model": tool_parameters.get("model", "llama-3.1-sonar-small-128k-online"),
- "messages": [
- {"role": "system", "content": "Be precise and concise."},
- {"role": "user", "content": tool_parameters["query"]},
- ],
- "max_tokens": tool_parameters.get("max_tokens", 4096),
- "temperature": tool_parameters.get("temperature", 0.7),
- "top_p": tool_parameters.get("top_p", 1),
- "top_k": tool_parameters.get("top_k", 5),
- "presence_penalty": tool_parameters.get("presence_penalty", 0),
- "frequency_penalty": tool_parameters.get("frequency_penalty", 1),
- "stream": False,
- }
-
- if "search_recency_filter" in tool_parameters:
- payload["search_recency_filter"] = tool_parameters["search_recency_filter"]
- if "return_citations" in tool_parameters:
- payload["return_citations"] = tool_parameters["return_citations"]
- if "search_domain_filter" in tool_parameters:
- if isinstance(tool_parameters["search_domain_filter"], str):
- payload["search_domain_filter"] = [tool_parameters["search_domain_filter"]]
- elif isinstance(tool_parameters["search_domain_filter"], list):
- payload["search_domain_filter"] = tool_parameters["search_domain_filter"]
-
- response = requests.post(url=PERPLEXITY_API_URL, json=payload, headers=headers)
- response.raise_for_status()
- valuable_res = self._parse_response(response.json())
-
- return [
- self.create_json_message(valuable_res),
- self.create_text_message(json.dumps(valuable_res, ensure_ascii=False, indent=2)),
- ]
diff --git a/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.yaml b/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.yaml
deleted file mode 100644
index 02a645df335aaf..00000000000000
--- a/api/core/tools/provider/builtin/perplexity/tools/perplexity_search.yaml
+++ /dev/null
@@ -1,178 +0,0 @@
-identity:
- name: perplexity
- author: Dify
- label:
- en_US: Perplexity Search
-description:
- human:
- en_US: Search information using Perplexity AI's language models.
- llm: This tool is used to search information using Perplexity AI's language models.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 查询
- human_description:
- en_US: The text query to be processed by the AI model.
- zh_Hans: 要由 AI 模型处理的文本查询。
- form: llm
- - name: model
- type: select
- required: false
- label:
- en_US: Model Name
- zh_Hans: 模型名称
- human_description:
- en_US: The Perplexity AI model to use for generating the response.
- zh_Hans: 用于生成响应的 Perplexity AI 模型。
- form: form
- default: "llama-3.1-sonar-small-128k-online"
- options:
- - value: llama-3.1-sonar-small-128k-online
- label:
- en_US: llama-3.1-sonar-small-128k-online
- zh_Hans: llama-3.1-sonar-small-128k-online
- - value: llama-3.1-sonar-large-128k-online
- label:
- en_US: llama-3.1-sonar-large-128k-online
- zh_Hans: llama-3.1-sonar-large-128k-online
- - value: llama-3.1-sonar-huge-128k-online
- label:
- en_US: llama-3.1-sonar-huge-128k-online
- zh_Hans: llama-3.1-sonar-huge-128k-online
- - name: max_tokens
- type: number
- required: false
- label:
- en_US: Max Tokens
- zh_Hans: 最大令牌数
- pt_BR: Máximo de Tokens
- human_description:
- en_US: The maximum number of tokens to generate in the response.
- zh_Hans: 在响应中生成的最大令牌数。
- pt_BR: O número máximo de tokens a serem gerados na resposta.
- form: form
- default: 4096
- min: 1
- max: 4096
- - name: temperature
- type: number
- required: false
- label:
- en_US: Temperature
- zh_Hans: 温度
- pt_BR: Temperatura
- human_description:
- en_US: Controls randomness in the output. Lower values make the output more focused and deterministic.
- zh_Hans: 控制输出的随机性。较低的值使输出更加集中和确定。
- form: form
- default: 0.7
- min: 0
- max: 1
- - name: top_k
- type: number
- required: false
- label:
- en_US: Top K
- zh_Hans: 取样数量
- human_description:
- en_US: The number of top results to consider for response generation.
- zh_Hans: 用于生成响应的顶部结果数量。
- form: form
- default: 5
- min: 1
- max: 100
- - name: top_p
- type: number
- required: false
- label:
- en_US: Top P
- zh_Hans: Top P
- human_description:
- en_US: Controls diversity via nucleus sampling.
- zh_Hans: 通过核心采样控制多样性。
- form: form
- default: 1
- min: 0.1
- max: 1
- step: 0.1
- - name: presence_penalty
- type: number
- required: false
- label:
- en_US: Presence Penalty
- zh_Hans: 存在惩罚
- human_description:
- en_US: Positive values penalize new tokens based on whether they appear in the text so far.
- zh_Hans: 正值会根据新词元是否已经出现在文本中来对其进行惩罚。
- form: form
- default: 0
- min: -1.0
- max: 1.0
- step: 0.1
- - name: frequency_penalty
- type: number
- required: false
- label:
- en_US: Frequency Penalty
- zh_Hans: 频率惩罚
- human_description:
- en_US: Positive values penalize new tokens based on their existing frequency in the text so far.
- zh_Hans: 正值会根据新词元在文本中已经出现的频率来对其进行惩罚。
- form: form
- default: 1
- min: 0.1
- max: 1.0
- step: 0.1
- - name: return_citations
- type: boolean
- required: false
- label:
- en_US: Return Citations
- zh_Hans: 返回引用
- human_description:
- en_US: Whether to return citations in the response.
- zh_Hans: 是否在响应中返回引用。
- form: form
- default: true
- - name: search_domain_filter
- type: string
- required: false
- label:
- en_US: Search Domain Filter
- zh_Hans: 搜索域过滤器
- human_description:
- en_US: Domain to filter the search results.
- zh_Hans: 用于过滤搜索结果的域名。
- form: form
- default: ""
- - name: search_recency_filter
- type: select
- required: false
- label:
- en_US: Search Recency Filter
- zh_Hans: 搜索时间过滤器
- human_description:
- en_US: Filter for search results based on recency.
- zh_Hans: 基于时间筛选搜索结果。
- form: form
- default: "month"
- options:
- - value: day
- label:
- en_US: Day
- zh_Hans: 天
- - value: week
- label:
- en_US: Week
- zh_Hans: 周
- - value: month
- label:
- en_US: Month
- zh_Hans: 月
- - value: year
- label:
- en_US: Year
- zh_Hans: 年
diff --git a/api/core/tools/provider/builtin/podcast_generator/_assets/icon.svg b/api/core/tools/provider/builtin/podcast_generator/_assets/icon.svg
deleted file mode 100644
index 01743c9cd31120..00000000000000
--- a/api/core/tools/provider/builtin/podcast_generator/_assets/icon.svg
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py
deleted file mode 100644
index a7f7ad2e78b9b0..00000000000000
--- a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py
+++ /dev/null
@@ -1,38 +0,0 @@
-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
-
-
-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")
-
- 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, base_url)
- else:
- raise ToolProviderCredentialValidationError(f"Unsupported TTS service: {tts_service}")
-
- 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()
- except openai.AuthenticationError:
- raise ToolProviderCredentialValidationError("Invalid OpenAI API key")
- except Exception as e:
- raise ToolProviderCredentialValidationError(f"Error validating OpenAI API key: {str(e)}")
diff --git a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml
deleted file mode 100644
index d4edb17b28638e..00000000000000
--- a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-identity:
- author: Dify
- name: podcast_generator
- label:
- en_US: Podcast Generator
- zh_Hans: 播客生成器
- description:
- en_US: Generate podcast audio using Text-to-Speech services
- zh_Hans: 使用文字转语音服务生成播客音频
- icon: icon.svg
-credentials_for_provider:
- tts_service:
- type: select
- required: true
- label:
- en_US: TTS Service
- zh_Hans: TTS 服务
- placeholder:
- en_US: Select a TTS service
- zh_Hans: 选择一个 TTS 服务
- options:
- - label:
- en_US: OpenAI TTS
- zh_Hans: OpenAI TTS
- value: openai
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- zh_Hans: API 密钥
- 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
deleted file mode 100644
index 704e0015d961a3..00000000000000
--- a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import concurrent.futures
-import io
-import random
-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
-from core.tools.tool.builtin_tool import BuiltinTool
-
-with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- from pydub import AudioSegment # type: ignore
-
-
-class PodcastAudioGeneratorTool(BuiltinTool):
- @staticmethod
- def _generate_silence(duration: float):
- # Generate silent WAV data using pydub
- silence = AudioSegment.silent(duration=int(duration * 1000)) # pydub uses milliseconds
- return silence
-
- @staticmethod
- def _generate_audio_segment(
- client: openai.OpenAI,
- line: str,
- voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
- index: int,
- ) -> tuple[int, Union[AudioSegment, str], Optional[AudioSegment]]:
- try:
- response = client.audio.speech.create(model="tts-1", voice=voice, input=line.strip(), response_format="wav")
- audio = AudioSegment.from_wav(io.BytesIO(response.content))
- silence_duration = random.uniform(0.1, 1.5)
- silence = PodcastAudioGeneratorTool._generate_silence(silence_duration)
- return index, audio, silence
- except Exception as e:
- return index, f"Error generating audio: {str(e)}", None
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- # Extract parameters
- script = tool_parameters.get("script", "")
- host1_voice = tool_parameters.get("host1_voice")
- host2_voice = tool_parameters.get("host2_voice")
-
- # Split the script into lines
- script_lines = [line for line in script.split("\n") if line.strip()]
-
- # Ensure voices are provided
- if not host1_voice or not host2_voice:
- raise ToolParameterValidationError("Host voices are required")
-
- # 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,
- base_url=openai_base_url,
- )
-
- # Create a thread pool
- max_workers = 5
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
- futures = []
- for i, line in enumerate(script_lines):
- voice = host1_voice if i % 2 == 0 else host2_voice
- future = executor.submit(self._generate_audio_segment, client, line, voice, i)
- futures.append(future)
-
- # Collect results
- audio_segments: list[Any] = [None] * len(script_lines)
- for future in concurrent.futures.as_completed(futures):
- index, audio, silence = future.result()
- if isinstance(audio, str): # Error occurred
- return self.create_text_message(audio)
- audio_segments[index] = (audio, silence)
-
- # Combine audio segments in the correct order
- combined_audio = AudioSegment.empty()
- for i, (audio, silence) in enumerate(audio_segments):
- if audio:
- combined_audio += audio
- if i < len(audio_segments) - 1 and silence:
- combined_audio += silence
-
- # Export the combined audio to a WAV file in memory
- buffer = io.BytesIO()
- combined_audio.export(buffer, format="wav")
- wav_bytes = buffer.getvalue()
-
- # Create a blob message with the combined audio
- 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,
- ),
- ]
diff --git a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.yaml b/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.yaml
deleted file mode 100644
index d6ae98f59522c5..00000000000000
--- a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.yaml
+++ /dev/null
@@ -1,95 +0,0 @@
-identity:
- name: podcast_audio_generator
- author: Dify
- label:
- en_US: Podcast Audio Generator
- zh_Hans: 播客音频生成器
-description:
- human:
- en_US: Generate a podcast audio file from a script with two alternating voices using OpenAI's TTS service.
- zh_Hans: 使用 OpenAI 的 TTS 服务,从包含两个交替声音的脚本生成播客音频文件。
- llm: This tool converts a prepared podcast script into an audio file using OpenAI's Text-to-Speech service, with two specified voices for alternating hosts.
-parameters:
- - name: script
- type: string
- required: true
- label:
- en_US: Podcast Script
- zh_Hans: 播客脚本
- human_description:
- en_US: A string containing alternating lines for two hosts, separated by newline characters.
- zh_Hans: 包含两位主持人交替台词的字符串,每行用换行符分隔。
- llm_description: A string representing the script, with alternating lines for two hosts separated by newline characters.
- form: llm
- - name: host1_voice
- type: select
- required: true
- label:
- en_US: Host 1 Voice
- zh_Hans: 主持人1 音色
- human_description:
- en_US: The voice for the first host.
- zh_Hans: 第一位主持人的音色。
- llm_description: The voice identifier for the first host's voice.
- options:
- - label:
- en_US: Alloy
- zh_Hans: Alloy
- value: alloy
- - label:
- en_US: Echo
- zh_Hans: Echo
- value: echo
- - label:
- en_US: Fable
- zh_Hans: Fable
- value: fable
- - label:
- en_US: Onyx
- zh_Hans: Onyx
- value: onyx
- - label:
- en_US: Nova
- zh_Hans: Nova
- value: nova
- - label:
- en_US: Shimmer
- zh_Hans: Shimmer
- value: shimmer
- form: form
- - name: host2_voice
- type: select
- required: true
- label:
- en_US: Host 2 Voice
- zh_Hans: 主持人2 音色
- human_description:
- en_US: The voice for the second host.
- zh_Hans: 第二位主持人的音色。
- llm_description: The voice identifier for the second host's voice.
- options:
- - label:
- en_US: Alloy
- zh_Hans: Alloy
- value: alloy
- - label:
- en_US: Echo
- zh_Hans: Echo
- value: echo
- - label:
- en_US: Fable
- zh_Hans: Fable
- value: fable
- - label:
- en_US: Onyx
- zh_Hans: Onyx
- value: onyx
- - label:
- en_US: Nova
- zh_Hans: Nova
- value: nova
- - label:
- en_US: Shimmer
- zh_Hans: Shimmer
- value: shimmer
- form: form
diff --git a/api/core/tools/provider/builtin/pubmed/_assets/icon.svg b/api/core/tools/provider/builtin/pubmed/_assets/icon.svg
deleted file mode 100644
index 6d6ff593f0c999..00000000000000
--- a/api/core/tools/provider/builtin/pubmed/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/pubmed/pubmed.py b/api/core/tools/provider/builtin/pubmed/pubmed.py
deleted file mode 100644
index ea3a477c30178d..00000000000000
--- a/api/core/tools/provider/builtin/pubmed/pubmed.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.pubmed.tools.pubmed_search import PubMedSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class PubMedProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- PubMedSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "John Doe",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/pubmed/pubmed.yaml b/api/core/tools/provider/builtin/pubmed/pubmed.yaml
deleted file mode 100644
index 5f8303147c397b..00000000000000
--- a/api/core/tools/provider/builtin/pubmed/pubmed.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-identity:
- author: Pink Banana
- name: pubmed
- label:
- en_US: PubMed
- zh_Hans: PubMed
- description:
- en_US: A search engine for biomedical literature.
- zh_Hans: 一款生物医学文献搜索引擎。
- icon: icon.svg
- tags:
- - medical
- - search
diff --git a/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.py b/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.py
deleted file mode 100644
index 3a4f374ea0b0bc..00000000000000
--- a/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import json
-import time
-import urllib.error
-import urllib.parse
-import urllib.request
-from typing import Any
-
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class PubMedAPIWrapper(BaseModel):
- """
- Wrapper around PubMed API.
-
- This wrapper will use the PubMed API to conduct searches and fetch
- document summaries. By default, it will return the document summaries
- of the top-k results of an input search.
-
- Parameters:
- top_k_results: number of the top-scored document used for the PubMed tool
- load_max_docs: a limit to the number of loaded documents
- load_all_available_meta:
- if True: the `metadata` of the loaded Documents gets all available meta info
- (see https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch)
- if False: the `metadata` gets only the most informative fields.
- """
-
- base_url_esearch: str = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?"
- base_url_efetch: str = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?"
- max_retry: int = 5
- sleep_time: float = 0.2
-
- # Default values for the parameters
- top_k_results: int = 3
- load_max_docs: int = 25
- ARXIV_MAX_QUERY_LENGTH: int = 300
- doc_content_chars_max: int = 2000
- load_all_available_meta: bool = False
- email: str = "your_email@example.com"
-
- def run(self, query: str) -> str:
- """
- Run PubMed search and get the article meta information.
- See https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch
- It uses only the most informative fields of article meta information.
- """
-
- try:
- # Retrieve the top-k results for the query
- docs = [
- f"Published: {result['pub_date']}\nTitle: {result['title']}\nSummary: {result['summary']}"
- for result in self.load(query[: self.ARXIV_MAX_QUERY_LENGTH])
- ]
-
- # Join the results and limit the character count
- return "\n\n".join(docs)[: self.doc_content_chars_max] if docs else "No good PubMed Result was found"
- except Exception as ex:
- return f"PubMed exception: {ex}"
-
- def load(self, query: str) -> list[dict]:
- """
- Search PubMed for documents matching the query.
- Return a list of dictionaries containing the document metadata.
- """
-
- url = (
- self.base_url_esearch
- + "db=pubmed&term="
- + str({urllib.parse.quote(query)})
- + f"&retmode=json&retmax={self.top_k_results}&usehistory=y"
- )
- result = urllib.request.urlopen(url)
- text = result.read().decode("utf-8")
- json_text = json.loads(text)
-
- articles = []
- webenv = json_text["esearchresult"]["webenv"]
- for uid in json_text["esearchresult"]["idlist"]:
- article = self.retrieve_article(uid, webenv)
- articles.append(article)
-
- # Convert the list of articles to a JSON string
- return articles
-
- def retrieve_article(self, uid: str, webenv: str) -> dict:
- url = self.base_url_efetch + "db=pubmed&retmode=xml&id=" + uid + "&webenv=" + webenv
-
- retry = 0
- while True:
- try:
- result = urllib.request.urlopen(url)
- break
- except urllib.error.HTTPError as e:
- if e.code == 429 and retry < self.max_retry:
- # Too Many Requests error
- # wait for an exponentially increasing amount of time
- print(f"Too Many Requests, waiting for {self.sleep_time:.2f} seconds...")
- time.sleep(self.sleep_time)
- self.sleep_time *= 2
- retry += 1
- else:
- raise e
-
- xml_text = result.read().decode("utf-8")
-
- # Get title
- title = ""
- if "" in xml_text and " " in xml_text:
- start_tag = ""
- end_tag = " "
- title = xml_text[xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)]
-
- # Get abstract
- abstract = ""
- if "" in xml_text and " " in xml_text:
- start_tag = ""
- end_tag = " "
- abstract = xml_text[xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)]
-
- # Get publication date
- pub_date = ""
- if "" in xml_text and " " in xml_text:
- start_tag = ""
- end_tag = " "
- pub_date = xml_text[xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)]
-
- # Return article as dictionary
- article = {
- "uid": uid,
- "title": title,
- "summary": abstract,
- "pub_date": pub_date,
- }
- return article
-
-
-class PubmedQueryRun(BaseModel):
- """Tool that searches the PubMed API."""
-
- name: str = "PubMed"
- description: str = (
- "A wrapper around PubMed.org "
- "Useful for when you need to answer questions about Physics, Mathematics, "
- "Computer Science, Quantitative Biology, Quantitative Finance, Statistics, "
- "Electrical Engineering, and Economics "
- "from scientific articles on PubMed.org. "
- "Input should be a search query."
- )
- api_wrapper: PubMedAPIWrapper = Field(default_factory=PubMedAPIWrapper)
-
- def _run(
- self,
- query: str,
- ) -> str:
- """Use the Arxiv tool."""
- return self.api_wrapper.run(query)
-
-
-class PubMedInput(BaseModel):
- query: str = Field(..., description="Search query.")
-
-
-class PubMedSearchTool(BuiltinTool):
- """
- Tool for performing a search using PubMed search engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invoke the PubMed search tool.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Any]): The parameters for the tool invocation.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation.
- """
- query = tool_parameters.get("query", "")
-
- if not query:
- return self.create_text_message("Please input query")
-
- tool = PubmedQueryRun(args_schema=PubMedInput)
-
- result = tool._run(query)
-
- return self.create_text_message(self.summary(user_id=user_id, content=result))
diff --git a/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.yaml b/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.yaml
deleted file mode 100644
index 77ab809fbc3e05..00000000000000
--- a/api/core/tools/provider/builtin/pubmed/tools/pubmed_search.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: pubmed_search
- author: Pink Banana
- label:
- en_US: PubMed Search
- zh_Hans: PubMed 搜索
-description:
- human:
- en_US: PubMed® comprises more than 35 million citations for biomedical literature from MEDLINE, life science journals, and online books. Citations may include links to full text content from PubMed Central and publisher web sites.
- zh_Hans: PubMed® 包含来自 MEDLINE、生命科学期刊和在线书籍的超过 3500 万篇生物医学文献引用。引用可能包括来自 PubMed Central 和出版商网站的全文内容链接。
- llm: Perform searches on PubMed and get results.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- human_description:
- en_US: The search query.
- zh_Hans: 搜索查询语句。
- llm_description: Key words for searching
- form: llm
diff --git a/api/core/tools/provider/builtin/qrcode/_assets/icon.svg b/api/core/tools/provider/builtin/qrcode/_assets/icon.svg
deleted file mode 100644
index 979bdda4558256..00000000000000
--- a/api/core/tools/provider/builtin/qrcode/_assets/icon.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/qrcode/qrcode.py b/api/core/tools/provider/builtin/qrcode/qrcode.py
deleted file mode 100644
index 8466b9a26b42b6..00000000000000
--- a/api/core/tools/provider/builtin/qrcode/qrcode.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.qrcode.tools.qrcode_generator import QRCodeGeneratorTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class QRCodeProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- QRCodeGeneratorTool().invoke(user_id="", tool_parameters={"content": "Dify 123 😊"})
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/qrcode/qrcode.yaml b/api/core/tools/provider/builtin/qrcode/qrcode.yaml
deleted file mode 100644
index 82e2a06e15cc18..00000000000000
--- a/api/core/tools/provider/builtin/qrcode/qrcode.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-identity:
- author: Bowen Liang
- name: qrcode
- label:
- en_US: QRCode
- zh_Hans: 二维码工具
- pt_BR: QRCode
- description:
- en_US: A tool for generating QR code (quick-response code) image.
- zh_Hans: 一个二维码工具
- pt_BR: A tool for generating QR code (quick-response code) image.
- icon: icon.svg
- tags:
- - utilities
diff --git a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py b/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py
deleted file mode 100644
index 4a47c4211f4fd4..00000000000000
--- a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import io
-import logging
-from typing import Any, Union
-
-from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q # type: ignore
-from qrcode.image.base import BaseImage # type: ignore
-from qrcode.image.pure import PyPNGImage # type: ignore
-from qrcode.main import QRCode # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class QRCodeGeneratorTool(BuiltinTool):
- error_correction_levels: dict[str, int] = {
- "L": ERROR_CORRECT_L, # <=7%
- "M": ERROR_CORRECT_M, # <=15%
- "Q": ERROR_CORRECT_Q, # <=25%
- "H": ERROR_CORRECT_H, # <=30%
- }
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get text content
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- # get border size
- border = tool_parameters.get("border", 0)
- if border < 0 or border > 100:
- return self.create_text_message("Invalid parameter border")
-
- # get error_correction
- error_correction = tool_parameters.get("error_correction", "")
- if error_correction not in self.error_correction_levels:
- return self.create_text_message("Invalid parameter error_correction")
-
- try:
- image = self._generate_qrcode(content, border, error_correction)
- image_bytes = self._image_to_byte_array(image)
- return self.create_blob_message(
- blob=image_bytes, meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
- except Exception:
- logging.exception(f"Failed to generate QR code for content: {content}")
- return self.create_text_message("Failed to generate QR code")
-
- def _generate_qrcode(self, content: str, border: int, error_correction: str) -> BaseImage:
- qr = QRCode(
- image_factory=PyPNGImage,
- error_correction=self.error_correction_levels.get(error_correction),
- border=border,
- )
- qr.add_data(data=content)
- qr.make(fit=True)
- img = qr.make_image()
- return img
-
- @staticmethod
- def _image_to_byte_array(image: BaseImage) -> bytes:
- byte_stream = io.BytesIO()
- image.save(byte_stream)
- return byte_stream.getvalue()
diff --git a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.yaml b/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.yaml
deleted file mode 100644
index 8c8b8c449ad513..00000000000000
--- a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.yaml
+++ /dev/null
@@ -1,76 +0,0 @@
-identity:
- name: qrcode_generator
- author: Bowen Liang
- label:
- en_US: Generate QR Code
- zh_Hans: 生成二维码
- pt_BR: Generate QR Code
-description:
- human:
- en_US: A tool for generating QR code image
- zh_Hans: 一个用于生成二维码的工具
- pt_BR: A tool for generating QR code image
- llm: A tool for generating QR code image
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: content text for QR code
- zh_Hans: 二维码文本内容
- pt_BR: content text for QR code
- human_description:
- en_US: content text for QR code
- zh_Hans: 二维码文本内容
- pt_BR: 二维码文本内容
- form: llm
- - name: error_correction
- type: select
- required: true
- default: M
- label:
- en_US: Error Correction
- zh_Hans: 容错等级
- pt_BR: Error Correction
- human_description:
- en_US: Error Correction in L, M, Q or H, from low to high, the bigger size of generated QR code with the better error correction effect
- zh_Hans: 容错等级,可设置为低、中、偏高或高,从低到高,生成的二维码越大且容错效果越好
- pt_BR: Error Correction in L, M, Q or H, from low to high, the bigger size of generated QR code with the better error correction effect
- options:
- - value: L
- label:
- en_US: Low
- zh_Hans: 低
- pt_BR: Low
- - value: M
- label:
- en_US: Medium
- zh_Hans: 中
- pt_BR: Medium
- - value: Q
- label:
- en_US: Quartile
- zh_Hans: 偏高
- pt_BR: Quartile
- - value: H
- label:
- en_US: High
- zh_Hans: 高
- pt_BR: High
- form: form
- - name: border
- type: number
- required: true
- default: 2
- min: 0
- max: 100
- label:
- en_US: border size
- zh_Hans: 边框粗细
- pt_BR: border size
- human_description:
- en_US: border size(default to 2)
- zh_Hans: 边框粗细的格数(默认为2)
- pt_BR: border size(default to 2)
- llm: border size, default to 2
- form: form
diff --git a/api/core/tools/provider/builtin/rapidapi/_assets/rapidapi.png b/api/core/tools/provider/builtin/rapidapi/_assets/rapidapi.png
deleted file mode 100644
index 9c7468bb172326..00000000000000
Binary files a/api/core/tools/provider/builtin/rapidapi/_assets/rapidapi.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/rapidapi/rapidapi.py b/api/core/tools/provider/builtin/rapidapi/rapidapi.py
deleted file mode 100644
index 31077b0894153b..00000000000000
--- a/api/core/tools/provider/builtin/rapidapi/rapidapi.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.rapidapi.tools.google_news import GooglenewsTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class RapidapiProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- GooglenewsTool().fork_tool_runtime(
- meta={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "language_region": "en-US",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/rapidapi/rapidapi.yaml b/api/core/tools/provider/builtin/rapidapi/rapidapi.yaml
deleted file mode 100644
index 3f1d1c58248cc1..00000000000000
--- a/api/core/tools/provider/builtin/rapidapi/rapidapi.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-identity:
- name: rapidapi
- author: Steven Sun
- label:
- en_US: RapidAPI
- zh_Hans: RapidAPI
- description:
- en_US: RapidAPI is the world's largest API marketplace with over 1,000,000 developers and 10,000 APIs.
- zh_Hans: RapidAPI是全球最大的API市场,拥有超过100万开发人员和10000个API。
- icon: rapidapi.png
- tags:
- - news
-credentials_for_provider:
- x-rapidapi-host:
- type: text-input
- required: true
- label:
- en_US: x-rapidapi-host
- zh_Hans: x-rapidapi-host
- placeholder:
- en_US: Please input your x-rapidapi-host
- zh_Hans: 请输入你的 x-rapidapi-host
- help:
- en_US: Get your x-rapidapi-host from RapidAPI.
- zh_Hans: 从 RapidAPI 获取您的 x-rapidapi-host。
- url: https://rapidapi.com/
- x-rapidapi-key:
- type: secret-input
- required: true
- label:
- en_US: x-rapidapi-key
- zh_Hans: x-rapidapi-key
- placeholder:
- en_US: Please input your x-rapidapi-key
- zh_Hans: 请输入你的 x-rapidapi-key
- help:
- en_US: Get your x-rapidapi-key from RapidAPI.
- zh_Hans: 从 RapidAPI 获取您的 x-rapidapi-key。
- url: https://rapidapi.com/
diff --git a/api/core/tools/provider/builtin/rapidapi/tools/google_news.py b/api/core/tools/provider/builtin/rapidapi/tools/google_news.py
deleted file mode 100644
index d4b6dc4a46b6ef..00000000000000
--- a/api/core/tools/provider/builtin/rapidapi/tools/google_news.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolInvokeError, ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GooglenewsTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- key = self.runtime.credentials.get("x-rapidapi-key", "")
- host = self.runtime.credentials.get("x-rapidapi-host", "")
- if not all([key, host]):
- raise ToolProviderCredentialValidationError("Please input correct x-rapidapi-key and x-rapidapi-host")
- headers = {"x-rapidapi-key": key, "x-rapidapi-host": host}
- lr = tool_parameters.get("language_region", "")
- url = f"https://{host}/latest?lr={lr}"
- response = requests.get(url, headers=headers)
- if response.status_code != 200:
- raise ToolInvokeError(f"Error {response.status_code}: {response.text}")
- return self.create_text_message(response.text)
-
- def validate_credentials(self, parameters: dict[str, Any]) -> None:
- parameters["validate"] = True
- self._invoke(parameters)
diff --git a/api/core/tools/provider/builtin/rapidapi/tools/google_news.yaml b/api/core/tools/provider/builtin/rapidapi/tools/google_news.yaml
deleted file mode 100644
index 547681b16663d5..00000000000000
--- a/api/core/tools/provider/builtin/rapidapi/tools/google_news.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-identity:
- name: google_news
- author: Steven Sun
- label:
- en_US: GoogleNews
- zh_Hans: 谷歌新闻
-description:
- human:
- en_US: google news is a news aggregator service developed by Google. It presents a continuous, customizable flow of articles organized from thousands of publishers and magazines.
- zh_Hans: 谷歌新闻是由谷歌开发的新闻聚合服务。它提供了一个持续的、可定制的文章流,这些文章是从成千上万的出版商和杂志中整理出来的。
- llm: A tool to get the latest news from Google News.
-parameters:
- - name: language_region
- type: string
- required: true
- label:
- en_US: Language and Region
- zh_Hans: 语言和地区
- human_description:
- en_US: The language and region determine the language and region of the search results, and its value is assigned according to the "National Language Code Comparison Table", such as en-US, which stands for English (United States); zh-CN, stands for Chinese (Simplified).
- zh_Hans: 语言和地区决定了搜索结果的语言和地区,其赋值按照《国家语言代码对照表》,形如en-US,代表英语(美国);zh-CN,代表中文(简体)。
- llm_description: The language and region determine the language and region of the search results, and its value is assigned according to the "National Language Code Comparison Table", such as en-US, which stands for English (United States); zh-CN, stands for Chinese (Simplified).
- default: en-US
- form: llm
diff --git a/api/core/tools/provider/builtin/regex/_assets/icon.svg b/api/core/tools/provider/builtin/regex/_assets/icon.svg
deleted file mode 100644
index 0231a2b4aa9da2..00000000000000
--- a/api/core/tools/provider/builtin/regex/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/regex/regex.py b/api/core/tools/provider/builtin/regex/regex.py
deleted file mode 100644
index c498105979f13e..00000000000000
--- a/api/core/tools/provider/builtin/regex/regex.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.regex.tools.regex_extract import RegexExpressionTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class RegexProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- RegexExpressionTool().invoke(
- user_id="",
- tool_parameters={
- "content": "1+(2+3)*4",
- "expression": r"(\d+)",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/regex/regex.yaml b/api/core/tools/provider/builtin/regex/regex.yaml
deleted file mode 100644
index d05776f214e8d2..00000000000000
--- a/api/core/tools/provider/builtin/regex/regex.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: zhuhao
- name: regex
- label:
- en_US: Regex
- zh_Hans: 正则表达式提取
- pt_BR: Regex
- description:
- en_US: A tool for regex extraction.
- zh_Hans: 一个用于正则表达式内容提取的工具。
- pt_BR: A tool for regex extraction.
- icon: icon.svg
- tags:
- - utilities
- - productivity
diff --git a/api/core/tools/provider/builtin/regex/tools/regex_extract.py b/api/core/tools/provider/builtin/regex/tools/regex_extract.py
deleted file mode 100644
index 786b4694040030..00000000000000
--- a/api/core/tools/provider/builtin/regex/tools/regex_extract.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import re
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class RegexExpressionTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get expression
- content = tool_parameters.get("content", "").strip()
- if not content:
- return self.create_text_message("Invalid content")
- expression = tool_parameters.get("expression", "").strip()
- if not expression:
- return self.create_text_message("Invalid expression")
- try:
- result = re.findall(expression, content)
- return self.create_text_message(str(result))
- except Exception as e:
- return self.create_text_message(f"Failed to extract result, error: {str(e)}")
diff --git a/api/core/tools/provider/builtin/regex/tools/regex_extract.yaml b/api/core/tools/provider/builtin/regex/tools/regex_extract.yaml
deleted file mode 100644
index de4100def176c9..00000000000000
--- a/api/core/tools/provider/builtin/regex/tools/regex_extract.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-identity:
- name: regex_extract
- author: zhuhao
- label:
- en_US: Regex Extract
- zh_Hans: 正则表达式内容提取
- pt_BR: Regex Extract
-description:
- human:
- en_US: A tool for extracting matching content using regular expressions.
- zh_Hans: 一个用于利用正则表达式提取匹配内容结果的工具。
- pt_BR: A tool for extracting matching content using regular expressions.
- llm: A tool for extracting matching content using regular expressions.
-parameters:
- - name: content
- type: string
- required: true
- label:
- en_US: Content to be extracted
- zh_Hans: 内容
- pt_BR: Content to be extracted
- human_description:
- en_US: Content to be extracted
- zh_Hans: 内容
- pt_BR: Content to be extracted
- form: llm
- - name: expression
- type: string
- required: true
- label:
- en_US: Regular expression
- zh_Hans: 正则表达式
- pt_BR: Regular expression
- human_description:
- en_US: Regular expression
- zh_Hans: 正则表达式
- pt_BR: Regular expression
- form: llm
diff --git a/api/core/tools/provider/builtin/searchapi/_assets/icon.svg b/api/core/tools/provider/builtin/searchapi/_assets/icon.svg
deleted file mode 100644
index 7660b2f351c43b..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/searchapi/searchapi.py b/api/core/tools/provider/builtin/searchapi/searchapi.py
deleted file mode 100644
index 109bba8b2d8f79..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/searchapi.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.searchapi.tools.google import GoogleTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SearchAPIProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- GoogleTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"query": "SearchApi dify", "result_type": "link"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/searchapi/searchapi.yaml b/api/core/tools/provider/builtin/searchapi/searchapi.yaml
deleted file mode 100644
index c2fa3f398e192f..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/searchapi.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-identity:
- author: SearchApi
- name: searchapi
- label:
- en_US: SearchApi
- zh_Hans: SearchApi
- pt_BR: SearchApi
- description:
- en_US: SearchApi is a robust real-time SERP API delivering structured data from a collection of search engines including Google Search, Google Jobs, YouTube, Google News, and many more.
- zh_Hans: SearchApi 是一个强大的实时 SERP API,可提供来自 Google 搜索、Google 招聘、YouTube、Google 新闻等搜索引擎集合的结构化数据。
- pt_BR: SearchApi is a robust real-time SERP API delivering structured data from a collection of search engines including Google Search, Google Jobs, YouTube, Google News, and many more.
- icon: icon.svg
- tags:
- - search
- - business
- - news
- - productivity
-credentials_for_provider:
- searchapi_api_key:
- type: secret-input
- required: true
- label:
- en_US: SearchApi API key
- zh_Hans: SearchApi API key
- pt_BR: SearchApi API key
- placeholder:
- en_US: Please input your SearchApi API key
- zh_Hans: 请输入你的 SearchApi API key
- pt_BR: Please input your SearchApi API key
- help:
- en_US: Get your SearchApi API key from SearchApi
- zh_Hans: 从 SearchApi 获取您的 SearchApi API key
- pt_BR: Get your SearchApi API key from SearchApi
- url: https://www.searchapi.io/
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google.py b/api/core/tools/provider/builtin/searchapi/tools/google.py
deleted file mode 100644
index 29d36f5f232694..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SEARCH_API_URL = "https://www.searchapi.io/api/v1/search"
-
-
-class SearchAPI:
- """
- SearchAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SearchAPI tool provider."""
- self.searchapi_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SearchAPI and parse result."""
- type = kwargs.get("result_type", "text")
- return self._process_response(self.results(query, **kwargs), type=type)
-
- def results(self, query: str, **kwargs: Any) -> dict:
- """Run query through SearchAPI and return the raw result."""
- params = self.get_params(query, **kwargs)
- response = requests.get(
- url=SEARCH_API_URL,
- params=params,
- headers={"Authorization": f"Bearer {self.searchapi_api_key}"},
- )
- response.raise_for_status()
- return response.json()
-
- def get_params(self, query: str, **kwargs: Any) -> dict[str, str]:
- """Get parameters for SearchAPI."""
- return {
- "engine": "google",
- "q": query,
- **{key: value for key, value in kwargs.items() if value not in {None, ""}},
- }
-
- @staticmethod
- def _process_response(res: dict, type: str) -> str:
- """Process response from SearchAPI."""
- if "error" in res:
- return res["error"]
-
- toret = ""
- if type == "text":
- if "answer_box" in res and "answer" in res["answer_box"]:
- toret += res["answer_box"]["answer"] + "\n"
- if "answer_box" in res and "snippet" in res["answer_box"]:
- toret += res["answer_box"]["snippet"] + "\n"
- if "knowledge_graph" in res and "description" in res["knowledge_graph"]:
- toret += res["knowledge_graph"]["description"] + "\n"
- if "organic_results" in res and "snippet" in res["organic_results"][0]:
- for item in res["organic_results"]:
- toret += "content: " + item["snippet"] + "\n" + "link: " + item["link"] + "\n"
- if toret == "":
- toret = "No good search result found"
-
- elif type == "link":
- if "answer_box" in res and "organic_result" in res["answer_box"]:
- if "title" in res["answer_box"]["organic_result"]:
- toret = (
- f"[{res['answer_box']['organic_result']['title']}]"
- f"({res['answer_box']['organic_result']['link']})\n"
- )
- elif "organic_results" in res and "link" in res["organic_results"][0]:
- toret = ""
- for item in res["organic_results"]:
- toret += f"[{item['title']}]({item['link']})\n"
- elif "related_questions" in res and "link" in res["related_questions"][0]:
- toret = ""
- for item in res["related_questions"]:
- toret += f"[{item['title']}]({item['link']})\n"
- elif "related_searches" in res and "link" in res["related_searches"][0]:
- toret = ""
- for item in res["related_searches"]:
- toret += f"[{item['title']}]({item['link']})\n"
- else:
- toret = "No good search result found"
- return toret
-
-
-class GoogleTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SearchApi tool.
- """
- query = tool_parameters["query"]
- result_type = tool_parameters["result_type"]
- num = tool_parameters.get("num", 10)
- google_domain = tool_parameters.get("google_domain", "google.com")
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["searchapi_api_key"]
- result = SearchAPI(api_key).run(
- query, result_type=result_type, num=num, google_domain=google_domain, gl=gl, hl=hl, location=location
- )
-
- if result_type == "text":
- return self.create_text_message(text=result)
- return self.create_link_message(link=result)
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google.yaml b/api/core/tools/provider/builtin/searchapi/tools/google.yaml
deleted file mode 100644
index 0dc1b6672436cd..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google.yaml
+++ /dev/null
@@ -1,1921 +0,0 @@
-identity:
- name: google_search_api
- author: SearchApi
- label:
- en_US: Google Search API
- zh_Hans: Google Search API
-description:
- human:
- en_US: A tool to retrieve answer boxes, knowledge graphs, snippets, and webpages from Google Search engine.
- zh_Hans: 一种从 Google 搜索引擎检索答案框、知识图、片段和网页的工具。
- llm: A tool to retrieve answer boxes, knowledge graphs, snippets, and webpages from Google Search engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: result_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: link
- label:
- en_US: link
- zh_Hans: 链接
- default: text
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, text or link
- zh_Hans: 用于选择结果类型,使用文本还是链接进行展示
- form: form
- - name: location
- type: string
- required: false
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: llm
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: AF
- label:
- en_US: Afghanistan
- zh_Hans: 阿富汗
- pt_BR: Afeganistão
- - value: AL
- label:
- en_US: Albania
- zh_Hans: 阿尔巴尼亚
- pt_BR: Albânia
- - value: DZ
- label:
- en_US: Algeria
- zh_Hans: 阿尔及利亚
- pt_BR: Argélia
- - value: AS
- label:
- en_US: American Samoa
- zh_Hans: 美属萨摩亚
- pt_BR: Samoa Americana
- - value: AD
- label:
- en_US: Andorra
- zh_Hans: 安道尔
- pt_BR: Andorra
- - value: AO
- label:
- en_US: Angola
- zh_Hans: 安哥拉
- pt_BR: Angola
- - value: AI
- label:
- en_US: Anguilla
- zh_Hans: 安圭拉
- pt_BR: Anguilla
- - value: AQ
- label:
- en_US: Antarctica
- zh_Hans: 南极洲
- pt_BR: Antártica
- - value: AG
- label:
- en_US: Antigua and Barbuda
- zh_Hans: 安提瓜和巴布达
- pt_BR: Antígua e Barbuda
- - value: AR
- label:
- en_US: Argentina
- zh_Hans: 阿根廷
- pt_BR: Argentina
- - value: AM
- label:
- en_US: Armenia
- zh_Hans: 亚美尼亚
- pt_BR: Armênia
- - value: AW
- label:
- en_US: Aruba
- zh_Hans: 阿鲁巴
- pt_BR: Aruba
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Austrália
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Áustria
- - value: AZ
- label:
- en_US: Azerbaijan
- zh_Hans: 阿塞拜疆
- pt_BR: Azerbaijão
- - value: BS
- label:
- en_US: Bahamas
- zh_Hans: 巴哈马
- pt_BR: Bahamas
- - value: BH
- label:
- en_US: Bahrain
- zh_Hans: 巴林
- pt_BR: Bahrein
- - value: BD
- label:
- en_US: Bangladesh
- zh_Hans: 孟加拉国
- pt_BR: Bangladesh
- - value: BB
- label:
- en_US: Barbados
- zh_Hans: 巴巴多斯
- pt_BR: Barbados
- - value: BY
- label:
- en_US: Belarus
- zh_Hans: 白俄罗斯
- pt_BR: Bielorrússia
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Bélgica
- - value: BZ
- label:
- en_US: Belize
- zh_Hans: 伯利兹
- pt_BR: Belize
- - value: BJ
- label:
- en_US: Benin
- zh_Hans: 贝宁
- pt_BR: Benim
- - value: BM
- label:
- en_US: Bermuda
- zh_Hans: 百慕大
- pt_BR: Bermudas
- - value: BT
- label:
- en_US: Bhutan
- zh_Hans: 不丹
- pt_BR: Butão
- - value: BO
- label:
- en_US: Bolivia
- zh_Hans: 玻利维亚
- pt_BR: Bolívia
- - value: BA
- label:
- en_US: Bosnia and Herzegovina
- zh_Hans: 波斯尼亚和黑塞哥维那
- pt_BR: Bósnia e Herzegovina
- - value: BW
- label:
- en_US: Botswana
- zh_Hans: 博茨瓦纳
- pt_BR: Botsuana
- - value: BV
- label:
- en_US: Bouvet Island
- zh_Hans: 布韦岛
- pt_BR: Ilha Bouvet
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brasil
- - value: IO
- label:
- en_US: British Indian Ocean Territory
- zh_Hans: 英属印度洋领地
- pt_BR: Território Britânico do Oceano Índico
- - value: BN
- label:
- en_US: Brunei Darussalam
- zh_Hans: 文莱
- pt_BR: Brunei Darussalam
- - value: BG
- label:
- en_US: Bulgaria
- zh_Hans: 保加利亚
- pt_BR: Bulgária
- - value: BF
- label:
- en_US: Burkina Faso
- zh_Hans: 布基纳法索
- pt_BR: Burkina Faso
- - value: BI
- label:
- en_US: Burundi
- zh_Hans: 布隆迪
- pt_BR: Burundi
- - value: KH
- label:
- en_US: Cambodia
- zh_Hans: 柬埔寨
- pt_BR: Camboja
- - value: CM
- label:
- en_US: Cameroon
- zh_Hans: 喀麦隆
- pt_BR: Camarões
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canadá
- - value: CV
- label:
- en_US: Cape Verde
- zh_Hans: 佛得角
- pt_BR: Cabo Verde
- - value: KY
- label:
- en_US: Cayman Islands
- zh_Hans: 开曼群岛
- pt_BR: Ilhas Cayman
- - value: CF
- label:
- en_US: Central African Republic
- zh_Hans: 中非共和国
- pt_BR: República Centro-Africana
- - value: TD
- label:
- en_US: Chad
- zh_Hans: 乍得
- pt_BR: Chade
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CN
- label:
- en_US: China
- zh_Hans: 中国
- pt_BR: China
- - value: CX
- label:
- en_US: Christmas Island
- zh_Hans: 圣诞岛
- pt_BR: Ilha do Natal
- - value: CC
- label:
- en_US: Cocos (Keeling) Islands
- zh_Hans: 科科斯(基林)群岛
- pt_BR: Ilhas Cocos (Keeling)
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colômbia
- - value: KM
- label:
- en_US: Comoros
- zh_Hans: 科摩罗
- pt_BR: Comores
- - value: CG
- label:
- en_US: Congo
- zh_Hans: 刚果
- pt_BR: Congo
- - value: CD
- label:
- en_US: Congo, the Democratic Republic of the
- zh_Hans: 刚果民主共和国
- pt_BR: Congo, República Democrática do
- - value: CK
- label:
- en_US: Cook Islands
- zh_Hans: 库克群岛
- pt_BR: Ilhas Cook
- - value: CR
- label:
- en_US: Costa Rica
- zh_Hans: 哥斯达黎加
- pt_BR: Costa Rica
- - value: CI
- label:
- en_US: Cote D'ivoire
- zh_Hans: 科特迪瓦
- pt_BR: Costa do Marfim
- - value: HR
- label:
- en_US: Croatia
- zh_Hans: 克罗地亚
- pt_BR: Croácia
- - value: CU
- label:
- en_US: Cuba
- zh_Hans: 古巴
- pt_BR: Cuba
- - value: CY
- label:
- en_US: Cyprus
- zh_Hans: 塞浦路斯
- pt_BR: Chipre
- - value: CZ
- label:
- en_US: Czech Republic
- zh_Hans: 捷克共和国
- pt_BR: República Tcheca
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Dinamarca
- - value: DJ
- label:
- en_US: Djibouti
- zh_Hans: 吉布提
- pt_BR: Djibuti
- - value: DM
- label:
- en_US: Dominica
- zh_Hans: 多米尼克
- pt_BR: Dominica
- - value: DO
- label:
- en_US: Dominican Republic
- zh_Hans: 多米尼加共和国
- pt_BR: República Dominicana
- - value: EC
- label:
- en_US: Ecuador
- zh_Hans: 厄瓜多尔
- pt_BR: Equador
- - value: EG
- label:
- en_US: Egypt
- zh_Hans: 埃及
- pt_BR: Egito
- - value: SV
- label:
- en_US: El Salvador
- zh_Hans: 萨尔瓦多
- pt_BR: El Salvador
- - value: GQ
- label:
- en_US: Equatorial Guinea
- zh_Hans: 赤道几内亚
- pt_BR: Guiné Equatorial
- - value: ER
- label:
- en_US: Eritrea
- zh_Hans: 厄立特里亚
- pt_BR: Eritreia
- - value: EE
- label:
- en_US: Estonia
- zh_Hans: 爱沙尼亚
- pt_BR: Estônia
- - value: ET
- label:
- en_US: Ethiopia
- zh_Hans: 埃塞俄比亚
- pt_BR: Etiópia
- - value: FK
- label:
- en_US: Falkland Islands (Malvinas)
- zh_Hans: 福克兰群岛(马尔维纳斯)
- pt_BR: Ilhas Falkland (Malvinas)
- - value: FO
- label:
- en_US: Faroe Islands
- zh_Hans: 法罗群岛
- pt_BR: Ilhas Faroe
- - value: FJ
- label:
- en_US: Fiji
- zh_Hans: 斐济
- pt_BR: Fiji
- - value: FI
- label:
- en_US: Finland
- zh_Hans: 芬兰
- pt_BR: Finlândia
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: França
- - value: GF
- label:
- en_US: French Guiana
- zh_Hans: 法属圭亚那
- pt_BR: Guiana Francesa
- - value: PF
- label:
- en_US: French Polynesia
- zh_Hans: 法属波利尼西亚
- pt_BR: Polinésia Francesa
- - value: TF
- label:
- en_US: French Southern Territories
- zh_Hans: 法属南部领地
- pt_BR: Territórios Franceses do Sul
- - value: GA
- label:
- en_US: Gabon
- zh_Hans: 加蓬
- pt_BR: Gabão
- - value: GM
- label:
- en_US: Gambia
- zh_Hans: 冈比亚
- pt_BR: Gâmbia
- - value: GE
- label:
- en_US: Georgia
- zh_Hans: 格鲁吉亚
- pt_BR: Geórgia
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Alemanha
- - value: GH
- label:
- en_US: Ghana
- zh_Hans: 加纳
- pt_BR: Gana
- - value: GI
- label:
- en_US: Gibraltar
- zh_Hans: 直布罗陀
- pt_BR: Gibraltar
- - value: GR
- label:
- en_US: Greece
- zh_Hans: 希腊
- pt_BR: Grécia
- - value: GL
- label:
- en_US: Greenland
- zh_Hans: 格陵兰
- pt_BR: Groenlândia
- - value: GD
- label:
- en_US: Grenada
- zh_Hans: 格林纳达
- pt_BR: Granada
- - value: GP
- label:
- en_US: Guadeloupe
- zh_Hans: 瓜德罗普
- pt_BR: Guadalupe
- - value: GU
- label:
- en_US: Guam
- zh_Hans: 关岛
- pt_BR: Guam
- - value: GT
- label:
- en_US: Guatemala
- zh_Hans: 危地马拉
- pt_BR: Guatemala
- - value: GN
- label:
- en_US: Guinea
- zh_Hans: 几内亚
- pt_BR: Guiné
- - value: GW
- label:
- en_US: Guinea-Bissau
- zh_Hans: 几内亚比绍
- pt_BR: Guiné-Bissau
- - value: GY
- label:
- en_US: Guyana
- zh_Hans: 圭亚那
- pt_BR: Guiana
- - value: HT
- label:
- en_US: Haiti
- zh_Hans: 海地
- pt_BR: Haiti
- - value: HM
- label:
- en_US: Heard Island and McDonald Islands
- zh_Hans: 赫德岛和麦克唐纳群岛
- pt_BR: Ilha Heard e Ilhas McDonald
- - value: VA
- label:
- en_US: Holy See (Vatican City State)
- zh_Hans: 教廷(梵蒂冈城国)
- pt_BR: Santa Sé (Estado da Cidade do Vaticano)
- - value: HN
- label:
- en_US: Honduras
- zh_Hans: 洪都拉斯
- pt_BR: Honduras
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: HU
- label:
- en_US: Hungary
- zh_Hans: 匈牙利
- pt_BR: Hungria
- - value: IS
- label:
- en_US: Iceland
- zh_Hans: 冰岛
- pt_BR: Islândia
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: Índia
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonésia
- - value: IR
- label:
- en_US: Iran, Islamic Republic of
- zh_Hans: 伊朗
- pt_BR: Irã
- - value: IQ
- label:
- en_US: Iraq
- zh_Hans: 伊拉克
- pt_BR: Iraque
- - value: IE
- label:
- en_US: Ireland
- zh_Hans: 爱尔兰
- pt_BR: Irlanda
- - value: IL
- label:
- en_US: Israel
- zh_Hans: 以色列
- pt_BR: Israel
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Itália
- - value: JM
- label:
- en_US: Jamaica
- zh_Hans: 牙买加
- pt_BR: Jamaica
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japão
- - value: JO
- label:
- en_US: Jordan
- zh_Hans: 约旦
- pt_BR: Jordânia
- - value: KZ
- label:
- en_US: Kazakhstan
- zh_Hans: 哈萨克斯坦
- pt_BR: Cazaquistão
- - value: KE
- label:
- en_US: Kenya
- zh_Hans: 肯尼亚
- pt_BR: Quênia
- - value: KI
- label:
- en_US: Kiribati
- zh_Hans: 基里巴斯
- pt_BR: Kiribati
- - value: KP
- label:
- en_US: Korea, Democratic People's Republic of
- zh_Hans: 朝鲜
- pt_BR: Coreia, República Democrática Popular da
- - value: KR
- label:
- en_US: Korea, Republic of
- zh_Hans: 韩国
- pt_BR: Coreia, República da
- - value: KW
- label:
- en_US: Kuwait
- zh_Hans: 科威特
- pt_BR: Kuwait
- - value: KG
- label:
- en_US: Kyrgyzstan
- zh_Hans: 吉尔吉斯斯坦
- pt_BR: Quirguistão
- - value: LA
- label:
- en_US: Lao People's Democratic Republic
- zh_Hans: 老挝
- pt_BR: República Democrática Popular do Laos
- - value: LV
- label:
- en_US: Latvia
- zh_Hans: 拉脱维亚
- pt_BR: Letônia
- - value: LB
- label:
- en_US: Lebanon
- zh_Hans: 黎巴嫩
- pt_BR: Líbano
- - value: LS
- label:
- en_US: Lesotho
- zh_Hans: 莱索托
- pt_BR: Lesoto
- - value: LR
- label:
- en_US: Liberia
- zh_Hans: 利比里亚
- pt_BR: Libéria
- - value: LY
- label:
- en_US: Libyan Arab Jamahiriya
- zh_Hans: 利比亚
- pt_BR: Líbia
- - value: LI
- label:
- en_US: Liechtenstein
- zh_Hans: 列支敦士登
- pt_BR: Liechtenstein
- - value: LT
- label:
- en_US: Lithuania
- zh_Hans: 立陶宛
- pt_BR: Lituânia
- - value: LU
- label:
- en_US: Luxembourg
- zh_Hans: 卢森堡
- pt_BR: Luxemburgo
- - value: MO
- label:
- en_US: Macao
- zh_Hans: 澳门
- pt_BR: Macau
- - value: MK
- label:
- en_US: Macedonia, the Former Yugosalv Republic of
- zh_Hans: 前南斯拉夫马其顿共和国
- pt_BR: Macedônia, Ex-República Iugoslava da
- - value: MG
- label:
- en_US: Madagascar
- zh_Hans: 马达加斯加
- pt_BR: Madagascar
- - value: MW
- label:
- en_US: Malawi
- zh_Hans: 马拉维
- pt_BR: Malaui
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malásia
- - value: MV
- label:
- en_US: Maldives
- zh_Hans: 马尔代夫
- pt_BR: Maldivas
- - value: ML
- label:
- en_US: Mali
- zh_Hans: 马里
- pt_BR: Mali
- - value: MT
- label:
- en_US: Malta
- zh_Hans: 马耳他
- pt_BR: Malta
- - value: MH
- label:
- en_US: Marshall Islands
- zh_Hans: 马绍尔群岛
- pt_BR: Ilhas Marshall
- - value: MQ
- label:
- en_US: Martinique
- zh_Hans: 马提尼克
- pt_BR: Martinica
- - value: MR
- label:
- en_US: Mauritania
- zh_Hans: 毛里塔尼亚
- pt_BR: Mauritânia
- - value: MU
- label:
- en_US: Mauritius
- zh_Hans: 毛里求斯
- pt_BR: Maurício
- - value: YT
- label:
- en_US: Mayotte
- zh_Hans: 马约特
- pt_BR: Mayotte
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: México
- - value: FM
- label:
- en_US: Micronesia, Federated States of
- zh_Hans: 密克罗尼西亚联邦
- pt_BR: Micronésia, Estados Federados da
- - value: MD
- label:
- en_US: Moldova, Republic of
- zh_Hans: 摩尔多瓦共和国
- pt_BR: Moldávia, República da
- - value: MC
- label:
- en_US: Monaco
- zh_Hans: 摩纳哥
- pt_BR: Mônaco
- - value: MN
- label:
- en_US: Mongolia
- zh_Hans: 蒙古
- pt_BR: Mongólia
- - value: MS
- label:
- en_US: Montserrat
- zh_Hans: 蒙特塞拉特
- pt_BR: Montserrat
- - value: MA
- label:
- en_US: Morocco
- zh_Hans: 摩洛哥
- pt_BR: Marrocos
- - value: MZ
- label:
- en_US: Mozambique
- zh_Hans: 莫桑比克
- pt_BR: Moçambique
- - value: MM
- label:
- en_US: Myanmar
- zh_Hans: 缅甸
- pt_BR: Mianmar
- - value: NA
- label:
- en_US: Namibia
- zh_Hans: 纳米比亚
- pt_BR: Namíbia
- - value: NR
- label:
- en_US: Nauru
- zh_Hans: 瑙鲁
- pt_BR: Nauru
- - value: NP
- label:
- en_US: Nepal
- zh_Hans: 尼泊尔
- pt_BR: Nepal
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Países Baixos
- - value: AN
- label:
- en_US: Netherlands Antilles
- zh_Hans: 荷属安的列斯
- pt_BR: Antilhas Holandesas
- - value: NC
- label:
- en_US: New Caledonia
- zh_Hans: 新喀里多尼亚
- pt_BR: Nova Caledônia
- - value: NZ
- label:
- en_US: New Zealand
- zh_Hans: 新西兰
- pt_BR: Nova Zelândia
- - value: NI
- label:
- en_US: Nicaragua
- zh_Hans: 尼加拉瓜
- pt_BR: Nicarágua
- - value: NE
- label:
- en_US: Niger
- zh_Hans: 尼日尔
- pt_BR: Níger
- - value: NG
- label:
- en_US: Nigeria
- zh_Hans: 尼日利亚
- pt_BR: Nigéria
- - value: NU
- label:
- en_US: Niue
- zh_Hans: 纽埃
- pt_BR: Niue
- - value: NF
- label:
- en_US: Norfolk Island
- zh_Hans: 诺福克岛
- pt_BR: Ilha Norfolk
- - value: MP
- label:
- en_US: Northern Mariana Islands
- zh_Hans: 北马里亚纳群岛
- pt_BR: Ilhas Marianas do Norte
- - value: "NO"
- label:
- en_US: Norway
- zh_Hans: 挪威
- pt_BR: Noruega
- - value: OM
- label:
- en_US: Oman
- zh_Hans: 阿曼
- pt_BR: Omã
- - value: PK
- label:
- en_US: Pakistan
- zh_Hans: 巴基斯坦
- pt_BR: Paquistão
- - value: PW
- label:
- en_US: Palau
- zh_Hans: 帕劳
- pt_BR: Palau
- - value: PS
- label:
- en_US: Palestinian Territory, Occupied
- zh_Hans: 巴勒斯坦领土
- pt_BR: Palestina, Território Ocupado
- - value: PA
- label:
- en_US: Panama
- zh_Hans: 巴拿马
- pt_BR: Panamá
- - value: PG
- label:
- en_US: Papua New Guinea
- zh_Hans: 巴布亚新几内亚
- pt_BR: Papua Nova Guiné
- - value: PY
- label:
- en_US: Paraguay
- zh_Hans: 巴拉圭
- pt_BR: Paraguai
- - value: PE
- label:
- en_US: Peru
- zh_Hans: 秘鲁
- pt_BR: Peru
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Filipinas
- - value: PN
- label:
- en_US: Pitcairn
- zh_Hans: 皮特凯恩岛
- pt_BR: Pitcairn
- - value: PL
- label:
- en_US: Poland
- zh_Hans: 波兰
- pt_BR: Polônia
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: PR
- label:
- en_US: Puerto Rico
- zh_Hans: 波多黎各
- pt_BR: Porto Rico
- - value: QA
- label:
- en_US: Qatar
- zh_Hans: 卡塔尔
- pt_BR: Catar
- - value: RE
- label:
- en_US: Reunion
- zh_Hans: 留尼旺
- pt_BR: Reunião
- - value: RO
- label:
- en_US: Romania
- zh_Hans: 罗马尼亚
- pt_BR: Romênia
- - value: RU
- label:
- en_US: Russian Federation
- zh_Hans: 俄罗斯联邦
- pt_BR: Rússia
- - value: RW
- label:
- en_US: Rwanda
- zh_Hans: 卢旺达
- pt_BR: Ruanda
- - value: SH
- label:
- en_US: Saint Helena
- zh_Hans: 圣赫勒拿
- pt_BR: Santa Helena
- - value: KN
- label:
- en_US: Saint Kitts and Nevis
- zh_Hans: 圣基茨和尼维斯
- pt_BR: São Cristóvão e Nevis
- - value: LC
- label:
- en_US: Saint Lucia
- zh_Hans: 圣卢西亚
- pt_BR: Santa Lúcia
- - value: PM
- label:
- en_US: Saint Pierre and Miquelon
- zh_Hans: 圣皮埃尔和密克隆
- pt_BR: São Pedro e Miquelon
- - value: VC
- label:
- en_US: Saint Vincent and the Grenadines
- zh_Hans: 圣文森特和格林纳丁斯
- pt_BR: São Vicente e Granadinas
- - value: WS
- label:
- en_US: Samoa
- zh_Hans: 萨摩亚
- pt_BR: Samoa
- - value: SM
- label:
- en_US: San Marino
- zh_Hans: 圣马力诺
- pt_BR: San Marino
- - value: ST
- label:
- en_US: Sao Tome and Principe
- zh_Hans: 圣多美和普林西比
- pt_BR: São Tomé e Príncipe
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Arábia Saudita
- - value: SN
- label:
- en_US: Senegal
- zh_Hans: 塞内加尔
- pt_BR: Senegal
- - value: RS
- label:
- en_US: Serbia and Montenegro
- zh_Hans: 塞尔维亚和黑山
- pt_BR: Sérvia e Montenegro
- - value: SC
- label:
- en_US: Seychelles
- zh_Hans: 塞舌尔
- pt_BR: Seicheles
- - value: SL
- label:
- en_US: Sierra Leone
- zh_Hans: 塞拉利昂
- pt_BR: Serra Leoa
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapura
- - value: SK
- label:
- en_US: Slovakia
- zh_Hans: 斯洛伐克
- pt_BR: Eslováquia
- - value: SI
- label:
- en_US: Slovenia
- zh_Hans: 斯洛文尼亚
- pt_BR: Eslovênia
- - value: SB
- label:
- en_US: Solomon Islands
- zh_Hans: 所罗门群岛
- pt_BR: Ilhas Salomão
- - value: SO
- label:
- en_US: Somalia
- zh_Hans: 索马里
- pt_BR: Somália
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: África do Sul
- - value: GS
- label:
- en_US: South Georgia and the South Sandwich Islands
- zh_Hans: 南乔治亚和南桑威奇群岛
- pt_BR: Geórgia do Sul e Ilhas Sandwich do Sul
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Espanha
- - value: LK
- label:
- en_US: Sri Lanka
- zh_Hans: 斯里兰卡
- pt_BR: Sri Lanka
- - value: SD
- label:
- en_US: Sudan
- zh_Hans: 苏丹
- pt_BR: Sudão
- - value: SR
- label:
- en_US: Suriname
- zh_Hans: 苏里南
- pt_BR: Suriname
- - value: SJ
- label:
- en_US: Svalbard and Jan Mayen
- zh_Hans: 斯瓦尔巴特和扬马延岛
- pt_BR: Svalbard e Jan Mayen
- - value: SZ
- label:
- en_US: Swaziland
- zh_Hans: 斯威士兰
- pt_BR: Essuatíni
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Suécia
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Suíça
- - value: SY
- label:
- en_US: Syrian Arab Republic
- zh_Hans: 叙利亚
- pt_BR: Síria
- - value: TW
- label:
- en_US: Taiwan, Province of China
- zh_Hans: 台湾
- pt_BR: Taiwan
- - value: TJ
- label:
- en_US: Tajikistan
- zh_Hans: 塔吉克斯坦
- pt_BR: Tajiquistão
- - value: TZ
- label:
- en_US: Tanzania, United Republic of
- zh_Hans: 坦桑尼亚联合共和国
- pt_BR: Tanzânia
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Tailândia
- - value: TL
- label:
- en_US: Timor-Leste
- zh_Hans: 东帝汶
- pt_BR: Timor-Leste
- - value: TG
- label:
- en_US: Togo
- zh_Hans: 多哥
- pt_BR: Togo
- - value: TK
- label:
- en_US: Tokelau
- zh_Hans: 托克劳
- pt_BR: Toquelau
- - value: TO
- label:
- en_US: Tonga
- zh_Hans: 汤加
- pt_BR: Tonga
- - value: TT
- label:
- en_US: Trinidad and Tobago
- zh_Hans: 特立尼达和多巴哥
- pt_BR: Trindade e Tobago
- - value: TN
- label:
- en_US: Tunisia
- zh_Hans: 突尼斯
- pt_BR: Tunísia
- - value: TR
- label:
- en_US: Turkey
- zh_Hans: 土耳其
- pt_BR: Turquia
- - value: TM
- label:
- en_US: Turkmenistan
- zh_Hans: 土库曼斯坦
- pt_BR: Turcomenistão
- - value: TC
- label:
- en_US: Turks and Caicos Islands
- zh_Hans: 特克斯和凯科斯群岛
- pt_BR: Ilhas Turks e Caicos
- - value: TV
- label:
- en_US: Tuvalu
- zh_Hans: 图瓦卢
- pt_BR: Tuvalu
- - value: UG
- label:
- en_US: Uganda
- zh_Hans: 乌干达
- pt_BR: Uganda
- - value: UA
- label:
- en_US: Ukraine
- zh_Hans: 乌克兰
- pt_BR: Ucrânia
- - value: AE
- label:
- en_US: United Arab Emirates
- zh_Hans: 阿联酋
- pt_BR: Emirados Árabes Unidos
- - value: UK
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: Reino Unido
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: Reino Unido
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: Estados Unidos
- - value: UM
- label:
- en_US: United States Minor Outlying Islands
- zh_Hans: 美国本土外小岛屿
- pt_BR: Ilhas Menores Distantes dos Estados Unidos
- - value: UY
- label:
- en_US: Uruguay
- zh_Hans: 乌拉圭
- pt_BR: Uruguai
- - value: UZ
- label:
- en_US: Uzbekistan
- zh_Hans: 乌兹别克斯坦
- pt_BR: Uzbequistão
- - value: VU
- label:
- en_US: Vanuatu
- zh_Hans: 瓦努阿图
- pt_BR: Vanuatu
- - value: VE
- label:
- en_US: Venezuela
- zh_Hans: 委内瑞拉
- pt_BR: Venezuela
- - value: VN
- label:
- en_US: Viet Nam
- zh_Hans: 越南
- pt_BR: Vietnã
- - value: VG
- label:
- en_US: Virgin Islands, British
- zh_Hans: 英属维尔京群岛
- pt_BR: Ilhas Virgens Britânicas
- - value: VI
- label:
- en_US: Virgin Islands, U.S.
- zh_Hans: 美属维尔京群岛
- pt_BR: Ilhas Virgens dos EUA
- - value: WF
- label:
- en_US: Wallis and Futuna
- zh_Hans: 瓦利斯和富图纳群岛
- pt_BR: Wallis e Futuna
- - value: EH
- label:
- en_US: Western Sahara
- zh_Hans: 西撒哈拉
- pt_BR: Saara Ocidental
- - value: YE
- label:
- en_US: Yemen
- zh_Hans: 也门
- pt_BR: Iémen
- - value: ZM
- label:
- en_US: Zambia
- zh_Hans: 赞比亚
- pt_BR: Zâmbia
- - value: ZW
- label:
- en_US: Zimbabwe
- zh_Hans: 津巴布韦
- pt_BR: Zimbábue
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: af
- label:
- en_US: Afrikaans
- zh_Hans: 南非语
- - value: ak
- label:
- en_US: Akan
- zh_Hans: 阿坎语
- - value: sq
- label:
- en_US: Albanian
- zh_Hans: 阿尔巴尼亚语
- - value: ws
- label:
- en_US: Samoa
- zh_Hans: 萨摩亚语
- - value: am
- label:
- en_US: Amharic
- zh_Hans: 阿姆哈拉语
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: hy
- label:
- en_US: Armenian
- zh_Hans: 亚美尼亚语
- - value: az
- label:
- en_US: Azerbaijani
- zh_Hans: 阿塞拜疆语
- - value: eu
- label:
- en_US: Basque
- zh_Hans: 巴斯克语
- - value: be
- label:
- en_US: Belarusian
- zh_Hans: 白俄罗斯语
- - value: bem
- label:
- en_US: Bemba
- zh_Hans: 班巴语
- - value: bn
- label:
- en_US: Bengali
- zh_Hans: 孟加拉语
- - value: bh
- label:
- en_US: Bihari
- zh_Hans: 比哈尔语
- - value: xx-bork
- label:
- en_US: Bork, bork, bork!
- zh_Hans: 博克语
- - value: bs
- label:
- en_US: Bosnian
- zh_Hans: 波斯尼亚语
- - value: br
- label:
- en_US: Breton
- zh_Hans: 布列塔尼语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: bt
- label:
- en_US: Bhutanese
- zh_Hans: 不丹语
- - value: km
- label:
- en_US: Cambodian
- zh_Hans: 高棉语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: chr
- label:
- en_US: Cherokee
- zh_Hans: 切罗基语
- - value: ny
- label:
- en_US: Chichewa
- zh_Hans: 齐切瓦语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: co
- label:
- en_US: Corsican
- zh_Hans: 科西嘉语
- - value: hr
- label:
- en_US: Croatian
- zh_Hans: 克罗地亚语
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: xx-elmer
- label:
- en_US: Elmer Fudd
- zh_Hans: 艾尔默福德语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: eo
- label:
- en_US: Esperanto
- zh_Hans: 世界语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: ee
- label:
- en_US: Ewe
- zh_Hans: 埃维语
- - value: fo
- label:
- en_US: Faroese
- zh_Hans: 法罗语
- - value: tl
- label:
- en_US: Filipino
- zh_Hans: 菲律宾语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: fy
- label:
- en_US: Frisian
- zh_Hans: 弗里西亚语
- - value: gaa
- label:
- en_US: Ga
- zh_Hans: 加语
- - value: gl
- label:
- en_US: Galician
- zh_Hans: 加利西亚语
- - value: ka
- label:
- en_US: Georgian
- zh_Hans: 格鲁吉亚语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: kl
- label:
- en_US: Greenlandic
- zh_Hans: 格陵兰语
- - value: gn
- label:
- en_US: Guarani
- zh_Hans: 瓜拉尼语
- - value: gu
- label:
- en_US: Gujarati
- zh_Hans: 古吉拉特语
- - value: xx-hacker
- label:
- en_US: Hacker
- zh_Hans: 黑客语
- - value: ht
- label:
- en_US: Haitian Creole
- zh_Hans: 海地克里奥尔语
- - value: ha
- label:
- en_US: Hausa
- zh_Hans: 豪萨语
- - value: haw
- label:
- en_US: Hawaiian
- zh_Hans: 夏威夷语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: is
- label:
- en_US: Icelandic
- zh_Hans: 冰岛语
- - value: ig
- label:
- en_US: Igbo
- zh_Hans: 伊博语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: ia
- label:
- en_US: Interlingua
- zh_Hans: 国际语
- - value: ga
- label:
- en_US: Irish
- 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: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: kk
- label:
- en_US: Kazakh
- zh_Hans: 哈萨克语
- - value: rw
- label:
- en_US: Kinyarwanda
- zh_Hans: 基尼亚卢旺达语
- - value: rn
- label:
- en_US: Kirundi
- zh_Hans: 基隆迪语
- - value: xx-klingon
- label:
- en_US: Klingon
- zh_Hans: 克林贡语
- - value: kg
- label:
- en_US: Kongo
- zh_Hans: 刚果语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: kri
- label:
- en_US: Krio (Sierra Leone)
- zh_Hans: 塞拉利昂克里奥尔语
- - value: ku
- label:
- en_US: Kurdish
- zh_Hans: 库尔德语
- - value: ckb
- label:
- en_US: Kurdish (Soranî)
- zh_Hans: 库尔德语(索拉尼)
- - value: ky
- label:
- en_US: Kyrgyz
- zh_Hans: 吉尔吉斯语
- - value: lo
- label:
- en_US: Laothian
- zh_Hans: 老挝语
- - value: la
- label:
- en_US: Latin
- zh_Hans: 拉丁语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: ln
- label:
- en_US: Lingala
- zh_Hans: 林加拉语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: loz
- label:
- en_US: Lozi
- zh_Hans: 洛齐语
- - value: lg
- label:
- en_US: Luganda
- zh_Hans: 卢干达语
- - value: ach
- label:
- en_US: Luo
- zh_Hans: 卢奥语
- - value: mk
- label:
- en_US: Macedonian
- zh_Hans: 马其顿语
- - value: mg
- label:
- en_US: Malagasy
- zh_Hans: 马尔加什语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mt
- label:
- en_US: Maltese
- zh_Hans: 马耳他语
- - value: mv
- label:
- en_US: Maldives
- zh_Hans: 马尔代夫语
- - value: mi
- label:
- en_US: Maori
- zh_Hans: 毛利语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: mfe
- label:
- en_US: Mauritian Creole
- zh_Hans: 毛里求斯克里奥尔语
- - value: mo
- label:
- en_US: Moldavian
- zh_Hans: 摩尔达维亚语
- - value: mn
- label:
- en_US: Mongolian
- zh_Hans: 蒙古语
- - value: sr-me
- label:
- en_US: Montenegrin
- zh_Hans: 黑山语
- - value: ne
- label:
- en_US: Nepali
- zh_Hans: 尼泊尔语
- - value: pcm
- label:
- en_US: Nigerian Pidgin
- zh_Hans: 尼日利亚皮钦语
- - value: nso
- label:
- en_US: Northern Sotho
- zh_Hans: 北索托语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: nn
- label:
- en_US: Norwegian (Nynorsk)
- zh_Hans: 挪威语(尼诺斯克语)
- - value: oc
- label:
- en_US: Occitan
- zh_Hans: 奥克语
- - value: or
- label:
- en_US: Oriya
- zh_Hans: 奥里亚语
- - value: om
- label:
- en_US: Oromo
- zh_Hans: 奥罗莫语
- - value: ps
- label:
- en_US: Pashto
- zh_Hans: 普什图语
- - value: fa
- label:
- en_US: Persian
- zh_Hans: 波斯语
- - value: xx-pirate
- label:
- en_US: Pirate
- zh_Hans: 海盗语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: qu
- label:
- en_US: Quechua
- zh_Hans: 克丘亚语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: rm
- label:
- en_US: Romansh
- zh_Hans: 罗曼什语
- - value: nyn
- label:
- en_US: Runyakitara
- zh_Hans: 卢尼亚基塔拉语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: gd
- label:
- en_US: Scots Gaelic
- zh_Hans: 苏格兰盖尔语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sh
- label:
- en_US: Serbo-Croatian
- zh_Hans: 塞尔维亚-克罗地亚语
- - value: st
- label:
- en_US: Sesotho
- zh_Hans: 塞索托语
- - value: tn
- label:
- en_US: Setswana
- zh_Hans: 塞茨瓦纳语
- - value: crs
- label:
- en_US: Seychellois Creole
- zh_Hans: 塞舌尔克里奥尔语
- - value: sn
- label:
- en_US: Shona
- zh_Hans: 绍纳语
- - value: sd
- label:
- en_US: Sindhi
- zh_Hans: 信德语
- - value: si
- label:
- en_US: Sinhalese
- zh_Hans: 僧伽罗语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: so
- label:
- en_US: Somali
- zh_Hans: 索马里语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: es-419
- label:
- en_US: Spanish (Latin American)
- zh_Hans: 西班牙语(拉丁美洲)
- - value: su
- label:
- en_US: Sundanese
- zh_Hans: 巽他语
- - value: sw
- label:
- en_US: Swahili
- zh_Hans: 斯瓦希里语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: tg
- label:
- en_US: Tajik
- zh_Hans: 塔吉克语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: tt
- label:
- en_US: Tatar
- zh_Hans: 鞑靼语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: ti
- label:
- en_US: Tigrinya
- zh_Hans: 提格利尼亚语
- - value: to
- label:
- en_US: Tonga
- zh_Hans: 汤加语
- - value: lua
- label:
- en_US: Tshiluba
- zh_Hans: 卢巴语
- - value: tum
- label:
- en_US: Tumbuka
- zh_Hans: 图布卡语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: tk
- label:
- en_US: Turkmen
- zh_Hans: 土库曼语
- - value: tw
- label:
- en_US: Twi
- zh_Hans: 契维语
- - value: ug
- label:
- en_US: Uighur
- 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: vu
- label:
- en_US: Vanuatu
- zh_Hans: 瓦努阿图语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- - value: cy
- label:
- en_US: Welsh
- zh_Hans: 威尔士语
- - value: wo
- label:
- en_US: Wolof
- zh_Hans: 沃洛夫语
- - value: xh
- label:
- en_US: Xhosa
- zh_Hans: 科萨语
- - value: yi
- label:
- en_US: Yiddish
- zh_Hans: 意第绪语
- - value: yo
- label:
- en_US: Yoruba
- zh_Hans: 约鲁巴语
- - value: zu
- label:
- en_US: Zulu
- zh_Hans: 祖鲁语
- - name: google_domain
- type: string
- required: false
- label:
- en_US: google_domain
- zh_Hans: google_domain
- human_description:
- en_US: Defines the Google domain of the search. Default is "google.com".
- zh_Hans: 定义搜索的 Google 域。默认为“google.com”。
- llm_description: Defines Google domain in which you want to search.
- form: llm
- - name: num
- type: number
- required: false
- label:
- en_US: num
- zh_Hans: num
- human_description:
- en_US: Specifies the number of results to display per page. Default is 10. Max number - 100, min - 1.
- zh_Hans: 指定每页显示的结果数。默认值为 10。最大数量 - 100,最小数量 - 1。
- llm_description: Specifies the num of results to display per page.
- form: llm
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google_jobs.py b/api/core/tools/provider/builtin/searchapi/tools/google_jobs.py
deleted file mode 100644
index de42360898b7e0..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google_jobs.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SEARCH_API_URL = "https://www.searchapi.io/api/v1/search"
-
-
-class SearchAPI:
- """
- SearchAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SearchAPI tool provider."""
- self.searchapi_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SearchAPI and parse result."""
- type = kwargs.get("result_type", "text")
- return self._process_response(self.results(query, **kwargs), type=type)
-
- def results(self, query: str, **kwargs: Any) -> dict:
- """Run query through SearchAPI and return the raw result."""
- params = self.get_params(query, **kwargs)
- response = requests.get(
- url=SEARCH_API_URL,
- params=params,
- headers={"Authorization": f"Bearer {self.searchapi_api_key}"},
- )
- response.raise_for_status()
- return response.json()
-
- def get_params(self, query: str, **kwargs: Any) -> dict[str, str]:
- """Get parameters for SearchAPI."""
- return {
- "engine": "google_jobs",
- "q": query,
- **{key: value for key, value in kwargs.items() if value not in {None, ""}},
- }
-
- @staticmethod
- def _process_response(res: dict, type: str) -> str:
- """Process response from SearchAPI."""
- if "error" in res:
- return res["error"]
-
- toret = ""
- if type == "text":
- if "jobs" in res and "title" in res["jobs"][0]:
- for item in res["jobs"]:
- toret += (
- "title: "
- + item["title"]
- + "\n"
- + "company_name: "
- + item["company_name"]
- + "content: "
- + item["description"]
- + "\n"
- )
- if toret == "":
- toret = "No good search result found"
-
- elif type == "link":
- if "jobs" in res and "apply_link" in res["jobs"][0]:
- for item in res["jobs"]:
- toret += f"[{item['title']} - {item['company_name']}]({item['apply_link']})\n"
- else:
- toret = "No good search result found"
- return toret
-
-
-class GoogleJobsTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SearchApi tool.
- """
- query = tool_parameters["query"]
- result_type = tool_parameters["result_type"]
- is_remote = tool_parameters.get("is_remote")
- google_domain = tool_parameters.get("google_domain", "google.com")
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- ltype = 1 if is_remote else None
-
- api_key = self.runtime.credentials["searchapi_api_key"]
- result = SearchAPI(api_key).run(
- query, result_type=result_type, google_domain=google_domain, gl=gl, hl=hl, location=location, ltype=ltype
- )
-
- if result_type == "text":
- return self.create_text_message(text=result)
- return self.create_link_message(link=result)
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google_jobs.yaml b/api/core/tools/provider/builtin/searchapi/tools/google_jobs.yaml
deleted file mode 100644
index 3e00e20fbd6e33..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google_jobs.yaml
+++ /dev/null
@@ -1,1403 +0,0 @@
-identity:
- name: google_jobs_api
- author: SearchApi
- label:
- en_US: Google Jobs API
- zh_Hans: Google Jobs API
-description:
- human:
- en_US: A tool to retrieve job titles, company names and description from Google Jobs engine.
- zh_Hans: 一个从 Google 招聘引擎检索职位名称、公司名称和描述的工具。
- llm: A tool to retrieve job titles, company names and description from Google Jobs engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: result_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: link
- label:
- en_US: link
- zh_Hans: 链接
- default: text
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, text or link
- zh_Hans: 用于选择结果类型,使用文本还是链接进行展示
- form: form
- - name: location
- type: string
- required: false
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: llm
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: DZ
- label:
- en_US: Algeria
- zh_Hans: 阿尔及利亚
- pt_BR: Algeria
- - value: AS
- label:
- en_US: American Samoa
- zh_Hans: 美属萨摩亚
- pt_BR: American Samoa
- - value: AO
- label:
- en_US: Angola
- zh_Hans: 安哥拉
- pt_BR: Angola
- - value: AI
- label:
- en_US: Anguilla
- zh_Hans: 安圭拉
- pt_BR: Anguilla
- - value: AG
- label:
- en_US: Antigua and Barbuda
- zh_Hans: 安提瓜和巴布达
- pt_BR: Antigua and Barbuda
- - value: AW
- label:
- en_US: Aruba
- zh_Hans: 阿鲁巴
- pt_BR: Aruba
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Austria
- - value: BS
- label:
- en_US: Bahamas
- zh_Hans: 巴哈马
- pt_BR: Bahamas
- - value: BH
- label:
- en_US: Bahrain
- zh_Hans: 巴林
- pt_BR: Bahrain
- - value: BD
- label:
- en_US: Bangladesh
- zh_Hans: 孟加拉国
- pt_BR: Bangladesh
- - value: BY
- label:
- en_US: Belarus
- zh_Hans: 白俄罗斯
- pt_BR: Belarus
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Belgium
- - value: BZ
- label:
- en_US: Belize
- zh_Hans: 伯利兹
- pt_BR: Belize
- - value: BJ
- label:
- en_US: Benin
- zh_Hans: 贝宁
- pt_BR: Benin
- - value: BM
- label:
- en_US: Bermuda
- zh_Hans: 百慕大
- pt_BR: Bermuda
- - value: BO
- label:
- en_US: Bolivia
- zh_Hans: 玻利维亚
- pt_BR: Bolivia
- - value: BW
- label:
- en_US: Botswana
- zh_Hans: 博茨瓦纳
- pt_BR: Botswana
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: IO
- label:
- en_US: British Indian Ocean Territory
- zh_Hans: 英属印度洋领地
- pt_BR: British Indian Ocean Territory
- - value: BF
- label:
- en_US: Burkina Faso
- zh_Hans: 布基纳法索
- pt_BR: Burkina Faso
- - value: BI
- label:
- en_US: Burundi
- zh_Hans: 布隆迪
- pt_BR: Burundi
- - value: CM
- label:
- en_US: Cameroon
- zh_Hans: 喀麦隆
- pt_BR: Cameroon
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: CV
- label:
- en_US: Cape Verde
- zh_Hans: 佛得角
- pt_BR: Cape Verde
- - value: KY
- label:
- en_US: Cayman Islands
- zh_Hans: 开曼群岛
- pt_BR: Cayman Islands
- - value: CF
- label:
- en_US: Central African Republic
- zh_Hans: 中非共和国
- pt_BR: Central African Republic
- - value: TD
- label:
- en_US: Chad
- zh_Hans: 乍得
- pt_BR: Chad
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colombia
- - value: CD
- label:
- en_US: Congo, the Democratic Republic of the
- zh_Hans: 刚果民主共和国
- pt_BR: Congo, the Democratic Republic of the
- - value: CR
- label:
- en_US: Costa Rica
- zh_Hans: 哥斯达黎加
- pt_BR: Costa Rica
- - value: CI
- label:
- en_US: Cote D'ivoire
- zh_Hans: 科特迪瓦
- pt_BR: Cote D'ivoire
- - value: CU
- label:
- en_US: Cuba
- zh_Hans: 古巴
- pt_BR: Cuba
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Denmark
- - value: DJ
- label:
- en_US: Djibouti
- zh_Hans: 吉布提
- pt_BR: Djibouti
- - value: DM
- label:
- en_US: Dominica
- zh_Hans: 多米尼克
- pt_BR: Dominica
- - value: DO
- label:
- en_US: Dominican Republic
- zh_Hans: 多米尼加共和国
- pt_BR: Dominican Republic
- - value: EC
- label:
- en_US: Ecuador
- zh_Hans: 厄瓜多尔
- pt_BR: Ecuador
- - value: EG
- label:
- en_US: Egypt
- zh_Hans: 埃及
- pt_BR: Egypt
- - value: SV
- label:
- en_US: El Salvador
- zh_Hans: 萨尔瓦多
- pt_BR: El Salvador
- - value: ET
- label:
- en_US: Ethiopia
- zh_Hans: 埃塞俄比亚
- pt_BR: Ethiopia
- - value: FK
- label:
- en_US: Falkland Islands (Malvinas)
- zh_Hans: 福克兰群岛(马尔维纳斯)
- pt_BR: Falkland Islands (Malvinas)
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GF
- label:
- en_US: French Guiana
- zh_Hans: 法属圭亚那
- pt_BR: French Guiana
- - value: PF
- label:
- en_US: French Polynesia
- zh_Hans: 法属波利尼西亚
- pt_BR: French Polynesia
- - value: TF
- label:
- en_US: French Southern Territories
- zh_Hans: 法属南部领地
- pt_BR: French Southern Territories
- - value: GA
- label:
- en_US: Gabon
- zh_Hans: 加蓬
- pt_BR: Gabon
- - value: GM
- label:
- en_US: Gambia
- zh_Hans: 冈比亚
- pt_BR: Gambia
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: GH
- label:
- en_US: Ghana
- zh_Hans: 加纳
- pt_BR: Ghana
- - value: GR
- label:
- en_US: Greece
- zh_Hans: 希腊
- pt_BR: Greece
- - value: GP
- label:
- en_US: Guadeloupe
- zh_Hans: 瓜德罗普
- pt_BR: Guadeloupe
- - value: GT
- label:
- en_US: Guatemala
- zh_Hans: 危地马拉
- pt_BR: Guatemala
- - value: GY
- label:
- en_US: Guyana
- zh_Hans: 圭亚那
- pt_BR: Guyana
- - value: HT
- label:
- en_US: Haiti
- zh_Hans: 海地
- pt_BR: Haiti
- - value: HN
- label:
- en_US: Honduras
- zh_Hans: 洪都拉斯
- pt_BR: Honduras
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonesia
- - value: IQ
- label:
- en_US: Iraq
- zh_Hans: 伊拉克
- pt_BR: Iraq
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Italy
- - value: JM
- label:
- en_US: Jamaica
- zh_Hans: 牙买加
- pt_BR: Jamaica
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: JO
- label:
- en_US: Jordan
- zh_Hans: 约旦
- pt_BR: Jordan
- - value: KZ
- label:
- en_US: Kazakhstan
- zh_Hans: 哈萨克斯坦
- pt_BR: Kazakhstan
- - value: KE
- label:
- en_US: Kenya
- zh_Hans: 肯尼亚
- pt_BR: Kenya
- - value: KW
- label:
- en_US: Kuwait
- zh_Hans: 科威特
- pt_BR: Kuwait
- - value: KG
- label:
- en_US: Kyrgyzstan
- zh_Hans: 吉尔吉斯斯坦
- pt_BR: Kyrgyzstan
- - value: LB
- label:
- en_US: Lebanon
- zh_Hans: 黎巴嫩
- pt_BR: Lebanon
- - value: LS
- label:
- en_US: Lesotho
- zh_Hans: 莱索托
- pt_BR: Lesotho
- - value: LY
- label:
- en_US: Libyan Arab Jamahiriya
- zh_Hans: 利比亚
- pt_BR: Libyan Arab Jamahiriya
- - value: MG
- label:
- en_US: Madagascar
- zh_Hans: 马达加斯加
- pt_BR: Madagascar
- - value: MW
- label:
- en_US: Malawi
- zh_Hans: 马拉维
- pt_BR: Malawi
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malaysia
- - value: ML
- label:
- en_US: Mali
- zh_Hans: 马里
- pt_BR: Mali
- - value: MQ
- label:
- en_US: Martinique
- zh_Hans: 马提尼克
- pt_BR: Martinique
- - value: MU
- label:
- en_US: Mauritius
- zh_Hans: 毛里求斯
- pt_BR: Mauritius
- - value: YT
- label:
- en_US: Mayotte
- zh_Hans: 马约特
- pt_BR: Mayotte
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: Mexico
- - value: MS
- label:
- en_US: Montserrat
- zh_Hans: 蒙特塞拉特
- pt_BR: Montserrat
- - value: MA
- label:
- en_US: Morocco
- zh_Hans: 摩洛哥
- pt_BR: Morocco
- - value: MZ
- label:
- en_US: Mozambique
- zh_Hans: 莫桑比克
- pt_BR: Mozambique
- - value: NA
- label:
- en_US: Namibia
- zh_Hans: 纳米比亚
- pt_BR: Namibia
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Netherlands
- - value: NC
- label:
- en_US: New Caledonia
- zh_Hans: 新喀里多尼亚
- pt_BR: New Caledonia
- - value: NI
- label:
- en_US: Nicaragua
- zh_Hans: 尼加拉瓜
- pt_BR: Nicaragua
- - value: NE
- label:
- en_US: Niger
- zh_Hans: 尼日尔
- pt_BR: Niger
- - value: NG
- label:
- en_US: Nigeria
- zh_Hans: 尼日利亚
- pt_BR: Nigeria
- - value: OM
- label:
- en_US: Oman
- zh_Hans: 阿曼
- pt_BR: Oman
- - value: PK
- label:
- en_US: Pakistan
- zh_Hans: 巴基斯坦
- pt_BR: Pakistan
- - value: PS
- label:
- en_US: Palestinian Territory, Occupied
- zh_Hans: 巴勒斯坦领土
- pt_BR: Palestinian Territory, Occupied
- - value: PA
- label:
- en_US: Panama
- zh_Hans: 巴拿马
- pt_BR: Panama
- - value: PY
- label:
- en_US: Paraguay
- zh_Hans: 巴拉圭
- pt_BR: Paraguay
- - value: PE
- label:
- en_US: Peru
- zh_Hans: 秘鲁
- pt_BR: Peru
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Philippines
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: PR
- label:
- en_US: Puerto Rico
- zh_Hans: 波多黎各
- pt_BR: Puerto Rico
- - value: QA
- label:
- en_US: Qatar
- zh_Hans: 卡塔尔
- pt_BR: Qatar
- - value: RE
- label:
- en_US: Reunion
- zh_Hans: 留尼旺
- pt_BR: Reunion
- - value: RU
- label:
- en_US: Russian Federation
- zh_Hans: 俄罗斯联邦
- pt_BR: Russian Federation
- - value: RW
- label:
- en_US: Rwanda
- zh_Hans: 卢旺达
- pt_BR: Rwanda
- - value: SH
- label:
- en_US: Saint Helena
- zh_Hans: 圣赫勒拿
- pt_BR: Saint Helena
- - value: PM
- label:
- en_US: Saint Pierre and Miquelon
- zh_Hans: 圣皮埃尔和密克隆
- pt_BR: Saint Pierre and Miquelon
- - value: VC
- label:
- en_US: Saint Vincent and the Grenadines
- zh_Hans: 圣文森特和格林纳丁斯
- pt_BR: Saint Vincent and the Grenadines
- - value: ST
- label:
- en_US: Sao Tome and Principe
- zh_Hans: 圣多美和普林西比
- pt_BR: Sao Tome and Principe
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Saudi Arabia
- - value: SN
- label:
- en_US: Senegal
- zh_Hans: 塞内加尔
- pt_BR: Senegal
- - value: SC
- label:
- en_US: Seychelles
- zh_Hans: 塞舌尔
- pt_BR: Seychelles
- - value: SL
- label:
- en_US: Sierra Leone
- zh_Hans: 塞拉利昂
- pt_BR: Sierra Leone
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: SO
- label:
- en_US: Somalia
- zh_Hans: 索马里
- pt_BR: Somalia
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: South Africa
- - value: GS
- label:
- en_US: South Georgia and the South Sandwich Islands
- zh_Hans: 南乔治亚和南桑威奇群岛
- pt_BR: South Georgia and the South Sandwich Islands
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Spain
- - value: LK
- label:
- en_US: Sri Lanka
- zh_Hans: 斯里兰卡
- pt_BR: Sri Lanka
- - value: SR
- label:
- en_US: Suriname
- zh_Hans: 苏里南
- pt_BR: Suriname
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Switzerland
- - value: TW
- label:
- en_US: Taiwan, Province of China
- zh_Hans: 中国台湾省
- pt_BR: Taiwan, Province of China
- - value: TZ
- label:
- en_US: Tanzania, United Republic of
- zh_Hans: 坦桑尼亚联合共和国
- pt_BR: Tanzania, United Republic of
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Thailand
- - value: TG
- label:
- en_US: Togo
- zh_Hans: 多哥
- pt_BR: Togo
- - value: TT
- label:
- en_US: Trinidad and Tobago
- zh_Hans: 特立尼达和多巴哥
- pt_BR: Trinidad and Tobago
- - value: TN
- label:
- en_US: Tunisia
- zh_Hans: 突尼斯
- pt_BR: Tunisia
- - value: TC
- label:
- en_US: Turks and Caicos Islands
- zh_Hans: 特克斯和凯科斯群岛
- pt_BR: Turks and Caicos Islands
- - value: UG
- label:
- en_US: Uganda
- zh_Hans: 乌干达
- pt_BR: Uganda
- - value: AE
- label:
- en_US: United Arab Emirates
- zh_Hans: 阿联酋
- pt_BR: United Arab Emirates
- - value: UK
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - value: UY
- label:
- en_US: Uruguay
- zh_Hans: 乌拉圭
- pt_BR: Uruguay
- - value: UZ
- label:
- en_US: Uzbekistan
- zh_Hans: 乌兹别克斯坦
- pt_BR: Uzbekistan
- - value: VE
- label:
- en_US: Venezuela
- zh_Hans: 委内瑞拉
- pt_BR: Venezuela
- - value: VN
- label:
- en_US: Viet Nam
- zh_Hans: 越南
- pt_BR: Viet Nam
- - value: VG
- label:
- en_US: Virgin Islands, British
- zh_Hans: 英属维尔京群岛
- pt_BR: Virgin Islands, British
- - value: VI
- label:
- en_US: Virgin Islands, U.S.
- zh_Hans: 美属维尔京群岛
- pt_BR: Virgin Islands, U.S.
- - value: ZM
- label:
- en_US: Zambia
- zh_Hans: 赞比亚
- pt_BR: Zambia
- - value: ZW
- label:
- en_US: Zimbabwe
- zh_Hans: 津巴布韦
- pt_BR: Zimbabwe
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: af
- label:
- en_US: Afrikaans
- zh_Hans: 南非语
- - value: ak
- label:
- en_US: Akan
- zh_Hans: 阿坎语
- - value: sq
- label:
- en_US: Albanian
- zh_Hans: 阿尔巴尼亚语
- - value: ws
- label:
- en_US: Samoa
- zh_Hans: 萨摩亚语
- - value: am
- label:
- en_US: Amharic
- zh_Hans: 阿姆哈拉语
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: hy
- label:
- en_US: Armenian
- zh_Hans: 亚美尼亚语
- - value: az
- label:
- en_US: Azerbaijani
- zh_Hans: 阿塞拜疆语
- - value: eu
- label:
- en_US: Basque
- zh_Hans: 巴斯克语
- - value: be
- label:
- en_US: Belarusian
- zh_Hans: 白俄罗斯语
- - value: bem
- label:
- en_US: Bemba
- zh_Hans: 班巴语
- - value: bn
- label:
- en_US: Bengali
- zh_Hans: 孟加拉语
- - value: bh
- label:
- en_US: Bihari
- zh_Hans: 比哈尔语
- - value: xx-bork
- label:
- en_US: Bork, bork, bork!
- zh_Hans: 博克语
- - value: bs
- label:
- en_US: Bosnian
- zh_Hans: 波斯尼亚语
- - value: br
- label:
- en_US: Breton
- zh_Hans: 布列塔尼语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: bt
- label:
- en_US: Bhutanese
- zh_Hans: 不丹语
- - value: km
- label:
- en_US: Cambodian
- zh_Hans: 高棉语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: chr
- label:
- en_US: Cherokee
- zh_Hans: 切罗基语
- - value: ny
- label:
- en_US: Chichewa
- zh_Hans: 齐切瓦语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: co
- label:
- en_US: Corsican
- zh_Hans: 科西嘉语
- - value: hr
- label:
- en_US: Croatian
- zh_Hans: 克罗地亚语
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: xx-elmer
- label:
- en_US: Elmer Fudd
- zh_Hans: 艾尔默福德语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: eo
- label:
- en_US: Esperanto
- zh_Hans: 世界语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: ee
- label:
- en_US: Ewe
- zh_Hans: 埃维语
- - value: fo
- label:
- en_US: Faroese
- zh_Hans: 法罗语
- - value: tl
- label:
- en_US: Filipino
- zh_Hans: 菲律宾语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: fy
- label:
- en_US: Frisian
- zh_Hans: 弗里西亚语
- - value: gaa
- label:
- en_US: Ga
- zh_Hans: 加语
- - value: gl
- label:
- en_US: Galician
- zh_Hans: 加利西亚语
- - value: ka
- label:
- en_US: Georgian
- zh_Hans: 格鲁吉亚语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: kl
- label:
- en_US: Greenlandic
- zh_Hans: 格陵兰语
- - value: gn
- label:
- en_US: Guarani
- zh_Hans: 瓜拉尼语
- - value: gu
- label:
- en_US: Gujarati
- zh_Hans: 古吉拉特语
- - value: xx-hacker
- label:
- en_US: Hacker
- zh_Hans: 黑客语
- - value: ht
- label:
- en_US: Haitian Creole
- zh_Hans: 海地克里奥尔语
- - value: ha
- label:
- en_US: Hausa
- zh_Hans: 豪萨语
- - value: haw
- label:
- en_US: Hawaiian
- zh_Hans: 夏威夷语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: is
- label:
- en_US: Icelandic
- zh_Hans: 冰岛语
- - value: ig
- label:
- en_US: Igbo
- zh_Hans: 伊博语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: ia
- label:
- en_US: Interlingua
- zh_Hans: 国际语
- - value: ga
- label:
- en_US: Irish
- 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: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: kk
- label:
- en_US: Kazakh
- zh_Hans: 哈萨克语
- - value: rw
- label:
- en_US: Kinyarwanda
- zh_Hans: 基尼亚卢旺达语
- - value: rn
- label:
- en_US: Kirundi
- zh_Hans: 基隆迪语
- - value: xx-klingon
- label:
- en_US: Klingon
- zh_Hans: 克林贡语
- - value: kg
- label:
- en_US: Kongo
- zh_Hans: 刚果语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: kri
- label:
- en_US: Krio (Sierra Leone)
- zh_Hans: 塞拉利昂克里奥尔语
- - value: ku
- label:
- en_US: Kurdish
- zh_Hans: 库尔德语
- - value: ckb
- label:
- en_US: Kurdish (Soranî)
- zh_Hans: 库尔德语(索拉尼)
- - value: ky
- label:
- en_US: Kyrgyz
- zh_Hans: 吉尔吉斯语
- - value: lo
- label:
- en_US: Laothian
- zh_Hans: 老挝语
- - value: la
- label:
- en_US: Latin
- zh_Hans: 拉丁语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: ln
- label:
- en_US: Lingala
- zh_Hans: 林加拉语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: loz
- label:
- en_US: Lozi
- zh_Hans: 洛齐语
- - value: lg
- label:
- en_US: Luganda
- zh_Hans: 卢干达语
- - value: ach
- label:
- en_US: Luo
- zh_Hans: 卢奥语
- - value: mk
- label:
- en_US: Macedonian
- zh_Hans: 马其顿语
- - value: mg
- label:
- en_US: Malagasy
- zh_Hans: 马尔加什语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mt
- label:
- en_US: Maltese
- zh_Hans: 马耳他语
- - value: mv
- label:
- en_US: Maldives
- zh_Hans: 马尔代夫语
- - value: mi
- label:
- en_US: Maori
- zh_Hans: 毛利语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: mfe
- label:
- en_US: Mauritian Creole
- zh_Hans: 毛里求斯克里奥尔语
- - value: mo
- label:
- en_US: Moldavian
- zh_Hans: 摩尔达维亚语
- - value: mn
- label:
- en_US: Mongolian
- zh_Hans: 蒙古语
- - value: sr-me
- label:
- en_US: Montenegrin
- zh_Hans: 黑山语
- - value: ne
- label:
- en_US: Nepali
- zh_Hans: 尼泊尔语
- - value: pcm
- label:
- en_US: Nigerian Pidgin
- zh_Hans: 尼日利亚皮钦语
- - value: nso
- label:
- en_US: Northern Sotho
- zh_Hans: 北索托语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: nn
- label:
- en_US: Norwegian (Nynorsk)
- zh_Hans: 挪威语(尼诺斯克语)
- - value: oc
- label:
- en_US: Occitan
- zh_Hans: 奥克语
- - value: or
- label:
- en_US: Oriya
- zh_Hans: 奥里亚语
- - value: om
- label:
- en_US: Oromo
- zh_Hans: 奥罗莫语
- - value: ps
- label:
- en_US: Pashto
- zh_Hans: 普什图语
- - value: fa
- label:
- en_US: Persian
- zh_Hans: 波斯语
- - value: xx-pirate
- label:
- en_US: Pirate
- zh_Hans: 海盗语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: qu
- label:
- en_US: Quechua
- zh_Hans: 克丘亚语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: rm
- label:
- en_US: Romansh
- zh_Hans: 罗曼什语
- - value: nyn
- label:
- en_US: Runyakitara
- zh_Hans: 卢尼亚基塔拉语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: gd
- label:
- en_US: Scots Gaelic
- zh_Hans: 苏格兰盖尔语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sh
- label:
- en_US: Serbo-Croatian
- zh_Hans: 塞尔维亚-克罗地亚语
- - value: st
- label:
- en_US: Sesotho
- zh_Hans: 塞索托语
- - value: tn
- label:
- en_US: Setswana
- zh_Hans: 塞茨瓦纳语
- - value: crs
- label:
- en_US: Seychellois Creole
- zh_Hans: 塞舌尔克里奥尔语
- - value: sn
- label:
- en_US: Shona
- zh_Hans: 绍纳语
- - value: sd
- label:
- en_US: Sindhi
- zh_Hans: 信德语
- - value: si
- label:
- en_US: Sinhalese
- zh_Hans: 僧伽罗语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: so
- label:
- en_US: Somali
- zh_Hans: 索马里语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: es-419
- label:
- en_US: Spanish (Latin American)
- zh_Hans: 西班牙语(拉丁美洲)
- - value: su
- label:
- en_US: Sundanese
- zh_Hans: 巽他语
- - value: sw
- label:
- en_US: Swahili
- zh_Hans: 斯瓦希里语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: tg
- label:
- en_US: Tajik
- zh_Hans: 塔吉克语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: tt
- label:
- en_US: Tatar
- zh_Hans: 鞑靼语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: ti
- label:
- en_US: Tigrinya
- zh_Hans: 提格利尼亚语
- - value: to
- label:
- en_US: Tonga
- zh_Hans: 汤加语
- - value: lua
- label:
- en_US: Tshiluba
- zh_Hans: 卢巴语
- - value: tum
- label:
- en_US: Tumbuka
- zh_Hans: 图布卡语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: tk
- label:
- en_US: Turkmen
- zh_Hans: 土库曼语
- - value: tw
- label:
- en_US: Twi
- zh_Hans: 契维语
- - value: ug
- label:
- en_US: Uighur
- 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: vu
- label:
- en_US: Vanuatu
- zh_Hans: 瓦努阿图语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- - value: cy
- label:
- en_US: Welsh
- zh_Hans: 威尔士语
- - value: wo
- label:
- en_US: Wolof
- zh_Hans: 沃洛夫语
- - value: xh
- label:
- en_US: Xhosa
- zh_Hans: 科萨语
- - value: yi
- label:
- en_US: Yiddish
- zh_Hans: 意第绪语
- - value: yo
- label:
- en_US: Yoruba
- zh_Hans: 约鲁巴语
- - value: zu
- label:
- en_US: Zulu
- zh_Hans: 祖鲁语
- - name: is_remote
- type: select
- label:
- en_US: is_remote
- zh_Hans: 很遥远
- human_description:
- en_US: Filter results based on the work arrangement. Set it to true to find jobs that offer work from home or remote work opportunities.
- zh_Hans: 根据工作安排过滤结果。将其设置为 true 可查找提供在家工作或远程工作机会的工作。
- required: false
- form: form
- options:
- - value: 'true'
- label:
- en_US: "true"
- zh_Hans: "true"
- - value: 'false'
- label:
- en_US: "false"
- zh_Hans: "false"
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google_news.py b/api/core/tools/provider/builtin/searchapi/tools/google_news.py
deleted file mode 100644
index c8b3ccda05e195..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google_news.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SEARCH_API_URL = "https://www.searchapi.io/api/v1/search"
-
-
-class SearchAPI:
- """
- SearchAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SearchAPI tool provider."""
- self.searchapi_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SearchAPI and parse result."""
- type = kwargs.get("result_type", "text")
- return self._process_response(self.results(query, **kwargs), type=type)
-
- def results(self, query: str, **kwargs: Any) -> dict:
- """Run query through SearchAPI and return the raw result."""
- params = self.get_params(query, **kwargs)
- response = requests.get(
- url=SEARCH_API_URL,
- params=params,
- headers={"Authorization": f"Bearer {self.searchapi_api_key}"},
- )
- response.raise_for_status()
- return response.json()
-
- def get_params(self, query: str, **kwargs: Any) -> dict[str, str]:
- """Get parameters for SearchAPI."""
- return {
- "engine": "google_news",
- "q": query,
- **{key: value for key, value in kwargs.items() if value not in {None, ""}},
- }
-
- @staticmethod
- def _process_response(res: dict, type: str) -> str:
- """Process response from SearchAPI."""
- if "error" in res:
- return res["error"]
-
- toret = ""
- if type == "text":
- if "organic_results" in res and "snippet" in res["organic_results"][0]:
- for item in res["organic_results"]:
- toret += "content: " + item["snippet"] + "\n" + "link: " + item["link"] + "\n"
- if "top_stories" in res and "title" in res["top_stories"][0]:
- for item in res["top_stories"]:
- toret += "title: " + item["title"] + "\n" + "link: " + item["link"] + "\n"
- if toret == "":
- toret = "No good search result found"
-
- elif type == "link":
- if "organic_results" in res and "title" in res["organic_results"][0]:
- for item in res["organic_results"]:
- toret += f"[{item['title']}]({item['link']})\n"
- elif "top_stories" in res and "title" in res["top_stories"][0]:
- for item in res["top_stories"]:
- toret += f"[{item['title']}]({item['link']})\n"
- else:
- toret = "No good search result found"
- return toret
-
-
-class GoogleNewsTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SearchApi tool.
- """
- query = tool_parameters["query"]
- result_type = tool_parameters["result_type"]
- num = tool_parameters.get("num", 10)
- google_domain = tool_parameters.get("google_domain", "google.com")
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["searchapi_api_key"]
- result = SearchAPI(api_key).run(
- query, result_type=result_type, num=num, google_domain=google_domain, gl=gl, hl=hl, location=location
- )
-
- if result_type == "text":
- return self.create_text_message(text=result)
- return self.create_link_message(link=result)
diff --git a/api/core/tools/provider/builtin/searchapi/tools/google_news.yaml b/api/core/tools/provider/builtin/searchapi/tools/google_news.yaml
deleted file mode 100644
index ff34af34cc9f5c..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/google_news.yaml
+++ /dev/null
@@ -1,1922 +0,0 @@
-identity:
- name: google_news_api
- author: SearchApi
- label:
- en_US: Google News API
- zh_Hans: Google News API
-description:
- human:
- en_US: A tool to retrieve organic search results snippets and links from Google News engine.
- zh_Hans: 一种从 Google 新闻引擎检索有机搜索结果片段和链接的工具。
- llm: A tool to retrieve organic search results snippets and links from Google News engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: result_type
- type: select
- required: true
- options:
- - value: text
- label:
- en_US: text
- zh_Hans: 文本
- - value: link
- label:
- en_US: link
- zh_Hans: 链接
- default: text
- label:
- en_US: Result type
- zh_Hans: 结果类型
- human_description:
- en_US: used for selecting the result type, text or link.
- zh_Hans: 用于选择结果类型,使用文本还是链接进行展示。
- form: form
- - name: location
- type: string
- required: false
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: llm
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: AF
- label:
- en_US: Afghanistan
- zh_Hans: 阿富汗
- pt_BR: Afeganistão
- - value: AL
- label:
- en_US: Albania
- zh_Hans: 阿尔巴尼亚
- pt_BR: Albânia
- - value: DZ
- label:
- en_US: Algeria
- zh_Hans: 阿尔及利亚
- pt_BR: Argélia
- - value: AS
- label:
- en_US: American Samoa
- zh_Hans: 美属萨摩亚
- pt_BR: Samoa Americana
- - value: AD
- label:
- en_US: Andorra
- zh_Hans: 安道尔
- pt_BR: Andorra
- - value: AO
- label:
- en_US: Angola
- zh_Hans: 安哥拉
- pt_BR: Angola
- - value: AI
- label:
- en_US: Anguilla
- zh_Hans: 安圭拉
- pt_BR: Anguilla
- - value: AQ
- label:
- en_US: Antarctica
- zh_Hans: 南极洲
- pt_BR: Antártica
- - value: AG
- label:
- en_US: Antigua and Barbuda
- zh_Hans: 安提瓜和巴布达
- pt_BR: Antígua e Barbuda
- - value: AR
- label:
- en_US: Argentina
- zh_Hans: 阿根廷
- pt_BR: Argentina
- - value: AM
- label:
- en_US: Armenia
- zh_Hans: 亚美尼亚
- pt_BR: Armênia
- - value: AW
- label:
- en_US: Aruba
- zh_Hans: 阿鲁巴
- pt_BR: Aruba
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Austrália
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Áustria
- - value: AZ
- label:
- en_US: Azerbaijan
- zh_Hans: 阿塞拜疆
- pt_BR: Azerbaijão
- - value: BS
- label:
- en_US: Bahamas
- zh_Hans: 巴哈马
- pt_BR: Bahamas
- - value: BH
- label:
- en_US: Bahrain
- zh_Hans: 巴林
- pt_BR: Bahrein
- - value: BD
- label:
- en_US: Bangladesh
- zh_Hans: 孟加拉国
- pt_BR: Bangladesh
- - value: BB
- label:
- en_US: Barbados
- zh_Hans: 巴巴多斯
- pt_BR: Barbados
- - value: BY
- label:
- en_US: Belarus
- zh_Hans: 白俄罗斯
- pt_BR: Bielorrússia
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Bélgica
- - value: BZ
- label:
- en_US: Belize
- zh_Hans: 伯利兹
- pt_BR: Belize
- - value: BJ
- label:
- en_US: Benin
- zh_Hans: 贝宁
- pt_BR: Benim
- - value: BM
- label:
- en_US: Bermuda
- zh_Hans: 百慕大
- pt_BR: Bermudas
- - value: BT
- label:
- en_US: Bhutan
- zh_Hans: 不丹
- pt_BR: Butão
- - value: BO
- label:
- en_US: Bolivia
- zh_Hans: 玻利维亚
- pt_BR: Bolívia
- - value: BA
- label:
- en_US: Bosnia and Herzegovina
- zh_Hans: 波斯尼亚和黑塞哥维那
- pt_BR: Bósnia e Herzegovina
- - value: BW
- label:
- en_US: Botswana
- zh_Hans: 博茨瓦纳
- pt_BR: Botsuana
- - value: BV
- label:
- en_US: Bouvet Island
- zh_Hans: 布韦岛
- pt_BR: Ilha Bouvet
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brasil
- - value: IO
- label:
- en_US: British Indian Ocean Territory
- zh_Hans: 英属印度洋领地
- pt_BR: Território Britânico do Oceano Índico
- - value: BN
- label:
- en_US: Brunei Darussalam
- zh_Hans: 文莱
- pt_BR: Brunei Darussalam
- - value: BG
- label:
- en_US: Bulgaria
- zh_Hans: 保加利亚
- pt_BR: Bulgária
- - value: BF
- label:
- en_US: Burkina Faso
- zh_Hans: 布基纳法索
- pt_BR: Burkina Faso
- - value: BI
- label:
- en_US: Burundi
- zh_Hans: 布隆迪
- pt_BR: Burundi
- - value: KH
- label:
- en_US: Cambodia
- zh_Hans: 柬埔寨
- pt_BR: Camboja
- - value: CM
- label:
- en_US: Cameroon
- zh_Hans: 喀麦隆
- pt_BR: Camarões
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canadá
- - value: CV
- label:
- en_US: Cape Verde
- zh_Hans: 佛得角
- pt_BR: Cabo Verde
- - value: KY
- label:
- en_US: Cayman Islands
- zh_Hans: 开曼群岛
- pt_BR: Ilhas Cayman
- - value: CF
- label:
- en_US: Central African Republic
- zh_Hans: 中非共和国
- pt_BR: República Centro-Africana
- - value: TD
- label:
- en_US: Chad
- zh_Hans: 乍得
- pt_BR: Chade
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CN
- label:
- en_US: China
- zh_Hans: 中国
- pt_BR: China
- - value: CX
- label:
- en_US: Christmas Island
- zh_Hans: 圣诞岛
- pt_BR: Ilha do Natal
- - value: CC
- label:
- en_US: Cocos (Keeling) Islands
- zh_Hans: 科科斯(基林)群岛
- pt_BR: Ilhas Cocos (Keeling)
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colômbia
- - value: KM
- label:
- en_US: Comoros
- zh_Hans: 科摩罗
- pt_BR: Comores
- - value: CG
- label:
- en_US: Congo
- zh_Hans: 刚果
- pt_BR: Congo
- - value: CD
- label:
- en_US: Congo, the Democratic Republic of the
- zh_Hans: 刚果民主共和国
- pt_BR: Congo, República Democrática do
- - value: CK
- label:
- en_US: Cook Islands
- zh_Hans: 库克群岛
- pt_BR: Ilhas Cook
- - value: CR
- label:
- en_US: Costa Rica
- zh_Hans: 哥斯达黎加
- pt_BR: Costa Rica
- - value: CI
- label:
- en_US: Cote D'ivoire
- zh_Hans: 科特迪瓦
- pt_BR: Costa do Marfim
- - value: HR
- label:
- en_US: Croatia
- zh_Hans: 克罗地亚
- pt_BR: Croácia
- - value: CU
- label:
- en_US: Cuba
- zh_Hans: 古巴
- pt_BR: Cuba
- - value: CY
- label:
- en_US: Cyprus
- zh_Hans: 塞浦路斯
- pt_BR: Chipre
- - value: CZ
- label:
- en_US: Czech Republic
- zh_Hans: 捷克共和国
- pt_BR: República Tcheca
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Dinamarca
- - value: DJ
- label:
- en_US: Djibouti
- zh_Hans: 吉布提
- pt_BR: Djibuti
- - value: DM
- label:
- en_US: Dominica
- zh_Hans: 多米尼克
- pt_BR: Dominica
- - value: DO
- label:
- en_US: Dominican Republic
- zh_Hans: 多米尼加共和国
- pt_BR: República Dominicana
- - value: EC
- label:
- en_US: Ecuador
- zh_Hans: 厄瓜多尔
- pt_BR: Equador
- - value: EG
- label:
- en_US: Egypt
- zh_Hans: 埃及
- pt_BR: Egito
- - value: SV
- label:
- en_US: El Salvador
- zh_Hans: 萨尔瓦多
- pt_BR: El Salvador
- - value: GQ
- label:
- en_US: Equatorial Guinea
- zh_Hans: 赤道几内亚
- pt_BR: Guiné Equatorial
- - value: ER
- label:
- en_US: Eritrea
- zh_Hans: 厄立特里亚
- pt_BR: Eritreia
- - value: EE
- label:
- en_US: Estonia
- zh_Hans: 爱沙尼亚
- pt_BR: Estônia
- - value: ET
- label:
- en_US: Ethiopia
- zh_Hans: 埃塞俄比亚
- pt_BR: Etiópia
- - value: FK
- label:
- en_US: Falkland Islands (Malvinas)
- zh_Hans: 福克兰群岛(马尔维纳斯)
- pt_BR: Ilhas Falkland (Malvinas)
- - value: FO
- label:
- en_US: Faroe Islands
- zh_Hans: 法罗群岛
- pt_BR: Ilhas Faroe
- - value: FJ
- label:
- en_US: Fiji
- zh_Hans: 斐济
- pt_BR: Fiji
- - value: FI
- label:
- en_US: Finland
- zh_Hans: 芬兰
- pt_BR: Finlândia
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: França
- - value: GF
- label:
- en_US: French Guiana
- zh_Hans: 法属圭亚那
- pt_BR: Guiana Francesa
- - value: PF
- label:
- en_US: French Polynesia
- zh_Hans: 法属波利尼西亚
- pt_BR: Polinésia Francesa
- - value: TF
- label:
- en_US: French Southern Territories
- zh_Hans: 法属南部领地
- pt_BR: Territórios Franceses do Sul
- - value: GA
- label:
- en_US: Gabon
- zh_Hans: 加蓬
- pt_BR: Gabão
- - value: GM
- label:
- en_US: Gambia
- zh_Hans: 冈比亚
- pt_BR: Gâmbia
- - value: GE
- label:
- en_US: Georgia
- zh_Hans: 格鲁吉亚
- pt_BR: Geórgia
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Alemanha
- - value: GH
- label:
- en_US: Ghana
- zh_Hans: 加纳
- pt_BR: Gana
- - value: GI
- label:
- en_US: Gibraltar
- zh_Hans: 直布罗陀
- pt_BR: Gibraltar
- - value: GR
- label:
- en_US: Greece
- zh_Hans: 希腊
- pt_BR: Grécia
- - value: GL
- label:
- en_US: Greenland
- zh_Hans: 格陵兰
- pt_BR: Groenlândia
- - value: GD
- label:
- en_US: Grenada
- zh_Hans: 格林纳达
- pt_BR: Granada
- - value: GP
- label:
- en_US: Guadeloupe
- zh_Hans: 瓜德罗普
- pt_BR: Guadalupe
- - value: GU
- label:
- en_US: Guam
- zh_Hans: 关岛
- pt_BR: Guam
- - value: GT
- label:
- en_US: Guatemala
- zh_Hans: 危地马拉
- pt_BR: Guatemala
- - value: GN
- label:
- en_US: Guinea
- zh_Hans: 几内亚
- pt_BR: Guiné
- - value: GW
- label:
- en_US: Guinea-Bissau
- zh_Hans: 几内亚比绍
- pt_BR: Guiné-Bissau
- - value: GY
- label:
- en_US: Guyana
- zh_Hans: 圭亚那
- pt_BR: Guiana
- - value: HT
- label:
- en_US: Haiti
- zh_Hans: 海地
- pt_BR: Haiti
- - value: HM
- label:
- en_US: Heard Island and McDonald Islands
- zh_Hans: 赫德岛和麦克唐纳群岛
- pt_BR: Ilha Heard e Ilhas McDonald
- - value: VA
- label:
- en_US: Holy See (Vatican City State)
- zh_Hans: 教廷(梵蒂冈城国)
- pt_BR: Santa Sé (Estado da Cidade do Vaticano)
- - value: HN
- label:
- en_US: Honduras
- zh_Hans: 洪都拉斯
- pt_BR: Honduras
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: HU
- label:
- en_US: Hungary
- zh_Hans: 匈牙利
- pt_BR: Hungria
- - value: IS
- label:
- en_US: Iceland
- zh_Hans: 冰岛
- pt_BR: Islândia
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: Índia
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonésia
- - value: IR
- label:
- en_US: Iran, Islamic Republic of
- zh_Hans: 伊朗
- pt_BR: Irã
- - value: IQ
- label:
- en_US: Iraq
- zh_Hans: 伊拉克
- pt_BR: Iraque
- - value: IE
- label:
- en_US: Ireland
- zh_Hans: 爱尔兰
- pt_BR: Irlanda
- - value: IL
- label:
- en_US: Israel
- zh_Hans: 以色列
- pt_BR: Israel
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Itália
- - value: JM
- label:
- en_US: Jamaica
- zh_Hans: 牙买加
- pt_BR: Jamaica
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japão
- - value: JO
- label:
- en_US: Jordan
- zh_Hans: 约旦
- pt_BR: Jordânia
- - value: KZ
- label:
- en_US: Kazakhstan
- zh_Hans: 哈萨克斯坦
- pt_BR: Cazaquistão
- - value: KE
- label:
- en_US: Kenya
- zh_Hans: 肯尼亚
- pt_BR: Quênia
- - value: KI
- label:
- en_US: Kiribati
- zh_Hans: 基里巴斯
- pt_BR: Kiribati
- - value: KP
- label:
- en_US: Korea, Democratic People's Republic of
- zh_Hans: 朝鲜
- pt_BR: Coreia, República Democrática Popular da
- - value: KR
- label:
- en_US: Korea, Republic of
- zh_Hans: 韩国
- pt_BR: Coreia, República da
- - value: KW
- label:
- en_US: Kuwait
- zh_Hans: 科威特
- pt_BR: Kuwait
- - value: KG
- label:
- en_US: Kyrgyzstan
- zh_Hans: 吉尔吉斯斯坦
- pt_BR: Quirguistão
- - value: LA
- label:
- en_US: Lao People's Democratic Republic
- zh_Hans: 老挝
- pt_BR: República Democrática Popular do Laos
- - value: LV
- label:
- en_US: Latvia
- zh_Hans: 拉脱维亚
- pt_BR: Letônia
- - value: LB
- label:
- en_US: Lebanon
- zh_Hans: 黎巴嫩
- pt_BR: Líbano
- - value: LS
- label:
- en_US: Lesotho
- zh_Hans: 莱索托
- pt_BR: Lesoto
- - value: LR
- label:
- en_US: Liberia
- zh_Hans: 利比里亚
- pt_BR: Libéria
- - value: LY
- label:
- en_US: Libyan Arab Jamahiriya
- zh_Hans: 利比亚
- pt_BR: Líbia
- - value: LI
- label:
- en_US: Liechtenstein
- zh_Hans: 列支敦士登
- pt_BR: Liechtenstein
- - value: LT
- label:
- en_US: Lithuania
- zh_Hans: 立陶宛
- pt_BR: Lituânia
- - value: LU
- label:
- en_US: Luxembourg
- zh_Hans: 卢森堡
- pt_BR: Luxemburgo
- - value: MO
- label:
- en_US: Macao
- zh_Hans: 澳门
- pt_BR: Macau
- - value: MK
- label:
- en_US: Macedonia, the Former Yugosalv Republic of
- zh_Hans: 前南斯拉夫马其顿共和国
- pt_BR: Macedônia, Ex-República Iugoslava da
- - value: MG
- label:
- en_US: Madagascar
- zh_Hans: 马达加斯加
- pt_BR: Madagascar
- - value: MW
- label:
- en_US: Malawi
- zh_Hans: 马拉维
- pt_BR: Malaui
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malásia
- - value: MV
- label:
- en_US: Maldives
- zh_Hans: 马尔代夫
- pt_BR: Maldivas
- - value: ML
- label:
- en_US: Mali
- zh_Hans: 马里
- pt_BR: Mali
- - value: MT
- label:
- en_US: Malta
- zh_Hans: 马耳他
- pt_BR: Malta
- - value: MH
- label:
- en_US: Marshall Islands
- zh_Hans: 马绍尔群岛
- pt_BR: Ilhas Marshall
- - value: MQ
- label:
- en_US: Martinique
- zh_Hans: 马提尼克
- pt_BR: Martinica
- - value: MR
- label:
- en_US: Mauritania
- zh_Hans: 毛里塔尼亚
- pt_BR: Mauritânia
- - value: MU
- label:
- en_US: Mauritius
- zh_Hans: 毛里求斯
- pt_BR: Maurício
- - value: YT
- label:
- en_US: Mayotte
- zh_Hans: 马约特
- pt_BR: Mayotte
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: México
- - value: FM
- label:
- en_US: Micronesia, Federated States of
- zh_Hans: 密克罗尼西亚联邦
- pt_BR: Micronésia, Estados Federados da
- - value: MD
- label:
- en_US: Moldova, Republic of
- zh_Hans: 摩尔多瓦共和国
- pt_BR: Moldávia, República da
- - value: MC
- label:
- en_US: Monaco
- zh_Hans: 摩纳哥
- pt_BR: Mônaco
- - value: MN
- label:
- en_US: Mongolia
- zh_Hans: 蒙古
- pt_BR: Mongólia
- - value: MS
- label:
- en_US: Montserrat
- zh_Hans: 蒙特塞拉特
- pt_BR: Montserrat
- - value: MA
- label:
- en_US: Morocco
- zh_Hans: 摩洛哥
- pt_BR: Marrocos
- - value: MZ
- label:
- en_US: Mozambique
- zh_Hans: 莫桑比克
- pt_BR: Moçambique
- - value: MM
- label:
- en_US: Myanmar
- zh_Hans: 缅甸
- pt_BR: Mianmar
- - value: NA
- label:
- en_US: Namibia
- zh_Hans: 纳米比亚
- pt_BR: Namíbia
- - value: NR
- label:
- en_US: Nauru
- zh_Hans: 瑙鲁
- pt_BR: Nauru
- - value: NP
- label:
- en_US: Nepal
- zh_Hans: 尼泊尔
- pt_BR: Nepal
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Países Baixos
- - value: AN
- label:
- en_US: Netherlands Antilles
- zh_Hans: 荷属安的列斯
- pt_BR: Antilhas Holandesas
- - value: NC
- label:
- en_US: New Caledonia
- zh_Hans: 新喀里多尼亚
- pt_BR: Nova Caledônia
- - value: NZ
- label:
- en_US: New Zealand
- zh_Hans: 新西兰
- pt_BR: Nova Zelândia
- - value: NI
- label:
- en_US: Nicaragua
- zh_Hans: 尼加拉瓜
- pt_BR: Nicarágua
- - value: NE
- label:
- en_US: Niger
- zh_Hans: 尼日尔
- pt_BR: Níger
- - value: NG
- label:
- en_US: Nigeria
- zh_Hans: 尼日利亚
- pt_BR: Nigéria
- - value: NU
- label:
- en_US: Niue
- zh_Hans: 纽埃
- pt_BR: Niue
- - value: NF
- label:
- en_US: Norfolk Island
- zh_Hans: 诺福克岛
- pt_BR: Ilha Norfolk
- - value: MP
- label:
- en_US: Northern Mariana Islands
- zh_Hans: 北马里亚纳群岛
- pt_BR: Ilhas Marianas do Norte
- - value: "NO"
- label:
- en_US: Norway
- zh_Hans: 挪威
- pt_BR: Noruega
- - value: OM
- label:
- en_US: Oman
- zh_Hans: 阿曼
- pt_BR: Omã
- - value: PK
- label:
- en_US: Pakistan
- zh_Hans: 巴基斯坦
- pt_BR: Paquistão
- - value: PW
- label:
- en_US: Palau
- zh_Hans: 帕劳
- pt_BR: Palau
- - value: PS
- label:
- en_US: Palestinian Territory, Occupied
- zh_Hans: 巴勒斯坦领土
- pt_BR: Palestina, Território Ocupado
- - value: PA
- label:
- en_US: Panama
- zh_Hans: 巴拿马
- pt_BR: Panamá
- - value: PG
- label:
- en_US: Papua New Guinea
- zh_Hans: 巴布亚新几内亚
- pt_BR: Papua Nova Guiné
- - value: PY
- label:
- en_US: Paraguay
- zh_Hans: 巴拉圭
- pt_BR: Paraguai
- - value: PE
- label:
- en_US: Peru
- zh_Hans: 秘鲁
- pt_BR: Peru
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Filipinas
- - value: PN
- label:
- en_US: Pitcairn
- zh_Hans: 皮特凯恩岛
- pt_BR: Pitcairn
- - value: PL
- label:
- en_US: Poland
- zh_Hans: 波兰
- pt_BR: Polônia
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: PR
- label:
- en_US: Puerto Rico
- zh_Hans: 波多黎各
- pt_BR: Porto Rico
- - value: QA
- label:
- en_US: Qatar
- zh_Hans: 卡塔尔
- pt_BR: Catar
- - value: RE
- label:
- en_US: Reunion
- zh_Hans: 留尼旺
- pt_BR: Reunião
- - value: RO
- label:
- en_US: Romania
- zh_Hans: 罗马尼亚
- pt_BR: Romênia
- - value: RU
- label:
- en_US: Russian Federation
- zh_Hans: 俄罗斯联邦
- pt_BR: Rússia
- - value: RW
- label:
- en_US: Rwanda
- zh_Hans: 卢旺达
- pt_BR: Ruanda
- - value: SH
- label:
- en_US: Saint Helena
- zh_Hans: 圣赫勒拿
- pt_BR: Santa Helena
- - value: KN
- label:
- en_US: Saint Kitts and Nevis
- zh_Hans: 圣基茨和尼维斯
- pt_BR: São Cristóvão e Nevis
- - value: LC
- label:
- en_US: Saint Lucia
- zh_Hans: 圣卢西亚
- pt_BR: Santa Lúcia
- - value: PM
- label:
- en_US: Saint Pierre and Miquelon
- zh_Hans: 圣皮埃尔和密克隆
- pt_BR: São Pedro e Miquelon
- - value: VC
- label:
- en_US: Saint Vincent and the Grenadines
- zh_Hans: 圣文森特和格林纳丁斯
- pt_BR: São Vicente e Granadinas
- - value: WS
- label:
- en_US: Samoa
- zh_Hans: 萨摩亚
- pt_BR: Samoa
- - value: SM
- label:
- en_US: San Marino
- zh_Hans: 圣马力诺
- pt_BR: San Marino
- - value: ST
- label:
- en_US: Sao Tome and Principe
- zh_Hans: 圣多美和普林西比
- pt_BR: São Tomé e Príncipe
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Arábia Saudita
- - value: SN
- label:
- en_US: Senegal
- zh_Hans: 塞内加尔
- pt_BR: Senegal
- - value: RS
- label:
- en_US: Serbia and Montenegro
- zh_Hans: 塞尔维亚和黑山
- pt_BR: Sérvia e Montenegro
- - value: SC
- label:
- en_US: Seychelles
- zh_Hans: 塞舌尔
- pt_BR: Seicheles
- - value: SL
- label:
- en_US: Sierra Leone
- zh_Hans: 塞拉利昂
- pt_BR: Serra Leoa
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapura
- - value: SK
- label:
- en_US: Slovakia
- zh_Hans: 斯洛伐克
- pt_BR: Eslováquia
- - value: SI
- label:
- en_US: Slovenia
- zh_Hans: 斯洛文尼亚
- pt_BR: Eslovênia
- - value: SB
- label:
- en_US: Solomon Islands
- zh_Hans: 所罗门群岛
- pt_BR: Ilhas Salomão
- - value: SO
- label:
- en_US: Somalia
- zh_Hans: 索马里
- pt_BR: Somália
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: África do Sul
- - value: GS
- label:
- en_US: South Georgia and the South Sandwich Islands
- zh_Hans: 南乔治亚和南桑威奇群岛
- pt_BR: Geórgia do Sul e Ilhas Sandwich do Sul
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Espanha
- - value: LK
- label:
- en_US: Sri Lanka
- zh_Hans: 斯里兰卡
- pt_BR: Sri Lanka
- - value: SD
- label:
- en_US: Sudan
- zh_Hans: 苏丹
- pt_BR: Sudão
- - value: SR
- label:
- en_US: Suriname
- zh_Hans: 苏里南
- pt_BR: Suriname
- - value: SJ
- label:
- en_US: Svalbard and Jan Mayen
- zh_Hans: 斯瓦尔巴特和扬马延岛
- pt_BR: Svalbard e Jan Mayen
- - value: SZ
- label:
- en_US: Swaziland
- zh_Hans: 斯威士兰
- pt_BR: Essuatíni
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Suécia
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Suíça
- - value: SY
- label:
- en_US: Syrian Arab Republic
- zh_Hans: 叙利亚
- pt_BR: Síria
- - value: TW
- label:
- en_US: Taiwan, Province of China
- zh_Hans: 台湾
- pt_BR: Taiwan
- - value: TJ
- label:
- en_US: Tajikistan
- zh_Hans: 塔吉克斯坦
- pt_BR: Tajiquistão
- - value: TZ
- label:
- en_US: Tanzania, United Republic of
- zh_Hans: 坦桑尼亚联合共和国
- pt_BR: Tanzânia
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Tailândia
- - value: TL
- label:
- en_US: Timor-Leste
- zh_Hans: 东帝汶
- pt_BR: Timor-Leste
- - value: TG
- label:
- en_US: Togo
- zh_Hans: 多哥
- pt_BR: Togo
- - value: TK
- label:
- en_US: Tokelau
- zh_Hans: 托克劳
- pt_BR: Toquelau
- - value: TO
- label:
- en_US: Tonga
- zh_Hans: 汤加
- pt_BR: Tonga
- - value: TT
- label:
- en_US: Trinidad and Tobago
- zh_Hans: 特立尼达和多巴哥
- pt_BR: Trindade e Tobago
- - value: TN
- label:
- en_US: Tunisia
- zh_Hans: 突尼斯
- pt_BR: Tunísia
- - value: TR
- label:
- en_US: Turkey
- zh_Hans: 土耳其
- pt_BR: Turquia
- - value: TM
- label:
- en_US: Turkmenistan
- zh_Hans: 土库曼斯坦
- pt_BR: Turcomenistão
- - value: TC
- label:
- en_US: Turks and Caicos Islands
- zh_Hans: 特克斯和凯科斯群岛
- pt_BR: Ilhas Turks e Caicos
- - value: TV
- label:
- en_US: Tuvalu
- zh_Hans: 图瓦卢
- pt_BR: Tuvalu
- - value: UG
- label:
- en_US: Uganda
- zh_Hans: 乌干达
- pt_BR: Uganda
- - value: UA
- label:
- en_US: Ukraine
- zh_Hans: 乌克兰
- pt_BR: Ucrânia
- - value: AE
- label:
- en_US: United Arab Emirates
- zh_Hans: 阿联酋
- pt_BR: Emirados Árabes Unidos
- - value: UK
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: Reino Unido
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: Reino Unido
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: Estados Unidos
- - value: UM
- label:
- en_US: United States Minor Outlying Islands
- zh_Hans: 美国本土外小岛屿
- pt_BR: Ilhas Menores Distantes dos Estados Unidos
- - value: UY
- label:
- en_US: Uruguay
- zh_Hans: 乌拉圭
- pt_BR: Uruguai
- - value: UZ
- label:
- en_US: Uzbekistan
- zh_Hans: 乌兹别克斯坦
- pt_BR: Uzbequistão
- - value: VU
- label:
- en_US: Vanuatu
- zh_Hans: 瓦努阿图
- pt_BR: Vanuatu
- - value: VE
- label:
- en_US: Venezuela
- zh_Hans: 委内瑞拉
- pt_BR: Venezuela
- - value: VN
- label:
- en_US: Viet Nam
- zh_Hans: 越南
- pt_BR: Vietnã
- - value: VG
- label:
- en_US: Virgin Islands, British
- zh_Hans: 英属维尔京群岛
- pt_BR: Ilhas Virgens Britânicas
- - value: VI
- label:
- en_US: Virgin Islands, U.S.
- zh_Hans: 美属维尔京群岛
- pt_BR: Ilhas Virgens dos EUA
- - value: WF
- label:
- en_US: Wallis and Futuna
- zh_Hans: 瓦利斯和富图纳群岛
- pt_BR: Wallis e Futuna
- - value: EH
- label:
- en_US: Western Sahara
- zh_Hans: 西撒哈拉
- pt_BR: Saara Ocidental
- - value: YE
- label:
- en_US: Yemen
- zh_Hans: 也门
- pt_BR: Iémen
- - value: ZM
- label:
- en_US: Zambia
- zh_Hans: 赞比亚
- pt_BR: Zâmbia
- - value: ZW
- label:
- en_US: Zimbabwe
- zh_Hans: 津巴布韦
- pt_BR: Zimbábue
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: af
- label:
- en_US: Afrikaans
- zh_Hans: 南非语
- - value: ak
- label:
- en_US: Akan
- zh_Hans: 阿坎语
- - value: sq
- label:
- en_US: Albanian
- zh_Hans: 阿尔巴尼亚语
- - value: ws
- label:
- en_US: Samoa
- zh_Hans: 萨摩亚语
- - value: am
- label:
- en_US: Amharic
- zh_Hans: 阿姆哈拉语
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: hy
- label:
- en_US: Armenian
- zh_Hans: 亚美尼亚语
- - value: az
- label:
- en_US: Azerbaijani
- zh_Hans: 阿塞拜疆语
- - value: eu
- label:
- en_US: Basque
- zh_Hans: 巴斯克语
- - value: be
- label:
- en_US: Belarusian
- zh_Hans: 白俄罗斯语
- - value: bem
- label:
- en_US: Bemba
- zh_Hans: 班巴语
- - value: bn
- label:
- en_US: Bengali
- zh_Hans: 孟加拉语
- - value: bh
- label:
- en_US: Bihari
- zh_Hans: 比哈尔语
- - value: xx-bork
- label:
- en_US: Bork, bork, bork!
- zh_Hans: 博克语
- - value: bs
- label:
- en_US: Bosnian
- zh_Hans: 波斯尼亚语
- - value: br
- label:
- en_US: Breton
- zh_Hans: 布列塔尼语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: bt
- label:
- en_US: Bhutanese
- zh_Hans: 不丹语
- - value: km
- label:
- en_US: Cambodian
- zh_Hans: 高棉语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: chr
- label:
- en_US: Cherokee
- zh_Hans: 切罗基语
- - value: ny
- label:
- en_US: Chichewa
- zh_Hans: 齐切瓦语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: co
- label:
- en_US: Corsican
- zh_Hans: 科西嘉语
- - value: hr
- label:
- en_US: Croatian
- zh_Hans: 克罗地亚语
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: xx-elmer
- label:
- en_US: Elmer Fudd
- zh_Hans: 艾尔默福德语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: eo
- label:
- en_US: Esperanto
- zh_Hans: 世界语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: ee
- label:
- en_US: Ewe
- zh_Hans: 埃维语
- - value: fo
- label:
- en_US: Faroese
- zh_Hans: 法罗语
- - value: tl
- label:
- en_US: Filipino
- zh_Hans: 菲律宾语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: fy
- label:
- en_US: Frisian
- zh_Hans: 弗里西亚语
- - value: gaa
- label:
- en_US: Ga
- zh_Hans: 加语
- - value: gl
- label:
- en_US: Galician
- zh_Hans: 加利西亚语
- - value: ka
- label:
- en_US: Georgian
- zh_Hans: 格鲁吉亚语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: kl
- label:
- en_US: Greenlandic
- zh_Hans: 格陵兰语
- - value: gn
- label:
- en_US: Guarani
- zh_Hans: 瓜拉尼语
- - value: gu
- label:
- en_US: Gujarati
- zh_Hans: 古吉拉特语
- - value: xx-hacker
- label:
- en_US: Hacker
- zh_Hans: 黑客语
- - value: ht
- label:
- en_US: Haitian Creole
- zh_Hans: 海地克里奥尔语
- - value: ha
- label:
- en_US: Hausa
- zh_Hans: 豪萨语
- - value: haw
- label:
- en_US: Hawaiian
- zh_Hans: 夏威夷语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: is
- label:
- en_US: Icelandic
- zh_Hans: 冰岛语
- - value: ig
- label:
- en_US: Igbo
- zh_Hans: 伊博语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: ia
- label:
- en_US: Interlingua
- zh_Hans: 国际语
- - value: ga
- label:
- en_US: Irish
- 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: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: kk
- label:
- en_US: Kazakh
- zh_Hans: 哈萨克语
- - value: rw
- label:
- en_US: Kinyarwanda
- zh_Hans: 基尼亚卢旺达语
- - value: rn
- label:
- en_US: Kirundi
- zh_Hans: 基隆迪语
- - value: xx-klingon
- label:
- en_US: Klingon
- zh_Hans: 克林贡语
- - value: kg
- label:
- en_US: Kongo
- zh_Hans: 刚果语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: kri
- label:
- en_US: Krio (Sierra Leone)
- zh_Hans: 塞拉利昂克里奥尔语
- - value: ku
- label:
- en_US: Kurdish
- zh_Hans: 库尔德语
- - value: ckb
- label:
- en_US: Kurdish (Soranî)
- zh_Hans: 库尔德语(索拉尼)
- - value: ky
- label:
- en_US: Kyrgyz
- zh_Hans: 吉尔吉斯语
- - value: lo
- label:
- en_US: Laothian
- zh_Hans: 老挝语
- - value: la
- label:
- en_US: Latin
- zh_Hans: 拉丁语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: ln
- label:
- en_US: Lingala
- zh_Hans: 林加拉语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: loz
- label:
- en_US: Lozi
- zh_Hans: 洛齐语
- - value: lg
- label:
- en_US: Luganda
- zh_Hans: 卢干达语
- - value: ach
- label:
- en_US: Luo
- zh_Hans: 卢奥语
- - value: mk
- label:
- en_US: Macedonian
- zh_Hans: 马其顿语
- - value: mg
- label:
- en_US: Malagasy
- zh_Hans: 马尔加什语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mt
- label:
- en_US: Maltese
- zh_Hans: 马耳他语
- - value: mv
- label:
- en_US: Maldives
- zh_Hans: 马尔代夫语
- - value: mi
- label:
- en_US: Maori
- zh_Hans: 毛利语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: mfe
- label:
- en_US: Mauritian Creole
- zh_Hans: 毛里求斯克里奥尔语
- - value: mo
- label:
- en_US: Moldavian
- zh_Hans: 摩尔达维亚语
- - value: mn
- label:
- en_US: Mongolian
- zh_Hans: 蒙古语
- - value: sr-me
- label:
- en_US: Montenegrin
- zh_Hans: 黑山语
- - value: ne
- label:
- en_US: Nepali
- zh_Hans: 尼泊尔语
- - value: pcm
- label:
- en_US: Nigerian Pidgin
- zh_Hans: 尼日利亚皮钦语
- - value: nso
- label:
- en_US: Northern Sotho
- zh_Hans: 北索托语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: nn
- label:
- en_US: Norwegian (Nynorsk)
- zh_Hans: 挪威语(尼诺斯克语)
- - value: oc
- label:
- en_US: Occitan
- zh_Hans: 奥克语
- - value: or
- label:
- en_US: Oriya
- zh_Hans: 奥里亚语
- - value: om
- label:
- en_US: Oromo
- zh_Hans: 奥罗莫语
- - value: ps
- label:
- en_US: Pashto
- zh_Hans: 普什图语
- - value: fa
- label:
- en_US: Persian
- zh_Hans: 波斯语
- - value: xx-pirate
- label:
- en_US: Pirate
- zh_Hans: 海盗语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: qu
- label:
- en_US: Quechua
- zh_Hans: 克丘亚语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: rm
- label:
- en_US: Romansh
- zh_Hans: 罗曼什语
- - value: nyn
- label:
- en_US: Runyakitara
- zh_Hans: 卢尼亚基塔拉语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: gd
- label:
- en_US: Scots Gaelic
- zh_Hans: 苏格兰盖尔语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sh
- label:
- en_US: Serbo-Croatian
- zh_Hans: 塞尔维亚-克罗地亚语
- - value: st
- label:
- en_US: Sesotho
- zh_Hans: 塞索托语
- - value: tn
- label:
- en_US: Setswana
- zh_Hans: 塞茨瓦纳语
- - value: crs
- label:
- en_US: Seychellois Creole
- zh_Hans: 塞舌尔克里奥尔语
- - value: sn
- label:
- en_US: Shona
- zh_Hans: 绍纳语
- - value: sd
- label:
- en_US: Sindhi
- zh_Hans: 信德语
- - value: si
- label:
- en_US: Sinhalese
- zh_Hans: 僧伽罗语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: so
- label:
- en_US: Somali
- zh_Hans: 索马里语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: es-419
- label:
- en_US: Spanish (Latin American)
- zh_Hans: 西班牙语(拉丁美洲)
- - value: su
- label:
- en_US: Sundanese
- zh_Hans: 巽他语
- - value: sw
- label:
- en_US: Swahili
- zh_Hans: 斯瓦希里语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: tg
- label:
- en_US: Tajik
- zh_Hans: 塔吉克语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: tt
- label:
- en_US: Tatar
- zh_Hans: 鞑靼语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: ti
- label:
- en_US: Tigrinya
- zh_Hans: 提格利尼亚语
- - value: to
- label:
- en_US: Tonga
- zh_Hans: 汤加语
- - value: lua
- label:
- en_US: Tshiluba
- zh_Hans: 卢巴语
- - value: tum
- label:
- en_US: Tumbuka
- zh_Hans: 图布卡语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: tk
- label:
- en_US: Turkmen
- zh_Hans: 土库曼语
- - value: tw
- label:
- en_US: Twi
- zh_Hans: 契维语
- - value: ug
- label:
- en_US: Uighur
- 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: vu
- label:
- en_US: Vanuatu
- zh_Hans: 瓦努阿图语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- - value: cy
- label:
- en_US: Welsh
- zh_Hans: 威尔士语
- - value: wo
- label:
- en_US: Wolof
- zh_Hans: 沃洛夫语
- - value: xh
- label:
- en_US: Xhosa
- zh_Hans: 科萨语
- - value: yi
- label:
- en_US: Yiddish
- zh_Hans: 意第绪语
- - value: yo
- label:
- en_US: Yoruba
- zh_Hans: 约鲁巴语
- - value: zu
- label:
- en_US: Zulu
- zh_Hans: 祖鲁语
- - name: google_domain
- type: string
- required: false
- label:
- en_US: google_domain
- zh_Hans: google_domain
- human_description:
- en_US: Defines the Google domain of the search. Default is "google.com".
- zh_Hans: 定义搜索的 Google 域。默认为“google.com”。
- llm_description: Defines Google domain in which you want to search.
- form: llm
- - name: num
- type: number
- required: false
- label:
- en_US: num
- zh_Hans: num
- human_description:
- en_US: Specifies the number of results to display per page. Default is 10. Max number - 100, min - 1.
- zh_Hans: 指定每页显示的结果数。默认值为 10。最大数量 - 100,最小数量 - 1。
- pt_BR: Specifies the number of results to display per page. Default is 10. Max number - 100, min - 1.
- llm_description: Specifies the num of results to display per page.
- form: llm
diff --git a/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.py b/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.py
deleted file mode 100644
index b14821f8312dd0..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SEARCH_API_URL = "https://www.searchapi.io/api/v1/search"
-
-
-class SearchAPI:
- """
- SearchAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SearchAPI tool provider."""
- self.searchapi_api_key = api_key
-
- def run(self, video_id: str, language: str, **kwargs: Any) -> str:
- """Run video_id through SearchAPI and parse result."""
- return self._process_response(self.results(video_id, language, **kwargs))
-
- def results(self, video_id: str, language: str, **kwargs: Any) -> dict:
- """Run video_id through SearchAPI and return the raw result."""
- params = self.get_params(video_id, language, **kwargs)
- response = requests.get(
- url=SEARCH_API_URL,
- params=params,
- headers={"Authorization": f"Bearer {self.searchapi_api_key}"},
- )
- response.raise_for_status()
- return response.json()
-
- def get_params(self, video_id: str, language: str, **kwargs: Any) -> dict[str, str]:
- """Get parameters for SearchAPI."""
- return {
- "engine": "youtube_transcripts",
- "video_id": video_id,
- "lang": language or "en",
- **{key: value for key, value in kwargs.items() if value not in {None, ""}},
- }
-
- @staticmethod
- def _process_response(res: dict) -> str:
- """Process response from SearchAPI."""
- if "error" in res:
- return res["error"]
-
- toret = ""
- if "transcripts" in res and "text" in res["transcripts"][0]:
- for item in res["transcripts"]:
- toret += item["text"] + " "
- if toret == "":
- toret = "No good search result found"
-
- return toret
-
-
-class YoutubeTranscriptsTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SearchApi tool.
- """
- video_id = tool_parameters["video_id"]
- language = tool_parameters.get("language", "en")
-
- api_key = self.runtime.credentials["searchapi_api_key"]
- result = SearchAPI(api_key).run(video_id, language=language)
-
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.yaml b/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.yaml
deleted file mode 100644
index 8bdcd6bb936d96..00000000000000
--- a/api/core/tools/provider/builtin/searchapi/tools/youtube_transcripts.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-identity:
- name: youtube_transcripts_api
- author: SearchApi
- label:
- en_US: YouTube Transcripts API
- zh_Hans: YouTube 脚本 API
-description:
- human:
- en_US: A tool to retrieve transcripts from the specific YouTube video.
- zh_Hans: 一种从特定 YouTube 视频检索文字记录的工具。
- llm: A tool to retrieve transcripts from the specific YouTube video.
-parameters:
- - name: video_id
- type: string
- required: true
- label:
- en_US: video_id
- zh_Hans: 视频ID
- human_description:
- en_US: Used to define the video you want to search. You can find the video id's in YouTube page that appears in URL. For example - https://www.youtube.com/watch?v=video_id.
- zh_Hans: 用于定义要搜索的视频。您可以在 URL 中显示的 YouTube 页面中找到视频 ID。例如 - https://www.youtube.com/watch?v=video_id。
- llm_description: Used to define the video you want to search.
- form: llm
- - name: language
- type: string
- required: false
- label:
- en_US: language
- zh_Hans: 语言
- human_description:
- en_US: Used to set the language for transcripts. The default value is "en". You can find all supported languages in SearchApi documentation.
- zh_Hans: 用于设置成绩单的语言。默认值为“en”。您可以在 SearchApi 文档中找到所有支持的语言。
- llm_description: Used to set the language for transcripts.
- form: llm
diff --git a/api/core/tools/provider/builtin/searxng/_assets/icon.svg b/api/core/tools/provider/builtin/searxng/_assets/icon.svg
deleted file mode 100644
index b94fe3728adbff..00000000000000
--- a/api/core/tools/provider/builtin/searxng/_assets/icon.svg
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/searxng/docker/settings.yml b/api/core/tools/provider/builtin/searxng/docker/settings.yml
deleted file mode 100644
index 18e18688002cbc..00000000000000
--- a/api/core/tools/provider/builtin/searxng/docker/settings.yml
+++ /dev/null
@@ -1,2501 +0,0 @@
-general:
- # Debug mode, only for development. Is overwritten by ${SEARXNG_DEBUG}
- debug: false
- # displayed name
- instance_name: "searxng"
- # For example: https://example.com/privacy
- privacypolicy_url: false
- # use true to use your own donation page written in searx/info/en/donate.md
- # use false to disable the donation link
- donation_url: false
- # mailto:contact@example.com
- contact_url: false
- # record stats
- enable_metrics: true
-
-brand:
- new_issue_url: https://github.com/searxng/searxng/issues/new
- docs_url: https://docs.searxng.org/
- public_instances: https://searx.space
- wiki_url: https://github.com/searxng/searxng/wiki
- issue_url: https://github.com/searxng/searxng/issues
- # custom:
- # maintainer: "Jon Doe"
- # # Custom entries in the footer: [title]: [link]
- # links:
- # Uptime: https://uptime.searxng.org/history/darmarit-org
- # About: "https://searxng.org"
-
-search:
- # Filter results. 0: None, 1: Moderate, 2: Strict
- safe_search: 0
- # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "yandex", "mwmbl",
- # "seznam", "startpage", "stract", "swisscows", "qwant", "wikipedia" - leave blank to turn it off
- # by default.
- autocomplete: ""
- # minimun characters to type before autocompleter starts
- autocomplete_min: 4
- # Default search language - leave blank to detect from browser information or
- # use codes from 'languages.py'
- default_lang: "auto"
- # max_page: 0 # if engine supports paging, 0 means unlimited numbers of pages
- # Available languages
- # languages:
- # - all
- # - en
- # - en-US
- # - de
- # - it-IT
- # - fr
- # - fr-BE
- # ban time in seconds after engine errors
- ban_time_on_fail: 5
- # max ban time in seconds after engine errors
- max_ban_time_on_fail: 120
- suspended_times:
- # Engine suspension time after error (in seconds; set to 0 to disable)
- # For error "Access denied" and "HTTP error [402, 403]"
- SearxEngineAccessDenied: 86400
- # For error "CAPTCHA"
- SearxEngineCaptcha: 86400
- # For error "Too many request" and "HTTP error 429"
- SearxEngineTooManyRequests: 3600
- # Cloudflare CAPTCHA
- cf_SearxEngineCaptcha: 1296000
- cf_SearxEngineAccessDenied: 86400
- # ReCAPTCHA
- recaptcha_SearxEngineCaptcha: 604800
-
- # remove format to deny access, use lower case.
- # formats: [html, csv, json, rss]
- formats:
- - html
- - json
-
-server:
- # Is overwritten by ${SEARXNG_PORT} and ${SEARXNG_BIND_ADDRESS}
- port: 8888
- bind_address: "127.0.0.1"
- # public URL of the instance, to ensure correct inbound links. Is overwritten
- # by ${SEARXNG_URL}.
- base_url: http://0.0.0.0:8081/ # "http://example.com/location"
- # rate limit the number of request on the instance, block some bots.
- # Is overwritten by ${SEARXNG_LIMITER}
- limiter: false
- # enable features designed only for public instances.
- # Is overwritten by ${SEARXNG_PUBLIC_INSTANCE}
- public_instance: false
-
- # If your instance owns a /etc/searxng/settings.yml file, then set the following
- # values there.
-
- secret_key: "772ba36386fb56d0f8fe818941552dabbe69220d4c0eb4a385a5729cdbc20c2d" # Is overwritten by ${SEARXNG_SECRET}
- # Proxy image results through SearXNG. Is overwritten by ${SEARXNG_IMAGE_PROXY}
- image_proxy: false
- # 1.0 and 1.1 are supported
- http_protocol_version: "1.0"
- # POST queries are more secure as they don't show up in history but may cause
- # problems when using Firefox containers
- method: "POST"
- default_http_headers:
- X-Content-Type-Options: nosniff
- X-Download-Options: noopen
- X-Robots-Tag: noindex, nofollow
- Referrer-Policy: no-referrer
-
-redis:
- # URL to connect redis database. Is overwritten by ${SEARXNG_REDIS_URL}.
- # https://docs.searxng.org/admin/settings/settings_redis.html#settings-redis
- url: false
-
-ui:
- # Custom static path - leave it blank if you didn't change
- static_path: ""
- # Is overwritten by ${SEARXNG_STATIC_USE_HASH}.
- static_use_hash: false
- # Custom templates path - leave it blank if you didn't change
- templates_path: ""
- # query_in_title: When true, the result page's titles contains the query
- # it decreases the privacy, since the browser can records the page titles.
- query_in_title: false
- # infinite_scroll: When true, automatically loads the next page when scrolling to bottom of the current page.
- infinite_scroll: false
- # ui theme
- default_theme: simple
- # center the results ?
- center_alignment: false
- # URL prefix of the internet archive, don't forget trailing slash (if needed).
- # cache_url: "https://webcache.googleusercontent.com/search?q=cache:"
- # Default interface locale - leave blank to detect from browser information or
- # use codes from the 'locales' config section
- default_locale: ""
- # Open result links in a new tab by default
- # results_on_new_tab: false
- theme_args:
- # style of simple theme: auto, light, dark
- simple_style: auto
- # Perform search immediately if a category selected.
- # Disable to select multiple categories at once and start the search manually.
- search_on_category_select: true
- # Hotkeys: default or vim
- hotkeys: default
-
-# Lock arbitrary settings on the preferences page. To find the ID of the user
-# setting you want to lock, check the ID of the form on the page "preferences".
-#
-# preferences:
-# lock:
-# - language
-# - autocomplete
-# - method
-# - query_in_title
-
-# searx supports result proxification using an external service:
-# https://github.com/asciimoo/morty uncomment below section if you have running
-# morty proxy the key is base64 encoded (keep the !!binary notation)
-# Note: since commit af77ec3, morty accepts a base64 encoded key.
-#
-# result_proxy:
-# url: http://127.0.0.1:3000/
-# # the key is a base64 encoded string, the YAML !!binary prefix is optional
-# key: !!binary "your_morty_proxy_key"
-# # [true|false] enable the "proxy" button next to each result
-# proxify_results: true
-
-# communication with search engines
-#
-outgoing:
- # default timeout in seconds, can be override by engine
- request_timeout: 3.0
- # the maximum timeout in seconds
- # max_request_timeout: 10.0
- # suffix of searx_useragent, could contain information like an email address
- # to the administrator
- useragent_suffix: ""
- # The maximum number of concurrent connections that may be established.
- pool_connections: 100
- # Allow the connection pool to maintain keep-alive connections below this
- # point.
- pool_maxsize: 20
- # See https://www.python-httpx.org/http2/
- enable_http2: true
- # uncomment below section if you want to use a custom server certificate
- # see https://www.python-httpx.org/advanced/#changing-the-verification-defaults
- # and https://www.python-httpx.org/compatibility/#ssl-configuration
- # verify: ~/.mitmproxy/mitmproxy-ca-cert.cer
- #
- # uncomment below section if you want to use a proxyq see: SOCKS proxies
- # https://2.python-requests.org/en/latest/user/advanced/#proxies
- # are also supported: see
- # https://2.python-requests.org/en/latest/user/advanced/#socks
- #
- # proxies:
- # all://:
- # - http://host.docker.internal:1080
- #
- # using_tor_proxy: true
- #
- # Extra seconds to add in order to account for the time taken by the proxy
- #
- # extra_proxy_timeout: 10
- #
- # uncomment below section only if you have more than one network interface
- # which can be the source of outgoing search requests
- #
- # source_ips:
- # - 1.1.1.1
- # - 1.1.1.2
- # - fe80::/126
-
-# External plugin configuration, for more details see
-# https://docs.searxng.org/dev/plugins.html
-#
-# plugins:
-# - plugin1
-# - plugin2
-# - ...
-
-# Comment or un-comment plugin to activate / deactivate by default.
-#
-# enabled_plugins:
-# # these plugins are enabled if nothing is configured ..
-# - 'Hash plugin'
-# - 'Self Information'
-# - 'Tracker URL remover'
-# - 'Ahmia blacklist' # activation depends on outgoing.using_tor_proxy
-# # these plugins are disabled if nothing is configured ..
-# - 'Hostnames plugin' # see 'hostnames' configuration below
-# - 'Basic Calculator'
-# - 'Open Access DOI rewrite'
-# - 'Tor check plugin'
-# # Read the docs before activate: auto-detection of the language could be
-# # detrimental to users expectations / users can activate the plugin in the
-# # preferences if they want.
-# - 'Autodetect search language'
-
-# Configuration of the "Hostnames plugin":
-#
-# hostnames:
-# replace:
-# '(.*\.)?youtube\.com$': 'invidious.example.com'
-# '(.*\.)?youtu\.be$': 'invidious.example.com'
-# '(.*\.)?reddit\.com$': 'teddit.example.com'
-# '(.*\.)?redd\.it$': 'teddit.example.com'
-# '(www\.)?twitter\.com$': 'nitter.example.com'
-# remove:
-# - '(.*\.)?facebook.com$'
-# low_priority:
-# - '(.*\.)?google(\..*)?$'
-# high_priority:
-# - '(.*\.)?wikipedia.org$'
-#
-# Alternatively you can use external files for configuring the "Hostnames plugin":
-#
-# hostnames:
-# replace: 'rewrite-hosts.yml'
-#
-# Content of 'rewrite-hosts.yml' (place the file in the same directory as 'settings.yml'):
-# '(.*\.)?youtube\.com$': 'invidious.example.com'
-# '(.*\.)?youtu\.be$': 'invidious.example.com'
-#
-
-checker:
- # disable checker when in debug mode
- off_when_debug: true
-
- # use "scheduling: false" to disable scheduling
- # scheduling: interval or int
-
- # to activate the scheduler:
- # * uncomment "scheduling" section
- # * add "cache2 = name=searxngcache,items=2000,blocks=2000,blocksize=4096,bitmap=1"
- # to your uwsgi.ini
-
- # scheduling:
- # start_after: [300, 1800] # delay to start the first run of the checker
- # every: [86400, 90000] # how often the checker runs
-
- # additional tests: only for the YAML anchors (see the engines section)
- #
- additional_tests:
- rosebud: &test_rosebud
- matrix:
- query: rosebud
- lang: en
- result_container:
- - not_empty
- - ['one_title_contains', 'citizen kane']
- test:
- - unique_results
-
- android: &test_android
- matrix:
- query: ['android']
- lang: ['en', 'de', 'fr', 'zh-CN']
- result_container:
- - not_empty
- - ['one_title_contains', 'google']
- test:
- - unique_results
-
- # tests: only for the YAML anchors (see the engines section)
- tests:
- infobox: &tests_infobox
- infobox:
- matrix:
- query: ["linux", "new york", "bbc"]
- result_container:
- - has_infobox
-
-categories_as_tabs:
- general:
- images:
- videos:
- news:
- map:
- music:
- it:
- science:
- files:
- social media:
-
-engines:
- - name: 9gag
- engine: 9gag
- shortcut: 9g
- disabled: true
-
- - name: alpine linux packages
- engine: alpinelinux
- disabled: true
- shortcut: alp
-
- - name: annas archive
- engine: annas_archive
- disabled: true
- shortcut: aa
-
- # - name: annas articles
- # engine: annas_archive
- # shortcut: aaa
- # # https://docs.searxng.org/dev/engines/online/annas_archive.html
- # aa_content: 'magazine' # book_fiction, book_unknown, book_nonfiction, book_comic
- # aa_ext: 'pdf' # pdf, epub, ..
- # aa_sort: oldest' # newest, oldest, largest, smallest
-
- - name: apk mirror
- engine: apkmirror
- timeout: 4.0
- shortcut: apkm
- disabled: true
-
- - name: apple app store
- engine: apple_app_store
- shortcut: aps
- disabled: true
-
- # Requires Tor
- - name: ahmia
- engine: ahmia
- categories: onions
- enable_http: true
- shortcut: ah
-
- - name: anaconda
- engine: xpath
- paging: true
- first_page_num: 0
- search_url: https://anaconda.org/search?q={query}&page={pageno}
- results_xpath: //tbody/tr
- url_xpath: ./td/h5/a[last()]/@href
- title_xpath: ./td/h5
- content_xpath: ./td[h5]/text()
- categories: it
- timeout: 6.0
- shortcut: conda
- disabled: true
-
- - name: arch linux wiki
- engine: archlinux
- shortcut: al
-
- - name: artic
- engine: artic
- shortcut: arc
- timeout: 4.0
-
- - name: arxiv
- engine: arxiv
- shortcut: arx
- timeout: 4.0
-
- - name: ask
- engine: ask
- shortcut: ask
- disabled: true
-
- # tmp suspended: dh key too small
- # - name: base
- # engine: base
- # shortcut: bs
-
- - name: bandcamp
- engine: bandcamp
- shortcut: bc
- categories: music
-
- - name: wikipedia
- engine: wikipedia
- shortcut: wp
- # add "list" to the array to get results in the results list
- display_type: ["infobox"]
- base_url: 'https://{language}.wikipedia.org/'
- categories: [general]
-
- - name: bilibili
- engine: bilibili
- shortcut: bil
- disabled: true
-
- - name: bing
- engine: bing
- shortcut: bi
- disabled: false
-
- - name: bing images
- engine: bing_images
- shortcut: bii
-
- - name: bing news
- engine: bing_news
- shortcut: bin
-
- - name: bing videos
- engine: bing_videos
- shortcut: biv
-
- - name: bitbucket
- engine: xpath
- paging: true
- search_url: https://bitbucket.org/repo/all/{pageno}?name={query}
- url_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]/@href
- title_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]
- content_xpath: //article[@class="repo-summary"]/p
- categories: [it, repos]
- timeout: 4.0
- disabled: true
- shortcut: bb
- about:
- website: https://bitbucket.org/
- wikidata_id: Q2493781
- official_api_documentation: https://developer.atlassian.com/bitbucket
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: bpb
- engine: bpb
- shortcut: bpb
- disabled: true
-
- - name: btdigg
- engine: btdigg
- shortcut: bt
- disabled: true
-
- - name: openverse
- engine: openverse
- categories: images
- shortcut: opv
-
- - name: media.ccc.de
- engine: ccc_media
- shortcut: c3tv
- # We don't set language: de here because media.ccc.de is not just
- # for a German audience. It contains many English videos and many
- # German videos have English subtitles.
- disabled: true
-
- - name: chefkoch
- engine: chefkoch
- shortcut: chef
- # to show premium or plus results too:
- # skip_premium: false
-
- # - name: core.ac.uk
- # engine: core
- # categories: science
- # shortcut: cor
- # # get your API key from: https://core.ac.uk/api-keys/register/
- # api_key: 'unset'
-
- - name: cppreference
- engine: cppreference
- shortcut: cpp
- paging: false
- disabled: true
-
- - name: crossref
- engine: crossref
- shortcut: cr
- timeout: 30
- disabled: true
-
- - name: crowdview
- engine: json_engine
- shortcut: cv
- categories: general
- paging: false
- search_url: https://crowdview-next-js.onrender.com/api/search-v3?query={query}
- results_query: results
- url_query: link
- title_query: title
- content_query: snippet
- disabled: true
- about:
- website: https://crowdview.ai/
-
- - name: yep
- engine: yep
- shortcut: yep
- categories: general
- search_type: web
- timeout: 5
- disabled: true
-
- - name: yep images
- engine: yep
- shortcut: yepi
- categories: images
- search_type: images
- disabled: true
-
- - name: yep news
- engine: yep
- shortcut: yepn
- categories: news
- search_type: news
- disabled: true
-
- - name: curlie
- engine: xpath
- shortcut: cl
- categories: general
- disabled: true
- paging: true
- lang_all: ''
- search_url: https://curlie.org/search?q={query}&lang={lang}&start={pageno}&stime=92452189
- page_size: 20
- results_xpath: //div[@id="site-list-content"]/div[@class="site-item"]
- url_xpath: ./div[@class="title-and-desc"]/a/@href
- title_xpath: ./div[@class="title-and-desc"]/a/div
- content_xpath: ./div[@class="title-and-desc"]/div[@class="site-descr"]
- about:
- website: https://curlie.org/
- wikidata_id: Q60715723
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: currency
- engine: currency_convert
- categories: general
- shortcut: cc
-
- - name: bahnhof
- engine: json_engine
- search_url: https://www.bahnhof.de/api/stations/search/{query}
- url_prefix: https://www.bahnhof.de/
- url_query: slug
- title_query: name
- content_query: state
- shortcut: bf
- disabled: true
- about:
- website: https://www.bahn.de
- wikidata_id: Q22811603
- use_official_api: false
- require_api_key: false
- results: JSON
- language: de
- tests:
- bahnhof:
- matrix:
- query: berlin
- lang: en
- result_container:
- - not_empty
- - ['one_title_contains', 'Berlin Hauptbahnhof']
- test:
- - unique_results
-
- - name: deezer
- engine: deezer
- shortcut: dz
- disabled: true
-
- - name: destatis
- engine: destatis
- shortcut: destat
- disabled: true
-
- - name: deviantart
- engine: deviantart
- shortcut: da
- timeout: 3.0
-
- - name: ddg definitions
- engine: duckduckgo_definitions
- shortcut: ddd
- weight: 2
- disabled: true
- tests: *tests_infobox
-
- # cloudflare protected
- # - name: digbt
- # engine: digbt
- # shortcut: dbt
- # timeout: 6.0
- # disabled: true
-
- - name: docker hub
- engine: docker_hub
- shortcut: dh
- categories: [it, packages]
-
- - name: encyclosearch
- engine: json_engine
- shortcut: es
- categories: general
- paging: true
- search_url: https://encyclosearch.org/encyclosphere/search?q={query}&page={pageno}&resultsPerPage=15
- results_query: Results
- url_query: SourceURL
- title_query: Title
- content_query: Description
- disabled: true
- about:
- website: https://encyclosearch.org
- official_api_documentation: https://encyclosearch.org/docs/#/rest-api
- use_official_api: true
- require_api_key: false
- results: JSON
-
- - name: erowid
- engine: xpath
- paging: true
- first_page_num: 0
- page_size: 30
- search_url: https://www.erowid.org/search.php?q={query}&s={pageno}
- url_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/@href
- title_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/text()
- content_xpath: //dl[@class="results-list"]/dd[@class="result-details"]
- categories: []
- shortcut: ew
- disabled: true
- about:
- website: https://www.erowid.org/
- wikidata_id: Q1430691
- official_api_documentation:
- use_official_api: false
- require_api_key: false
- results: HTML
-
- # - name: elasticsearch
- # shortcut: es
- # engine: elasticsearch
- # base_url: http://localhost:9200
- # username: elastic
- # password: changeme
- # index: my-index
- # # available options: match, simple_query_string, term, terms, custom
- # query_type: match
- # # if query_type is set to custom, provide your query here
- # #custom_query_json: {"query":{"match_all": {}}}
- # #show_metadata: false
- # disabled: true
-
- - name: wikidata
- engine: wikidata
- shortcut: wd
- timeout: 3.0
- weight: 2
- # add "list" to the array to get results in the results list
- display_type: ["infobox"]
- tests: *tests_infobox
- categories: [general]
-
- - name: duckduckgo
- engine: duckduckgo
- shortcut: ddg
-
- - name: duckduckgo images
- engine: duckduckgo_extra
- categories: [images, web]
- ddg_category: images
- shortcut: ddi
- disabled: true
-
- - name: duckduckgo videos
- engine: duckduckgo_extra
- categories: [videos, web]
- ddg_category: videos
- shortcut: ddv
- disabled: true
-
- - name: duckduckgo news
- engine: duckduckgo_extra
- categories: [news, web]
- ddg_category: news
- shortcut: ddn
- disabled: true
-
- - name: duckduckgo weather
- engine: duckduckgo_weather
- shortcut: ddw
- disabled: true
-
- - name: apple maps
- engine: apple_maps
- shortcut: apm
- disabled: true
- timeout: 5.0
-
- - name: emojipedia
- engine: emojipedia
- timeout: 4.0
- shortcut: em
- disabled: true
-
- - name: tineye
- engine: tineye
- shortcut: tin
- timeout: 9.0
- disabled: true
-
- - name: etymonline
- engine: xpath
- paging: true
- search_url: https://etymonline.com/search?page={pageno}&q={query}
- url_xpath: //a[contains(@class, "word__name--")]/@href
- title_xpath: //a[contains(@class, "word__name--")]
- content_xpath: //section[contains(@class, "word__defination")]
- first_page_num: 1
- shortcut: et
- categories: [dictionaries]
- about:
- website: https://www.etymonline.com/
- wikidata_id: Q1188617
- official_api_documentation:
- use_official_api: false
- require_api_key: false
- results: HTML
-
- # - name: ebay
- # engine: ebay
- # shortcut: eb
- # base_url: 'https://www.ebay.com'
- # disabled: true
- # timeout: 5
-
- - name: 1x
- engine: www1x
- shortcut: 1x
- timeout: 3.0
- disabled: true
-
- - name: fdroid
- engine: fdroid
- shortcut: fd
- disabled: true
-
- - name: findthatmeme
- engine: findthatmeme
- shortcut: ftm
- disabled: true
-
- - name: flickr
- categories: images
- shortcut: fl
- # You can use the engine using the official stable API, but you need an API
- # key, see: https://www.flickr.com/services/apps/create/
- # engine: flickr
- # api_key: 'apikey' # required!
- # Or you can use the html non-stable engine, activated by default
- engine: flickr_noapi
-
- - name: free software directory
- engine: mediawiki
- shortcut: fsd
- categories: [it, software wikis]
- base_url: https://directory.fsf.org/
- search_type: title
- timeout: 5.0
- disabled: true
- about:
- website: https://directory.fsf.org/
- wikidata_id: Q2470288
-
- # - name: freesound
- # engine: freesound
- # shortcut: fnd
- # disabled: true
- # timeout: 15.0
- # API key required, see: https://freesound.org/docs/api/overview.html
- # api_key: MyAPIkey
-
- - name: frinkiac
- engine: frinkiac
- shortcut: frk
- disabled: true
-
- - name: fyyd
- engine: fyyd
- shortcut: fy
- timeout: 8.0
- disabled: true
-
- - name: geizhals
- engine: geizhals
- shortcut: geiz
- disabled: true
-
- - name: genius
- engine: genius
- shortcut: gen
-
- - name: gentoo
- engine: mediawiki
- shortcut: ge
- categories: ["it", "software wikis"]
- base_url: "https://wiki.gentoo.org/"
- api_path: "api.php"
- search_type: text
- timeout: 10
-
- - name: gitlab
- engine: json_engine
- paging: true
- search_url: https://gitlab.com/api/v4/projects?search={query}&page={pageno}
- url_query: web_url
- title_query: name_with_namespace
- content_query: description
- page_size: 20
- categories: [it, repos]
- shortcut: gl
- timeout: 10.0
- disabled: true
- about:
- website: https://about.gitlab.com/
- wikidata_id: Q16639197
- official_api_documentation: https://docs.gitlab.com/ee/api/
- use_official_api: false
- require_api_key: false
- results: JSON
-
- - name: github
- engine: github
- shortcut: gh
-
- - name: codeberg
- # https://docs.searxng.org/dev/engines/online/gitea.html
- engine: gitea
- base_url: https://codeberg.org
- shortcut: cb
- disabled: true
-
- - name: gitea.com
- engine: gitea
- base_url: https://gitea.com
- shortcut: gitea
- disabled: true
-
- - name: goodreads
- engine: goodreads
- shortcut: good
- timeout: 4.0
- disabled: true
-
- - name: google
- engine: google
- shortcut: go
- # additional_tests:
- # android: *test_android
-
- - name: google images
- engine: google_images
- shortcut: goi
- # additional_tests:
- # android: *test_android
- # dali:
- # matrix:
- # query: ['Dali Christ']
- # lang: ['en', 'de', 'fr', 'zh-CN']
- # result_container:
- # - ['one_title_contains', 'Salvador']
-
- - name: google news
- engine: google_news
- shortcut: gon
- # additional_tests:
- # android: *test_android
-
- - name: google videos
- engine: google_videos
- shortcut: gov
- # additional_tests:
- # android: *test_android
-
- - name: google scholar
- engine: google_scholar
- shortcut: gos
-
- - name: google play apps
- engine: google_play
- categories: [files, apps]
- shortcut: gpa
- play_categ: apps
- disabled: true
-
- - name: google play movies
- engine: google_play
- categories: videos
- shortcut: gpm
- play_categ: movies
- disabled: true
-
- - name: material icons
- engine: material_icons
- categories: images
- shortcut: mi
- disabled: true
-
- - name: gpodder
- engine: json_engine
- shortcut: gpod
- timeout: 4.0
- paging: false
- search_url: https://gpodder.net/search.json?q={query}
- url_query: url
- title_query: title
- content_query: description
- page_size: 19
- categories: music
- disabled: true
- about:
- website: https://gpodder.net
- wikidata_id: Q3093354
- official_api_documentation: https://gpoddernet.readthedocs.io/en/latest/api/
- use_official_api: false
- requires_api_key: false
- results: JSON
-
- - name: habrahabr
- engine: xpath
- paging: true
- search_url: https://habr.com/en/search/page{pageno}/?q={query}
- results_xpath: //article[contains(@class, "tm-articles-list__item")]
- url_xpath: .//a[@class="tm-title__link"]/@href
- title_xpath: .//a[@class="tm-title__link"]
- content_xpath: .//div[contains(@class, "article-formatted-body")]
- categories: it
- timeout: 4.0
- disabled: true
- shortcut: habr
- about:
- website: https://habr.com/
- wikidata_id: Q4494434
- official_api_documentation: https://habr.com/en/docs/help/api/
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: hackernews
- engine: hackernews
- shortcut: hn
- disabled: true
-
- - name: hex
- engine: hex
- shortcut: hex
- disabled: true
- # Valid values: name inserted_at updated_at total_downloads recent_downloads
- sort_criteria: "recent_downloads"
- page_size: 10
-
- - name: crates.io
- engine: crates
- shortcut: crates
- disabled: true
- timeout: 6.0
-
- - name: hoogle
- engine: xpath
- search_url: https://hoogle.haskell.org/?hoogle={query}
- results_xpath: '//div[@class="result"]'
- title_xpath: './/div[@class="ans"]//a'
- url_xpath: './/div[@class="ans"]//a/@href'
- content_xpath: './/div[@class="from"]'
- page_size: 20
- categories: [it, packages]
- shortcut: ho
- about:
- website: https://hoogle.haskell.org/
- wikidata_id: Q34010
- official_api_documentation: https://hackage.haskell.org/api
- use_official_api: false
- require_api_key: false
- results: JSON
-
- - name: imdb
- engine: imdb
- shortcut: imdb
- timeout: 6.0
- disabled: true
-
- - name: imgur
- engine: imgur
- shortcut: img
- disabled: true
-
- - name: ina
- engine: ina
- shortcut: in
- timeout: 6.0
- disabled: true
-
- - name: invidious
- engine: invidious
- # Instanes will be selected randomly, see https://api.invidious.io/ for
- # instances that are stable (good uptime) and close to you.
- base_url:
- - https://invidious.io.lol
- - https://invidious.fdn.fr
- - https://yt.artemislena.eu
- - https://invidious.tiekoetter.com
- - https://invidious.flokinet.to
- - https://vid.puffyan.us
- - https://invidious.privacydev.net
- - https://inv.tux.pizza
- shortcut: iv
- timeout: 3.0
- disabled: true
-
- - name: jisho
- engine: jisho
- shortcut: js
- timeout: 3.0
- disabled: true
-
- - name: kickass
- engine: kickass
- base_url:
- - https://kickasstorrents.to
- - https://kickasstorrents.cr
- - https://kickasstorrent.cr
- - https://kickass.sx
- - https://kat.am
- shortcut: kc
- timeout: 4.0
- disabled: true
-
- - name: lemmy communities
- engine: lemmy
- lemmy_type: Communities
- shortcut: leco
-
- - name: lemmy users
- engine: lemmy
- network: lemmy communities
- lemmy_type: Users
- shortcut: leus
-
- - name: lemmy posts
- engine: lemmy
- network: lemmy communities
- lemmy_type: Posts
- shortcut: lepo
-
- - name: lemmy comments
- engine: lemmy
- network: lemmy communities
- lemmy_type: Comments
- shortcut: lecom
-
- - name: library genesis
- engine: xpath
- # search_url: https://libgen.is/search.php?req={query}
- search_url: https://libgen.rs/search.php?req={query}
- url_xpath: //a[contains(@href,"book/index.php?md5")]/@href
- title_xpath: //a[contains(@href,"book/")]/text()[1]
- content_xpath: //td/a[1][contains(@href,"=author")]/text()
- categories: files
- timeout: 7.0
- disabled: true
- shortcut: lg
- about:
- website: https://libgen.fun/
- wikidata_id: Q22017206
- official_api_documentation:
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: z-library
- engine: zlibrary
- shortcut: zlib
- categories: files
- timeout: 7.0
- disabled: true
-
- - name: library of congress
- engine: loc
- shortcut: loc
- categories: images
-
- - name: libretranslate
- engine: libretranslate
- # https://github.com/LibreTranslate/LibreTranslate?tab=readme-ov-file#mirrors
- base_url:
- - https://translate.terraprint.co
- - https://trans.zillyhuhn.com
- # api_key: abc123
- shortcut: lt
- disabled: true
-
- - name: lingva
- engine: lingva
- shortcut: lv
- # set lingva instance in url, by default it will use the official instance
- # url: https://lingva.thedaviddelta.com
-
- - name: lobste.rs
- engine: xpath
- search_url: https://lobste.rs/search?q={query}&what=stories&order=relevance
- results_xpath: //li[contains(@class, "story")]
- url_xpath: .//a[@class="u-url"]/@href
- title_xpath: .//a[@class="u-url"]
- content_xpath: .//a[@class="domain"]
- categories: it
- shortcut: lo
- timeout: 5.0
- disabled: true
- about:
- website: https://lobste.rs/
- wikidata_id: Q60762874
- official_api_documentation:
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: mastodon users
- engine: mastodon
- mastodon_type: accounts
- base_url: https://mastodon.social
- shortcut: mau
-
- - name: mastodon hashtags
- engine: mastodon
- mastodon_type: hashtags
- base_url: https://mastodon.social
- shortcut: mah
-
- # - name: matrixrooms
- # engine: mrs
- # # https://docs.searxng.org/dev/engines/online/mrs.html
- # # base_url: https://mrs-api-host
- # shortcut: mtrx
- # disabled: true
-
- - name: mdn
- shortcut: mdn
- engine: json_engine
- categories: [it]
- paging: true
- search_url: https://developer.mozilla.org/api/v1/search?q={query}&page={pageno}
- results_query: documents
- url_query: mdn_url
- url_prefix: https://developer.mozilla.org
- title_query: title
- content_query: summary
- about:
- website: https://developer.mozilla.org
- wikidata_id: Q3273508
- official_api_documentation: null
- use_official_api: false
- require_api_key: false
- results: JSON
-
- - name: metacpan
- engine: metacpan
- shortcut: cpan
- disabled: true
- number_of_results: 20
-
- # - name: meilisearch
- # engine: meilisearch
- # shortcut: mes
- # enable_http: true
- # base_url: http://localhost:7700
- # index: my-index
-
- - name: mixcloud
- engine: mixcloud
- shortcut: mc
-
- # MongoDB engine
- # Required dependency: pymongo
- # - name: mymongo
- # engine: mongodb
- # shortcut: md
- # exact_match_only: false
- # host: '127.0.0.1'
- # port: 27017
- # enable_http: true
- # results_per_page: 20
- # database: 'business'
- # collection: 'reviews' # name of the db collection
- # key: 'name' # key in the collection to search for
-
- - name: mozhi
- engine: mozhi
- base_url:
- - https://mozhi.aryak.me
- - https://translate.bus-hit.me
- - https://nyc1.mz.ggtyler.dev
- # mozhi_engine: google - see https://mozhi.aryak.me for supported engines
- timeout: 4.0
- shortcut: mz
- disabled: true
-
- - name: mwmbl
- engine: mwmbl
- # api_url: https://api.mwmbl.org
- shortcut: mwm
- disabled: true
-
- - name: npm
- engine: npm
- shortcut: npm
- timeout: 5.0
- disabled: true
-
- - name: nyaa
- engine: nyaa
- shortcut: nt
- disabled: true
-
- - name: mankier
- engine: json_engine
- search_url: https://www.mankier.com/api/v2/mans/?q={query}
- results_query: results
- url_query: url
- title_query: name
- content_query: description
- categories: it
- shortcut: man
- about:
- website: https://www.mankier.com/
- official_api_documentation: https://www.mankier.com/api
- use_official_api: true
- require_api_key: false
- results: JSON
-
- # read https://docs.searxng.org/dev/engines/online/mullvad_leta.html
- # - name: mullvadleta
- # engine: mullvad_leta
- # leta_engine: google # choose one of the following: google, brave
- # use_cache: true # Only 100 non-cache searches per day, suggested only for private instances
- # search_url: https://leta.mullvad.net
- # categories: [general, web]
- # shortcut: ml
-
- - name: odysee
- engine: odysee
- shortcut: od
- disabled: true
-
- - name: openairedatasets
- engine: json_engine
- paging: true
- search_url: https://api.openaire.eu/search/datasets?format=json&page={pageno}&size=10&title={query}
- results_query: response/results/result
- url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$
- title_query: metadata/oaf:entity/oaf:result/title/$
- content_query: metadata/oaf:entity/oaf:result/description/$
- content_html_to_text: true
- categories: "science"
- shortcut: oad
- timeout: 5.0
- about:
- website: https://www.openaire.eu/
- wikidata_id: Q25106053
- official_api_documentation: https://api.openaire.eu/
- use_official_api: false
- require_api_key: false
- results: JSON
-
- - name: openairepublications
- engine: json_engine
- paging: true
- search_url: https://api.openaire.eu/search/publications?format=json&page={pageno}&size=10&title={query}
- results_query: response/results/result
- url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$
- title_query: metadata/oaf:entity/oaf:result/title/$
- content_query: metadata/oaf:entity/oaf:result/description/$
- content_html_to_text: true
- categories: science
- shortcut: oap
- timeout: 5.0
- about:
- website: https://www.openaire.eu/
- wikidata_id: Q25106053
- official_api_documentation: https://api.openaire.eu/
- use_official_api: false
- require_api_key: false
- results: JSON
-
- - name: openmeteo
- engine: open_meteo
- shortcut: om
- disabled: true
-
- # - name: opensemanticsearch
- # engine: opensemantic
- # shortcut: oss
- # base_url: 'http://localhost:8983/solr/opensemanticsearch/'
-
- - name: openstreetmap
- engine: openstreetmap
- shortcut: osm
-
- - name: openrepos
- engine: xpath
- paging: true
- search_url: https://openrepos.net/search/node/{query}?page={pageno}
- url_xpath: //li[@class="search-result"]//h3[@class="title"]/a/@href
- title_xpath: //li[@class="search-result"]//h3[@class="title"]/a
- content_xpath: //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"]
- categories: files
- timeout: 4.0
- disabled: true
- shortcut: or
- about:
- website: https://openrepos.net/
- wikidata_id:
- official_api_documentation:
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: packagist
- engine: json_engine
- paging: true
- search_url: https://packagist.org/search.json?q={query}&page={pageno}
- results_query: results
- url_query: url
- title_query: name
- content_query: description
- categories: [it, packages]
- disabled: true
- timeout: 5.0
- shortcut: pack
- about:
- website: https://packagist.org
- wikidata_id: Q108311377
- official_api_documentation: https://packagist.org/apidoc
- use_official_api: true
- require_api_key: false
- results: JSON
-
- - name: pdbe
- engine: pdbe
- shortcut: pdb
- # Hide obsolete PDB entries. Default is not to hide obsolete structures
- # hide_obsolete: false
-
- - name: photon
- engine: photon
- shortcut: ph
-
- - name: pinterest
- engine: pinterest
- shortcut: pin
-
- - name: piped
- engine: piped
- shortcut: ppd
- categories: videos
- piped_filter: videos
- timeout: 3.0
-
- # URL to use as link and for embeds
- frontend_url: https://srv.piped.video
- # Instance will be selected randomly, for more see https://piped-instances.kavin.rocks/
- backend_url:
- - https://pipedapi.kavin.rocks
- - https://pipedapi-libre.kavin.rocks
- - https://pipedapi.adminforge.de
-
- - name: piped.music
- engine: piped
- network: piped
- shortcut: ppdm
- categories: music
- piped_filter: music_songs
- timeout: 3.0
-
- - name: piratebay
- engine: piratebay
- shortcut: tpb
- # You may need to change this URL to a proxy if piratebay is blocked in your
- # country
- url: https://thepiratebay.org/
- timeout: 3.0
-
- - name: pixiv
- shortcut: pv
- engine: pixiv
- disabled: true
- inactive: true
- pixiv_image_proxies:
- - https://pximg.example.org
- # A proxy is required to load the images. Hosting an image proxy server
- # for Pixiv:
- # --> https://pixivfe.pages.dev/hosting-image-proxy-server/
- # Proxies from public instances. Ask the public instances owners if they
- # agree to receive traffic from SearXNG!
- # --> https://codeberg.org/VnPower/PixivFE#instances
- # --> https://github.com/searxng/searxng/pull/3192#issuecomment-1941095047
- # image proxy of https://pixiv.cat
- # - https://i.pixiv.cat
- # image proxy of https://www.pixiv.pics
- # - https://pximg.cocomi.eu.org
- # image proxy of https://pixivfe.exozy.me
- # - https://pximg.exozy.me
- # image proxy of https://pixivfe.ducks.party
- # - https://pixiv.ducks.party
- # image proxy of https://pixiv.perennialte.ch
- # - https://pximg.perennialte.ch
-
- - name: podcastindex
- engine: podcastindex
- shortcut: podcast
-
- # Required dependency: psychopg2
- # - name: postgresql
- # engine: postgresql
- # database: postgres
- # username: postgres
- # password: postgres
- # limit: 10
- # query_str: 'SELECT * from my_table WHERE my_column = %(query)s'
- # shortcut : psql
-
- - name: presearch
- engine: presearch
- search_type: search
- categories: [general, web]
- shortcut: ps
- timeout: 4.0
- disabled: true
-
- - name: presearch images
- engine: presearch
- network: presearch
- search_type: images
- categories: [images, web]
- timeout: 4.0
- shortcut: psimg
- disabled: true
-
- - name: presearch videos
- engine: presearch
- network: presearch
- search_type: videos
- categories: [general, web]
- timeout: 4.0
- shortcut: psvid
- disabled: true
-
- - name: presearch news
- engine: presearch
- network: presearch
- search_type: news
- categories: [news, web]
- timeout: 4.0
- shortcut: psnews
- disabled: true
-
- - name: pub.dev
- engine: xpath
- shortcut: pd
- search_url: https://pub.dev/packages?q={query}&page={pageno}
- paging: true
- results_xpath: //div[contains(@class,"packages-item")]
- url_xpath: ./div/h3/a/@href
- title_xpath: ./div/h3/a
- content_xpath: ./div/div/div[contains(@class,"packages-description")]/span
- categories: [packages, it]
- timeout: 3.0
- disabled: true
- first_page_num: 1
- about:
- website: https://pub.dev/
- official_api_documentation: https://pub.dev/help/api
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: pubmed
- engine: pubmed
- shortcut: pub
- timeout: 3.0
-
- - name: pypi
- shortcut: pypi
- engine: pypi
-
- - name: qwant
- qwant_categ: web
- engine: qwant
- disabled: true
- shortcut: qw
- categories: [general, web]
- additional_tests:
- rosebud: *test_rosebud
-
- - name: qwant news
- qwant_categ: news
- engine: qwant
- shortcut: qwn
- categories: news
- network: qwant
-
- - name: qwant images
- qwant_categ: images
- engine: qwant
- shortcut: qwi
- categories: [images, web]
- network: qwant
-
- - name: qwant videos
- qwant_categ: videos
- engine: qwant
- shortcut: qwv
- categories: [videos, web]
- network: qwant
-
- # - name: library
- # engine: recoll
- # shortcut: lib
- # base_url: 'https://recoll.example.org/'
- # search_dir: ''
- # mount_prefix: /export
- # dl_prefix: 'https://download.example.org'
- # timeout: 30.0
- # categories: files
- # disabled: true
-
- # - name: recoll library reference
- # engine: recoll
- # base_url: 'https://recoll.example.org/'
- # search_dir: reference
- # mount_prefix: /export
- # dl_prefix: 'https://download.example.org'
- # shortcut: libr
- # timeout: 30.0
- # categories: files
- # disabled: true
-
- - name: radio browser
- engine: radio_browser
- shortcut: rb
-
- - name: reddit
- engine: reddit
- shortcut: re
- page_size: 25
- disabled: true
-
- - name: rottentomatoes
- engine: rottentomatoes
- shortcut: rt
- disabled: true
-
- # Required dependency: redis
- # - name: myredis
- # shortcut : rds
- # engine: redis_server
- # exact_match_only: false
- # host: '127.0.0.1'
- # port: 6379
- # enable_http: true
- # password: ''
- # db: 0
-
- # tmp suspended: bad certificate
- # - name: scanr structures
- # shortcut: scs
- # engine: scanr_structures
- # disabled: true
-
- - name: searchmysite
- engine: xpath
- shortcut: sms
- categories: general
- paging: true
- search_url: https://searchmysite.net/search/?q={query}&page={pageno}
- results_xpath: //div[contains(@class,'search-result')]
- url_xpath: .//a[contains(@class,'result-link')]/@href
- title_xpath: .//span[contains(@class,'result-title-txt')]/text()
- content_xpath: ./p[@id='result-hightlight']
- disabled: true
- about:
- website: https://searchmysite.net
-
- - name: sepiasearch
- engine: sepiasearch
- shortcut: sep
-
- - name: soundcloud
- engine: soundcloud
- shortcut: sc
-
- - name: stackoverflow
- engine: stackexchange
- shortcut: st
- api_site: 'stackoverflow'
- categories: [it, q&a]
-
- - name: askubuntu
- engine: stackexchange
- shortcut: ubuntu
- api_site: 'askubuntu'
- categories: [it, q&a]
-
- - name: internetarchivescholar
- engine: internet_archive_scholar
- shortcut: ias
- timeout: 15.0
-
- - name: superuser
- engine: stackexchange
- shortcut: su
- api_site: 'superuser'
- categories: [it, q&a]
-
- - name: discuss.python
- engine: discourse
- shortcut: dpy
- base_url: 'https://discuss.python.org'
- categories: [it, q&a]
- disabled: true
-
- - name: caddy.community
- engine: discourse
- shortcut: caddy
- base_url: 'https://caddy.community'
- categories: [it, q&a]
- disabled: true
-
- - name: pi-hole.community
- engine: discourse
- shortcut: pi
- categories: [it, q&a]
- base_url: 'https://discourse.pi-hole.net'
- disabled: true
-
- - name: searchcode code
- engine: searchcode_code
- shortcut: scc
- disabled: true
-
- # - name: searx
- # engine: searx_engine
- # shortcut: se
- # instance_urls :
- # - http://127.0.0.1:8888/
- # - ...
- # disabled: true
-
- - name: semantic scholar
- engine: semantic_scholar
- disabled: true
- shortcut: se
-
- # Spotify needs API credentials
- # - name: spotify
- # engine: spotify
- # shortcut: stf
- # api_client_id: *******
- # api_client_secret: *******
-
- # - name: solr
- # engine: solr
- # shortcut: slr
- # base_url: http://localhost:8983
- # collection: collection_name
- # sort: '' # sorting: asc or desc
- # field_list: '' # comma separated list of field names to display on the UI
- # default_fields: '' # default field to query
- # query_fields: '' # query fields
- # enable_http: true
-
- # - name: springer nature
- # engine: springer
- # # get your API key from: https://dev.springernature.com/signup
- # # working API key, for test & debug: "a69685087d07eca9f13db62f65b8f601"
- # api_key: 'unset'
- # shortcut: springer
- # timeout: 15.0
-
- - name: startpage
- engine: startpage
- shortcut: sp
- timeout: 6.0
- disabled: true
- additional_tests:
- rosebud: *test_rosebud
-
- - name: tokyotoshokan
- engine: tokyotoshokan
- shortcut: tt
- timeout: 6.0
- disabled: true
-
- - name: solidtorrents
- engine: solidtorrents
- shortcut: solid
- timeout: 4.0
- base_url:
- - https://solidtorrents.to
- - https://bitsearch.to
-
- # For this demo of the sqlite engine download:
- # https://liste.mediathekview.de/filmliste-v2.db.bz2
- # and unpack into searx/data/filmliste-v2.db
- # Query to test: "!demo concert"
- #
- # - name: demo
- # engine: sqlite
- # shortcut: demo
- # categories: general
- # result_template: default.html
- # database: searx/data/filmliste-v2.db
- # query_str: >-
- # SELECT title || ' (' || time(duration, 'unixepoch') || ')' AS title,
- # COALESCE( NULLIF(url_video_hd,''), NULLIF(url_video_sd,''), url_video) AS url,
- # description AS content
- # FROM film
- # WHERE title LIKE :wildcard OR description LIKE :wildcard
- # ORDER BY duration DESC
-
- - name: tagesschau
- engine: tagesschau
- # when set to false, display URLs from Tagesschau, and not the actual source
- # (e.g. NDR, WDR, SWR, HR, ...)
- use_source_url: true
- shortcut: ts
- disabled: true
-
- - name: tmdb
- engine: xpath
- paging: true
- categories: movies
- search_url: https://www.themoviedb.org/search?page={pageno}&query={query}
- results_xpath: //div[contains(@class,"movie") or contains(@class,"tv")]//div[contains(@class,"card")]
- url_xpath: .//div[contains(@class,"poster")]/a/@href
- thumbnail_xpath: .//img/@src
- title_xpath: .//div[contains(@class,"title")]//h2
- content_xpath: .//div[contains(@class,"overview")]
- shortcut: tm
- disabled: true
-
- # Requires Tor
- - name: torch
- engine: xpath
- paging: true
- search_url:
- http://xmh57jrknzkhv6y3ls3ubitzfqnkrwxhopf5aygthi7d6rplyvk3noyd.onion/cgi-bin/omega/omega?P={query}&DEFAULTOP=and
- results_xpath: //table//tr
- url_xpath: ./td[2]/a
- title_xpath: ./td[2]/b
- content_xpath: ./td[2]/small
- categories: onions
- enable_http: true
- shortcut: tch
-
- # torznab engine lets you query any torznab compatible indexer. Using this
- # engine in combination with Jackett opens the possibility to query a lot of
- # public and private indexers directly from SearXNG. More details at:
- # https://docs.searxng.org/dev/engines/online/torznab.html
- #
- # - name: Torznab EZTV
- # engine: torznab
- # shortcut: eztv
- # base_url: http://localhost:9117/api/v2.0/indexers/eztv/results/torznab
- # enable_http: true # if using localhost
- # api_key: xxxxxxxxxxxxxxx
- # show_magnet_links: true
- # show_torrent_files: false
- # # https://github.com/Jackett/Jackett/wiki/Jackett-Categories
- # torznab_categories: # optional
- # - 2000
- # - 5000
-
- # tmp suspended - too slow, too many errors
- # - name: urbandictionary
- # engine : xpath
- # search_url : https://www.urbandictionary.com/define.php?term={query}
- # url_xpath : //*[@class="word"]/@href
- # title_xpath : //*[@class="def-header"]
- # content_xpath: //*[@class="meaning"]
- # shortcut: ud
-
- - name: unsplash
- engine: unsplash
- shortcut: us
-
- - name: yandex music
- engine: yandex_music
- shortcut: ydm
- disabled: true
- # https://yandex.com/support/music/access.html
- inactive: true
-
- - name: yahoo
- engine: yahoo
- shortcut: yh
- disabled: true
-
- - name: yahoo news
- engine: yahoo_news
- shortcut: yhn
-
- - name: youtube
- shortcut: yt
- # You can use the engine using the official stable API, but you need an API
- # key See: https://console.developers.google.com/project
- #
- # engine: youtube_api
- # api_key: 'apikey' # required!
- #
- # Or you can use the html non-stable engine, activated by default
- engine: youtube_noapi
-
- - name: dailymotion
- engine: dailymotion
- shortcut: dm
-
- - name: vimeo
- engine: vimeo
- shortcut: vm
- disabled: true
-
- - name: wiby
- engine: json_engine
- paging: true
- search_url: https://wiby.me/json/?q={query}&p={pageno}
- url_query: URL
- title_query: Title
- content_query: Snippet
- categories: [general, web]
- shortcut: wib
- disabled: true
- about:
- website: https://wiby.me/
-
- - name: alexandria
- engine: json_engine
- shortcut: alx
- categories: general
- paging: true
- search_url: https://api.alexandria.org/?a=1&q={query}&p={pageno}
- results_query: results
- title_query: title
- url_query: url
- content_query: snippet
- timeout: 1.5
- disabled: true
- about:
- website: https://alexandria.org/
- official_api_documentation: https://github.com/alexandria-org/alexandria-api/raw/master/README.md
- use_official_api: true
- require_api_key: false
- results: JSON
-
- - name: wikibooks
- engine: mediawiki
- weight: 0.5
- shortcut: wb
- categories: [general, wikimedia]
- base_url: "https://{language}.wikibooks.org/"
- search_type: text
- disabled: true
- about:
- website: https://www.wikibooks.org/
- wikidata_id: Q367
-
- - name: wikinews
- engine: mediawiki
- shortcut: wn
- categories: [news, wikimedia]
- base_url: "https://{language}.wikinews.org/"
- search_type: text
- srsort: create_timestamp_desc
- about:
- website: https://www.wikinews.org/
- wikidata_id: Q964
-
- - name: wikiquote
- engine: mediawiki
- weight: 0.5
- shortcut: wq
- categories: [general, wikimedia]
- base_url: "https://{language}.wikiquote.org/"
- search_type: text
- disabled: true
- additional_tests:
- rosebud: *test_rosebud
- about:
- website: https://www.wikiquote.org/
- wikidata_id: Q369
-
- - name: wikisource
- engine: mediawiki
- weight: 0.5
- shortcut: ws
- categories: [general, wikimedia]
- base_url: "https://{language}.wikisource.org/"
- search_type: text
- disabled: true
- about:
- website: https://www.wikisource.org/
- wikidata_id: Q263
-
- - name: wikispecies
- engine: mediawiki
- shortcut: wsp
- categories: [general, science, wikimedia]
- base_url: "https://species.wikimedia.org/"
- search_type: text
- disabled: true
- about:
- website: https://species.wikimedia.org/
- wikidata_id: Q13679
- tests:
- wikispecies:
- matrix:
- query: "Campbell, L.I. et al. 2011: MicroRNAs"
- lang: en
- result_container:
- - not_empty
- - ['one_title_contains', 'Tardigrada']
- test:
- - unique_results
-
- - name: wiktionary
- engine: mediawiki
- shortcut: wt
- categories: [dictionaries, wikimedia]
- base_url: "https://{language}.wiktionary.org/"
- search_type: text
- about:
- website: https://www.wiktionary.org/
- wikidata_id: Q151
-
- - name: wikiversity
- engine: mediawiki
- weight: 0.5
- shortcut: wv
- categories: [general, wikimedia]
- base_url: "https://{language}.wikiversity.org/"
- search_type: text
- disabled: true
- about:
- website: https://www.wikiversity.org/
- wikidata_id: Q370
-
- - name: wikivoyage
- engine: mediawiki
- weight: 0.5
- shortcut: wy
- categories: [general, wikimedia]
- base_url: "https://{language}.wikivoyage.org/"
- search_type: text
- disabled: true
- about:
- website: https://www.wikivoyage.org/
- wikidata_id: Q373
-
- - name: wikicommons.images
- engine: wikicommons
- shortcut: wc
- categories: images
- search_type: images
- number_of_results: 10
-
- - name: wikicommons.videos
- engine: wikicommons
- shortcut: wcv
- categories: videos
- search_type: videos
- number_of_results: 10
-
- - name: wikicommons.audio
- engine: wikicommons
- shortcut: wca
- categories: music
- search_type: audio
- number_of_results: 10
-
- - name: wikicommons.files
- engine: wikicommons
- shortcut: wcf
- categories: files
- search_type: files
- number_of_results: 10
-
- - name: wolframalpha
- shortcut: wa
- # You can use the engine using the official stable API, but you need an API
- # key. See: https://products.wolframalpha.com/api/
- #
- # engine: wolframalpha_api
- # api_key: ''
- #
- # Or you can use the html non-stable engine, activated by default
- engine: wolframalpha_noapi
- timeout: 6.0
- categories: general
- disabled: true
-
- - name: dictzone
- engine: dictzone
- shortcut: dc
-
- - name: mymemory translated
- engine: translated
- shortcut: tl
- timeout: 5.0
- # You can use without an API key, but you are limited to 1000 words/day
- # See: https://mymemory.translated.net/doc/usagelimits.php
- # api_key: ''
-
- # Required dependency: mysql-connector-python
- # - name: mysql
- # engine: mysql_server
- # database: mydatabase
- # username: user
- # password: pass
- # limit: 10
- # query_str: 'SELECT * from mytable WHERE fieldname=%(query)s'
- # shortcut: mysql
-
- - name: 1337x
- engine: 1337x
- shortcut: 1337x
- disabled: true
-
- - name: duden
- engine: duden
- shortcut: du
- disabled: true
-
- - name: seznam
- shortcut: szn
- engine: seznam
- disabled: true
-
- # - name: deepl
- # engine: deepl
- # shortcut: dpl
- # # You can use the engine using the official stable API, but you need an API key
- # # See: https://www.deepl.com/pro-api?cta=header-pro-api
- # api_key: '' # required!
- # timeout: 5.0
- # disabled: true
-
- - name: mojeek
- shortcut: mjk
- engine: mojeek
- categories: [general, web]
- disabled: true
-
- - name: mojeek images
- shortcut: mjkimg
- engine: mojeek
- categories: [images, web]
- search_type: images
- paging: false
- disabled: true
-
- - name: mojeek news
- shortcut: mjknews
- engine: mojeek
- categories: [news, web]
- search_type: news
- paging: false
- disabled: true
-
- - name: moviepilot
- engine: moviepilot
- shortcut: mp
- disabled: true
-
- - name: naver
- shortcut: nvr
- categories: [general, web]
- engine: xpath
- paging: true
- search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno}
- url_xpath: //a[@class="link_tit"]/@href
- title_xpath: //a[@class="link_tit"]
- content_xpath: //div[@class="total_dsc_wrap"]/a
- first_page_num: 1
- page_size: 10
- disabled: true
- about:
- website: https://www.naver.com/
- wikidata_id: Q485639
- official_api_documentation: https://developers.naver.com/docs/nmt/examples/
- use_official_api: false
- require_api_key: false
- results: HTML
- language: ko
-
- - name: rubygems
- shortcut: rbg
- engine: xpath
- paging: true
- search_url: https://rubygems.org/search?page={pageno}&query={query}
- results_xpath: /html/body/main/div/a[@class="gems__gem"]
- url_xpath: ./@href
- title_xpath: ./span/h2
- content_xpath: ./span/p
- suggestion_xpath: /html/body/main/div/div[@class="search__suggestions"]/p/a
- first_page_num: 1
- categories: [it, packages]
- disabled: true
- about:
- website: https://rubygems.org/
- wikidata_id: Q1853420
- official_api_documentation: https://guides.rubygems.org/rubygems-org-api/
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: peertube
- engine: peertube
- shortcut: ptb
- paging: true
- # alternatives see: https://instances.joinpeertube.org/instances
- # base_url: https://tube.4aem.com
- categories: videos
- disabled: true
- timeout: 6.0
-
- - name: mediathekviewweb
- engine: mediathekviewweb
- shortcut: mvw
- disabled: true
-
- - name: yacy
- # https://docs.searxng.org/dev/engines/online/yacy.html
- engine: yacy
- categories: general
- search_type: text
- base_url:
- - https://yacy.searchlab.eu
- # see https://github.com/searxng/searxng/pull/3631#issuecomment-2240903027
- # - https://search.kyun.li
- # - https://yacy.securecomcorp.eu
- # - https://yacy.myserv.ca
- # - https://yacy.nsupdate.info
- # - https://yacy.electroncash.de
- shortcut: ya
- disabled: true
- # if you aren't using HTTPS for your local yacy instance disable https
- # enable_http: false
- search_mode: 'global'
- # timeout can be reduced in 'local' search mode
- timeout: 5.0
-
- - name: yacy images
- engine: yacy
- network: yacy
- categories: images
- search_type: image
- shortcut: yai
- disabled: true
- # timeout can be reduced in 'local' search mode
- timeout: 5.0
-
- - name: rumble
- engine: rumble
- shortcut: ru
- base_url: https://rumble.com/
- paging: true
- categories: videos
- disabled: true
-
- - name: livespace
- engine: livespace
- shortcut: ls
- categories: videos
- disabled: true
- timeout: 5.0
-
- - name: wordnik
- engine: wordnik
- shortcut: def
- base_url: https://www.wordnik.com/
- categories: [dictionaries]
- timeout: 5.0
-
- - name: woxikon.de synonyme
- engine: xpath
- shortcut: woxi
- categories: [dictionaries]
- timeout: 5.0
- disabled: true
- search_url: https://synonyme.woxikon.de/synonyme/{query}.php
- url_xpath: //div[@class="upper-synonyms"]/a/@href
- content_xpath: //div[@class="synonyms-list-group"]
- title_xpath: //div[@class="upper-synonyms"]/a
- no_result_for_http_status: [404]
- about:
- website: https://www.woxikon.de/
- wikidata_id: # No Wikidata ID
- use_official_api: false
- require_api_key: false
- results: HTML
- language: de
-
- - name: seekr news
- engine: seekr
- shortcut: senews
- categories: news
- seekr_category: news
- disabled: true
-
- - name: seekr images
- engine: seekr
- network: seekr news
- shortcut: seimg
- categories: images
- seekr_category: images
- disabled: true
-
- - name: seekr videos
- engine: seekr
- network: seekr news
- shortcut: sevid
- categories: videos
- seekr_category: videos
- disabled: true
-
- - name: sjp.pwn
- engine: sjp
- shortcut: sjp
- base_url: https://sjp.pwn.pl/
- timeout: 5.0
- disabled: true
-
- - name: stract
- engine: stract
- shortcut: str
- disabled: true
-
- - name: svgrepo
- engine: svgrepo
- shortcut: svg
- timeout: 10.0
- disabled: true
-
- - name: tootfinder
- engine: tootfinder
- shortcut: toot
-
- - name: voidlinux
- engine: voidlinux
- shortcut: void
- disabled: true
-
- - name: wallhaven
- engine: wallhaven
- # api_key: abcdefghijklmnopqrstuvwxyz
- shortcut: wh
-
- # wikimini: online encyclopedia for children
- # The fulltext and title parameter is necessary for Wikimini because
- # sometimes it will not show the results and redirect instead
- - name: wikimini
- engine: xpath
- shortcut: wkmn
- search_url: https://fr.wikimini.org/w/index.php?search={query}&title=Sp%C3%A9cial%3ASearch&fulltext=Search
- url_xpath: //li/div[@class="mw-search-result-heading"]/a/@href
- title_xpath: //li//div[@class="mw-search-result-heading"]/a
- content_xpath: //li/div[@class="searchresult"]
- categories: general
- disabled: true
- about:
- website: https://wikimini.org/
- wikidata_id: Q3568032
- use_official_api: false
- require_api_key: false
- results: HTML
- language: fr
-
- - name: wttr.in
- engine: wttr
- shortcut: wttr
- timeout: 9.0
-
- - name: yummly
- engine: yummly
- shortcut: yum
- disabled: true
-
- - name: brave
- engine: brave
- shortcut: br
- time_range_support: true
- paging: true
- categories: [general, web]
- brave_category: search
- # brave_spellcheck: true
-
- - name: brave.images
- engine: brave
- network: brave
- shortcut: brimg
- categories: [images, web]
- brave_category: images
-
- - name: brave.videos
- engine: brave
- network: brave
- shortcut: brvid
- categories: [videos, web]
- brave_category: videos
-
- - name: brave.news
- engine: brave
- network: brave
- shortcut: brnews
- categories: news
- brave_category: news
-
- # - name: brave.goggles
- # engine: brave
- # network: brave
- # shortcut: brgog
- # time_range_support: true
- # paging: true
- # categories: [general, web]
- # brave_category: goggles
- # Goggles: # required! This should be a URL ending in .goggle
-
- - name: lib.rs
- shortcut: lrs
- engine: lib_rs
- disabled: true
-
- - name: sourcehut
- shortcut: srht
- engine: xpath
- paging: true
- search_url: https://sr.ht/projects?page={pageno}&search={query}
- results_xpath: (//div[@class="event-list"])[1]/div[@class="event"]
- url_xpath: ./h4/a[2]/@href
- title_xpath: ./h4/a[2]
- content_xpath: ./p
- first_page_num: 1
- categories: [it, repos]
- disabled: true
- about:
- website: https://sr.ht
- wikidata_id: Q78514485
- official_api_documentation: https://man.sr.ht/
- use_official_api: false
- require_api_key: false
- results: HTML
-
- - name: goo
- shortcut: goo
- engine: xpath
- paging: true
- search_url: https://search.goo.ne.jp/web.jsp?MT={query}&FR={pageno}0
- url_xpath: //div[@class="result"]/p[@class='title fsL1']/a/@href
- title_xpath: //div[@class="result"]/p[@class='title fsL1']/a
- content_xpath: //p[contains(@class,'url fsM')]/following-sibling::p
- first_page_num: 0
- categories: [general, web]
- disabled: true
- timeout: 4.0
- about:
- website: https://search.goo.ne.jp
- wikidata_id: Q249044
- use_official_api: false
- require_api_key: false
- results: HTML
- language: ja
-
- - name: bt4g
- engine: bt4g
- shortcut: bt4g
-
- - name: pkg.go.dev
- engine: pkg_go_dev
- shortcut: pgo
- disabled: true
-
-# Doku engine lets you access to any Doku wiki instance:
-# A public one or a privete/corporate one.
-# - name: ubuntuwiki
-# engine: doku
-# shortcut: uw
-# base_url: 'https://doc.ubuntu-fr.org'
-
-# Be careful when enabling this engine if you are
-# running a public instance. Do not expose any sensitive
-# information. You can restrict access by configuring a list
-# of access tokens under tokens.
-# - name: git grep
-# engine: command
-# command: ['git', 'grep', '{{QUERY}}']
-# shortcut: gg
-# tokens: []
-# disabled: true
-# delimiter:
-# chars: ':'
-# keys: ['filepath', 'code']
-
-# Be careful when enabling this engine if you are
-# running a public instance. Do not expose any sensitive
-# information. You can restrict access by configuring a list
-# of access tokens under tokens.
-# - name: locate
-# engine: command
-# command: ['locate', '{{QUERY}}']
-# shortcut: loc
-# tokens: []
-# disabled: true
-# delimiter:
-# chars: ' '
-# keys: ['line']
-
-# Be careful when enabling this engine if you are
-# running a public instance. Do not expose any sensitive
-# information. You can restrict access by configuring a list
-# of access tokens under tokens.
-# - name: find
-# engine: command
-# command: ['find', '.', '-name', '{{QUERY}}']
-# query_type: path
-# shortcut: fnd
-# tokens: []
-# disabled: true
-# delimiter:
-# chars: ' '
-# keys: ['line']
-
-# Be careful when enabling this engine if you are
-# running a public instance. Do not expose any sensitive
-# information. You can restrict access by configuring a list
-# of access tokens under tokens.
-# - name: pattern search in files
-# engine: command
-# command: ['fgrep', '{{QUERY}}']
-# shortcut: fgr
-# tokens: []
-# disabled: true
-# delimiter:
-# chars: ' '
-# keys: ['line']
-
-# Be careful when enabling this engine if you are
-# running a public instance. Do not expose any sensitive
-# information. You can restrict access by configuring a list
-# of access tokens under tokens.
-# - name: regex search in files
-# engine: command
-# command: ['grep', '{{QUERY}}']
-# shortcut: gr
-# tokens: []
-# disabled: true
-# delimiter:
-# chars: ' '
-# keys: ['line']
-
-doi_resolvers:
- oadoi.org: 'https://oadoi.org/'
- doi.org: 'https://doi.org/'
- doai.io: 'https://dissem.in/'
- sci-hub.se: 'https://sci-hub.se/'
- sci-hub.st: 'https://sci-hub.st/'
- sci-hub.ru: 'https://sci-hub.ru/'
-
-default_doi_resolver: 'oadoi.org'
diff --git a/api/core/tools/provider/builtin/searxng/docker/uwsgi.ini b/api/core/tools/provider/builtin/searxng/docker/uwsgi.ini
deleted file mode 100644
index 9db3d762649fc5..00000000000000
--- a/api/core/tools/provider/builtin/searxng/docker/uwsgi.ini
+++ /dev/null
@@ -1,54 +0,0 @@
-[uwsgi]
-# Who will run the code
-uid = searxng
-gid = searxng
-
-# Number of workers (usually CPU count)
-# default value: %k (= number of CPU core, see Dockerfile)
-workers = %k
-
-# Number of threads per worker
-# default value: 4 (see Dockerfile)
-threads = 4
-
-# The right granted on the created socket
-chmod-socket = 666
-
-# Plugin to use and interpreter config
-single-interpreter = true
-master = true
-plugin = python3
-lazy-apps = true
-enable-threads = 4
-
-# Module to import
-module = searx.webapp
-
-# Virtualenv and python path
-pythonpath = /usr/local/searxng/
-chdir = /usr/local/searxng/searx/
-
-# automatically set processes name to something meaningful
-auto-procname = true
-
-# Disable request logging for privacy
-disable-logging = true
-log-5xx = true
-
-# Set the max size of a request (request-body excluded)
-buffer-size = 8192
-
-# No keep alive
-# See https://github.com/searx/searx-docker/issues/24
-add-header = Connection: close
-
-# Follow SIGTERM convention
-# See https://github.com/searxng/searxng/issues/3427
-die-on-term
-
-# uwsgi serves the static files
-static-map = /static=/usr/local/searxng/searx/static
-# expires set to one day
-static-expires = /* 86400
-static-gzip-all = True
-offload-threads = 4
diff --git a/api/core/tools/provider/builtin/searxng/searxng.py b/api/core/tools/provider/builtin/searxng/searxng.py
deleted file mode 100644
index b7bbcc60b1ed26..00000000000000
--- a/api/core/tools/provider/builtin/searxng/searxng.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.searxng.tools.searxng_search import SearXNGSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SearXNGProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- SearXNGSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"query": "SearXNG", "limit": 1, "search_type": "general"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/searxng/searxng.yaml b/api/core/tools/provider/builtin/searxng/searxng.yaml
deleted file mode 100644
index 9554c93d5a0c53..00000000000000
--- a/api/core/tools/provider/builtin/searxng/searxng.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-identity:
- author: Junytang
- name: searxng
- label:
- en_US: SearXNG
- zh_Hans: SearXNG
- description:
- en_US: A free internet metasearch engine.
- zh_Hans: 开源免费的互联网元搜索引擎
- icon: icon.svg
- tags:
- - search
- - productivity
-credentials_for_provider:
- searxng_base_url:
- type: text-input
- required: true
- label:
- en_US: SearXNG base URL
- zh_Hans: SearXNG base URL
- placeholder:
- en_US: Please input your SearXNG base URL
- zh_Hans: 请输入您的 SearXNG base URL
- url: https://docs.dify.ai/tutorials/tool-configuration/searxng
diff --git a/api/core/tools/provider/builtin/searxng/tools/searxng_search.py b/api/core/tools/provider/builtin/searxng/tools/searxng_search.py
deleted file mode 100644
index c5e339a108e5b2..00000000000000
--- a/api/core/tools/provider/builtin/searxng/tools/searxng_search.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SearXNGSearchTool(BuiltinTool):
- """
- Tool for performing a search using SearXNG engine.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invoke the SearXNG search tool.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Any]): The parameters for the tool invocation.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation.
- """
-
- host = self.runtime.credentials.get("searxng_base_url")
- if not host:
- raise Exception("SearXNG api is required")
-
- response = requests.get(
- host,
- params={
- "q": tool_parameters.get("query"),
- "format": "json",
- "categories": tool_parameters.get("search_type", "general"),
- },
- )
-
- if response.status_code != 200:
- raise Exception(f"Error {response.status_code}: {response.text}")
-
- res = response.json().get("results", [])
- if not res:
- return self.create_text_message(f"No results found, get response: {response.content}")
-
- return [self.create_json_message(item) for item in res]
diff --git a/api/core/tools/provider/builtin/searxng/tools/searxng_search.yaml b/api/core/tools/provider/builtin/searxng/tools/searxng_search.yaml
deleted file mode 100644
index a5e448a30375b4..00000000000000
--- a/api/core/tools/provider/builtin/searxng/tools/searxng_search.yaml
+++ /dev/null
@@ -1,69 +0,0 @@
-identity:
- name: searxng_search
- author: Junytang
- label:
- en_US: SearXNG Search
- zh_Hans: SearXNG 搜索
-description:
- human:
- en_US: SearXNG is a free internet metasearch engine which aggregates results from more than 70 search services.
- zh_Hans: SearXNG 是一个免费的互联网元搜索引擎,它从70多个不同的搜索服务中聚合搜索结果。
- llm: Perform searches on SearXNG and get results.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- llm_description: Key words for searching
- form: llm
- - name: search_type
- type: select
- required: true
- label:
- en_US: search type
- zh_Hans: 搜索类型
- default: general
- options:
- - value: general
- label:
- en_US: General
- zh_Hans: 综合
- - value: images
- label:
- en_US: Images
- zh_Hans: 图片
- - value: videos
- label:
- en_US: Videos
- zh_Hans: 视频
- - value: news
- label:
- en_US: News
- zh_Hans: 新闻
- - value: map
- label:
- en_US: Map
- zh_Hans: 地图
- - value: music
- label:
- en_US: Music
- zh_Hans: 音乐
- - value: it
- label:
- en_US: It
- zh_Hans: 信息技术
- - value: science
- label:
- en_US: Science
- zh_Hans: 科学
- - value: files
- label:
- en_US: Files
- zh_Hans: 文件
- - value: social_media
- label:
- en_US: Social Media
- zh_Hans: 社交媒体
- form: form
diff --git a/api/core/tools/provider/builtin/serper/_assets/icon.svg b/api/core/tools/provider/builtin/serper/_assets/icon.svg
deleted file mode 100644
index 3f973a552e5e17..00000000000000
--- a/api/core/tools/provider/builtin/serper/_assets/icon.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- serper
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/serper/serper.py b/api/core/tools/provider/builtin/serper/serper.py
deleted file mode 100644
index cb1d090a9dd4b0..00000000000000
--- a/api/core/tools/provider/builtin/serper/serper.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.serper.tools.serper_search import SerperSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SerperProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- SerperSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"query": "test", "result_type": "link"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/serper/serper.yaml b/api/core/tools/provider/builtin/serper/serper.yaml
deleted file mode 100644
index b3b2d76c4b6573..00000000000000
--- a/api/core/tools/provider/builtin/serper/serper.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- author: zhuhao
- name: serper
- label:
- en_US: Serper
- zh_Hans: Serper
- pt_BR: Serper
- description:
- en_US: Serper is a powerful real-time search engine tool API that provides structured data from Google Search.
- zh_Hans: Serper 是一个强大的实时搜索引擎工具API,可提供来自 Google 搜索引擎搜索的结构化数据。
- pt_BR: Serper is a powerful real-time search engine tool API that provides structured data from Google Search.
- icon: icon.svg
- tags:
- - search
-credentials_for_provider:
- serperapi_api_key:
- type: secret-input
- required: true
- label:
- en_US: Serper API key
- zh_Hans: Serper API key
- pt_BR: Serper API key
- placeholder:
- en_US: Please input your Serper API key
- zh_Hans: 请输入你的 Serper API key
- pt_BR: Please input your Serper API key
- help:
- en_US: Get your Serper API key from Serper
- zh_Hans: 从 Serper 获取您的 Serper API key
- pt_BR: Get your Serper API key from Serper
- url: https://serper.dev/api-key
diff --git a/api/core/tools/provider/builtin/serper/tools/serper_search.py b/api/core/tools/provider/builtin/serper/tools/serper_search.py
deleted file mode 100644
index 7baebbf95855e0..00000000000000
--- a/api/core/tools/provider/builtin/serper/tools/serper_search.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SERPER_API_URL = "https://google.serper.dev/search"
-
-
-class SerperSearchTool(BuiltinTool):
- def _parse_response(self, response: dict) -> dict:
- result = {}
- if "knowledgeGraph" in response:
- result["title"] = response["knowledgeGraph"].get("title", "")
- result["description"] = response["knowledgeGraph"].get("description", "")
- if "organic" in response:
- result["organic"] = [
- {"title": item.get("title", ""), "link": item.get("link", ""), "snippet": item.get("snippet", "")}
- for item in response["organic"]
- ]
- return result
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- params = {"q": tool_parameters["query"], "gl": "us", "hl": "en"}
- headers = {"X-API-KEY": self.runtime.credentials["serperapi_api_key"], "Content-Type": "application/json"}
- response = requests.get(url=SERPER_API_URL, params=params, headers=headers)
- response.raise_for_status()
- valuable_res = self._parse_response(response.json())
- return self.create_json_message(valuable_res)
diff --git a/api/core/tools/provider/builtin/serper/tools/serper_search.yaml b/api/core/tools/provider/builtin/serper/tools/serper_search.yaml
deleted file mode 100644
index e1c0a056e65513..00000000000000
--- a/api/core/tools/provider/builtin/serper/tools/serper_search.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: serper
- author: zhuhao
- label:
- en_US: Serper
- zh_Hans: Serper
- pt_BR: Serper
-description:
- human:
- en_US: A tool for performing a Google search and extracting snippets and webpages.Input should be a search query.
- zh_Hans: 一个用于执行 Google 搜索并提取片段和网页的工具。输入应该是一个搜索查询。
- pt_BR: A tool for performing a Google search and extracting snippets and webpages.Input should be a search query.
- llm: A tool for performing a Google search and extracting snippets and webpages.Input should be a search query.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: used for searching
- zh_Hans: 用于搜索网页内容
- pt_BR: used for searching
- llm_description: key words for searching
- form: llm
diff --git a/api/core/tools/provider/builtin/siliconflow/_assets/icon.svg b/api/core/tools/provider/builtin/siliconflow/_assets/icon.svg
deleted file mode 100644
index ad6b384f7acd21..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/siliconflow/siliconflow.py b/api/core/tools/provider/builtin/siliconflow/siliconflow.py
deleted file mode 100644
index 37a0b0755b1d39..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/siliconflow.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SiliconflowProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- url = "https://api.siliconflow.cn/v1/models"
- headers = {
- "accept": "application/json",
- "authorization": f"Bearer {credentials.get('siliconFlow_api_key')}",
- }
-
- response = requests.get(url, headers=headers)
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("SiliconFlow API key is invalid")
diff --git a/api/core/tools/provider/builtin/siliconflow/siliconflow.yaml b/api/core/tools/provider/builtin/siliconflow/siliconflow.yaml
deleted file mode 100644
index 46be99f262f211..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/siliconflow.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-identity:
- author: hjlarry
- name: siliconflow
- label:
- en_US: SiliconFlow
- zh_CN: 硅基流动
- description:
- en_US: The image generation API provided by SiliconFlow includes Flux and Stable Diffusion models.
- zh_CN: 硅基流动提供的图片生成 API,包含 Flux 和 Stable Diffusion 模型。
- icon: icon.svg
- tags:
- - image
-credentials_for_provider:
- siliconFlow_api_key:
- type: secret-input
- required: true
- label:
- en_US: SiliconFlow API Key
- placeholder:
- en_US: Please input your SiliconFlow API key
- url: https://cloud.siliconflow.cn/account/ak
diff --git a/api/core/tools/provider/builtin/siliconflow/tools/flux.py b/api/core/tools/provider/builtin/siliconflow/tools/flux.py
deleted file mode 100644
index 0d16ff385eb30d..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/tools/flux.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-FLUX_URL = {
- "schnell": "https://api.siliconflow.cn/v1/black-forest-labs/FLUX.1-schnell/text-to-image",
- "dev": "https://api.siliconflow.cn/v1/image/generations",
-}
-
-
-class FluxTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "accept": "application/json",
- "content-type": "application/json",
- "authorization": f"Bearer {self.runtime.credentials['siliconFlow_api_key']}",
- }
-
- payload = {
- "prompt": tool_parameters.get("prompt"),
- "image_size": tool_parameters.get("image_size", "1024x1024"),
- "seed": tool_parameters.get("seed"),
- "num_inference_steps": tool_parameters.get("num_inference_steps", 20),
- }
- model = tool_parameters.get("model", "schnell")
- url = FLUX_URL.get(model)
- if model == "dev":
- payload["model"] = "black-forest-labs/FLUX.1-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 in res.get("images", []):
- result.append(self.create_image_message(image=image.get("url"), save_as=self.VariableKey.IMAGE.value))
- return result
diff --git a/api/core/tools/provider/builtin/siliconflow/tools/flux.yaml b/api/core/tools/provider/builtin/siliconflow/tools/flux.yaml
deleted file mode 100644
index d06b9bf3e1f489..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/tools/flux.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
-identity:
- name: flux
- author: hjlarry
- label:
- en_US: Flux
- icon: icon.svg
-description:
- human:
- en_US: Generate image via SiliconFlow's flux model.
- llm: This tool is used to generate image from prompt via SiliconFlow's flux 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 image.
- form: llm
- - name: model
- type: select
- required: true
- options:
- - value: schnell
- label:
- en_US: Flux.1-schnell
- - value: dev
- label:
- en_US: Flux.1-dev
- default: schnell
- label:
- en_US: Choose Image Model
- zh_Hans: 选择生成图片的模型
- form: form
- - name: image_size
- type: select
- required: true
- options:
- - value: 1024x1024
- label:
- en_US: 1024x1024
- - value: 768x1024
- label:
- en_US: 768x1024
- - value: 576x1024
- label:
- en_US: 576x1024
- - value: 512x1024
- label:
- en_US: 512x1024
- - value: 1024x576
- label:
- en_US: 1024x576
- - value: 768x512
- label:
- en_US: 768x512
- default: 1024x1024
- label:
- en_US: Choose Image Size
- zh_Hans: 选择生成的图片大小
- form: form
- - name: num_inference_steps
- type: number
- required: true
- default: 20
- min: 1
- max: 100
- label:
- en_US: Num Inference Steps
- zh_Hans: 生成图片的步数
- form: form
- human_description:
- en_US: The number of inference steps to perform. More steps produce higher quality but take longer.
- zh_Hans: 执行的推理步骤数量。更多的步骤可以产生更高质量的结果,但需要更长的时间。
- - name: seed
- type: number
- 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
diff --git a/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.py b/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.py
deleted file mode 100644
index db43790c06aaa6..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-SILICONFLOW_API_URL = "https://api.siliconflow.cn/v1/image/generations"
-
-SD_MODELS = {
- "sd_3": "stabilityai/stable-diffusion-3-medium",
- "sd_xl": "stabilityai/stable-diffusion-xl-base-1.0",
- "sd_3.5_large": "stabilityai/stable-diffusion-3-5-large",
-}
-
-
-class StableDiffusionTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- headers = {
- "accept": "application/json",
- "content-type": "application/json",
- "authorization": f"Bearer {self.runtime.credentials['siliconFlow_api_key']}",
- }
-
- model = tool_parameters.get("model", "sd_3")
- sd_model = SD_MODELS.get(model)
-
- payload = {
- "model": sd_model,
- "prompt": tool_parameters.get("prompt"),
- "negative_prompt": tool_parameters.get("negative_prompt", ""),
- "image_size": tool_parameters.get("image_size", "1024x1024"),
- "batch_size": tool_parameters.get("batch_size", 1),
- "seed": tool_parameters.get("seed"),
- "guidance_scale": tool_parameters.get("guidance_scale", 7.5),
- "num_inference_steps": tool_parameters.get("num_inference_steps", 20),
- }
-
- response = requests.post(SILICONFLOW_API_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 in res.get("images", []):
- result.append(self.create_image_message(image=image.get("url"), save_as=self.VariableKey.IMAGE.value))
- return result
diff --git a/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.yaml b/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.yaml
deleted file mode 100644
index b330c92e163a38..00000000000000
--- a/api/core/tools/provider/builtin/siliconflow/tools/stable_diffusion.yaml
+++ /dev/null
@@ -1,124 +0,0 @@
-identity:
- name: stable_diffusion
- author: hjlarry
- label:
- en_US: Stable Diffusion
- icon: icon.svg
-description:
- human:
- en_US: Generate image via SiliconFlow's stable diffusion model.
- llm: This tool is used to generate image from prompt via SiliconFlow's stable diffusion 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 image.
- form: llm
- - name: negative_prompt
- type: string
- label:
- en_US: negative prompt
- zh_Hans: 负面提示词
- human_description:
- en_US: Describe what you don't want included in the image.
- zh_Hans: 描述您不希望包含在图片中的内容。
- llm_description: Describe what you don't want included in the image.
- form: llm
- - name: model
- type: select
- required: true
- options:
- - value: sd_3
- label:
- en_US: Stable Diffusion 3
- - value: sd_xl
- label:
- en_US: Stable Diffusion XL
- - value: sd_3.5_large
- label:
- en_US: Stable Diffusion 3.5 Large
- default: sd_3
- label:
- en_US: Choose Image Model
- zh_Hans: 选择生成图片的模型
- form: form
- - name: image_size
- type: select
- required: true
- options:
- - value: 1024x1024
- label:
- en_US: 1024x1024
- - value: 1024x2048
- label:
- en_US: 1024x2048
- - value: 1152x2048
- label:
- en_US: 1152x2048
- - value: 1536x1024
- label:
- en_US: 1536x1024
- - value: 1536x2048
- label:
- en_US: 1536x2048
- - value: 2048x1152
- label:
- en_US: 2048x1152
- default: 1024x1024
- label:
- en_US: Choose Image Size
- zh_Hans: 选择生成图片的大小
- form: form
- - name: batch_size
- type: number
- required: true
- default: 1
- min: 1
- max: 4
- label:
- en_US: Number Images
- zh_Hans: 生成图片的数量
- form: form
- - name: guidance_scale
- type: number
- required: true
- default: 7.5
- min: 0
- max: 100
- label:
- en_US: Guidance Scale
- zh_Hans: 与提示词紧密性
- human_description:
- en_US: Classifier Free Guidance. How close you want the model to stick to your prompt when looking for a related image to show you.
- zh_Hans: 无分类器引导。您希望模型在寻找相关图片向您展示时,与您的提示保持多紧密的关联度。
- form: form
- - name: num_inference_steps
- type: number
- required: true
- default: 20
- min: 1
- max: 100
- 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: seed
- type: number
- 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
diff --git a/api/core/tools/provider/builtin/slack/_assets/icon.svg b/api/core/tools/provider/builtin/slack/_assets/icon.svg
deleted file mode 100644
index e43c2c47dc128e..00000000000000
--- a/api/core/tools/provider/builtin/slack/_assets/icon.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
- Slack
-
-
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/slack/slack.py b/api/core/tools/provider/builtin/slack/slack.py
deleted file mode 100644
index 2de7911f63072a..00000000000000
--- a/api/core/tools/provider/builtin/slack/slack.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from core.tools.provider.builtin.slack.tools.slack_webhook import SlackWebhookTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SlackProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- SlackWebhookTool()
- pass
diff --git a/api/core/tools/provider/builtin/slack/slack.yaml b/api/core/tools/provider/builtin/slack/slack.yaml
deleted file mode 100644
index 1070ffbf038a40..00000000000000
--- a/api/core/tools/provider/builtin/slack/slack.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-identity:
- author: Pan YANG
- name: slack
- label:
- en_US: Slack
- zh_Hans: Slack
- pt_BR: Slack
- description:
- en_US: Slack Webhook
- zh_Hans: Slack Webhook
- pt_BR: Slack Webhook
- icon: icon.svg
- tags:
- - social
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/slack/tools/slack_webhook.py b/api/core/tools/provider/builtin/slack/tools/slack_webhook.py
deleted file mode 100644
index 85e0de76755898..00000000000000
--- a/api/core/tools/provider/builtin/slack/tools/slack_webhook.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SlackWebhookTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Incoming Webhooks
- API Document: https://api.slack.com/messaging/webhooks
- """
-
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- webhook_url = tool_parameters.get("webhook_url", "")
-
- if not webhook_url.startswith("https://hooks.slack.com/"):
- return self.create_text_message(
- f"Invalid parameter webhook_url ${webhook_url}, not a valid Slack webhook URL"
- )
-
- headers = {
- "Content-Type": "application/json",
- }
- params = {}
- payload = {
- "text": content,
- }
-
- try:
- res = httpx.post(webhook_url, headers=headers, params=params, json=payload)
- if res.is_success:
- return self.create_text_message("Text message was sent successfully")
- else:
- return self.create_text_message(
- f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to send message through webhook. {}".format(e))
diff --git a/api/core/tools/provider/builtin/slack/tools/slack_webhook.yaml b/api/core/tools/provider/builtin/slack/tools/slack_webhook.yaml
deleted file mode 100644
index b838d743733ec9..00000000000000
--- a/api/core/tools/provider/builtin/slack/tools/slack_webhook.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- name: slack_webhook
- author: Pan YANG
- label:
- en_US: Incoming Webhook to send message
- zh_Hans: 通过入站 Webhook 发送消息
- pt_BR: Incoming Webhook to send message
- icon: icon.svg
-description:
- human:
- en_US: Sending a message on Slack via the Incoming Webhook
- zh_Hans: 通过入站 Webhook 在 Slack 上发送消息
- pt_BR: Sending a message on Slack via the Incoming Webhook
- llm: A tool for sending messages to a chat on Slack.
-parameters:
- - name: webhook_url
- type: string
- required: true
- label:
- en_US: Slack Incoming Webhook url
- zh_Hans: Slack 入站 Webhook 的 url
- pt_BR: Slack Incoming Webhook url
- human_description:
- en_US: Slack Incoming Webhook url
- zh_Hans: Slack 入站 Webhook 的 url
- pt_BR: Slack Incoming Webhook url
- form: form
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- pt_BR: content
- human_description:
- en_US: Content to sent to the channel or person.
- zh_Hans: 消息内容文本
- pt_BR: Content to sent to the channel or person.
- llm_description: Content of the message
- form: llm
diff --git a/api/core/tools/provider/builtin/slidespeak/_assets/icon.png b/api/core/tools/provider/builtin/slidespeak/_assets/icon.png
deleted file mode 100644
index 4cac578330b156..00000000000000
Binary files a/api/core/tools/provider/builtin/slidespeak/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/slidespeak/slidespeak.py b/api/core/tools/provider/builtin/slidespeak/slidespeak.py
deleted file mode 100644
index 14c7c4880e892f..00000000000000
--- a/api/core/tools/provider/builtin/slidespeak/slidespeak.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from typing import Any
-
-import requests
-from yarl import URL
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SlideSpeakProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- api_key = credentials.get("slidespeak_api_key")
- base_url = credentials.get("base_url")
-
- if not api_key:
- raise ToolProviderCredentialValidationError("API key is missing")
-
- if base_url:
- base_url = str(URL(base_url) / "v1")
-
- headers = {"Content-Type": "application/json", "X-API-Key": api_key}
-
- test_task_id = "xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- url = f"{base_url or 'https://api.slidespeak.co/api/v1'}/task_status/{test_task_id}"
-
- response = requests.get(url, headers=headers)
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("Invalid SlidePeak API key")
diff --git a/api/core/tools/provider/builtin/slidespeak/slidespeak.yaml b/api/core/tools/provider/builtin/slidespeak/slidespeak.yaml
deleted file mode 100644
index 9f6927f1bdcdf3..00000000000000
--- a/api/core/tools/provider/builtin/slidespeak/slidespeak.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-identity:
- author: Kalo Chin
- name: slidespeak
- label:
- en_US: SlideSpeak
- zh_Hans: SlideSpeak
- description:
- en_US: Generate presentation slides using SlideSpeak API
- zh_Hans: 使用 SlideSpeak API 生成演示幻灯片
- icon: icon.png
-
-credentials_for_provider:
- slidespeak_api_key:
- type: secret-input
- required: true
- label:
- en_US: API Key
- zh_Hans: API 密钥
- placeholder:
- en_US: Enter your SlideSpeak API key
- zh_Hans: 输入您的 SlideSpeak API 密钥
- url: https://app.slidespeak.co/settings/developer
diff --git a/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.py b/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.py
deleted file mode 100644
index aa4ee63e9767c9..00000000000000
--- a/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.py
+++ /dev/null
@@ -1,163 +0,0 @@
-import asyncio
-from dataclasses import asdict, dataclass
-from enum import Enum
-from typing import Any, Optional, Union
-
-import aiohttp
-from pydantic import ConfigDict
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SlidesGeneratorTool(BuiltinTool):
- """
- Tool for generating presentations using the SlideSpeak API.
- """
-
- model_config = ConfigDict(arbitrary_types_allowed=True)
-
- headers: Optional[dict[str, str]] = None
- base_url: Optional[str] = None
- timeout: Optional[aiohttp.ClientTimeout] = None
- poll_interval: Optional[int] = None
-
- class TaskState(Enum):
- FAILURE = "FAILURE"
- REVOKED = "REVOKED"
- SUCCESS = "SUCCESS"
- PENDING = "PENDING"
- RECEIVED = "RECEIVED"
- STARTED = "STARTED"
-
- @dataclass
- class PresentationRequest:
- plain_text: str
- length: Optional[int] = None
- theme: Optional[str] = None
-
- async def _generate_presentation(
- self,
- session: aiohttp.ClientSession,
- request: PresentationRequest,
- ) -> dict[str, Any]:
- """Generate a new presentation asynchronously"""
- async with session.post(
- f"{self.base_url}/presentation/generate",
- headers=self.headers,
- json=asdict(request),
- timeout=self.timeout,
- ) as response:
- response.raise_for_status()
- return await response.json()
-
- async def _get_task_status(
- self,
- session: aiohttp.ClientSession,
- task_id: str,
- ) -> dict[str, Any]:
- """Get the status of a task asynchronously"""
- async with session.get(
- f"{self.base_url}/task_status/{task_id}",
- headers=self.headers,
- timeout=self.timeout,
- ) as response:
- response.raise_for_status()
- return await response.json()
-
- async def _wait_for_completion(
- self,
- session: aiohttp.ClientSession,
- task_id: str,
- ) -> str:
- """Wait for task completion and return download URL"""
- while True:
- status = await self._get_task_status(session, task_id)
- task_status = self.TaskState(status["task_status"])
- if task_status == self.TaskState.SUCCESS:
- return status["task_result"]["url"]
- if task_status in [self.TaskState.FAILURE, self.TaskState.REVOKED]:
- raise Exception(f"Task failed with status: {task_status.value}")
- await asyncio.sleep(self.poll_interval)
-
- async def _generate_slides(
- self,
- plain_text: str,
- length: Optional[int],
- theme: Optional[str],
- ) -> str:
- """Generate slides and return the download URL"""
- async with aiohttp.ClientSession() as session:
- request = self.PresentationRequest(
- plain_text=plain_text,
- length=length,
- theme=theme,
- )
- result = await self._generate_presentation(session, request)
- task_id = result["task_id"]
- download_url = await self._wait_for_completion(session, task_id)
- return download_url
-
- async def _fetch_presentation(
- self,
- session: aiohttp.ClientSession,
- download_url: str,
- ) -> bytes:
- """Fetch the presentation file from the download URL"""
- async with session.get(download_url, timeout=self.timeout) as response:
- response.raise_for_status()
- return await response.read()
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """Synchronous invoke method that runs asynchronous code"""
-
- async def async_invoke():
- # Extract parameters
- plain_text = tool_parameters.get("plain_text", "")
- length = tool_parameters.get("length")
- theme = tool_parameters.get("theme")
-
- # Ensure runtime and credentials
- if not self.runtime or not self.runtime.credentials:
- raise ToolProviderCredentialValidationError("Tool runtime or credentials are missing")
-
- # Get API key from credentials
- api_key = self.runtime.credentials.get("slidespeak_api_key")
- if not api_key:
- raise ToolProviderCredentialValidationError("SlideSpeak API key is missing")
-
- # Set configuration
- self.headers = {
- "Content-Type": "application/json",
- "X-API-Key": api_key,
- }
- self.base_url = "https://api.slidespeak.co/api/v1"
- self.timeout = aiohttp.ClientTimeout(total=30)
- self.poll_interval = 2
-
- # Run the asynchronous slide generation
- try:
- download_url = await self._generate_slides(plain_text, length, theme)
-
- # Fetch the presentation file
- async with aiohttp.ClientSession() as session:
- presentation_bytes = await self._fetch_presentation(session, download_url)
-
- return [
- self.create_text_message(download_url),
- self.create_blob_message(
- blob=presentation_bytes,
- meta={"mime_type": "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
- ),
- ]
- except Exception as e:
- return [self.create_text_message(f"An error occurred: {str(e)}")]
-
- # Run the asynchronous code synchronously
- result = asyncio.run(async_invoke())
- return result
diff --git a/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.yaml b/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.yaml
deleted file mode 100644
index f881dadb20f82b..00000000000000
--- a/api/core/tools/provider/builtin/slidespeak/tools/slides_generator.yaml
+++ /dev/null
@@ -1,102 +0,0 @@
-identity:
- name: slide_generator
- author: Kalo Chin
- label:
- en_US: Slides Generator
- zh_Hans: 幻灯片生成器
-description:
- human:
- en_US: Generate presentation slides from text using SlideSpeak API.
- zh_Hans: 使用 SlideSpeak API 从文本生成演示幻灯片。
- llm: This tool converts text input into a presentation using the SlideSpeak API service, with options for slide length and theme.
-parameters:
- - name: plain_text
- type: string
- required: true
- label:
- en_US: Topic or Content
- zh_Hans: 主题或内容
- human_description:
- en_US: The topic or content to be converted into presentation slides.
- zh_Hans: 需要转换为幻灯片的内容或主题。
- llm_description: A string containing the topic or content to be transformed into presentation slides.
- form: llm
- - name: length
- type: number
- required: false
- label:
- en_US: Number of Slides
- zh_Hans: 幻灯片数量
- human_description:
- en_US: The desired number of slides in the presentation (optional).
- zh_Hans: 演示文稿中所需的幻灯片数量(可选)。
- llm_description: Optional parameter specifying the number of slides to generate.
- form: form
- - name: theme
- type: select
- required: false
- label:
- en_US: Presentation Theme
- zh_Hans: 演示主题
- human_description:
- en_US: The visual theme for the presentation (optional).
- zh_Hans: 演示文稿的视觉主题(可选)。
- llm_description: Optional parameter specifying the presentation theme.
- options:
- - label:
- en_US: Adam
- zh_Hans: Adam
- value: adam
- - label:
- en_US: Aurora
- zh_Hans: Aurora
- value: aurora
- - label:
- en_US: Bruno
- zh_Hans: Bruno
- value: bruno
- - label:
- en_US: Clyde
- zh_Hans: Clyde
- value: clyde
- - label:
- en_US: Daniel
- zh_Hans: Daniel
- value: daniel
- - label:
- en_US: Default
- zh_Hans: Default
- value: default
- - label:
- en_US: Eddy
- zh_Hans: Eddy
- value: eddy
- - label:
- en_US: Felix
- zh_Hans: Felix
- value: felix
- - label:
- en_US: Gradient
- zh_Hans: Gradient
- value: gradient
- - label:
- en_US: Iris
- zh_Hans: Iris
- value: iris
- - label:
- en_US: Lavender
- zh_Hans: Lavender
- value: lavender
- - label:
- en_US: Monolith
- zh_Hans: Monolith
- value: monolith
- - label:
- en_US: Nebula
- zh_Hans: Nebula
- value: nebula
- - label:
- en_US: Nexus
- zh_Hans: Nexus
- value: nexus
- form: form
diff --git a/api/core/tools/provider/builtin/spark/__init__.py b/api/core/tools/provider/builtin/spark/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/spark/_assets/icon.svg b/api/core/tools/provider/builtin/spark/_assets/icon.svg
deleted file mode 100644
index ef0a9131a48e43..00000000000000
--- a/api/core/tools/provider/builtin/spark/_assets/icon.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/api/core/tools/provider/builtin/spark/spark.py b/api/core/tools/provider/builtin/spark/spark.py
deleted file mode 100644
index e0b1a58a3f679a..00000000000000
--- a/api/core/tools/provider/builtin/spark/spark.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import json
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.spark.tools.spark_img_generation import spark_response
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SparkProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- if "APPID" not in credentials or not credentials.get("APPID"):
- raise ToolProviderCredentialValidationError("APPID is required.")
- if "APISecret" not in credentials or not credentials.get("APISecret"):
- raise ToolProviderCredentialValidationError("APISecret is required.")
- if "APIKey" not in credentials or not credentials.get("APIKey"):
- raise ToolProviderCredentialValidationError("APIKey is required.")
-
- appid = credentials.get("APPID")
- apisecret = credentials.get("APISecret")
- apikey = credentials.get("APIKey")
- prompt = "a cute black dog"
-
- try:
- response = spark_response(prompt, appid, apikey, apisecret)
- data = json.loads(response)
- code = data["header"]["code"]
-
- if code == 0:
- # 0 success,
- pass
- else:
- raise ToolProviderCredentialValidationError("image generate error, code:{}".format(code))
- except Exception as e:
- raise ToolProviderCredentialValidationError("APPID APISecret APIKey is invalid. {}".format(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/spark/spark.yaml b/api/core/tools/provider/builtin/spark/spark.yaml
deleted file mode 100644
index fa1543443a2af8..00000000000000
--- a/api/core/tools/provider/builtin/spark/spark.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-identity:
- author: Onelevenvy
- name: spark
- label:
- en_US: Spark
- zh_Hans: 讯飞星火
- pt_BR: Spark
- description:
- en_US: Spark Platform Toolkit
- zh_Hans: 讯飞星火平台工具
- pt_BR: Pacote de Ferramentas da Plataforma Spark
- icon: icon.svg
- tags:
- - image
-credentials_for_provider:
- APPID:
- type: secret-input
- required: true
- label:
- en_US: Spark APPID
- zh_Hans: APPID
- pt_BR: Spark APPID
- help:
- en_US: Please input your APPID
- zh_Hans: 请输入你的 APPID
- pt_BR: Please input your APPID
- placeholder:
- en_US: Please input your APPID
- zh_Hans: 请输入你的 APPID
- pt_BR: Please input your APPID
- APISecret:
- type: secret-input
- required: true
- label:
- en_US: Spark APISecret
- zh_Hans: APISecret
- pt_BR: Spark APISecret
- help:
- en_US: Please input your Spark APISecret
- zh_Hans: 请输入你的 APISecret
- pt_BR: Please input your Spark APISecret
- placeholder:
- en_US: Please input your Spark APISecret
- zh_Hans: 请输入你的 APISecret
- pt_BR: Please input your Spark APISecret
- APIKey:
- type: secret-input
- required: true
- label:
- en_US: Spark APIKey
- zh_Hans: APIKey
- pt_BR: Spark APIKey
- help:
- en_US: Please input your Spark APIKey
- zh_Hans: 请输入你的 APIKey
- pt_BR: Please input your Spark APIKey
- placeholder:
- en_US: Please input your Spark APIKey
- zh_Hans: 请输入你的 APIKey
- pt_BR: Please input Spark APIKey
- url: https://console.xfyun.cn/services
diff --git a/api/core/tools/provider/builtin/spark/tools/spark_img_generation.py b/api/core/tools/provider/builtin/spark/tools/spark_img_generation.py
deleted file mode 100644
index 81d9e8d94185f7..00000000000000
--- a/api/core/tools/provider/builtin/spark/tools/spark_img_generation.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import base64
-import hashlib
-import hmac
-import json
-from base64 import b64decode
-from datetime import datetime
-from time import mktime
-from typing import Any, Union
-from urllib.parse import urlencode
-from wsgiref.handlers import format_date_time
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class AssembleHeaderError(Exception):
- def __init__(self, msg):
- self.message = msg
-
-
-class Url:
- def __init__(self, host, path, schema):
- self.host = host
- self.path = path
- self.schema = schema
-
-
-# calculate sha256 and encode to base64
-def sha256base64(data):
- sha256 = hashlib.sha256()
- sha256.update(data)
- digest = base64.b64encode(sha256.digest()).decode(encoding="utf-8")
- return digest
-
-
-def parse_url(request_url):
- stidx = request_url.index("://")
- host = request_url[stidx + 3 :]
- schema = request_url[: stidx + 3]
- edidx = host.index("/")
- if edidx <= 0:
- raise AssembleHeaderError("invalid request url:" + request_url)
- path = host[edidx:]
- host = host[:edidx]
- u = Url(host, path, schema)
- return u
-
-
-def assemble_ws_auth_url(request_url, method="GET", api_key="", api_secret=""):
- u = parse_url(request_url)
- host = u.host
- path = u.path
- now = datetime.now()
- date = format_date_time(mktime(now.timetuple()))
- signature_origin = "host: {}\ndate: {}\n{} {} HTTP/1.1".format(host, date, method, path)
- signature_sha = hmac.new(
- api_secret.encode("utf-8"),
- signature_origin.encode("utf-8"),
- digestmod=hashlib.sha256,
- ).digest()
- signature_sha = base64.b64encode(signature_sha).decode(encoding="utf-8")
- authorization_origin = (
- f'api_key="{api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha}"'
- )
-
- authorization = base64.b64encode(authorization_origin.encode("utf-8")).decode(encoding="utf-8")
- values = {"host": host, "date": date, "authorization": authorization}
-
- return request_url + "?" + urlencode(values)
-
-
-def get_body(appid, text):
- body = {
- "header": {"app_id": appid, "uid": "123456789"},
- "parameter": {"chat": {"domain": "general", "temperature": 0.5, "max_tokens": 4096}},
- "payload": {"message": {"text": [{"role": "user", "content": text}]}},
- }
- return body
-
-
-def spark_response(text, appid, apikey, apisecret):
- host = "http://spark-api.cn-huabei-1.xf-yun.com/v2.1/tti"
- url = assemble_ws_auth_url(host, method="POST", api_key=apikey, api_secret=apisecret)
- content = get_body(appid, text)
- response = requests.post(url, json=content, headers={"content-type": "application/json"}).text
- return response
-
-
-class SparkImgGeneratorTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
-
- if "APPID" not in self.runtime.credentials or not self.runtime.credentials.get("APPID"):
- return self.create_text_message("APPID is required.")
- if "APISecret" not in self.runtime.credentials or not self.runtime.credentials.get("APISecret"):
- return self.create_text_message("APISecret is required.")
- if "APIKey" not in self.runtime.credentials or not self.runtime.credentials.get("APIKey"):
- return self.create_text_message("APIKey is required.")
-
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
- res = self.img_generation(prompt)
- result = []
- for image in res:
- result.append(
- self.create_blob_message(
- blob=b64decode(image["base64_image"]),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- )
- )
- return result
-
- def img_generation(self, prompt):
- response = spark_response(
- text=prompt,
- appid=self.runtime.credentials.get("APPID"),
- apikey=self.runtime.credentials.get("APIKey"),
- apisecret=self.runtime.credentials.get("APISecret"),
- )
- data = json.loads(response)
- code = data["header"]["code"]
- if code != 0:
- return self.create_text_message(f"error: {code}, {data}")
- else:
- text = data["payload"]["choices"]["text"]
- image_content = text[0]
- image_base = image_content["content"]
- json_data = {"base64_image": image_base}
- return [json_data]
diff --git a/api/core/tools/provider/builtin/spark/tools/spark_img_generation.yaml b/api/core/tools/provider/builtin/spark/tools/spark_img_generation.yaml
deleted file mode 100644
index d44bbc9564ef88..00000000000000
--- a/api/core/tools/provider/builtin/spark/tools/spark_img_generation.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-identity:
- name: spark_img_generation
- author: Onelevenvy
- label:
- en_US: Spark Image Generation
- zh_Hans: 图片生成
- pt_BR: Geração de imagens Spark
- icon: icon.svg
- description:
- en_US: Spark Image Generation
- zh_Hans: 图片生成
- pt_BR: Geração de imagens Spark
-description:
- human:
- en_US: Generate images based on user input, with image generation API
- provided by Spark
- zh_Hans: 根据用户的输入生成图片,由讯飞星火提供图片生成api
- pt_BR: Gerar imagens com base na entrada do usuário, com API de geração
- de imagem fornecida pela Spark
- llm: spark_img_generation is a tool used to generate images from text
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt
- zh_Hans: 图像提示词
- pt_BR: Image prompt
- llm_description: Image prompt of spark_img_generation tooll, you should
- describe the image you want to generate as a list of words as possible
- as detailed
- form: llm
diff --git a/api/core/tools/provider/builtin/spider/_assets/icon.svg b/api/core/tools/provider/builtin/spider/_assets/icon.svg
deleted file mode 100644
index 604a09d01d7444..00000000000000
--- a/api/core/tools/provider/builtin/spider/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-Spider v1 Logo
diff --git a/api/core/tools/provider/builtin/spider/spider.py b/api/core/tools/provider/builtin/spider/spider.py
deleted file mode 100644
index 5959555318722e..00000000000000
--- a/api/core/tools/provider/builtin/spider/spider.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.spider.spiderApp import Spider
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class SpiderProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- app = Spider(api_key=credentials["spider_api_key"])
- app.scrape_url(url="https://spider.cloud")
- except AttributeError as e:
- # Handle cases where NoneType is not iterable, which might indicate API issues
- if "NoneType" in str(e) and "not iterable" in str(e):
- raise ToolProviderCredentialValidationError("API is currently down, try again in 15 minutes", str(e))
- else:
- raise ToolProviderCredentialValidationError("An unexpected error occurred.", str(e))
- except Exception as e:
- raise ToolProviderCredentialValidationError("An unexpected error occurred.", str(e))
diff --git a/api/core/tools/provider/builtin/spider/spider.yaml b/api/core/tools/provider/builtin/spider/spider.yaml
deleted file mode 100644
index 45702c85ddea24..00000000000000
--- a/api/core/tools/provider/builtin/spider/spider.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- author: William Espegren
- name: spider
- label:
- en_US: Spider
- zh_CN: Spider
- description:
- en_US: Spider API integration, returning LLM-ready data by scraping & crawling websites.
- zh_CN: Spider API 集成,通过爬取和抓取网站返回 LLM-ready 数据。
- icon: icon.svg
- tags:
- - search
- - utilities
-credentials_for_provider:
- spider_api_key:
- type: secret-input
- required: true
- label:
- en_US: Spider API Key
- zh_CN: Spider API 密钥
- placeholder:
- en_US: Please input your Spider API key
- zh_CN: 请输入您的 Spider API 密钥
- help:
- en_US: Get your Spider API key from your Spider dashboard
- zh_CN: 从您的 Spider 仪表板中获取 Spider API 密钥。
- url: https://spider.cloud/
diff --git a/api/core/tools/provider/builtin/spider/spiderApp.py b/api/core/tools/provider/builtin/spider/spiderApp.py
deleted file mode 100644
index 4bc446a1a092a3..00000000000000
--- a/api/core/tools/provider/builtin/spider/spiderApp.py
+++ /dev/null
@@ -1,221 +0,0 @@
-import os
-from typing import Literal, Optional, TypedDict
-
-import requests
-
-
-class RequestParamsDict(TypedDict, total=False):
- url: Optional[str]
- request: Optional[Literal["http", "chrome", "smart"]]
- limit: Optional[int]
- return_format: Optional[Literal["raw", "markdown", "html2text", "text", "bytes"]]
- tld: Optional[bool]
- depth: Optional[int]
- cache: Optional[bool]
- budget: Optional[dict[str, int]]
- locale: Optional[str]
- cookies: Optional[str]
- stealth: Optional[bool]
- headers: Optional[dict[str, str]]
- anti_bot: Optional[bool]
- metadata: Optional[bool]
- viewport: Optional[dict[str, int]]
- encoding: Optional[str]
- subdomains: Optional[bool]
- user_agent: Optional[str]
- store_data: Optional[bool]
- gpt_config: Optional[list[str]]
- fingerprint: Optional[bool]
- storageless: Optional[bool]
- readability: Optional[bool]
- proxy_enabled: Optional[bool]
- respect_robots: Optional[bool]
- query_selector: Optional[str]
- full_resources: Optional[bool]
- request_timeout: Optional[int]
- run_in_background: Optional[bool]
- skip_config_checks: Optional[bool]
-
-
-class Spider:
- def __init__(self, api_key: Optional[str] = None):
- """
- Initialize the Spider with an API key.
-
- :param api_key: A string of the API key for Spider. Defaults to the SPIDER_API_KEY environment variable.
- :raises ValueError: If no API key is provided.
- """
- self.api_key = api_key or os.getenv("SPIDER_API_KEY")
- if self.api_key is None:
- raise ValueError("No API key provided")
-
- def api_post(
- self,
- endpoint: str,
- data: dict,
- stream: bool,
- content_type: str = "application/json",
- ):
- """
- Send a POST request to the specified API endpoint.
-
- :param endpoint: The API endpoint to which the POST request is sent.
- :param data: The data (dictionary) to be sent in the POST request.
- :param stream: Boolean indicating if the response should be streamed.
- :return: The JSON response or the raw response stream if stream is True.
- """
- headers = self._prepare_headers(content_type)
- response = self._post_request(f"https://api.spider.cloud/v1/{endpoint}", data, headers, stream)
-
- if stream:
- return response
- elif response.status_code == 200:
- return response.json()
- else:
- self._handle_error(response, f"post to {endpoint}")
-
- def api_get(self, endpoint: str, stream: bool, content_type: str = "application/json"):
- """
- Send a GET request to the specified endpoint.
-
- :param endpoint: The API endpoint from which to retrieve data.
- :return: The JSON decoded response.
- """
- headers = self._prepare_headers(content_type)
- response = self._get_request(f"https://api.spider.cloud/v1/{endpoint}", headers, stream)
- if response.status_code == 200:
- return response.json()
- else:
- self._handle_error(response, f"get from {endpoint}")
-
- def get_credits(self):
- """
- Retrieve the account's remaining credits.
-
- :return: JSON response containing the number of credits left.
- """
- return self.api_get("credits", stream=False)
-
- def scrape_url(
- self,
- url: str,
- params: Optional[RequestParamsDict] = None,
- stream: bool = False,
- content_type: str = "application/json",
- ):
- """
- Scrape data from the specified URL.
-
- :param url: The URL from which to scrape data.
- :param params: Optional dictionary of additional parameters for the scrape request.
- :return: JSON response containing the scraping results.
- """
- params = params or {}
-
- # Add { "return_format": "markdown" } to the params if not already present
- if "return_format" not in params:
- params["return_format"] = "markdown"
-
- # Set limit to 1
- params["limit"] = 1
-
- return self.api_post("crawl", {"url": url, **(params or {})}, stream, content_type)
-
- def crawl_url(
- self,
- url: str,
- params: Optional[RequestParamsDict] = None,
- stream: bool = False,
- content_type: str = "application/json",
- ):
- """
- Start crawling at the specified URL.
-
- :param url: The URL to begin crawling.
- :param params: Optional dictionary with additional parameters to customize the crawl.
- :param stream: Boolean indicating if the response should be streamed. Defaults to False.
- :return: JSON response or the raw response stream if streaming enabled.
- """
- params = params or {}
-
- # Add { "return_format": "markdown" } to the params if not already present
- if "return_format" not in params:
- params["return_format"] = "markdown"
-
- return self.api_post("crawl", {"url": url, **(params or {})}, stream, content_type)
-
- def links(
- self,
- url: str,
- params: Optional[RequestParamsDict] = None,
- stream: bool = False,
- content_type: str = "application/json",
- ):
- """
- Retrieve links from the specified URL.
-
- :param url: The URL from which to extract links.
- :param params: Optional parameters for the link retrieval request.
- :return: JSON response containing the links.
- """
- return self.api_post("links", {"url": url, **(params or {})}, stream, content_type)
-
- def extract_contacts(
- self,
- url: str,
- params: Optional[RequestParamsDict] = None,
- stream: bool = False,
- content_type: str = "application/json",
- ):
- """
- Extract contact information from the specified URL.
-
- :param url: The URL from which to extract contact information.
- :param params: Optional parameters for the contact extraction.
- :return: JSON response containing extracted contact details.
- """
- return self.api_post(
- "pipeline/extract-contacts",
- {"url": url, **(params or {})},
- stream,
- content_type,
- )
-
- def label(
- self,
- url: str,
- params: Optional[RequestParamsDict] = None,
- stream: bool = False,
- content_type: str = "application/json",
- ):
- """
- Apply labeling to data extracted from the specified URL.
-
- :param url: The URL to label data from.
- :param params: Optional parameters to guide the labeling process.
- :return: JSON response with labeled data.
- """
- return self.api_post("pipeline/label", {"url": url, **(params or {})}, stream, content_type)
-
- def _prepare_headers(self, content_type: str = "application/json"):
- return {
- "Content-Type": content_type,
- "Authorization": f"Bearer {self.api_key}",
- "User-Agent": "Spider-Client/0.0.27",
- }
-
- def _post_request(self, url: str, data, headers, stream=False):
- return requests.post(url, headers=headers, json=data, stream=stream)
-
- def _get_request(self, url: str, headers, stream=False):
- return requests.get(url, headers=headers, stream=stream)
-
- def _delete_request(self, url: str, headers, stream=False):
- return requests.delete(url, headers=headers, stream=stream)
-
- def _handle_error(self, response, action):
- if response.status_code in {402, 409, 500}:
- error_message = response.json().get("error", "Unknown error occurred")
- raise Exception(f"Failed to {action}. Status code: {response.status_code}. Error: {error_message}")
- else:
- raise Exception(f"Unexpected error occurred while trying to {action}. Status code: {response.status_code}")
diff --git a/api/core/tools/provider/builtin/spider/tools/scraper_crawler.py b/api/core/tools/provider/builtin/spider/tools/scraper_crawler.py
deleted file mode 100644
index 20d2daef550de1..00000000000000
--- a/api/core/tools/provider/builtin/spider/tools/scraper_crawler.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.spider.spiderApp import Spider
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class ScrapeTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- # initialize the app object with the api key
- app = Spider(api_key=self.runtime.credentials["spider_api_key"])
-
- url = tool_parameters["url"]
- mode = tool_parameters["mode"]
-
- options = {
- "limit": tool_parameters.get("limit", 0),
- "depth": tool_parameters.get("depth", 0),
- "blacklist": tool_parameters.get("blacklist", "").split(",") if tool_parameters.get("blacklist") else [],
- "whitelist": tool_parameters.get("whitelist", "").split(",") if tool_parameters.get("whitelist") else [],
- "readability": tool_parameters.get("readability", False),
- }
-
- result = ""
-
- try:
- if mode == "scrape":
- scrape_result = app.scrape_url(
- url=url,
- params=options,
- )
-
- for i in scrape_result:
- result += "URL: " + i.get("url", "") + "\n"
- result += "CONTENT: " + i.get("content", "") + "\n\n"
- elif mode == "crawl":
- crawl_result = app.crawl_url(
- url=tool_parameters["url"],
- params=options,
- )
- for i in crawl_result:
- result += "URL: " + i.get("url", "") + "\n"
- result += "CONTENT: " + i.get("content", "") + "\n\n"
- except Exception as e:
- return self.create_text_message("An error occurred", str(e))
-
- return self.create_text_message(result)
diff --git a/api/core/tools/provider/builtin/spider/tools/scraper_crawler.yaml b/api/core/tools/provider/builtin/spider/tools/scraper_crawler.yaml
deleted file mode 100644
index 5b20c2fc2f70ad..00000000000000
--- a/api/core/tools/provider/builtin/spider/tools/scraper_crawler.yaml
+++ /dev/null
@@ -1,102 +0,0 @@
-identity:
- name: scraper_crawler
- author: William Espegren
- label:
- en_US: Web Scraper & Crawler
- zh_Hans: 网页抓取与爬虫
-description:
- human:
- en_US: A tool for scraping & crawling webpages. Input should be a url.
- zh_Hans: 用于抓取和爬取网页的工具。输入应该是一个网址。
- llm: A tool for scraping & crawling webpages. Input should be a url.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: URL
- zh_Hans: 网址
- human_description:
- en_US: url to be scraped or crawled
- zh_Hans: 要抓取或爬取的网址
- llm_description: url to either be scraped or crawled
- form: llm
- - name: mode
- type: select
- required: true
- options:
- - value: scrape
- label:
- en_US: scrape
- zh_Hans: 抓取
- - value: crawl
- label:
- en_US: crawl
- zh_Hans: 爬取
- default: crawl
- label:
- en_US: Mode
- zh_Hans: 模式
- human_description:
- en_US: used for selecting to either scrape the website or crawl the entire website following subpages
- zh_Hans: 用于选择抓取网站或爬取整个网站及其子页面
- form: form
- - name: limit
- type: number
- required: false
- label:
- en_US: maximum number of pages to crawl
- zh_Hans: 最大爬取页面数
- human_description:
- en_US: specify the maximum number of pages to crawl per website. the crawler will stop after reaching this limit.
- zh_Hans: 指定每个网站要爬取的最大页面数。爬虫将在达到此限制后停止。
- form: form
- min: 0
- default: 0
- - name: depth
- type: number
- required: false
- label:
- en_US: maximum depth of pages to crawl
- zh_Hans: 最大爬取深度
- human_description:
- en_US: the crawl limit for maximum depth.
- zh_Hans: 最大爬取深度的限制。
- form: form
- min: 0
- default: 0
- - name: blacklist
- type: string
- required: false
- label:
- en_US: url patterns to exclude
- zh_Hans: 要排除的URL模式
- human_description:
- en_US: blacklist a set of paths that you do not want to crawl. you can use regex patterns to help with the list.
- zh_Hans: 指定一组不想爬取的路径。您可以使用正则表达式模式来帮助定义列表。
- placeholder:
- en_US: /blog/*, /about
- form: form
- - name: whitelist
- type: string
- required: false
- label:
- en_US: URL patterns to include
- zh_Hans: 要包含的URL模式
- human_description:
- en_US: Whitelist a set of paths that you want to crawl, ignoring all other routes that do not match the patterns. You can use regex patterns to help with the list.
- zh_Hans: 指定一组要爬取的路径,忽略所有不匹配模式的其他路由。您可以使用正则表达式模式来帮助定义列表。
- placeholder:
- en_US: /blog/*, /about
- form: form
- - name: readability
- type: boolean
- required: false
- label:
- en_US: Pre-process the content for LLM usage
- zh_Hans: 仅返回页面的主要内容
- human_description:
- en_US: Use Mozilla's readability to pre-process the content for reading. This may drastically improve the content for LLM usage.
- zh_Hans: 如果启用,爬虫将仅返回页面的主要内容,不包括标题、导航、页脚等。
- form: form
- default: false
diff --git a/api/core/tools/provider/builtin/stability/_assets/icon.svg b/api/core/tools/provider/builtin/stability/_assets/icon.svg
deleted file mode 100644
index 56357a35557ac3..00000000000000
--- a/api/core/tools/provider/builtin/stability/_assets/icon.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/stability/stability.py b/api/core/tools/provider/builtin/stability/stability.py
deleted file mode 100644
index f09d81ac270288..00000000000000
--- a/api/core/tools/provider/builtin/stability/stability.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin.stability.tools.base import BaseStabilityAuthorization
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class StabilityToolProvider(BuiltinToolProviderController, BaseStabilityAuthorization):
- """
- This class is responsible for providing the stability tool.
- """
-
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- """
- This method is responsible for validating the credentials.
- """
- self.sd_validate_credentials(credentials)
diff --git a/api/core/tools/provider/builtin/stability/stability.yaml b/api/core/tools/provider/builtin/stability/stability.yaml
deleted file mode 100644
index c3e01c1e314d51..00000000000000
--- a/api/core/tools/provider/builtin/stability/stability.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- author: Dify
- name: stability
- label:
- en_US: Stability
- zh_Hans: Stability
- pt_BR: Stability
- description:
- en_US: Activating humanity's potential through generative AI
- zh_Hans: 通过生成式 AI 激活人类的潜力
- pt_BR: Activating humanity's potential through generative AI
- icon: icon.svg
- tags:
- - image
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API key
- zh_Hans: API key
- pt_BR: API key
- placeholder:
- en_US: Please input your API key
- zh_Hans: 请输入你的 API key
- pt_BR: Please input your API key
- help:
- en_US: Get your API key from Stability
- zh_Hans: 从 Stability 获取你的 API key
- pt_BR: Get your API key from Stability
- url: https://platform.stability.ai/account/keys
diff --git a/api/core/tools/provider/builtin/stability/tools/base.py b/api/core/tools/provider/builtin/stability/tools/base.py
deleted file mode 100644
index 2d1cd928703151..00000000000000
--- a/api/core/tools/provider/builtin/stability/tools/base.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import requests
-from yarl import URL
-
-from core.tools.errors import ToolProviderCredentialValidationError
-
-
-class BaseStabilityAuthorization:
- def sd_validate_credentials(self, credentials: dict):
- """
- This method is responsible for validating the credentials.
- """
- api_key = credentials.get("api_key", "")
- if not api_key:
- raise ToolProviderCredentialValidationError("API key is required.")
-
- response = requests.get(
- URL("https://api.stability.ai") / "v1" / "user" / "account",
- headers=self.generate_authorization_headers(credentials),
- timeout=(5, 30),
- )
-
- if not response.ok:
- raise ToolProviderCredentialValidationError("Invalid API key.")
-
- return True
-
- def generate_authorization_headers(self, credentials: dict) -> dict[str, str]:
- """
- This method is responsible for generating the authorization headers.
- """
- return {"Authorization": f"Bearer {credentials.get('api_key', '')}"}
diff --git a/api/core/tools/provider/builtin/stability/tools/text2image.py b/api/core/tools/provider/builtin/stability/tools/text2image.py
deleted file mode 100644
index 6bcf315484ad50..00000000000000
--- a/api/core/tools/provider/builtin/stability/tools/text2image.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from typing import Any
-
-from httpx import post
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.provider.builtin.stability.tools.base import BaseStabilityAuthorization
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class StableDiffusionTool(BuiltinTool, BaseStabilityAuthorization):
- """
- This class is responsible for providing the stable diffusion tool.
- """
-
- model_endpoint_map: dict[str, str] = {
- "sd3": "https://api.stability.ai/v2beta/stable-image/generate/sd3",
- "sd3-turbo": "https://api.stability.ai/v2beta/stable-image/generate/sd3",
- "core": "https://api.stability.ai/v2beta/stable-image/generate/core",
- }
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invoke the tool.
- """
- payload = {
- "prompt": tool_parameters.get("prompt", ""),
- "aspect_ratio": tool_parameters.get("aspect_ratio", "16:9") or tool_parameters.get("aspect_radio", "16:9"),
- "mode": "text-to-image",
- "seed": tool_parameters.get("seed", 0),
- "output_format": "png",
- }
-
- model = tool_parameters.get("model", "core")
-
- if model in {"sd3", "sd3-turbo"}:
- payload["model"] = tool_parameters.get("model")
-
- if model != "sd3-turbo":
- payload["negative_prompt"] = tool_parameters.get("negative_prompt", "")
-
- response = post(
- self.model_endpoint_map[tool_parameters.get("model", "core")],
- headers={
- "accept": "image/*",
- **self.generate_authorization_headers(self.runtime.credentials),
- },
- files={key: (None, str(value)) for key, value in payload.items()},
- timeout=(5, 30),
- )
-
- if not response.status_code == 200:
- raise Exception(response.text)
-
- return self.create_blob_message(
- blob=response.content, meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
diff --git a/api/core/tools/provider/builtin/stability/tools/text2image.yaml b/api/core/tools/provider/builtin/stability/tools/text2image.yaml
deleted file mode 100644
index 21345f9f187f07..00000000000000
--- a/api/core/tools/provider/builtin/stability/tools/text2image.yaml
+++ /dev/null
@@ -1,142 +0,0 @@
-identity:
- name: stability_text2image
- author: Dify
- label:
- en_US: StableDiffusion
- zh_Hans: 稳定扩散
- pt_BR: StableDiffusion
-description:
- human:
- en_US: A tool for generate images based on the text input
- zh_Hans: 一个基于文本输入生成图像的工具
- pt_BR: A tool for generate images based on the text input
- llm: A tool for generate images based on the text input
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: used for generating images
- zh_Hans: 用于生成图像
- pt_BR: used for generating images
- llm_description: key words for generating images
- form: llm
- - name: model
- type: select
- default: sd3-turbo
- required: true
- label:
- en_US: Model
- zh_Hans: 模型
- pt_BR: Model
- options:
- - value: core
- label:
- en_US: Core
- zh_Hans: Core
- pt_BR: Core
- - value: sd3
- label:
- en_US: Stable Diffusion 3
- zh_Hans: Stable Diffusion 3
- pt_BR: Stable Diffusion 3
- - value: sd3-turbo
- label:
- en_US: Stable Diffusion 3 Turbo
- zh_Hans: Stable Diffusion 3 Turbo
- pt_BR: Stable Diffusion 3 Turbo
- human_description:
- en_US: Model for generating images
- zh_Hans: 用于生成图像的模型
- pt_BR: Model for generating images
- llm_description: Model for generating images
- form: form
- - name: negative_prompt
- type: string
- default: bad art, ugly, deformed, watermark, duplicated, discontinuous lines
- required: false
- label:
- en_US: Negative Prompt
- zh_Hans: 负面提示
- pt_BR: Negative Prompt
- human_description:
- en_US: Negative Prompt
- zh_Hans: 负面提示
- pt_BR: Negative Prompt
- llm_description: Negative Prompt
- form: form
- - name: seeds
- type: number
- default: 0
- required: false
- label:
- en_US: Seeds
- zh_Hans: 种子
- pt_BR: Seeds
- human_description:
- en_US: Seeds
- zh_Hans: 种子
- pt_BR: Seeds
- llm_description: Seeds
- min: 0
- max: 4294967294
- form: form
- - name: aspect_ratio
- type: select
- default: '16:9'
- options:
- - value: '16:9'
- label:
- en_US: '16:9'
- zh_Hans: '16:9'
- pt_BR: '16:9'
- - value: '1:1'
- label:
- en_US: '1:1'
- zh_Hans: '1:1'
- pt_BR: '1:1'
- - value: '21:9'
- label:
- en_US: '21:9'
- zh_Hans: '21:9'
- pt_BR: '21:9'
- - value: '2:3'
- label:
- en_US: '2:3'
- zh_Hans: '2:3'
- pt_BR: '2:3'
- - value: '4:5'
- label:
- en_US: '4:5'
- zh_Hans: '4:5'
- pt_BR: '4:5'
- - value: '5:4'
- label:
- en_US: '5:4'
- zh_Hans: '5:4'
- pt_BR: '5:4'
- - value: '9:16'
- label:
- en_US: '9:16'
- zh_Hans: '9:16'
- pt_BR: '9:16'
- - value: '9:21'
- label:
- en_US: '9:21'
- zh_Hans: '9:21'
- pt_BR: '9:21'
- required: false
- label:
- en_US: Aspect Ratio
- zh_Hans: 长宽比
- pt_BR: Aspect Ratio
- human_description:
- en_US: Aspect Ratio
- zh_Hans: 长宽比
- pt_BR: Aspect Ratio
- llm_description: Aspect Ratio
- form: form
diff --git a/api/core/tools/provider/builtin/stablediffusion/_assets/icon.png b/api/core/tools/provider/builtin/stablediffusion/_assets/icon.png
deleted file mode 100644
index fc372b28f1ccfd..00000000000000
Binary files a/api/core/tools/provider/builtin/stablediffusion/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/stablediffusion/stablediffusion.py b/api/core/tools/provider/builtin/stablediffusion/stablediffusion.py
deleted file mode 100644
index abaa297cf36eb1..00000000000000
--- a/api/core/tools/provider/builtin/stablediffusion/stablediffusion.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.stablediffusion.tools.stable_diffusion import StableDiffusionTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class StableDiffusionProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- StableDiffusionTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).validate_models()
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/stablediffusion/stablediffusion.yaml b/api/core/tools/provider/builtin/stablediffusion/stablediffusion.yaml
deleted file mode 100644
index 9b3c804f722dfc..00000000000000
--- a/api/core/tools/provider/builtin/stablediffusion/stablediffusion.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-identity:
- author: Dify
- name: stablediffusion
- label:
- en_US: Stable Diffusion
- zh_Hans: Stable Diffusion
- pt_BR: Stable Diffusion
- description:
- en_US: Stable Diffusion is a tool for generating images which can be deployed locally.
- zh_Hans: Stable Diffusion 是一个可以在本地部署的图片生成的工具。
- pt_BR: Stable Diffusion is a tool for generating images which can be deployed locally.
- icon: icon.png
- tags:
- - image
-credentials_for_provider:
- base_url:
- type: secret-input
- required: true
- label:
- en_US: Base URL
- zh_Hans: StableDiffusion服务器的Base URL
- pt_BR: Base URL
- placeholder:
- en_US: Please input your StableDiffusion server's Base URL
- zh_Hans: 请输入你的 StableDiffusion 服务器的 Base URL
- pt_BR: Please input your StableDiffusion server's Base URL
- model:
- type: text-input
- required: true
- label:
- en_US: Model
- zh_Hans: 模型
- pt_BR: Model
- placeholder:
- en_US: Please input your model
- zh_Hans: 请输入你的模型名称
- pt_BR: Please input your model
- help:
- en_US: The model name of the StableDiffusion server
- zh_Hans: StableDiffusion服务器的模型名称
- pt_BR: The model name of the StableDiffusion server
- url: https://docs.dify.ai/tutorials/tool-configuration/stable-diffusion
diff --git a/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.py b/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.py
deleted file mode 100644
index 64fdc961b4c5db..00000000000000
--- a/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.py
+++ /dev/null
@@ -1,390 +0,0 @@
-import io
-import json
-from base64 import b64decode, b64encode
-from copy import deepcopy
-from typing import Any, Union
-
-from httpx import get, post
-from PIL import Image
-from yarl import URL
-
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolParameterOption
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-# All commented out parameters default to null
-DRAW_TEXT_OPTIONS = {
- # Prompts
- "prompt": "",
- "negative_prompt": "",
- # "styles": [],
- # Seeds
- "seed": -1,
- "subseed": -1,
- "subseed_strength": 0,
- "seed_resize_from_h": -1,
- "seed_resize_from_w": -1,
- # Samplers
- "sampler_name": "DPM++ 2M",
- # "scheduler": "",
- # "sampler_index": "Automatic",
- # Latent Space Options
- "batch_size": 1,
- "n_iter": 1,
- "steps": 10,
- "cfg_scale": 7,
- "width": 512,
- "height": 512,
- # "restore_faces": True,
- # "tiling": True,
- "do_not_save_samples": False,
- "do_not_save_grid": False,
- # "eta": 0,
- # "denoising_strength": 0.75,
- # "s_min_uncond": 0,
- # "s_churn": 0,
- # "s_tmax": 0,
- # "s_tmin": 0,
- # "s_noise": 0,
- "override_settings": {},
- "override_settings_restore_afterwards": True,
- # Refinement Options
- "refiner_checkpoint": "",
- "refiner_switch_at": 0,
- "disable_extra_networks": False,
- # "firstpass_image": "",
- # "comments": "",
- # High-Resolution Options
- "enable_hr": False,
- "firstphase_width": 0,
- "firstphase_height": 0,
- "hr_scale": 2,
- # "hr_upscaler": "",
- "hr_second_pass_steps": 0,
- "hr_resize_x": 0,
- "hr_resize_y": 0,
- # "hr_checkpoint_name": "",
- # "hr_sampler_name": "",
- # "hr_scheduler": "",
- "hr_prompt": "",
- "hr_negative_prompt": "",
- # Task Options
- # "force_task_id": "",
- # Script Options
- # "script_name": "",
- "script_args": [],
- # Output Options
- "send_images": True,
- "save_images": False,
- "alwayson_scripts": {},
- # "infotext": "",
-}
-
-
-class StableDiffusionTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # base url
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return self.create_text_message("Please input base_url")
-
- if tool_parameters.get("model"):
- self.runtime.credentials["model"] = tool_parameters["model"]
-
- model = self.runtime.credentials.get("model", None)
- if not model:
- return self.create_text_message("Please input model")
-
- # set model
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "options")
- response = post(url, data=json.dumps({"sd_model_checkpoint": model}))
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("Failed to set model, please tell user to set model")
- except Exception as e:
- raise ToolProviderCredentialValidationError("Failed to set model, please tell user to set model")
-
- # get image id and image variable
- image_id = tool_parameters.get("image_id", "")
- image_variable = self.get_default_image_variable()
- # Return text2img if there's no image ID or no image variable
- if not image_id or not image_variable:
- return self.text2img(base_url=base_url, tool_parameters=tool_parameters)
-
- # Proceed with image-to-image generation
- return self.img2img(base_url=base_url, tool_parameters=tool_parameters)
-
- def validate_models(self) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- validate models
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- raise ToolProviderCredentialValidationError("Please input base_url")
- model = self.runtime.credentials.get("model", None)
- if not model:
- raise ToolProviderCredentialValidationError("Please input model")
-
- api_url = str(URL(base_url) / "sdapi" / "v1" / "sd-models")
- response = get(url=api_url, timeout=10)
- if response.status_code == 404:
- # try draw a picture
- self._invoke(
- user_id="test",
- tool_parameters={
- "prompt": "a cat",
- "width": 1024,
- "height": 1024,
- "steps": 1,
- "lora": "",
- },
- )
- elif response.status_code != 200:
- raise ToolProviderCredentialValidationError("Failed to get models")
- else:
- models = [d["model_name"] for d in response.json()]
- if len([d for d in models if d == model]) > 0:
- return self.create_text_message(json.dumps(models))
- else:
- raise ToolProviderCredentialValidationError(f"model {model} does not exist")
- except Exception as e:
- raise ToolProviderCredentialValidationError(f"Failed to get models, {e}")
-
- def get_sd_models(self) -> list[str]:
- """
- get sd models
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "sdapi" / "v1" / "sd-models")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- return []
- else:
- return [d["model_name"] for d in response.json()]
- except Exception as e:
- return []
-
- def get_sample_methods(self) -> list[str]:
- """
- get sample method
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "sdapi" / "v1" / "samplers")
- response = get(url=api_url, timeout=(2, 10))
- if response.status_code != 200:
- return []
- else:
- return [d["name"] for d in response.json()]
- except Exception as e:
- return []
-
- def img2img(
- self, base_url: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- generate image
- """
-
- # Fetch the binary data of the image
- image_variable = self.get_default_image_variable()
- image_binary = self.get_variable_file(image_variable.name)
- if not image_binary:
- return self.create_text_message("Image not found, please request user to generate image firstly.")
-
- # Convert image to RGB and save as PNG
- try:
- with Image.open(io.BytesIO(image_binary)) as image, io.BytesIO() as buffer:
- image.convert("RGB").save(buffer, format="PNG")
- image_binary = buffer.getvalue()
- except Exception as e:
- return self.create_text_message(f"Failed to process the image: {str(e)}")
-
- # copy draw options
- draw_options = deepcopy(DRAW_TEXT_OPTIONS)
- # set image options
- model = tool_parameters.get("model", "")
- draw_options_image = {
- "init_images": [b64encode(image_binary).decode("utf-8")],
- "denoising_strength": 0.9,
- "restore_faces": False,
- "script_args": [],
- "override_settings": {"sd_model_checkpoint": model},
- "resize_mode": 0,
- "image_cfg_scale": 0,
- # "mask": None,
- "mask_blur_x": 4,
- "mask_blur_y": 4,
- "mask_blur": 0,
- "mask_round": True,
- "inpainting_fill": 0,
- "inpaint_full_res": True,
- "inpaint_full_res_padding": 0,
- "inpainting_mask_invert": 0,
- "initial_noise_multiplier": 0,
- # "latent_mask": None,
- "include_init_images": True,
- }
- # update key and values
- draw_options.update(draw_options_image)
- draw_options.update(tool_parameters)
-
- # get prompt lora model
- prompt = tool_parameters.get("prompt", "")
- lora = tool_parameters.get("lora", "")
- model = tool_parameters.get("model", "")
- if lora:
- draw_options["prompt"] = f"{lora},{prompt}"
- else:
- draw_options["prompt"] = prompt
-
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "img2img")
- response = post(url, data=json.dumps(draw_options), timeout=120)
- if response.status_code != 200:
- return self.create_text_message("Failed to generate image")
-
- image = response.json()["images"][0]
-
- return self.create_blob_message(
- blob=b64decode(image), meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
-
- except Exception as e:
- return self.create_text_message("Failed to generate image")
-
- def text2img(
- self, base_url: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- generate image
- """
- # copy draw options
- draw_options = deepcopy(DRAW_TEXT_OPTIONS)
- draw_options.update(tool_parameters)
- # get prompt lora model
- prompt = tool_parameters.get("prompt", "")
- lora = tool_parameters.get("lora", "")
- model = tool_parameters.get("model", "")
- if lora:
- draw_options["prompt"] = f"{lora},{prompt}"
- else:
- draw_options["prompt"] = prompt
- draw_options["override_settings"]["sd_model_checkpoint"] = model
-
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "txt2img")
- response = post(url, data=json.dumps(draw_options), timeout=120)
- if response.status_code != 200:
- return self.create_text_message("Failed to generate image")
-
- image = response.json()["images"][0]
-
- return self.create_blob_message(
- blob=b64decode(image), meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
-
- except Exception as e:
- return self.create_text_message("Failed to generate image")
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- parameters = [
- ToolParameter(
- name="prompt",
- label=I18nObject(en_US="Prompt", zh_Hans="Prompt"),
- human_description=I18nObject(
- en_US="Image prompt, you can check the official documentation of Stable Diffusion",
- zh_Hans="图像提示词,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Image prompt of Stable Diffusion, you should describe the image you want to generate"
- " as a list of words as possible as detailed, the prompt must be written in English.",
- required=True,
- ),
- ]
- if len(self.list_default_image_variables()) != 0:
- parameters.append(
- ToolParameter(
- name="image_id",
- label=I18nObject(en_US="image_id", zh_Hans="image_id"),
- human_description=I18nObject(
- en_US="Image id of the image you want to generate based on, if you want to generate image based"
- " on the default image, you can leave this field empty.",
- zh_Hans="您想要生成的图像的图像 ID,如果您想要基于默认图像生成图像,则可以将此字段留空。",
- ),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Image id of the original image, you can leave this field empty if you want to"
- " generate a new image.",
- required=True,
- options=[
- ToolParameterOption(value=i.name, label=I18nObject(en_US=i.name, zh_Hans=i.name))
- for i in self.list_default_image_variables()
- ],
- )
- )
-
- if self.runtime.credentials:
- try:
- models = self.get_sd_models()
- if len(models) != 0:
- parameters.append(
- ToolParameter(
- name="model",
- label=I18nObject(en_US="Model", zh_Hans="Model"),
- human_description=I18nObject(
- en_US="Model of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- zh_Hans="Stable Diffusion 的模型,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Model of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- required=True,
- default=models[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in models
- ],
- )
- )
-
- except:
- pass
-
- sample_methods = self.get_sample_methods()
- if len(sample_methods) != 0:
- parameters.append(
- ToolParameter(
- name="sampler_name",
- label=I18nObject(en_US="Sampling method", zh_Hans="Sampling method"),
- human_description=I18nObject(
- en_US="Sampling method of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- zh_Hans="Stable Diffusion 的Sampling method,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Sampling method of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- required=True,
- default=sample_methods[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in sample_methods
- ],
- )
- )
- return parameters
diff --git a/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.yaml b/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.yaml
deleted file mode 100644
index bbbdb16caf21bb..00000000000000
--- a/api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.yaml
+++ /dev/null
@@ -1,104 +0,0 @@
-identity:
- name: stable_diffusion
- author: Dify
- label:
- en_US: Stable Diffusion WebUI
- zh_Hans: Stable Diffusion WebUI
- pt_BR: Stable Diffusion WebUI
-description:
- human:
- en_US: A tool for generating images which can be deployed locally, you can use stable-diffusion-webui to deploy it.
- zh_Hans: 一个可以在本地部署的图片生成的工具,您可以使用 stable-diffusion-webui 来部署它。
- pt_BR: A tool for generating images which can be deployed locally, you can use stable-diffusion-webui to deploy it.
- llm: draw the image you want based on your prompt.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of Stable Diffusion
- zh_Hans: 图像提示词,您可以查看 Stable Diffusion 的官方文档
- pt_BR: Image prompt, you can check the official documentation of Stable Diffusion
- llm_description: Image prompt of Stable Diffusion, you should describe the image you want to generate as a list of words as possible as detailed, the prompt must be written in English.
- form: llm
- - name: model
- type: string
- required: false
- label:
- en_US: Model Name
- zh_Hans: 模型名称
- pt_BR: Model Name
- human_description:
- en_US: Model Name
- zh_Hans: 模型名称
- pt_BR: Model Name
- form: form
- - name: lora
- type: string
- required: false
- label:
- en_US: Lora
- zh_Hans: Lora
- pt_BR: Lora
- human_description:
- en_US: Lora
- zh_Hans: Lora
- pt_BR: Lora
- form: form
- default: ""
- - name: steps
- type: number
- required: false
- label:
- en_US: Steps
- zh_Hans: Steps
- pt_BR: Steps
- human_description:
- en_US: Steps
- zh_Hans: Steps
- pt_BR: Steps
- form: form
- default: 10
- - name: width
- type: number
- required: false
- label:
- en_US: Width
- zh_Hans: Width
- pt_BR: Width
- human_description:
- en_US: Width
- zh_Hans: Width
- pt_BR: Width
- form: form
- default: 1024
- - name: height
- type: number
- required: false
- label:
- en_US: Height
- zh_Hans: Height
- pt_BR: Height
- human_description:
- en_US: Height
- zh_Hans: Height
- pt_BR: Height
- form: form
- default: 1024
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- pt_BR: Negative prompt
- human_description:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- pt_BR: Negative prompt
- form: form
- default: bad art, ugly, deformed, watermark, duplicated, discontinuous lines
diff --git a/api/core/tools/provider/builtin/stackexchange/_assets/icon.svg b/api/core/tools/provider/builtin/stackexchange/_assets/icon.svg
deleted file mode 100644
index 7042bc0e4156c9..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/stackexchange/stackexchange.py b/api/core/tools/provider/builtin/stackexchange/stackexchange.py
deleted file mode 100644
index 9680c633cc701c..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/stackexchange.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.stackexchange.tools.searchStackExQuestions import SearchStackExQuestionsTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class StackExchangeProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- SearchStackExQuestionsTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "intitle": "Test",
- "sort": "relevance",
- "order": "desc",
- "site": "stackoverflow",
- "accepted": True,
- "pagesize": 1,
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/stackexchange/stackexchange.yaml b/api/core/tools/provider/builtin/stackexchange/stackexchange.yaml
deleted file mode 100644
index d382a3cca9cef2..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/stackexchange.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-identity:
- author: Richards Tu
- name: stackexchange
- label:
- en_US: Stack Exchange
- zh_Hans: Stack Exchange
- description:
- en_US: Access questions and answers from the Stack Exchange and its sub-sites.
- zh_Hans: 从 Stack Exchange 和其子论坛获取问题和答案。
- icon: icon.svg
- tags:
- - search
- - utilities
diff --git a/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.py b/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.py
deleted file mode 100644
index 534532009501f5..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any, Union
-
-import requests
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class FetchAnsByStackExQuesIDInput(BaseModel):
- id: int = Field(..., description="The question ID")
- site: str = Field(..., description="The Stack Exchange site")
- order: str = Field(..., description="asc or desc")
- sort: str = Field(..., description="activity, votes, creation")
- pagesize: int = Field(..., description="Number of answers per page")
- page: int = Field(..., description="Page number")
-
-
-class FetchAnsByStackExQuesIDTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- input = FetchAnsByStackExQuesIDInput(**tool_parameters)
-
- params = {
- "site": input.site,
- "filter": "!nNPvSNdWme",
- "order": input.order,
- "sort": input.sort,
- "pagesize": input.pagesize,
- "page": input.page,
- }
-
- response = requests.get(f"https://api.stackexchange.com/2.3/questions/{input.id}/answers", params=params)
-
- if response.status_code == 200:
- return self.create_text_message(self.summary(user_id=user_id, content=response.text))
- else:
- return self.create_text_message(f"API request failed with status code {response.status_code}")
diff --git a/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.yaml b/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.yaml
deleted file mode 100644
index d663bce6097441..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/tools/fetchAnsByStackExQuesID.yaml
+++ /dev/null
@@ -1,107 +0,0 @@
-identity:
- name: fetchAnsByStackExQuesID
- author: Richards Tu
- label:
- en_US: Fetch Stack Exchange Answers
- zh_Hans: 获取 Stack Exchange 答案
-description:
- human:
- en_US: A tool for retrieving answers for a specific Stack Exchange question ID. Must be used with the searchStackExQuesID tool.
- zh_Hans: 用于检索特定Stack Exchange问题ID的答案的工具。必须与searchStackExQuesID工具一起使用。
- llm: A tool for retrieving answers for Stack Exchange question ID.
-parameters:
- - name: id
- type: string
- required: true
- label:
- en_US: Question ID
- zh_Hans: 问题ID
- human_description:
- en_US: The ID of the Stack Exchange question to fetch answers for.
- zh_Hans: 要获取答案的Stack Exchange问题的ID。
- llm_description: The ID of the Stack Exchange question.
- form: llm
- - name: site
- type: string
- required: true
- label:
- en_US: Stack Exchange site
- zh_Hans: Stack Exchange站点
- human_description:
- en_US: The Stack Exchange site the question is from, e.g. stackoverflow, unix, etc.
- zh_Hans: 问题所在的Stack Exchange站点,例如stackoverflow、unix等。
- llm_description: Stack Exchange site identifier - 'stackoverflow', 'serverfault', 'superuser', 'askubuntu', 'unix', 'cs', 'softwareengineering', 'codegolf', 'codereview', 'cstheory', 'security', 'cryptography', 'reverseengineering', 'datascience', 'devops', 'ux', 'dba', 'gis', 'webmasters', 'arduino', 'raspberrypi', 'networkengineering', 'iot', 'tor', 'sqa', 'mathoverflow', 'math', 'mathematica', 'dsp', 'gamedev', 'robotics', 'genai', 'computergraphics'.
- form: llm
- - name: filter
- type: string
- required: true
- label:
- en_US: Filter
- zh_Hans: 过滤器
- human_description:
- en_US: This is required in order to actually get the body of the answer.
- zh_Hans: 为了实际获取答案的正文是必需的。
- options:
- - value: "!nNPvSNdWme"
- label:
- en_US: Must Select
- zh_Hans: 必须选择
- form: form
- default: "!nNPvSNdWme"
- - name: order
- type: string
- required: true
- label:
- en_US: Sort direction
- zh_Hans: 排序方向
- human_description:
- en_US: The direction to sort the answers - ascending or descending.
- zh_Hans: 答案的排序方向 - 升序或降序。
- form: form
- options:
- - value: asc
- label:
- en_US: Ascending
- zh_Hans: 升序
- - value: desc
- label:
- en_US: Descending
- zh_Hans: 降序
- default: desc
- - name: sort
- type: string
- required: true
- label:
- en_US: Sort order
- zh_Hans: 排序
- human_description:
- en_US: The sort order for the answers - activity, votes, or creation date.
- zh_Hans: 答案的排序顺序 - 活动、投票或创建日期。
- llm_description: activity, votes, or creation.
- form: llm
- - name: pagesize
- type: number
- required: true
- label:
- en_US: Results per page
- zh_Hans: 每页结果数
- human_description:
- en_US: The number of answers to return per page.
- zh_Hans: 每页返回的答案数。
- form: form
- min: 1
- max: 5
- default: 1
- - name: page
- type: number
- required: true
- label:
- en_US: Page number
- zh_Hans: 页码
- human_description:
- en_US: The page number of answers to retrieve.
- zh_Hans: 要检索的答案的页码。
- form: form
- min: 1
- max: 5
- default: 3
diff --git a/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.py b/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.py
deleted file mode 100644
index 4a25a808adf26a..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Any, Union
-
-import requests
-from pydantic import BaseModel, Field
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SearchStackExQuestionsInput(BaseModel):
- intitle: str = Field(..., description="The search query.")
- sort: str = Field(..., description="The sort order - relevance, activity, votes, creation.")
- order: str = Field(..., description="asc or desc")
- site: str = Field(..., description="The Stack Exchange site.")
- tagged: str = Field(None, description="Semicolon-separated tags to include.")
- nottagged: str = Field(None, description="Semicolon-separated tags to exclude.")
- accepted: bool = Field(..., description="true for only accepted answers, false otherwise")
- pagesize: int = Field(..., description="Number of results per page")
-
-
-class SearchStackExQuestionsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- input = SearchStackExQuestionsInput(**tool_parameters)
-
- params = {
- "intitle": input.intitle,
- "sort": input.sort,
- "order": input.order,
- "site": input.site,
- "accepted": input.accepted,
- "pagesize": input.pagesize,
- }
- if input.tagged:
- params["tagged"] = input.tagged
- if input.nottagged:
- params["nottagged"] = input.nottagged
-
- response = requests.get("https://api.stackexchange.com/2.3/search", params=params)
-
- if response.status_code == 200:
- return self.create_text_message(self.summary(user_id=user_id, content=response.text))
- else:
- return self.create_text_message(f"API request failed with status code {response.status_code}")
diff --git a/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.yaml b/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.yaml
deleted file mode 100644
index bbfbae38b06e1a..00000000000000
--- a/api/core/tools/provider/builtin/stackexchange/tools/searchStackExQuestions.yaml
+++ /dev/null
@@ -1,121 +0,0 @@
-identity:
- name: searchStackExQuestions
- author: Richards Tu
- label:
- en_US: Search Stack Exchange Questions
- zh_Hans: 搜索Stack Exchange问题
-description:
- human:
- en_US: A tool for searching questions on a Stack Exchange site.
- zh_Hans: 在Stack Exchange站点上搜索问题的工具。
- llm: A tool for searching questions on Stack Exchange site.
-parameters:
- - name: intitle
- type: string
- required: true
- label:
- en_US: Search query
- zh_Hans: 搜索查询
- human_description:
- en_US: The search query to use for finding questions.
- zh_Hans: 用于查找问题的搜索查询。
- llm_description: The search query.
- form: llm
- - name: sort
- type: string
- required: true
- label:
- en_US: Sort order
- zh_Hans: 排序
- human_description:
- en_US: The sort order for the search results - relevance, activity, votes, or creation date.
- zh_Hans: 搜索结果的排序顺序 - 相关性、活动、投票或创建日期。
- llm_description: The sort order - 'relevance', 'activity', 'votes', or 'creation'.
- form: llm
- - name: order
- type: select
- required: true
- label:
- en_US: Sort direction
- zh_Hans: 排序方向
- human_description:
- en_US: The direction to sort - ascending or descending.
- zh_Hans: 排序方向 - 升序或降序。
- form: form
- options:
- - value: asc
- label:
- en_US: Ascending
- zh_Hans: 升序
- - value: desc
- label:
- en_US: Descending
- zh_Hans: 降序
- default: desc
- - name: site
- type: string
- required: true
- label:
- en_US: Stack Exchange site
- zh_Hans: Stack Exchange 站点
- human_description:
- en_US: The Stack Exchange site to search, e.g. stackoverflow, unix, etc.
- zh_Hans: 要搜索的Stack Exchange站点,例如stackoverflow、unix等。
- llm_description: Stack Exchange site identifier - 'stackoverflow', 'serverfault', 'superuser', 'askubuntu', 'unix', 'cs', 'softwareengineering', 'codegolf', 'codereview', 'cstheory', 'security', 'cryptography', 'reverseengineering', 'datascience', 'devops', 'ux', 'dba', 'gis', 'webmasters', 'arduino', 'raspberrypi', 'networkengineering', 'iot', 'tor', 'sqa', 'mathoverflow', 'math', 'mathematica', 'dsp', 'gamedev', 'robotics', 'genai', 'computergraphics'.
- form: llm
- - name: tagged
- type: string
- required: false
- label:
- en_US: Include tags
- zh_Hans: 包含标签
- human_description:
- en_US: A semicolon-separated list of tags that questions must have.
- zh_Hans: 问题必须具有的标签的分号分隔列表。
- llm_description: Semicolon-separated tags to include. Leave blank if not needed.
- form: llm
- - name: nottagged
- type: string
- required: false
- label:
- en_US: Exclude tags
- zh_Hans: 排除标签
- human_description:
- en_US: A semicolon-separated list of tags to exclude from the search.
- zh_Hans: 从搜索中排除的标签的分号分隔列表。
- llm_description: Semicolon-separated tags to exclude. Leave blank if not needed.
- form: llm
- - name: accepted
- type: boolean
- required: true
- label:
- en_US: Has accepted answer
- zh_Hans: 有已接受的答案
- human_description:
- en_US: Whether to limit to only questions that have an accepted answer.
- zh_Hans: 是否限制为只有已接受答案的问题。
- form: form
- options:
- - value: 'true'
- label:
- en_US: 'Yes'
- zh_Hans: 是
- - value: 'false'
- label:
- en_US: 'No'
- zh_Hans: 否
- default: 'true'
- - name: pagesize
- type: number
- required: true
- label:
- en_US: Results per page
- zh_Hans: 每页结果数
- human_description:
- en_US: The number of results to return per page.
- zh_Hans: 每页返回的结果数。
- llm_description: The number of results per page.
- form: form
- min: 1
- max: 50
- default: 10
diff --git a/api/core/tools/provider/builtin/stepfun/__init__.py b/api/core/tools/provider/builtin/stepfun/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/core/tools/provider/builtin/stepfun/_assets/icon.png b/api/core/tools/provider/builtin/stepfun/_assets/icon.png
deleted file mode 100644
index 85b96d0c74c24c..00000000000000
Binary files a/api/core/tools/provider/builtin/stepfun/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/stepfun/stepfun.py b/api/core/tools/provider/builtin/stepfun/stepfun.py
deleted file mode 100644
index 239db85b1118b0..00000000000000
--- a/api/core/tools/provider/builtin/stepfun/stepfun.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.stepfun.tools.image import StepfunTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class StepfunProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- StepfunTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "prompt": "cute girl, blue eyes, white hair, anime style",
- "size": "256x256",
- "n": 1,
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/stepfun/stepfun.yaml b/api/core/tools/provider/builtin/stepfun/stepfun.yaml
deleted file mode 100644
index e8139a4d7d6cfd..00000000000000
--- a/api/core/tools/provider/builtin/stepfun/stepfun.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-identity:
- author: Stepfun
- name: stepfun
- label:
- en_US: Image-1X
- zh_Hans: 阶跃星辰绘画
- description:
- en_US: Image-1X
- zh_Hans: 阶跃星辰绘画
- icon: icon.png
- tags:
- - image
- - productivity
-credentials_for_provider:
- stepfun_api_key:
- type: secret-input
- required: true
- label:
- en_US: Stepfun API key
- zh_Hans: 阶跃星辰API key
- placeholder:
- en_US: Please input your Stepfun API key
- zh_Hans: 请输入你的阶跃星辰 API key
- url: https://platform.stepfun.com/interface-key
- stepfun_base_url:
- type: text-input
- required: false
- label:
- en_US: Stepfun base URL
- zh_Hans: 阶跃星辰 base URL
- placeholder:
- en_US: Please input your Stepfun base URL
- zh_Hans: 请输入你的阶跃星辰 base URL
diff --git a/api/core/tools/provider/builtin/stepfun/tools/image.py b/api/core/tools/provider/builtin/stepfun/tools/image.py
deleted file mode 100644
index 61cc14fac6ca93..00000000000000
--- a/api/core/tools/provider/builtin/stepfun/tools/image.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from typing import Any, Union
-
-from openai import OpenAI
-from yarl import URL
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class StepfunTool(BuiltinTool):
- """Stepfun Image Generation Tool"""
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- base_url = self.runtime.credentials.get("stepfun_base_url") or "https://api.stepfun.com"
- base_url = str(URL(base_url) / "v1")
-
- client = OpenAI(
- api_key=self.runtime.credentials["stepfun_api_key"],
- base_url=base_url,
- )
-
- extra_body = {}
- model = "step-1x-medium"
- # prompt
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
- if len(prompt) > 1024:
- return self.create_text_message("The prompt length should less than 1024")
- seed = tool_parameters.get("seed", 0)
- if seed > 0:
- extra_body["seed"] = seed
- steps = tool_parameters.get("steps", 50)
- if steps > 0:
- extra_body["steps"] = steps
- cfg_scale = tool_parameters.get("cfg_scale", 7.5)
- if cfg_scale > 0:
- extra_body["cfg_scale"] = cfg_scale
-
- # call openapi stepfun model
- response = client.images.generate(
- prompt=prompt,
- model=model,
- size=tool_parameters.get("size", "1024x1024"),
- n=tool_parameters.get("n", 1),
- extra_body=extra_body,
- )
-
- result = []
- for image in response.data:
- result.append(self.create_image_message(image=image.url))
- result.append(
- self.create_json_message(
- {
- "url": image.url,
- }
- )
- )
- return result
diff --git a/api/core/tools/provider/builtin/stepfun/tools/image.yaml b/api/core/tools/provider/builtin/stepfun/tools/image.yaml
deleted file mode 100644
index dfda6ed1914848..00000000000000
--- a/api/core/tools/provider/builtin/stepfun/tools/image.yaml
+++ /dev/null
@@ -1,133 +0,0 @@
-identity:
- name: stepfun
- author: Stepfun
- label:
- en_US: step-1x
- zh_Hans: 阶跃星辰绘画
- pt_BR: step-1x
- description:
- en_US: step-1x is a powerful drawing tool by stepfun, you can draw the image based on your prompt
- zh_Hans: step-1x 系列是阶跃星辰提供的强大的绘画工具,它可以根据您的提示词绘制出您想要的图像。
- pt_BR: step-1x is a powerful drawing tool by stepfun, you can draw the image based on your prompt
-description:
- human:
- en_US: step-1x is a text to image tool
- zh_Hans: step-1x 是一个文本/图像到图像的工具
- pt_BR: step-1x is a text to image tool
- llm: step-1x is a tool used to generate images from text or image
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: Image prompt, you can check the official documentation of step-1x
- zh_Hans: 图像提示词,您可以查看 step-1x 的官方文档
- pt_BR: Image prompt, you can check the official documentation of step-1x
- llm_description: Image prompt of step-1x you should describe the image you want to generate as a list of words as possible as detailed
- form: llm
- - name: size
- type: select
- required: false
- human_description:
- en_US: The size of the generated image
- zh_Hans: 生成的图片大小
- pt_BR: The size of the generated image
- label:
- en_US: Image size
- zh_Hans: 图像大小
- pt_BR: Image size
- form: form
- options:
- - value: 256x256
- label:
- en_US: 256x256
- zh_Hans: 256x256
- pt_BR: 256x256
- - value: 512x512
- label:
- en_US: 512x512
- zh_Hans: 512x512
- pt_BR: 512x512
- - value: 768x768
- label:
- en_US: 768x768
- zh_Hans: 768x768
- pt_BR: 768x768
- - value: 1024x1024
- label:
- en_US: 1024x1024
- zh_Hans: 1024x1024
- pt_BR: 1024x1024
- - value: 1280x800
- label:
- en_US: 1280x800
- zh_Hans: 1280x800
- pt_BR: 1280x800
- - value: 800x1280
- label:
- en_US: 800x1280
- zh_Hans: 800x1280
- pt_BR: 800x1280
- default: 1024x1024
- - name: n
- type: number
- required: true
- human_description:
- en_US: Number of generated images, now only one image can be generated at a time
- zh_Hans: 生成的图像数量,当前仅支持每次生成一张图片
- pt_BR: Number of generated images, now only one image can be generated at a time
- label:
- en_US: Number of generated images
- zh_Hans: 生成的图像数量
- pt_BR: Number of generated images
- form: form
- default: 1
- min: 1
- max: 1
- - name: seed
- type: number
- required: false
- label:
- en_US: seed
- zh_Hans: seed
- pt_BR: seed
- human_description:
- en_US: seed
- zh_Hans: seed
- pt_BR: seed
- form: form
- default: 10
- - name: steps
- type: number
- required: false
- label:
- en_US: Steps
- zh_Hans: Steps
- pt_BR: Steps
- human_description:
- en_US: Steps, now support integers between 1 and 100
- zh_Hans: Steps, 当前支持 1~100 之间整数
- pt_BR: Steps, now support integers between 1 and 100
- form: form
- default: 50
- min: 1
- max: 100
- - name: cfg_scale
- type: number
- required: false
- label:
- en_US: classifier-free guidance scale
- zh_Hans: classifier-free guidance scale
- pt_BR: classifier-free guidance scale
- human_description:
- en_US: classifier-free guidance scale
- zh_Hans: classifier-free guidance scale
- pt_BR: classifier-free guidance scale
- form: form
- default: 7.5
- min: 1
- max: 10
diff --git a/api/core/tools/provider/builtin/tavily/_assets/icon.png b/api/core/tools/provider/builtin/tavily/_assets/icon.png
deleted file mode 100644
index fdb40ab5689ba9..00000000000000
Binary files a/api/core/tools/provider/builtin/tavily/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/tavily/tavily.py b/api/core/tools/provider/builtin/tavily/tavily.py
deleted file mode 100644
index a702b0a74e6131..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tavily.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.tavily.tools.tavily_search import TavilySearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class TavilyProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- TavilySearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "Sachin Tendulkar",
- "search_depth": "basic",
- "include_answer": True,
- "include_images": False,
- "include_raw_content": False,
- "max_results": 5,
- "include_domains": "",
- "exclude_domains": "",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/tavily/tavily.yaml b/api/core/tools/provider/builtin/tavily/tavily.yaml
deleted file mode 100644
index aba621b094e816..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tavily.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-identity:
- author: Yash Parmar, Kalo Chin
- name: tavily
- label:
- en_US: Tavily Search & Extract
- zh_Hans: Tavily 搜索和提取
- description:
- en_US: A powerful AI-native search engine and web content extraction tool that provides highly relevant search results and raw content extraction from web pages.
- zh_Hans: 一个强大的原生AI搜索引擎和网页内容提取工具,提供高度相关的搜索结果和网页原始内容提取。
- icon: icon.png
- tags:
- - search
-credentials_for_provider:
- tavily_api_key:
- type: secret-input
- required: true
- label:
- en_US: Tavily API key
- zh_Hans: Tavily API key
- placeholder:
- en_US: Please input your Tavily API key
- zh_Hans: 请输入你的 Tavily API key
- help:
- en_US: Get your Tavily API key from Tavily
- zh_Hans: 从 TavilyApi 获取您的 Tavily API key
- url: https://app.tavily.com/home
diff --git a/api/core/tools/provider/builtin/tavily/tools/tavily_extract.py b/api/core/tools/provider/builtin/tavily/tools/tavily_extract.py
deleted file mode 100644
index a37548018d44ff..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tools/tavily_extract.py
+++ /dev/null
@@ -1,145 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-TAVILY_API_URL = "https://api.tavily.com"
-
-
-class TavilyExtract:
- """
- A class for extracting content from web pages using the Tavily Extract API.
-
- Args:
- api_key (str): The API key for accessing the Tavily Extract API.
-
- Methods:
- extract_content: Retrieves extracted content from the Tavily Extract API.
- """
-
- def __init__(self, api_key: str) -> None:
- self.api_key = api_key
-
- def extract_content(self, params: dict[str, Any]) -> dict:
- """
- Retrieves extracted content from the Tavily Extract API.
-
- Args:
- params (Dict[str, Any]): The extraction parameters.
-
- Returns:
- dict: The extracted content.
-
- """
- # Ensure required parameters are set
- if "api_key" not in params:
- params["api_key"] = self.api_key
-
- # Process parameters
- processed_params = self._process_params(params)
-
- response = requests.post(f"{TAVILY_API_URL}/extract", json=processed_params)
- response.raise_for_status()
- return response.json()
-
- def _process_params(self, params: dict[str, Any]) -> dict:
- """
- Processes and validates the extraction parameters.
-
- Args:
- params (Dict[str, Any]): The extraction parameters.
-
- Returns:
- dict: The processed parameters.
- """
- processed_params = {}
-
- # Process 'urls'
- if "urls" in params:
- urls = params["urls"]
- if isinstance(urls, str):
- processed_params["urls"] = [url.strip() for url in urls.replace(",", " ").split()]
- elif isinstance(urls, list):
- processed_params["urls"] = urls
- else:
- raise ValueError("The 'urls' parameter is required.")
-
- # Only include 'api_key'
- processed_params["api_key"] = params.get("api_key", self.api_key)
-
- return processed_params
-
-
-class TavilyExtractTool(BuiltinTool):
- """
- A tool for extracting content from web pages using Tavily Extract.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invokes the Tavily Extract tool with the given user ID and tool parameters.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (Dict[str, Any]): The parameters for the Tavily Extract tool.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the Tavily Extract tool invocation.
- """
- urls = tool_parameters.get("urls", "")
- api_key = self.runtime.credentials.get("tavily_api_key")
- if not api_key:
- return self.create_text_message(
- "Tavily API key is missing. Please set the 'tavily_api_key' in credentials."
- )
- if not urls:
- return self.create_text_message("Please input at least one URL to extract.")
-
- tavily_extract = TavilyExtract(api_key)
- try:
- raw_results = tavily_extract.extract_content(tool_parameters)
- except requests.HTTPError as e:
- return self.create_text_message(f"Error occurred while extracting content: {str(e)}")
-
- if not raw_results.get("results"):
- return self.create_text_message("No content could be extracted from the provided URLs.")
- else:
- # Always return JSON message with all data
- json_message = self.create_json_message(raw_results)
-
- # Create text message based on user-selected parameters
- text_message_content = self._format_results_as_text(raw_results)
- text_message = self.create_text_message(text=text_message_content)
-
- return [json_message, text_message]
-
- def _format_results_as_text(self, raw_results: dict) -> str:
- """
- Formats the raw extraction results into a markdown text based on user-selected parameters.
-
- Args:
- raw_results (dict): The raw extraction results.
-
- Returns:
- str: The formatted markdown text.
- """
- output_lines = []
-
- for idx, result in enumerate(raw_results.get("results", []), 1):
- url = result.get("url", "")
- raw_content = result.get("raw_content", "")
-
- output_lines.append(f"## Extracted Content {idx}: {url}\n")
- output_lines.append(f"**Raw Content:**\n{raw_content}\n")
- output_lines.append("---\n")
-
- if raw_results.get("failed_results"):
- output_lines.append("## Failed URLs:\n")
- for failed in raw_results["failed_results"]:
- url = failed.get("url", "")
- error = failed.get("error", "Unknown error")
- output_lines.append(f"- {url}: {error}\n")
-
- return "\n".join(output_lines)
diff --git a/api/core/tools/provider/builtin/tavily/tools/tavily_extract.yaml b/api/core/tools/provider/builtin/tavily/tools/tavily_extract.yaml
deleted file mode 100644
index a04da73b540f4d..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tools/tavily_extract.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-identity:
- name: tavily_extract
- author: Kalo Chin
- label:
- en_US: Tavily Extract
- zh_Hans: Tavily Extract
-description:
- human:
- en_US: A web extraction tool built specifically for AI agents (LLMs), delivering raw content from web pages.
- zh_Hans: 专为人工智能代理 (LLM) 构建的网页提取工具,提供网页的原始内容。
- llm: A tool for extracting raw content from web pages, designed for AI agents (LLMs).
-parameters:
- - name: urls
- type: string
- required: true
- label:
- en_US: URLs
- zh_Hans: URLs
- human_description:
- en_US: A comma-separated list of URLs to extract content from.
- zh_Hans: 要从中提取内容的 URL 的逗号分隔列表。
- llm_description: A comma-separated list of URLs to extract content from.
- form: llm
diff --git a/api/core/tools/provider/builtin/tavily/tools/tavily_search.py b/api/core/tools/provider/builtin/tavily/tools/tavily_search.py
deleted file mode 100644
index ea41ea3ca3c61f..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tools/tavily_search.py
+++ /dev/null
@@ -1,195 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-TAVILY_API_URL = "https://api.tavily.com"
-
-
-class TavilySearch:
- """
- A class for performing search operations using the Tavily Search API.
-
- Args:
- api_key (str): The API key for accessing the Tavily Search API.
-
- Methods:
- raw_results: Retrieves raw search results from the Tavily Search API.
- """
-
- def __init__(self, api_key: str) -> None:
- self.api_key = api_key
-
- def raw_results(self, params: dict[str, Any]) -> dict:
- """
- Retrieves raw search results from the Tavily Search API.
-
- Args:
- params (Dict[str, Any]): The search parameters.
-
- Returns:
- dict: The raw search results.
-
- """
- # Ensure required parameters are set
- params["api_key"] = self.api_key
-
- # Process parameters to ensure correct types
- processed_params = self._process_params(params)
-
- response = requests.post(f"{TAVILY_API_URL}/search", json=processed_params)
- response.raise_for_status()
- return response.json()
-
- def _process_params(self, params: dict[str, Any]) -> dict:
- """
- Processes and validates the search parameters.
-
- Args:
- params (Dict[str, Any]): The search parameters.
-
- Returns:
- dict: The processed parameters.
- """
- processed_params = {}
-
- for key, value in params.items():
- if value is None or value == "None":
- continue
- if key in ["include_domains", "exclude_domains"]:
- if isinstance(value, str):
- # Split the string by commas or spaces and strip whitespace
- processed_params[key] = [domain.strip() for domain in value.replace(",", " ").split()]
- elif key in ["include_images", "include_image_descriptions", "include_answer", "include_raw_content"]:
- # Ensure boolean type
- if isinstance(value, str):
- processed_params[key] = value.lower() == "true"
- else:
- processed_params[key] = bool(value)
- elif key in ["max_results", "days"]:
- if isinstance(value, str):
- processed_params[key] = int(value)
- else:
- processed_params[key] = value
- elif key in ["search_depth", "topic", "query", "api_key"]:
- processed_params[key] = value
- else:
- # Unrecognized parameter
- pass
-
- # Set defaults if not present
- processed_params.setdefault("search_depth", "basic")
- processed_params.setdefault("topic", "general")
- processed_params.setdefault("max_results", 5)
-
- # If topic is 'news', ensure 'days' is set
- if processed_params.get("topic") == "news":
- processed_params.setdefault("days", 3)
-
- return processed_params
-
-
-class TavilySearchTool(BuiltinTool):
- """
- A tool for searching Tavily using a given query.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- Invokes the Tavily search tool with the given user ID and tool parameters.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (Dict[str, Any]): The parameters for the Tavily search tool.
-
- Returns:
- ToolInvokeMessage | list[ToolInvokeMessage]: The result of the Tavily search tool invocation.
- """
- query = tool_parameters.get("query", "")
- api_key = self.runtime.credentials.get("tavily_api_key")
- if not api_key:
- return self.create_text_message(
- "Tavily API key is missing. Please set the 'tavily_api_key' in credentials."
- )
- if not query:
- return self.create_text_message("Please input a query.")
-
- tavily_search = TavilySearch(api_key)
- try:
- raw_results = tavily_search.raw_results(tool_parameters)
- except requests.HTTPError as e:
- return self.create_text_message(f"Error occurred while searching: {str(e)}")
-
- if not raw_results.get("results"):
- return self.create_text_message(f"No results found for '{query}' in Tavily.")
- else:
- # Always return JSON message with all data
- json_message = self.create_json_message(raw_results)
-
- # Create text message based on user-selected parameters
- text_message_content = self._format_results_as_text(raw_results, tool_parameters)
- text_message = self.create_text_message(text=text_message_content)
-
- return [json_message, text_message]
-
- def _format_results_as_text(self, raw_results: dict, tool_parameters: dict[str, Any]) -> str:
- """
- Formats the raw results into a markdown text based on user-selected parameters.
-
- Args:
- raw_results (dict): The raw search results.
- tool_parameters (dict): The tool parameters selected by the user.
-
- Returns:
- str: The formatted markdown text.
- """
- output_lines = []
-
- # Include answer if requested
- if tool_parameters.get("include_answer", False) and raw_results.get("answer"):
- output_lines.append(f"**Answer:** {raw_results['answer']}\n")
-
- # Include images if requested
- if tool_parameters.get("include_images", False) and raw_results.get("images"):
- output_lines.append("**Images:**\n")
- for image in raw_results["images"]:
- if tool_parameters.get("include_image_descriptions", False) and "description" in image:
- output_lines.append(f"![{image['description']}]({image['url']})\n")
- else:
- output_lines.append(f"\n")
-
- # Process each result
- if "results" in raw_results:
- for idx, result in enumerate(raw_results["results"], 1):
- title = result.get("title", "No Title")
- url = result.get("url", "")
- content = result.get("content", "")
- published_date = result.get("published_date", "")
- score = result.get("score", "")
-
- output_lines.append(f"### Result {idx}: [{title}]({url})\n")
-
- # Include published date if available and topic is 'news'
- if tool_parameters.get("topic") == "news" and published_date:
- output_lines.append(f"**Published Date:** {published_date}\n")
-
- output_lines.append(f"**URL:** {url}\n")
-
- # Include score (relevance)
- if score:
- output_lines.append(f"**Relevance Score:** {score}\n")
-
- # Include content
- if content:
- output_lines.append(f"**Content:**\n{content}\n")
-
- # Include raw content if requested
- if tool_parameters.get("include_raw_content", False) and result.get("raw_content"):
- output_lines.append(f"**Raw Content:**\n{result['raw_content']}\n")
-
- # Add a separator
- output_lines.append("---\n")
-
- return "\n".join(output_lines)
diff --git a/api/core/tools/provider/builtin/tavily/tools/tavily_search.yaml b/api/core/tools/provider/builtin/tavily/tools/tavily_search.yaml
deleted file mode 100644
index 14b2829701fe48..00000000000000
--- a/api/core/tools/provider/builtin/tavily/tools/tavily_search.yaml
+++ /dev/null
@@ -1,152 +0,0 @@
-identity:
- name: tavily_search
- author: Yash Parmar
- label:
- en_US: Tavily Search
- zh_Hans: Tavily Search
-description:
- human:
- en_US: A search engine tool built specifically for AI agents (LLMs), delivering real-time, accurate, and factual results at speed.
- zh_Hans: 专为人工智能代理 (LLM) 构建的搜索引擎工具,可快速提供实时、准确和真实的结果。
- llm: A tool for search engine built specifically for AI agents (LLMs), delivering real-time, accurate, and factual results at speed.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 查询
- human_description:
- en_US: The search query you want to execute with Tavily.
- zh_Hans: 您想用 Tavily 执行的搜索查询。
- llm_description: The search query.
- form: llm
- - name: search_depth
- type: select
- required: false
- label:
- en_US: Search Depth
- zh_Hans: 搜索深度
- human_description:
- en_US: The depth of the search.
- zh_Hans: 搜索的深度。
- form: form
- options:
- - value: basic
- label:
- en_US: Basic
- zh_Hans: 基本
- - value: advanced
- label:
- en_US: Advanced
- zh_Hans: 高级
- default: basic
- - name: topic
- type: select
- required: false
- label:
- en_US: Topic
- zh_Hans: 主题
- human_description:
- en_US: The category of the search.
- zh_Hans: 搜索的类别。
- form: form
- options:
- - value: general
- label:
- en_US: General
- zh_Hans: 一般
- - value: news
- label:
- en_US: News
- zh_Hans: 新闻
- default: general
- - name: days
- type: number
- required: false
- label:
- en_US: Days
- zh_Hans: 天数
- human_description:
- en_US: The number of days back from the current date to include in the search results (only applicable when "topic" is "news").
- zh_Hans: 从当前日期起向前追溯的天数,以包含在搜索结果中(仅当“topic”为“news”时适用)。
- form: form
- min: 1
- default: 3
- - name: max_results
- type: number
- required: false
- label:
- en_US: Max Results
- zh_Hans: 最大结果数
- human_description:
- en_US: The maximum number of search results to return.
- zh_Hans: 要返回的最大搜索结果数。
- form: form
- min: 1
- max: 20
- default: 5
- - name: include_images
- type: boolean
- required: false
- label:
- en_US: Include Images
- zh_Hans: 包含图片
- human_description:
- en_US: Include a list of query-related images in the response.
- zh_Hans: 在响应中包含与查询相关的图片列表。
- form: form
- default: false
- - name: include_image_descriptions
- type: boolean
- required: false
- label:
- en_US: Include Image Descriptions
- zh_Hans: 包含图片描述
- human_description:
- en_US: When include_images is True, adds descriptive text for each image.
- zh_Hans: 当 include_images 为 True 时,为每个图像添加描述文本。
- form: form
- default: false
- - name: include_answer
- type: boolean
- required: false
- label:
- en_US: Include Answer
- zh_Hans: 包含答案
- human_description:
- en_US: Include a short answer to the original query in the response.
- zh_Hans: 在响应中包含对原始查询的简短回答。
- form: form
- default: false
- - name: include_raw_content
- type: boolean
- required: false
- label:
- en_US: Include Raw Content
- zh_Hans: 包含原始内容
- human_description:
- en_US: Include the cleaned and parsed HTML content of each search result.
- zh_Hans: 包含每个搜索结果的已清理和解析的HTML内容。
- form: form
- default: false
- - name: include_domains
- type: string
- required: false
- label:
- en_US: Include Domains
- zh_Hans: 包含域
- human_description:
- en_US: A comma-separated list of domains to specifically include in the search results.
- zh_Hans: 要在搜索结果中特别包含的域的逗号分隔列表。
- form: form
- - name: exclude_domains
- type: string
- required: false
- label:
- en_US: Exclude Domains
- zh_Hans: 排除域
- human_description:
- en_US: A comma-separated list of domains to specifically exclude from the search results.
- zh_Hans: 要从搜索结果中特别排除的域的逗号分隔列表。
- form: form
diff --git a/api/core/tools/provider/builtin/tianditu/_assets/icon.svg b/api/core/tools/provider/builtin/tianditu/_assets/icon.svg
deleted file mode 100644
index 749d4bda265ab0..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/_assets/icon.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/tianditu/tianditu.py b/api/core/tools/provider/builtin/tianditu/tianditu.py
deleted file mode 100644
index cb7d7bd8bb2c41..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tianditu.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.tianditu.tools.poisearch import PoiSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class TiandituProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- PoiSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "content": "北京",
- "specify": "156110000",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/tianditu/tianditu.yaml b/api/core/tools/provider/builtin/tianditu/tianditu.yaml
deleted file mode 100644
index 77af834bdc5893..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tianditu.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- author: Listeng
- name: tianditu
- label:
- en_US: Tianditu
- zh_Hans: 天地图
- pt_BR: Tianditu
- description:
- en_US: The Tianditu tool provided the functions of place name search, geocoding, static maps generation, etc. in China region.
- zh_Hans: 天地图工具可以调用天地图的接口,实现中国区域内的地名搜索、地理编码、静态地图等功能。
- pt_BR: The Tianditu tool provided the functions of place name search, geocoding, static maps generation, etc. in China region.
- icon: icon.svg
- tags:
- - utilities
- - travel
-credentials_for_provider:
- tianditu_api_key:
- type: secret-input
- required: true
- label:
- en_US: Tianditu API Key
- zh_Hans: 天地图Key
- pt_BR: Tianditu API key
- placeholder:
- en_US: Please input your Tianditu API key
- zh_Hans: 请输入你的天地图Key
- pt_BR: Please input your Tianditu API key
- help:
- en_US: Get your Tianditu API key from Tianditu
- zh_Hans: 获取您的天地图Key
- pt_BR: Get your Tianditu API key from Tianditu
- url: http://lbs.tianditu.gov.cn/home.html
diff --git a/api/core/tools/provider/builtin/tianditu/tools/geocoder.py b/api/core/tools/provider/builtin/tianditu/tools/geocoder.py
deleted file mode 100644
index 690a0aed6f5aff..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/geocoder.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GeocoderTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- base_url = "http://api.tianditu.gov.cn/geocoder"
-
- keyword = tool_parameters.get("keyword", "")
- if not keyword:
- return self.create_text_message("Invalid parameter keyword")
-
- tk = self.runtime.credentials["tianditu_api_key"]
-
- params = {
- "keyWord": keyword,
- }
-
- result = requests.get(base_url + "?ds=" + json.dumps(params, ensure_ascii=False) + "&tk=" + tk).json()
-
- return self.create_json_message(result)
diff --git a/api/core/tools/provider/builtin/tianditu/tools/geocoder.yaml b/api/core/tools/provider/builtin/tianditu/tools/geocoder.yaml
deleted file mode 100644
index d6a168f9502019..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/geocoder.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-identity:
- name: geocoder
- author: Listeng
- label:
- en_US: Get coords converted from address name
- zh_Hans: 地理编码
- pt_BR: Get coords converted from address name
-description:
- human:
- en_US: Geocoder
- zh_Hans: 中国区域地理编码查询
- pt_BR: Geocoder
- llm: A tool for geocoder in China
-parameters:
- - name: keyword
- type: string
- required: true
- label:
- en_US: keyword
- zh_Hans: 搜索的关键字
- pt_BR: keyword
- human_description:
- en_US: keyword
- zh_Hans: 搜索的关键字
- pt_BR: keyword
- form: llm
diff --git a/api/core/tools/provider/builtin/tianditu/tools/poisearch.py b/api/core/tools/provider/builtin/tianditu/tools/poisearch.py
deleted file mode 100644
index 798dd94d335654..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/poisearch.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class PoiSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- geocoder_base_url = "http://api.tianditu.gov.cn/geocoder"
- base_url = "http://api.tianditu.gov.cn/v2/search"
-
- keyword = tool_parameters.get("keyword", "")
- if not keyword:
- return self.create_text_message("Invalid parameter keyword")
-
- baseAddress = tool_parameters.get("baseAddress", "")
- if not baseAddress:
- return self.create_text_message("Invalid parameter baseAddress")
-
- tk = self.runtime.credentials["tianditu_api_key"]
-
- base_coords = requests.get(
- geocoder_base_url
- + "?ds="
- + json.dumps(
- {
- "keyWord": baseAddress,
- },
- ensure_ascii=False,
- )
- + "&tk="
- + tk
- ).json()
-
- params = {
- "keyWord": keyword,
- "queryRadius": 5000,
- "queryType": 3,
- "pointLonlat": base_coords["location"]["lon"] + "," + base_coords["location"]["lat"],
- "start": 0,
- "count": 100,
- }
-
- result = requests.get(
- base_url + "?postStr=" + json.dumps(params, ensure_ascii=False) + "&type=query&tk=" + tk
- ).json()
-
- return self.create_json_message(result)
diff --git a/api/core/tools/provider/builtin/tianditu/tools/poisearch.yaml b/api/core/tools/provider/builtin/tianditu/tools/poisearch.yaml
deleted file mode 100644
index 01289d24e3d29a..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/poisearch.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-identity:
- name: point_of_interest_search
- author: Listeng
- label:
- en_US: Point of Interest search
- zh_Hans: 兴趣点搜索
- pt_BR: Point of Interest search
-description:
- human:
- en_US: Search for certain types of points of interest around a location
- zh_Hans: 搜索某个位置周边的5公里内某种类型的兴趣点
- pt_BR: Search for certain types of points of interest around a location
- llm: A tool for searching for certain types of points of interest around a location
-parameters:
- - name: keyword
- type: string
- required: true
- label:
- en_US: poi keyword
- zh_Hans: 兴趣点的关键字
- pt_BR: poi keyword
- human_description:
- en_US: poi keyword
- zh_Hans: 兴趣点的关键字
- pt_BR: poi keyword
- form: llm
- - name: baseAddress
- type: string
- required: true
- label:
- en_US: base current point
- zh_Hans: 当前位置的关键字
- pt_BR: base current point
- human_description:
- en_US: base current point
- zh_Hans: 当前位置的关键字
- pt_BR: base current point
- form: llm
diff --git a/api/core/tools/provider/builtin/tianditu/tools/staticmap.py b/api/core/tools/provider/builtin/tianditu/tools/staticmap.py
deleted file mode 100644
index aeaef088057686..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/staticmap.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import json
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class PoiSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
-
- geocoder_base_url = "http://api.tianditu.gov.cn/geocoder"
- base_url = "http://api.tianditu.gov.cn/staticimage"
-
- keyword = tool_parameters.get("keyword", "")
- if not keyword:
- return self.create_text_message("Invalid parameter keyword")
-
- tk = self.runtime.credentials["tianditu_api_key"]
-
- keyword_coords = requests.get(
- geocoder_base_url
- + "?ds="
- + json.dumps(
- {
- "keyWord": keyword,
- },
- ensure_ascii=False,
- )
- + "&tk="
- + tk
- ).json()
- coords = keyword_coords["location"]["lon"] + "," + keyword_coords["location"]["lat"]
-
- result = requests.get(
- base_url + "?center=" + coords + "&markers=" + coords + "&width=400&height=300&zoom=14&tk=" + tk
- ).content
-
- return self.create_blob_message(
- blob=result, meta={"mime_type": "image/png"}, save_as=self.VariableKey.IMAGE.value
- )
diff --git a/api/core/tools/provider/builtin/tianditu/tools/staticmap.yaml b/api/core/tools/provider/builtin/tianditu/tools/staticmap.yaml
deleted file mode 100644
index fc54c428066af5..00000000000000
--- a/api/core/tools/provider/builtin/tianditu/tools/staticmap.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-identity:
- name: generate_static_map
- author: Listeng
- label:
- en_US: Generate a static map
- zh_Hans: 生成静态地图
- pt_BR: Generate a static map
-description:
- human:
- en_US: Generate a static map
- zh_Hans: 生成静态地图
- pt_BR: Generate a static map
- llm: A tool for generate a static map
-parameters:
- - name: keyword
- type: string
- required: true
- label:
- en_US: keyword
- zh_Hans: 搜索的关键字
- pt_BR: keyword
- human_description:
- en_US: keyword
- zh_Hans: 搜索的关键字
- pt_BR: keyword
- form: llm
diff --git a/api/core/tools/provider/builtin/time/time.py b/api/core/tools/provider/builtin/time/time.py
deleted file mode 100644
index e4df8d616cba38..00000000000000
--- a/api/core/tools/provider/builtin/time/time.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.time.tools.current_time import CurrentTimeTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class WikiPediaProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- CurrentTimeTool().invoke(
- user_id="",
- tool_parameters={},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/time/tools/current_time.py b/api/core/tools/provider/builtin/time/tools/current_time.py
deleted file mode 100644
index 6464bb6602b60a..00000000000000
--- a/api/core/tools/provider/builtin/time/tools/current_time.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from datetime import UTC, datetime
-from typing import Any, Union
-
-from pytz import timezone as pytz_timezone
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CurrentTimeTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # get timezone
- tz = tool_parameters.get("timezone", "UTC")
- fm = tool_parameters.get("format") or "%Y-%m-%d %H:%M:%S %Z"
- if tz == "UTC":
- return self.create_text_message(f"{datetime.now(UTC).strftime(fm)}")
-
- try:
- tz = pytz_timezone(tz)
- except:
- return self.create_text_message(f"Invalid timezone: {tz}")
- return self.create_text_message(f"{datetime.now(tz).strftime(fm)}")
diff --git a/api/core/tools/provider/builtin/time/tools/localtime_to_timestamp.py b/api/core/tools/provider/builtin/time/tools/localtime_to_timestamp.py
deleted file mode 100644
index e16b732d0242db..00000000000000
--- a/api/core/tools/provider/builtin/time/tools/localtime_to_timestamp.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from datetime import datetime
-from typing import Any, Union
-
-import pytz
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolInvokeError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class LocaltimeToTimestampTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Convert localtime to timestamp
- """
- localtime = tool_parameters.get("localtime")
- timezone = tool_parameters.get("timezone", "Asia/Shanghai")
- if not timezone:
- timezone = None
- time_format = "%Y-%m-%d %H:%M:%S"
-
- timestamp = self.localtime_to_timestamp(localtime, time_format, timezone)
- if not timestamp:
- return self.create_text_message(f"Invalid localtime: {localtime}")
-
- return self.create_text_message(f"{timestamp}")
-
- @staticmethod
- def localtime_to_timestamp(localtime: str, time_format: str, local_tz=None) -> int | None:
- try:
- if local_tz is None:
- local_tz = datetime.now().astimezone().tzinfo
- if isinstance(local_tz, str):
- local_tz = pytz.timezone(local_tz)
- local_time = datetime.strptime(localtime, time_format)
- localtime = local_tz.localize(local_time)
- timestamp = int(localtime.timestamp())
- return timestamp
- except Exception as e:
- raise ToolInvokeError(str(e))
diff --git a/api/core/tools/provider/builtin/time/tools/timestamp_to_localtime.py b/api/core/tools/provider/builtin/time/tools/timestamp_to_localtime.py
deleted file mode 100644
index bcdd34fd4ec54d..00000000000000
--- a/api/core/tools/provider/builtin/time/tools/timestamp_to_localtime.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from datetime import datetime
-from typing import Any, Union
-
-import pytz
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolInvokeError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class TimestampToLocaltimeTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Convert timestamp to localtime
- """
- timestamp = tool_parameters.get("timestamp")
- timezone = tool_parameters.get("timezone", "Asia/Shanghai")
- if not timezone:
- timezone = None
- time_format = "%Y-%m-%d %H:%M:%S"
-
- locatime = self.timestamp_to_localtime(timestamp, timezone)
- if not locatime:
- return self.create_text_message(f"Invalid timestamp: {timestamp}")
-
- localtime_format = locatime.strftime(time_format)
-
- return self.create_text_message(f"{localtime_format}")
-
- @staticmethod
- def timestamp_to_localtime(timestamp: int, local_tz=None) -> datetime | None:
- try:
- if local_tz is None:
- local_tz = datetime.now().astimezone().tzinfo
- if isinstance(local_tz, str):
- local_tz = pytz.timezone(local_tz)
- local_time = datetime.fromtimestamp(timestamp, local_tz)
- return local_time
- except Exception as e:
- raise ToolInvokeError(str(e))
diff --git a/api/core/tools/provider/builtin/time/tools/weekday.py b/api/core/tools/provider/builtin/time/tools/weekday.py
deleted file mode 100644
index b327e54e171048..00000000000000
--- a/api/core/tools/provider/builtin/time/tools/weekday.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import calendar
-from datetime import datetime
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class WeekdayTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Calculate the day of the week for a given date
- """
- year = tool_parameters.get("year")
- month = tool_parameters.get("month")
- day = tool_parameters.get("day")
-
- date_obj = self.convert_datetime(year, month, day)
- if not date_obj:
- return self.create_text_message(f"Invalid date: Year {year}, Month {month}, Day {day}.")
-
- weekday_name = calendar.day_name[date_obj.weekday()]
- month_name = calendar.month_name[month]
- readable_date = f"{month_name} {date_obj.day}, {date_obj.year}"
- return self.create_text_message(f"{readable_date} is {weekday_name}.")
-
- @staticmethod
- def convert_datetime(year, month, day) -> datetime | None:
- try:
- # allowed range in datetime module
- if not (year >= 1 and 1 <= month <= 12 and 1 <= day <= 31):
- return None
-
- year = int(year)
- month = int(month)
- day = int(day)
- return datetime(year, month, day)
- except ValueError:
- return None
diff --git a/api/core/tools/provider/builtin/transcript/_assets/icon.svg b/api/core/tools/provider/builtin/transcript/_assets/icon.svg
deleted file mode 100644
index 83b0700fecbf30..00000000000000
--- a/api/core/tools/provider/builtin/transcript/_assets/icon.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/transcript/tools/transcript.py b/api/core/tools/provider/builtin/transcript/tools/transcript.py
deleted file mode 100644
index ac7565d9eef5b8..00000000000000
--- a/api/core/tools/provider/builtin/transcript/tools/transcript.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from typing import Any, Union
-from urllib.parse import parse_qs, urlparse
-
-from youtube_transcript_api import YouTubeTranscriptApi # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class YouTubeTranscriptTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the YouTube transcript tool
- """
- try:
- # Extract parameters with defaults
- video_input = tool_parameters["video_id"]
- language = tool_parameters.get("language")
- output_format = tool_parameters.get("format", "text")
- preserve_formatting = tool_parameters.get("preserve_formatting", False)
- proxy = tool_parameters.get("proxy")
- cookies = tool_parameters.get("cookies")
-
- # Extract video ID from URL if needed
- video_id = self._extract_video_id(video_input)
-
- # Common kwargs for API calls
- kwargs = {"proxies": {"https": proxy} if proxy else None, "cookies": cookies}
-
- try:
- if language:
- transcript_list = YouTubeTranscriptApi.list_transcripts(video_id, **kwargs)
- try:
- transcript = transcript_list.find_transcript([language])
- except:
- # If requested language not found, try translating from English
- transcript = transcript_list.find_transcript(["en"]).translate(language)
- transcript_data = transcript.fetch()
- else:
- transcript_data = YouTubeTranscriptApi.get_transcript(
- video_id, preserve_formatting=preserve_formatting, **kwargs
- )
-
- # Format output
- formatter_class = {
- "json": "JSONFormatter",
- "pretty": "PrettyPrintFormatter",
- "srt": "SRTFormatter",
- "vtt": "WebVTTFormatter",
- }.get(output_format)
-
- if formatter_class:
- from youtube_transcript_api import formatters
-
- formatter = getattr(formatters, formatter_class)()
- formatted_transcript = formatter.format_transcript(transcript_data)
- else:
- formatted_transcript = " ".join(entry["text"] for entry in transcript_data)
-
- return self.create_text_message(text=formatted_transcript)
-
- except Exception as e:
- return self.create_text_message(text=f"Error getting transcript: {str(e)}")
-
- except Exception as e:
- return self.create_text_message(text=f"Error processing request: {str(e)}")
-
- def _extract_video_id(self, video_input: str) -> str:
- """
- Extract video ID from URL or return as-is if already an ID
- """
- if "youtube.com" in video_input or "youtu.be" in video_input:
- # Parse URL
- parsed_url = urlparse(video_input)
- if "youtube.com" in parsed_url.netloc:
- return parse_qs(parsed_url.query)["v"][0]
- else: # youtu.be
- return parsed_url.path[1:]
- return video_input # Assume it's already a video ID
diff --git a/api/core/tools/provider/builtin/transcript/tools/transcript.yaml b/api/core/tools/provider/builtin/transcript/tools/transcript.yaml
deleted file mode 100644
index c654634a6c0be5..00000000000000
--- a/api/core/tools/provider/builtin/transcript/tools/transcript.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-identity:
- name: free_youtube_transcript
- author: Tao Wang
- label:
- en_US: Free YouTube Transcript API
- zh_Hans: 免费获取 YouTube 转录
-description:
- human:
- en_US: Get transcript from a YouTube video for free.
- zh_Hans: 免费获取 YouTube 视频的转录文案。
- llm: A tool for retrieving transcript from YouTube videos.
-parameters:
- - name: video_id
- type: string
- required: true
- label:
- en_US: Video ID/URL
- zh_Hans: 视频ID
- human_description:
- en_US: Used to define the video from which the transcript will be fetched. You can find the id in the video url. For example - https://www.youtube.com/watch?v=video_id.
- zh_Hans: 您要哪条视频的转录文案?您可以在视频链接中找到id。例如 - https://www.youtube.com/watch?v=video_id。
- llm_description: Used to define the video from which the transcript will be fetched. For example - https://www.youtube.com/watch?v=video_id.
- form: llm
- - name: language
- type: string
- required: false
- label:
- en_US: Language Code
- zh_Hans: 语言
- human_description:
- en_US: Language code (e.g. 'en', 'zh') for the transcript.
- zh_Hans: 字幕语言代码(如'en'、'zh')。留空则自动选择。
- llm_description: Used to set the language for transcripts.
- form: form
- - name: format
- type: select
- required: false
- default: text
- options:
- - value: text
- label:
- en_US: Plain Text
- zh_Hans: 纯文本
- - value: json
- label:
- en_US: JSON Format
- zh_Hans: JSON 格式
- - value: pretty
- label:
- en_US: Pretty Print Format
- zh_Hans: 美化格式
- - value: srt
- label:
- en_US: SRT Format
- zh_Hans: SRT 格式
- - value: vtt
- label:
- en_US: WebVTT Format
- zh_Hans: WebVTT 格式
- label:
- en_US: Output Format
- zh_Hans: 输出格式
- human_description:
- en_US: Format of the transcript output
- zh_Hans: 字幕输出格式
- llm_description: The format to output the transcript in. Options are text (plain text), json (raw transcript data), srt (SubRip format), or vtt (WebVTT format)
- form: form
- - name: preserve_formatting
- type: boolean
- required: false
- default: false
- label:
- en_US: Preserve Formatting
- zh_Hans: 保留格式
- human_description:
- en_US: Keep HTML formatting elements like (italics) and (bold)
- zh_Hans: 保留HTML格式元素,如(斜体)和(粗体)
- llm_description: Whether to preserve HTML formatting elements in the transcript text
- form: form
- - name: proxy
- type: string
- required: false
- label:
- en_US: HTTPS Proxy
- zh_Hans: HTTPS 代理
- human_description:
- en_US: HTTPS proxy URL (e.g. https://user:pass@domain:port)
- zh_Hans: HTTPS 代理地址(如 https://user:pass@domain:port)
- llm_description: HTTPS proxy to use for the request. Format should be https://user:pass@domain:port
- form: form
- - name: cookies
- type: string
- required: false
- label:
- en_US: Cookies File Path
- zh_Hans: Cookies 文件路径
- human_description:
- en_US: Path to cookies.txt file for accessing age-restricted videos
- zh_Hans: 用于访问年龄限制视频的 cookies.txt 文件路径
- llm_description: Path to a cookies.txt file containing YouTube cookies, needed for accessing age-restricted videos
- form: form
diff --git a/api/core/tools/provider/builtin/transcript/transcript.py b/api/core/tools/provider/builtin/transcript/transcript.py
deleted file mode 100644
index 4fda1499882fe0..00000000000000
--- a/api/core/tools/provider/builtin/transcript/transcript.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class YouTubeTranscriptProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- """
- No credentials needed for YouTube Transcript API
- """
- pass
diff --git a/api/core/tools/provider/builtin/transcript/transcript.yaml b/api/core/tools/provider/builtin/transcript/transcript.yaml
deleted file mode 100644
index 0786b454c35df3..00000000000000
--- a/api/core/tools/provider/builtin/transcript/transcript.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-identity:
- author: Tao Wang
- name: transcript
- label:
- en_US: Transcript
- zh_Hans: Transcript
- description:
- en_US: Get transcripts from YouTube videos
- zh_Hans: 获取 YouTube 视频的字幕/转录文本
- icon: icon.svg
- tags:
- - videos
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/trello/_assets/icon.svg b/api/core/tools/provider/builtin/trello/_assets/icon.svg
deleted file mode 100644
index f8e2bd47c0b818..00000000000000
--- a/api/core/tools/provider/builtin/trello/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/trello/tools/create_board.py b/api/core/tools/provider/builtin/trello/tools/create_board.py
deleted file mode 100644
index 5a61d221578995..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_board.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CreateBoardTool(BuiltinTool):
- """
- Tool for creating a new Trello board.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to create a new Trello board.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_name = tool_parameters.get("name")
-
- if not (api_key and token and board_name):
- return self.create_text_message("Missing required parameters: API key, token, or board name.")
-
- url = "https://api.trello.com/1/boards/"
- query_params = {"name": board_name, "key": api_key, "token": token}
-
- try:
- response = requests.post(url, params=query_params)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to create board")
-
- board = response.json()
- return self.create_text_message(
- text=f"Board created successfully! Board name: {board['name']}, ID: {board['id']}"
- )
diff --git a/api/core/tools/provider/builtin/trello/tools/create_board.yaml b/api/core/tools/provider/builtin/trello/tools/create_board.yaml
deleted file mode 100644
index 60dbab61f5ee5c..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_board.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: create_board
- author: Yash Parmar
- label:
- en_US: Create Board
- zh_Hans: 创建看板
- pt_BR: Criar Quadro
-description:
- human:
- en_US: Creates a new Trello board with a specified name. This tool allows users to quickly add new boards to their Trello account, facilitating project organization and management.
- zh_Hans: 使用指定的名称创建一个新的 Trello 看板。此工具允许用户快速向其 Trello 账户添加新的看板,促进项目组织和管理。
- pt_BR: Cria um novo quadro Trello com um nome especificado. Esta ferramenta permite que os usuários adicionem rapidamente novos quadros à sua conta Trello, facilitando a organização e gestão de projetos.
- llm: Create a new Trello board using the specified name. This functionality simplifies the addition of boards, enhancing project organization and management within Trello.
-parameters:
- - name: name
- type: string
- required: true
- label:
- en_US: Board Name
- zh_Hans: 看板名称
- pt_BR: Nome do Quadro
- human_description:
- en_US: The name for the new Trello board. This name helps in identifying and organizing your projects on Trello.
- zh_Hans: 新 Trello 看板的名称。这个名称有助于在 Trello 上识别和组织您的项目。
- pt_BR: O nome para o novo quadro Trello. Este nome ajuda a identificar e organizar seus projetos no Trello.
- llm_description: Specify the name for your new Trello board, aiding in project identification and organization within Trello.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/create_list_on_board.py b/api/core/tools/provider/builtin/trello/tools/create_list_on_board.py
deleted file mode 100644
index b32b0124dd31da..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_list_on_board.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CreateListOnBoardTool(BuiltinTool):
- """
- Tool for creating a list on a Trello board by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to create a list on a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID and list name.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("id")
- list_name = tool_parameters.get("name")
-
- if not (api_key and token and board_id and list_name):
- return self.create_text_message("Missing required parameters: API key, token, board ID, or list name.")
-
- url = f"https://api.trello.com/1/boards/{board_id}/lists"
- params = {"name": list_name, "key": api_key, "token": token}
-
- try:
- response = requests.post(url, params=params)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to create list")
-
- new_list = response.json()
- return self.create_text_message(
- text=f"List '{new_list['name']}' created successfully with Id {new_list['id']} on board {board_id}."
- )
diff --git a/api/core/tools/provider/builtin/trello/tools/create_list_on_board.yaml b/api/core/tools/provider/builtin/trello/tools/create_list_on_board.yaml
deleted file mode 100644
index 789b92437a3b3e..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_list_on_board.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- name: create_list_on_board
- author: Yash Parmar
- label:
- en_US: Create List on Board
- zh_Hans: 在看板上创建列表
- pt_BR: Criar Lista no Quadro
-description:
- human:
- en_US: Creates a new list on a specified Trello board by providing the board's ID and the desired name for the list. Streamlines the process of organizing board content.
- zh_Hans: 通过提供看板的 ID 和列表的所需名称,在指定的 Trello 看板上创建一个新列表。简化了组织看板内容的过程。
- pt_BR: Cria uma nova lista em um quadro Trello especificado, fornecendo o ID do quadro e o nome desejado para a lista. Facilita o processo de organização do conteúdo do quadro.
- llm: Generate a new list within a Trello board by specifying the board's ID and a name for the list. Enhances board management by allowing quick additions of new lists.
-parameters:
- - name: id
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier of the Trello board where the new list will be created.
- zh_Hans: 新列表将被创建在其上的 Trello 看板的唯一标识符。
- pt_BR: O identificador único do quadro Trello onde a nova lista será criada.
- llm_description: Input the ID of the Trello board to pinpoint where the new list should be added, ensuring correct placement.
- form: llm
- - name: name
- type: string
- required: true
- label:
- en_US: List Name
- zh_Hans: 列表名称
- pt_BR: Nome da Lista
- human_description:
- en_US: The name for the new list to be created on the Trello board.
- zh_Hans: 将在 Trello 看板上创建的新列表的名称。
- pt_BR: O nome para a nova lista que será criada no quadro Trello.
- llm_description: Provide a name for the new list, defining its purpose or content focus, to facilitate board organization.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.py b/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.py
deleted file mode 100644
index e98efb81ca673e..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class CreateNewCardOnBoardTool(BuiltinTool):
- """
- Tool for creating a new card on a Trello board.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool, None]]) -> ToolInvokeMessage:
- """
- Invoke the tool to create a new card on a Trello board.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool, None]]): The parameters for the tool invocation,
- including details for the new card.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
-
- # Ensure required parameters are present
- if "name" not in tool_parameters or "idList" not in tool_parameters:
- return self.create_text_message("Missing required parameters: name or idList.")
-
- url = "https://api.trello.com/1/cards"
- params = {**tool_parameters, "key": api_key, "token": token}
-
- try:
- response = requests.post(url, params=params)
- response.raise_for_status()
- new_card = response.json()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to create card")
-
- return self.create_text_message(
- text=f"New card '{new_card['name']}' created successfully with ID {new_card['id']}."
- )
diff --git a/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.yaml b/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.yaml
deleted file mode 100644
index 9953af718ddd58..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/create_new_card_on_board.yaml
+++ /dev/null
@@ -1,145 +0,0 @@
-identity:
- name: create_new_card_on_board
- author: Yash Parmar
- label:
- en_US: Create New Card on Board
- zh_Hans: 在看板上创建新卡片
- pt_BR: Criar Novo Cartão no Quadro
-description:
- human:
- en_US: Creates a new card on a Trello board with specified details like name, description, list ID, and other optional parameters. Facilitates task addition and project management within Trello.
- zh_Hans: 用指定的详情(如名称、描述、列表 ID 和其他可选参数)在 Trello 看板上创建一个新卡片。便于在 Trello 中添加任务和管理项目。
- pt_BR: Cria um novo cartão em um quadro Trello com detalhes especificados, como nome, descrição, ID da lista e outros parâmetros opcionais. Facilita a adição de tarefas e a gestão de projetos dentro do Trello.
- llm: Initiate a new card on a Trello board by specifying essential details such as the card's name, description, and the list it belongs to, among other settings. Streamlines project task additions and organizational workflows.
-parameters:
- - name: name
- type: string
- required: true
- label:
- en_US: Card Name
- zh_Hans: 卡片名称
- pt_BR: Nome do Cartão
- human_description:
- en_US: The name for the new card. Acts as the primary identifier and summary of the card's purpose.
- zh_Hans: 新卡片的名称。作为卡片目的的主要标识和总结。
- pt_BR: O nome para o novo cartão. Funciona como o identificador principal e resumo do propósito do cartão.
- llm_description: Provide a concise, descriptive name for the card, outlining its main focus or task.
- form: llm
- # Include additional parameters like desc, pos, due, idList, etc., following the same pattern.
- - name: desc
- type: string
- required: false
- label:
- en_US: Card Description
- zh_Hans: 卡片描述
- pt_BR: Descrição do Cartão
- human_description:
- en_US: Optional. A brief description of the card's purpose or contents.
- zh_Hans: 可选。卡片目的或内容的简要描述。
- pt_BR: Opcional. Uma breve descrição do propósito ou conteúdo do cartão.
- llm_description: Add a brief description to the card to provide context or additional information about its purpose.
- form: llm
- - name: pos
- type: string
- required: false
- label:
- en_US: Position
- zh_Hans: 位置
- pt_BR: Posição
- human_description:
- en_US: Optional. The position of the card in the list. Can be 'top', 'bottom', or a positive number.
- zh_Hans: 可选。卡片在列表中的位置。可以是“top”、“bottom” 或正数。
- pt_BR: Opcional. A posição do cartão na lista. Pode ser 'top', 'bottom' ou um número positivo.
- llm_description: Specify the position of the card within the list, either at the top, bottom, or a specific numerical index.
- form: llm
- - name: due
- type: string
- required: false
- label:
- en_US: Due Date
- zh_Hans: 截止日期
- pt_BR: Data de Vencimento
- human_description:
- en_US: Optional. The due date for the card in the format 'MM/DD/YYYY'.
- zh_Hans: 可选。卡片的截止日期,格式为“MM/DD/YYYY”。
- pt_BR: Opcional. A data de vencimento do cartão no formato 'MM/DD/YYYY'.
- llm_description: Set a due date for the card to establish a deadline for completion or action.
- form: llm
- - name: start
- type: string
- required: false
- label:
- en_US: Start Date
- zh_Hans: 开始日期
- pt_BR: Data de Início
- human_description:
- en_US: Optional. The start date for the card in the format 'MM/DD/YYYY'.
- zh_Hans: 可选。卡片的开始日期,格式为“MM/DD/YYYY”。
- pt_BR: Opcional. A data de início do cartão no formato 'MM/DD/YYYY'.
- llm_description: Specify a start date for the card to mark the beginning of a task or project phase.
- form: llm
- - name: dueComplete
- type: boolean
- required: false
- label:
- en_US: Due Complete
- zh_Hans: 截止日期已完成
- pt_BR: Vencimento Concluído
- human_description:
- en_US: Optional. Set to true if the due date has been completed, or false if it is pending.
- zh_Hans: 可选。如果截止日期已完成,则设置为 true;如果尚未完成,则设置为 false。
- pt_BR: Opcional. Defina como true se a data de vencimento foi concluída, ou como false se estiver pendente.
- llm_description: Indicate whether the due date for the card has been marked as complete or is still pending.
- form: llm
- - name: idList
- type: string
- required: true
- label:
- en_US: List ID
- zh_Hans: 列表 ID
- pt_BR: ID da Lista
- human_description:
- en_US: The unique identifier of the list where the card will be added.
- zh_Hans: 卡片将被添加到的列表的唯一标识符。
- pt_BR: O identificador único da lista onde o cartão será adicionado.
- llm_description: Input the ID of the list where the card should be placed, ensuring it is added to the correct list.
- form: llm
- - name: idMembers
- type: string
- required: false
- label:
- en_US: Member IDs
- zh_Hans: 成员 ID
- pt_BR: IDs de Membros
- human_description:
- en_US: Optional. The IDs of members to assign to the card.
- zh_Hans: 可选。要分配给卡片的成员的 ID。
- pt_BR: Opcional. Os IDs dos membros a serem atribuídos ao cartão.
- llm_description: Specify the IDs of members to assign to the card, allowing for task delegation or collaboration.
- form: llm
- - name: idLabels
- type: string
- required: false
- label:
- en_US: Label IDs
- zh_Hans: 标签 ID
- pt_BR: IDs de Etiquetas
- human_description:
- en_US: Optional. The IDs of labels to assign to the card.
- zh_Hans: 可选。要分配给卡片的标签的 ID。
- pt_BR: Opcional. Os IDs das etiquetas a serem atribuídos ao cartão.
- llm_description: Assign specific labels to the card by providing their IDs, aiding in visual categorization or prioritization.
- form: llm
- - name: urlSource
- type: string
- required: false
- label:
- en_US: Source URL
- zh_Hans: 来源 URL
- pt_BR: URL de Origem
- human_description:
- en_US: Optional. The URL to attach as the card's source.
- zh_Hans: 可选。要附加为卡片来源的 URL。
- pt_BR: Opcional. O URL a ser anexado como a fonte do cartão.
- llm_description: Provide a URL to serve as the source reference for the card, linking to external resources or documents.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/delete_board.py b/api/core/tools/provider/builtin/trello/tools/delete_board.py
deleted file mode 100644
index 7fc9d1f13c2664..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/delete_board.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DeleteBoardTool(BuiltinTool):
- """
- Tool for deleting a Trello board by ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to delete a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}?key={api_key}&token={token}"
-
- try:
- response = requests.delete(url)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to delete board")
-
- return self.create_text_message(text=f"Board with ID {board_id} deleted successfully.")
diff --git a/api/core/tools/provider/builtin/trello/tools/delete_board.yaml b/api/core/tools/provider/builtin/trello/tools/delete_board.yaml
deleted file mode 100644
index f043e78870d062..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/delete_board.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: delete_board
- author: Yash Parmar
- label:
- en_US: Delete Board
- zh_Hans: 删除看板
- pt_BR: Excluir Quadro
-description:
- human:
- en_US: Deletes a Trello board using its unique ID. This tool allows for the removal of boards that are no longer needed, ensuring a tidy workspace.
- zh_Hans: 使用其唯一 ID 删除 Trello 看板。此工具允许删除不再需要的看板,确保工作区整洁。
- pt_BR: Exclui um quadro Trello usando seu ID único. Esta ferramenta permite a remoção de quadros que não são mais necessários, garantindo um espaço de trabalho organizado.
- llm: Remove a Trello board by specifying its ID. This functionality is helpful for cleaning up unnecessary boards from your Trello account.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier for the Trello board you wish to delete. This ensures the specific board is accurately targeted for deletion.
- zh_Hans: 您希望删除的 Trello 看板的唯一标识符。这确保了准确地针对特定看板进行删除。
- pt_BR: O identificador único para o quadro Trello que você deseja excluir. Isso garante que o quadro específico seja precisamente direcionado para exclusão.
- llm_description: Enter the ID of the Trello board you want to remove. This ID is essential to identify the board precisely and perform the deletion.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/delete_card.py b/api/core/tools/provider/builtin/trello/tools/delete_card.py
deleted file mode 100644
index 1de98d639ebb7d..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/delete_card.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class DeleteCardByIdTool(BuiltinTool):
- """
- Tool for deleting a Trello card by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to delete a Trello card by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the card ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- card_id = tool_parameters.get("id")
-
- if not (api_key and token and card_id):
- return self.create_text_message("Missing required parameters: API key, token, or card ID.")
-
- url = f"https://api.trello.com/1/cards/{card_id}?key={api_key}&token={token}"
-
- try:
- response = requests.delete(url)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to delete card")
-
- return self.create_text_message(text=f"Card with ID {card_id} has been successfully deleted.")
diff --git a/api/core/tools/provider/builtin/trello/tools/delete_card.yaml b/api/core/tools/provider/builtin/trello/tools/delete_card.yaml
deleted file mode 100644
index 8898ef1bde3680..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/delete_card.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: delete_card_by_id
- author: Yash Parmar
- label:
- en_US: Delete Card by ID
- zh_Hans: 通过 ID 删除卡片
- pt_BR: Deletar Cartão por ID
-description:
- human:
- en_US: Deletes a Trello card using its unique ID. This tool facilitates the removal of cards that are no longer needed, maintaining an organized board.
- zh_Hans: 使用其唯一 ID 删除 Trello 卡片。此工具便于删除不再需要的卡片,保持看板的有序。
- pt_BR: Exclui um cartão Trello usando seu ID único. Esta ferramenta facilita a remoção de cartões que não são mais necessários, mantendo um quadro organizado.
- llm: Remove a specific Trello card by providing its ID. Ideal for cleaning up and organizing your Trello boards by eliminating unwanted cards.
-parameters:
- - name: id
- type: string
- required: true
- label:
- en_US: Card ID
- zh_Hans: 卡片 ID
- pt_BR: ID do Cartão
- human_description:
- en_US: The unique identifier of the Trello card you wish to delete. This ensures the precise card is removed.
- zh_Hans: 您希望删除的 Trello 卡片的唯一标识符。这确保了精确移除特定卡片。
- pt_BR: O identificador único do cartão Trello que você deseja excluir. Isso garante que o cartão exato seja removido.
- llm_description: Input the ID of the Trello card targeted for deletion to ensure accurate and specific removal.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.py b/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.py
deleted file mode 100644
index 0c5ed9ea8533ff..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class FetchAllBoardsTool(BuiltinTool):
- """
- Tool for fetching all boards from Trello.
- """
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the fetch all boards tool.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation.
-
- Returns:
- Union[ToolInvokeMessage, List[ToolInvokeMessage]]: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
-
- if not (api_key and token):
- return self.create_text_message("Missing Trello API key or token in credentials.")
-
- # Including board filter in the request if provided
- board_filter = tool_parameters.get("boards", "open")
- url = f"https://api.trello.com/1/members/me/boards?filter={board_filter}&key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status() # Raises stored HTTPError, if one occurred.
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to fetch boards")
-
- boards = response.json()
-
- if not boards:
- return self.create_text_message("No boards found in Trello.")
-
- # Creating a string with both board names and IDs
- boards_info = ", ".join([f"{board['name']} (ID: {board['id']})" for board in boards])
- return self.create_text_message(text=f"Boards: {boards_info}")
diff --git a/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.yaml b/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.yaml
deleted file mode 100644
index d0ac4beaaa723a..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/fetch_all_boards.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-identity:
- name: fetch_all_boards
- author: Yash Parmar
- label:
- en_US: Fetch All Boards
- zh_Hans: 获取所有看板
- pt_BR: Buscar Todos os Quadros
-description:
- human:
- en_US: Retrieves all the Trello boards associated with the user's account. This tool provides a quick overview of all open boards, aiding in efficient project management and organization.
- zh_Hans: 检索与用户账户关联的所有 Trello 看板。该工具提供了所有打开的看板的快速概览,有助于高效的项目管理和组织。
- pt_BR: Recupera todos os quadros do Trello associados à conta do usuário. Esta ferramenta oferece uma visão geral rápida de todos os quadros abertos, auxiliando na gestão e organização eficiente do projeto.
- llm: This tool fetches all Trello boards linked to the user's account, offering a swift snapshot of open boards to streamline project management and organization tasks.
-parameters:
- - name: boards
- type: string
- required: false
- default: open
- label:
- en_US: Boards filter
- zh_Hans: 看板过滤器
- pt_BR: Filtro de quadros
- human_description:
- en_US: Specifies the type of boards to retrieve. Default is 'open', fetching all open boards. Other options include 'closed', 'members', 'organization', etc.
- zh_Hans: 指定要检索的看板类型。默认为“open”,获取所有打开的看板。其他选项包括“closed”,“members”,“organization”等。
- pt_BR: Especifica o tipo de quadros a serem recuperados. O padrão é 'open', buscando todos os quadros abertos. Outras opções incluem 'closed', 'members', 'organization', etc.
- llm_description: Determines the category of boards to be displayed, with 'open' as the default setting to show all open boards. Variants like 'closed', 'members', and 'organization' are also selectable.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_actions.py b/api/core/tools/provider/builtin/trello/tools/get_board_actions.py
deleted file mode 100644
index cabc7ce09359d5..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_actions.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetBoardActionsTool(BuiltinTool):
- """
- Tool for retrieving actions for a Trello board by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to retrieve actions for a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}/actions?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status()
- actions = response.json()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to retrieve board actions")
-
- actions_summary = "\n".join(
- [f"{action['type']}: {action.get('data', {}).get('text', 'No details available')}" for action in actions]
- )
- return self.create_text_message(text=f"Actions for Board ID {board_id}:\n{actions_summary}")
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_actions.yaml b/api/core/tools/provider/builtin/trello/tools/get_board_actions.yaml
deleted file mode 100644
index 1ba89f9e44abbd..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_actions.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: get_board_actions
- author: Yash Parmar
- label:
- en_US: Get Board Actions
- zh_Hans: 获取看板操作
- pt_BR: Obter Ações do Quadro
-description:
- human:
- en_US: Retrieves a list of actions (such as updates, movements, and comments) for a Trello board by its ID. This tool provides insights into the board's activity history.
- zh_Hans: 通过其 ID 为 Trello 看板检索操作列表(如更新、移动和评论)。此工具提供了看板活动历史的见解。
- pt_BR: Recupera uma lista de ações (como atualizações, movimentos e comentários) para um quadro Trello pelo seu ID. Esta ferramenta oferece insights sobre o histórico de atividades do quadro.
- llm: Fetch the sequence of actions performed on a Trello board, such as card updates, movements, and comments, by providing the board's ID. Offers a historical view of board activities.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier of the Trello board for which you want to retrieve actions. It targets the specific board to fetch its activity log.
- zh_Hans: 您想要检索操作的 Trello 看板的唯一标识符。它定位特定的看板以获取其活动日志。
- pt_BR: O identificador único do quadro Trello para o qual você deseja recuperar ações. Direciona especificamente para o quadro para buscar seu registro de atividades.
- llm_description: Input the ID of the Trello board to access its detailed action history, including all updates, comments, and movements related to the board.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_by_id.py b/api/core/tools/provider/builtin/trello/tools/get_board_by_id.py
deleted file mode 100644
index fe42cd9c5cbf86..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_by_id.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetBoardByIdTool(BuiltinTool):
- """
- Tool for retrieving detailed information about a Trello board by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to retrieve a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status()
- board = response.json()
- board_details = self.format_board_details(board)
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to retrieve board")
-
- return self.create_text_message(text=board_details)
-
- def format_board_details(self, board: dict) -> str:
- """
- Format the board details into a human-readable string.
-
- Args:
- board (dict): The board information as a dictionary.
-
- Returns:
- str: Formatted board details.
- """
- details = (
- f"Board Name: {board['name']}\n"
- f"Board ID: {board['id']}\n"
- f"Description: {board['desc'] or 'No description provided.'}\n"
- f"Status: {'Closed' if board['closed'] else 'Open'}\n"
- f"Organization ID: {board['idOrganization'] or 'Not part of an organization.'}\n"
- f"URL: {board['url']}\n"
- f"Short URL: {board['shortUrl']}\n"
- f"Permission Level: {board['prefs']['permissionLevel']}\n"
- f"Background Color: {board['prefs']['backgroundColor']}"
- )
- return details
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_by_id.yaml b/api/core/tools/provider/builtin/trello/tools/get_board_by_id.yaml
deleted file mode 100644
index 45c93006ba4414..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_by_id.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: get_board_by_id
- author: Yash Parmar
- label:
- en_US: Get Board by ID
- zh_Hans: 通过 ID 获取看板
- pt_BR: Obter Quadro por ID
-description:
- human:
- en_US: Retrieves detailed information about a specific Trello board using its unique ID. This tool enables users to quickly access board details without navigating through the Trello interface.
- zh_Hans: 使用其唯一 ID 检索有关特定 Trello 看板的详细信息。此工具使用户能够快速访问看板详情,无需通过 Trello 界面导航。
- pt_BR: Recupera informações detalhadas sobre um quadro Trello específico usando seu ID único. Esta ferramenta permite que os usuários acessem rapidamente os detalhes do quadro sem navegar pela interface do Trello.
- llm: Access details of a Trello board by providing its ID. This tool offers a direct way to view board information, simplifying the process of managing and reviewing Trello boards.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier for the Trello board you wish to retrieve. This ID enables precise targeting and fetching of the board's details.
- zh_Hans: 您希望检索的 Trello 看板的唯一标识符。此 ID 使能够准确定位和获取看板的详细信息。
- pt_BR: O identificador único do quadro Trello que você deseja recuperar. Este ID permite o direcionamento preciso e a obtenção dos detalhes do quadro.
- llm_description: Input the ID of the Trello board to get its details. This unique ID ensures accurate retrieval of information about the specified board.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_cards.py b/api/core/tools/provider/builtin/trello/tools/get_board_cards.py
deleted file mode 100644
index ff2b1221e767de..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_cards.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetBoardCardsTool(BuiltinTool):
- """
- Tool for retrieving cards on a Trello board by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to retrieve cards on a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}/cards?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status()
- cards = response.json()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to retrieve board cards")
-
- cards_summary = "\n".join([f"{card['name']} (ID: {card['id']})" for card in cards])
- return self.create_text_message(text=f"Cards for Board ID {board_id}:\n{cards_summary}")
diff --git a/api/core/tools/provider/builtin/trello/tools/get_board_cards.yaml b/api/core/tools/provider/builtin/trello/tools/get_board_cards.yaml
deleted file mode 100644
index 852ea278af341c..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_board_cards.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: get_board_cards
- author: Yash Parmar
- label:
- en_US: Get Board Cards
- zh_Hans: 获取看板卡片
- pt_BR: Obter Cartões do Quadro
-description:
- human:
- en_US: Retrieves all cards present on a specific Trello board by its ID, providing a list of card names and their IDs. Useful for managing and organizing project tasks.
- zh_Hans: 通过其 ID 检索特定 Trello 看板上的所有卡片,提供卡片名称及其 ID 的列表。用于管理和组织项目任务。
- pt_BR: Recupera todos os cartões presentes em um quadro Trello específico pelo seu ID, fornecendo uma lista dos nomes dos cartões e seus IDs. Útil para gerenciar e organizar tarefas de projetos.
- llm: Obtain a list of all cards on a specific Trello board by entering the board's ID. This tool helps in quickly assessing the tasks or items associated with the board.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier of the Trello board from which you want to retrieve cards. It specifies the exact board to gather card details from.
- zh_Hans: 您想要从中检索卡片的 Trello 看板的唯一标识符。它指定了要从中收集卡片详细信息的确切看板。
- pt_BR: O identificador único do quadro Trello do qual você deseja recuperar os cartões. Especifica o quadro exato para obter detalhes dos cartões.
- llm_description: Input the ID of the Trello board to fetch its cards, allowing for a detailed overview of the board's contents.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.py b/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.py
deleted file mode 100644
index 3d7f9f4ad1c996..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetFilteredBoardCardsTool(BuiltinTool):
- """
- Tool for retrieving filtered cards on a Trello board by its ID and a specified filter.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to retrieve filtered cards on a Trello board by its ID and filter.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID and filter.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
- filter = tool_parameters.get("filter")
-
- if not (api_key and token and board_id and filter):
- return self.create_text_message("Missing required parameters: API key, token, board ID, or filter.")
-
- url = f"https://api.trello.com/1/boards/{board_id}/cards/{filter}?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status()
- filtered_cards = response.json()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to retrieve filtered cards")
-
- card_details = "\n".join([f"{card['name']} (ID: {card['id']})" for card in filtered_cards])
- return self.create_text_message(
- text=f"Filtered Cards for Board ID {board_id} with Filter '{filter}':\n{card_details}"
- )
diff --git a/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.yaml b/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.yaml
deleted file mode 100644
index 390595645771e4..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_filterd_board_cards.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- name: get_filtered_board_cards
- author: Yash Parmar
- label:
- en_US: Get Filtered Board Cards
- zh_Hans: 获取筛选的看板卡片
- pt_BR: Obter Cartões Filtrados do Quadro
-description:
- human:
- en_US: Retrieves cards from a Trello board using a specified filter and the board's ID. Filters include options like 'all', 'open', 'closed', 'none', and 'visible', allowing for tailored views of board content.
- zh_Hans: 使用指定的过滤器和看板的 ID 从 Trello 看板检索卡片。过滤器包括 'all', 'open', 'closed', 'none' 和 'visible' 等选项,允许对看板内容进行定制查看。
- pt_BR: Recupera cartões de um quadro Trello usando um filtro especificado e o ID do quadro. Os filtros incluem opções como 'all', 'open', 'closed', 'none' e 'visible', permitindo visualizações personalizadas do conteúdo do quadro.
- llm: Access cards on a Trello board through specific filters such as 'all', 'open', 'closed', 'none', and 'visible' by providing the board's ID. This feature enables focused examination of the board's cards.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier for the Trello board from which to retrieve the filtered cards.
- zh_Hans: 用于检索筛选卡片的 Trello 看板的唯一标识符。
- pt_BR: O identificador único do quadro Trello do qual os cartões filtrados serão recuperados.
- llm_description: Enter the Trello board's ID to specify from which board to fetch the cards using the filter.
- form: llm
- - name: filter
- type: string
- required: true
- label:
- en_US: Filter
- zh_Hans: 过滤器
- pt_BR: Filtro
- human_description:
- en_US: The filter to apply when retrieving cards. Valid values are 'all', 'open', 'closed', 'none', and 'visible'.
- zh_Hans: 检索卡片时应用的过滤器。有效值为 'all', 'open', 'closed', 'none', 和 'visible'。
- pt_BR: O filtro a ser aplicado ao recuperar cartões. Os valores válidos são 'all', 'open', 'closed', 'none' e 'visible'.
- llm_description: Specify the filter for card retrieval. Choose from 'all', 'open', 'closed', 'none', or 'visible' to control which cards are fetched.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.py b/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.py
deleted file mode 100644
index ccf404068f225e..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class GetListsFromBoardTool(BuiltinTool):
- """
- Tool for retrieving all lists from a specified Trello board by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool]]) -> ToolInvokeMessage:
- """
- Invoke the tool to get all lists from a specified Trello board.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool]]): The parameters for the tool invocation,
- including the board ID.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.get("boardId")
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}/lists?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status()
- lists = response.json()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to retrieve lists")
-
- lists_info = "\n".join([f"{list['name']} (ID: {list['id']})" for list in lists])
- return self.create_text_message(text=f"Lists on Board ID {board_id}:\n{lists_info}")
diff --git a/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.yaml b/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.yaml
deleted file mode 100644
index 31028a80404de3..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/get_lists_on_board.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: get_lists_from_board
- author: Yash Parmar
- label:
- en_US: Get Lists from Board
- zh_Hans: 获取看板的列表
- pt_BR: Obter Listas do Quadro
-description:
- human:
- en_US: Retrieves all lists from a specified Trello board by its ID, providing an overview of the board's organization and current phases or categories.
- zh_Hans: 通过其 ID 从指定的 Trello 看板检索所有列表,提供看板组织和当前阶段或类别的概览。
- pt_BR: Recupera todas as listas de um quadro Trello especificado pelo seu ID, fornecendo uma visão geral da organização do quadro e das fases ou categorias atuais.
- llm: Fetch and display all lists from a specific Trello board by inputting the board's ID. This aids in understanding the board's structure and task categorization.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier of the Trello board from which to retrieve the lists.
- zh_Hans: 用于检索列表的 Trello 看板的唯一标识符。
- pt_BR: O identificador único do quadro Trello do qual as listas serão recuperadas.
- llm_description: Enter the ID of the Trello board to obtain a detailed list of all its lists, providing insight into the board's structure.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/update_board.py b/api/core/tools/provider/builtin/trello/tools/update_board.py
deleted file mode 100644
index 1e358b00f49add..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/update_board.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class UpdateBoardByIdTool(BuiltinTool):
- """
- Tool for updating a Trello board by its ID with various parameters.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool, None]]) -> ToolInvokeMessage:
- """
- Invoke the tool to update a Trello board by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool, None]]): The parameters for the tool invocation,
- including board ID and updates.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- board_id = tool_parameters.pop("boardId", None)
-
- if not (api_key and token and board_id):
- return self.create_text_message("Missing required parameters: API key, token, or board ID.")
-
- url = f"https://api.trello.com/1/boards/{board_id}"
-
- # Removing parameters not intended for update action or with None value
- params = {k: v for k, v in tool_parameters.items() if v is not None}
- params["key"] = api_key
- params["token"] = token
-
- try:
- response = requests.put(url, params=params)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to update board")
-
- updated_board = response.json()
- return self.create_text_message(text=f"Board '{updated_board['name']}' updated successfully.")
diff --git a/api/core/tools/provider/builtin/trello/tools/update_board.yaml b/api/core/tools/provider/builtin/trello/tools/update_board.yaml
deleted file mode 100644
index 487919631ade34..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/update_board.yaml
+++ /dev/null
@@ -1,157 +0,0 @@
-identity:
- name: update_board_by_id
- author: Yash Parmar
- label:
- en_US: Update Board by ID
- zh_Hans: 通过 ID 更新看板
- pt_BR: Atualizar Quadro por ID
-description:
- human:
- en_US: Updates a Trello board's settings based on the provided ID and parameters. Allows for changing the board's name, description, status, and other preferences.
- zh_Hans: 根据提供的 ID 和参数更新 Trello 看板的设置。允许更改看板的名称、描述、状态和其他偏好设置。
- pt_BR: Atualiza as configurações de um quadro Trello com base no ID fornecido e nos parâmetros. Permite alterar o nome, descrição, status e outras preferências do quadro.
- llm: Modify a Trello board's attributes like its name, description, and visibility settings using the board's ID. This tool streamlines board customization and management.
-parameters:
- - name: boardId
- type: string
- required: true
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: The unique identifier of the Trello board you want to update. Ensures targeted and precise updates.
- zh_Hans: 您要更新的 Trello 看板的唯一标识符。确保目标准确和更新精确。
- pt_BR: O identificador único do quadro Trello que você deseja atualizar. Garante atualizações direcionadas e precisas.
- llm_description: Provide the specific ID of the Trello board you aim to update to ensure accuracy in modification process.
- form: llm
- - name: name
- type: string
- required: false
- label:
- en_US: Board Name
- zh_Hans: 看板名称
- pt_BR: Nome do Quadro
- human_description:
- en_US: Optional. The new name for the board.
- zh_Hans: 可选。看板的新名称。
- pt_BR: Opcional. O novo nome para o quadro.
- llm_description: Enter a new name for the board if you wish to change it; this name identifies the board in Trello.
- form: llm
- - name: desc
- type: string
- required: false
- label:
- en_US: Board Description
- zh_Hans: 看板描述
- pt_BR: Descrição do Quadro
- human_description:
- en_US: Optional. The new description for the board.
- zh_Hans: 可选。看板的新描述。
- pt_BR: Opcional. A nova descrição para o quadro.
- llm_description: Provide a new description for the board if you wish to update it; this description provides additional context about the board.
- form: llm
- - name: closed
- type: boolean
- required: false
- label:
- en_US: Closed
- zh_Hans: 已关闭
- pt_BR: Fechado
- human_description:
- en_US: Optional. Set to true to close the board, or false to keep it open.
- zh_Hans: 可选。设置为 true 以关闭看板,或设置为 false 以保持打开。
- pt_BR: Opcional. Defina como true para fechar o quadro ou como false para mantê-lo aberto.
- llm_description: Specify whether the board should be closed or kept open by setting this parameter to true or false.
- form: llm
- - name: subscribed
- type: string
- required: false
- label:
- en_US: Subscribed
- zh_Hans: 订阅
- pt_BR: Inscrito
- human_description:
- en_US: Optional. Set to true to subscribe to the board, or false to unsubscribe.
- zh_Hans: 可选。设置为 true 以订阅看板,或设置为 false 以取消订阅。
- pt_BR: Opcional. Defina como true para se inscrever no quadro ou como false para cancelar a inscrição.
- llm_description: Choose to subscribe or unsubscribe from the board by setting this parameter to true or false.
- form: llm
- - name: idOrganization
- type: string
- required: false
- label:
- en_US: Organization ID
- zh_Hans: 组织 ID
- pt_BR: ID da Organização
- human_description:
- en_US: Optional. The ID of the organization to which the board belongs.
- zh_Hans: 可选。看板所属组织的 ID。
- pt_BR: Opcional. O ID da organização à qual o quadro pertence.
- llm_description: Input the ID of the organization to which the board is associated, if applicable.
- form: llm
- - name: prefs_permissionLevel
- type: string
- required: false
- label:
- en_US: Permission Level
- zh_Hans: 权限级别
- pt_BR: Nível de Permissão
- human_description:
- en_US: Optional. The permission level for the board. Valid values are 'private', 'org', or 'public'.
- zh_Hans: 可选。看板的权限级别。有效值为 'private'、'org' 或 'public'。
- pt_BR: Opcional. O nível de permissão para o quadro. Os valores válidos são 'private', 'org' ou 'public'.
- llm_description: Specify the permission level for the board by choosing from 'private', 'org', or 'public'.
- form: llm
- - name: prefs_selfJoin
- type: boolean
- required: false
- label:
- en_US: Allow Self-Join
- zh_Hans: 允许自行加入
- pt_BR: Permitir Auto-Inscrição
- human_description:
- en_US: Optional. Set to true to allow members to join the board without an invitation, or false to require an invitation.
- zh_Hans: 可选。设置为 true 以允许成员加入看板而无需邀请,或设置为 false 以要求邀请。
- pt_BR: Opcional. Defina como true para permitir que os membros se inscrevam no quadro sem um convite, ou como false para exigir um convite.
- llm_description: Choose whether to allow members to join the board without an invitation by setting this parameter to true or false.
- form: llm
- - name: prefs_cardCovers
- type: boolean
- required: false
- label:
- en_US: Card Covers
- zh_Hans: 卡片封面
- pt_BR: Capas de Cartão
- human_description:
- en_US: Optional. Set to true to enable card covers, or false to disable them.
- zh_Hans: 可选。设置为 true 以启用卡片封面,或设置为 false 以禁用卡片封面。
- pt_BR: Opcional. Defina como true para habilitar capas de cartão ou como false para desabilitá-las.
- llm_description: Enable or disable card covers by setting this parameter to true or false.
- form: llm
- - name: prefs_hideVotes
- type: boolean
- required: false
- label:
- en_US: Hide Votes
- zh_Hans: 隐藏投票
- pt_BR: Ocultar Votos
- human_description:
- en_US: Optional. Set to true to hide votes, or false to show them.
- zh_Hans: 可选。设置为 true 以隐藏投票,或设置为 false 以显示投票。
- pt_BR: Opcional. Defina como true para ocultar votos ou como false para mostrá-los.
- llm_description: Choose to hide or show votes by setting this parameter to true or false.
- form: llm
- - name: prefs_invitations
- type: string
- required: false
- label:
- en_US: Invitations
- zh_Hans: 邀请
- pt_BR: Convites
- human_description:
- en_US: Optional. Set to 'members' to allow only board members to send invitations, or 'admins' to allow admins to send invitations.
- zh_Hans: 可选。设置为 'members' 以仅允许看板成员发送邀请,或设置为 'admins' 以允许管理员发送邀请。
- pt_BR: Opcional. Defina como 'members' para permitir que apenas membros do quadro enviem convites, ou 'admins' para permitir que os administradores enviem convites.
- llm_description: Choose who can send invitations by setting this parameter to 'members' or 'admins'.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/tools/update_card.py b/api/core/tools/provider/builtin/trello/tools/update_card.py
deleted file mode 100644
index d25fcbafaa6326..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/update_card.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class UpdateCardByIdTool(BuiltinTool):
- """
- Tool for updating a Trello card by its ID.
- """
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Union[str, int, bool, None]]) -> ToolInvokeMessage:
- """
- Invoke the tool to update a Trello card by its ID.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (dict[str, Union[str, int, bool, None]]): The parameters for the tool invocation,
- including the card ID and updates.
-
- Returns:
- ToolInvokeMessage: The result of the tool invocation.
- """
- api_key = self.runtime.credentials.get("trello_api_key")
- token = self.runtime.credentials.get("trello_api_token")
- card_id = tool_parameters.get("id")
-
- if not (api_key and token and card_id):
- return self.create_text_message("Missing required parameters: API key, token, or card ID.")
-
- # Constructing the URL and the payload for the PUT request
- url = f"https://api.trello.com/1/cards/{card_id}"
- params = {k: v for k, v in tool_parameters.items() if v is not None and k != "id"}
- params.update({"key": api_key, "token": token})
-
- try:
- response = requests.put(url, params=params)
- response.raise_for_status()
- except requests.exceptions.RequestException as e:
- return self.create_text_message("Failed to update card")
-
- updated_card_info = f"Card '{card_id}' updated successfully."
- return self.create_text_message(text=updated_card_info)
diff --git a/api/core/tools/provider/builtin/trello/tools/update_card.yaml b/api/core/tools/provider/builtin/trello/tools/update_card.yaml
deleted file mode 100644
index 5240dfc3ed2564..00000000000000
--- a/api/core/tools/provider/builtin/trello/tools/update_card.yaml
+++ /dev/null
@@ -1,81 +0,0 @@
-identity:
- name: update_card_by_id
- author: Yash Parmar
- label:
- en_US: Update Card by ID
- zh_Hans: 通过 ID 更新卡片
- pt_BR: Atualizar Cartão por ID
-description:
- human:
- en_US: Updates specified attributes of a Trello card, such as its name, description, list ID, and board ID, by providing the card's unique ID.
- zh_Hans: 通过提供卡片的唯一 ID,更新 Trello 卡片的特定属性,如其名称、描述、列表 ID 和看板 ID。
- pt_BR: Atualiza atributos específicos de um cartão Trello, como seu nome, descrição, ID da lista e ID do quadro, fornecendo o ID único do cartão.
- llm: Modify a Trello card's key details, including name, description, and its placement on the board, by using the card's ID. Enables precise and targeted updates to card information.
-parameters:
- - name: id
- type: string
- required: true
- label:
- en_US: Card ID
- zh_Hans: 卡片 ID
- pt_BR: ID do Cartão
- human_description:
- en_US: The unique identifier of the Trello card you intend to update.
- zh_Hans: 您打算更新的 Trello 卡片的唯一标识符。
- pt_BR: O identificador único do cartão Trello que você pretende atualizar.
- llm_description: Input the ID of the Trello card to be updated to ensure the correct card is targeted.
- form: llm
- # Include other parameters following the same pattern
- - name: name
- type: string
- required: false
- label:
- en_US: New Name
- zh_Hans: 新名称
- pt_BR: Novo Nome
- human_description:
- en_US: Optional. The new name to assign to the card.
- zh_Hans: 可选。要分配给卡片的新名称。
- pt_BR: Opcional. O novo nome a ser atribuído ao cartão.
- llm_description: Specify a new name for the card if changing it. This name is what will be displayed on the Trello board.
- form: llm
- # Add definitions for desc, idList and idBoard parameters
- - name: desc
- type: string
- required: false
- label:
- en_US: New Description
- zh_Hans: 新描述
- pt_BR: Nova Descrição
- human_description:
- en_US: Optional. The new description to assign to the card.
- zh_Hans: 可选。要分配给卡片的新描述。
- pt_BR: Opcional. A nova descrição a ser atribuída ao cartão.
- llm_description: Provide a new description for the card if you wish to update it; this description provides additional context about the card.
- form: llm
- - name: idList
- type: string
- required: false
- label:
- en_US: List ID
- zh_Hans: 列表 ID
- pt_BR: ID da Lista
- human_description:
- en_US: Optional. The ID of the list to which the card should be moved.
- zh_Hans: 可选。卡片应移动到的列表的 ID。
- pt_BR: Opcional. O ID da lista para a qual o cartão deve ser movido.
- llm_description: Enter the ID of the list where you want to move the card. This action relocates the card to the specified list.
- form: llm
- - name: idBoard
- type: string
- required: false
- label:
- en_US: Board ID
- zh_Hans: 看板 ID
- pt_BR: ID do Quadro
- human_description:
- en_US: Optional. The ID of the board to which the card should be moved.
- zh_Hans: 可选。卡片应移动到的看板的 ID。
- pt_BR: Opcional. O ID do quadro para o qual o cartão deve ser movido.
- llm_description: Provide the ID of the board where you want to move the card. This action relocates the card to the specified board.
- form: llm
diff --git a/api/core/tools/provider/builtin/trello/trello.py b/api/core/tools/provider/builtin/trello/trello.py
deleted file mode 100644
index e0dca50ec99aee..00000000000000
--- a/api/core/tools/provider/builtin/trello/trello.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from typing import Any
-
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class TrelloProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- """Validate Trello API credentials by making a test API call.
-
- Args:
- credentials (dict[str, Any]): The Trello API credentials to validate.
-
- Raises:
- ToolProviderCredentialValidationError: If the credentials are invalid.
- """
- api_key = credentials.get("trello_api_key")
- token = credentials.get("trello_api_token")
- url = f"https://api.trello.com/1/members/me?key={api_key}&token={token}"
-
- try:
- response = requests.get(url)
- response.raise_for_status() # Raises an HTTPError for bad responses
- except requests.exceptions.HTTPError as e:
- if response.status_code == 401:
- # Unauthorized, indicating invalid credentials
- raise ToolProviderCredentialValidationError("Invalid Trello credentials: Unauthorized.")
- # Handle other potential HTTP errors
- raise ToolProviderCredentialValidationError("Error validating Trello credentials")
- except requests.exceptions.RequestException as e:
- # Handle other exceptions, such as connection errors
- raise ToolProviderCredentialValidationError("Error validating Trello credentials")
diff --git a/api/core/tools/provider/builtin/trello/trello.yaml b/api/core/tools/provider/builtin/trello/trello.yaml
deleted file mode 100644
index 49c9f4f9a178f8..00000000000000
--- a/api/core/tools/provider/builtin/trello/trello.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-identity:
- author: Yash Parmar
- name: trello
- label:
- en_US: Trello
- zh_Hans: Trello
- pt_BR: Trello
- description:
- en_US: "Trello: A visual tool for organizing your work and life."
- zh_Hans: "Trello: 一个用于组织工作和生活的视觉工具。"
- pt_BR: "Trello: Uma ferramenta visual para organizar seu trabalho e vida."
- icon: icon.svg
- tags:
- - productivity
-credentials_for_provider:
- trello_api_key:
- type: secret-input
- required: true
- label:
- en_US: Trello API key
- zh_Hans: Trello API key
- pt_BR: Trello API key
- placeholder:
- en_US: Enter your Trello API key
- zh_Hans: 输入您的 Trello API key
- pt_BR: Insira sua chave API do Trello
- help:
- en_US: Obtain your API key from Trello's website.
- zh_Hans: 从 Trello 网站获取您的 API key。
- pt_BR: Obtenha sua chave API no site do Trello.
- url: https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/
- trello_api_token:
- type: secret-input
- required: true
- label:
- en_US: Trello API token
- zh_Hans: Trello API token
- pt_BR: Trello API token
- placeholder:
- en_US: Enter your Trello API token
- zh_Hans: 输入您的 Trello API token
- pt_BR: Insira seu token API do Trello
- help:
- en_US: Secure your API token from Trello's website.
- zh_Hans: 从 Trello 网站获取您的 API token。
- pt_BR: Garanta seu token API no site do Trello.
- url: https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/
diff --git a/api/core/tools/provider/builtin/twilio/_assets/icon.svg b/api/core/tools/provider/builtin/twilio/_assets/icon.svg
deleted file mode 100644
index a1e2bd12c27d64..00000000000000
--- a/api/core/tools/provider/builtin/twilio/_assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/twilio/tools/send_message.py b/api/core/tools/provider/builtin/twilio/tools/send_message.py
deleted file mode 100644
index 98a108f4ec7e93..00000000000000
--- a/api/core/tools/provider/builtin/twilio/tools/send_message.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from typing import Any, Optional, Union
-
-from pydantic import BaseModel, field_validator
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class TwilioAPIWrapper(BaseModel):
- """Messaging Client using Twilio.
-
- To use, you should have the ``twilio`` python package installed,
- and the environment variables ``TWILIO_ACCOUNT_SID``, ``TWILIO_AUTH_TOKEN``, and
- ``TWILIO_FROM_NUMBER``, or pass `account_sid`, `auth_token`, and `from_number` as
- named parameters to the constructor.
- """
-
- client: Any = None #: :meta private:
- account_sid: Optional[str] = None
- """Twilio account string identifier."""
- auth_token: Optional[str] = None
- """Twilio auth token."""
- from_number: Optional[str] = None
- """A Twilio phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164)
- format, an
- [alphanumeric sender ID](https://www.twilio.com/docs/sms/send-messages#use-an-alphanumeric-sender-id),
- or a [Channel Endpoint address](https://www.twilio.com/docs/sms/channels#channel-addresses)
- that is enabled for the type of message you want to send. Phone numbers or
- [short codes](https://www.twilio.com/docs/sms/api/short-code) purchased from
- Twilio also work here. You cannot, for example, spoof messages from a private
- cell phone number. If you are using `messaging_service_sid`, this parameter
- must be empty.
- """
-
- @field_validator("client", mode="before")
- @classmethod
- def set_validator(cls, values: dict) -> dict:
- """Validate that api key and python package exists in environment."""
- try:
- from twilio.rest import Client # type: ignore
- except ImportError:
- raise ImportError("Could not import twilio python package. Please install it with `pip install twilio`.")
- account_sid = values.get("account_sid")
- auth_token = values.get("auth_token")
- values["from_number"] = values.get("from_number")
- values["client"] = Client(account_sid, auth_token)
-
- return values
-
- def run(self, body: str, to: str) -> str:
- """Run body through Twilio and respond with message sid.
-
- Args:
- body: The text of the message you want to send. Can be up to 1,600
- characters in length.
- to: The destination phone number in
- [E.164](https://www.twilio.com/docs/glossary/what-e164) format for
- SMS/MMS or
- [Channel user address](https://www.twilio.com/docs/sms/channels#channel-addresses)
- for other 3rd-party channels.
- """
- message = self.client.messages.create(to, from_=self.from_number, body=body)
- return message.sid
-
-
-class SendMessageTool(BuiltinTool):
- """
- A tool for sending messages using Twilio API.
-
- Args:
- user_id (str): The ID of the user invoking the tool.
- tool_parameters (Dict[str, Any]): The parameters required for sending the message.
-
- Returns:
- Union[ToolInvokeMessage, List[ToolInvokeMessage]]: The result of invoking the tool,
- which includes the status of the message sending operation.
- """
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- account_sid = self.runtime.credentials["account_sid"]
- auth_token = self.runtime.credentials["auth_token"]
- from_number = self.runtime.credentials["from_number"]
-
- message = tool_parameters["message"]
- to_number = tool_parameters["to_number"]
-
- if to_number.startswith("whatsapp:"):
- from_number = f"whatsapp: {from_number}"
-
- twilio = TwilioAPIWrapper(account_sid=account_sid, auth_token=auth_token, from_number=from_number)
-
- # Sending the message through Twilio
- result = twilio.run(message, to_number)
-
- return self.create_text_message(text="Message sent successfully.")
diff --git a/api/core/tools/provider/builtin/twilio/tools/send_message.yaml b/api/core/tools/provider/builtin/twilio/tools/send_message.yaml
deleted file mode 100644
index e129698c86aeb6..00000000000000
--- a/api/core/tools/provider/builtin/twilio/tools/send_message.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- name: send_message
- author: Yash Parmar
- label:
- en_US: SendMessage
- zh_Hans: 发送消息
- pt_BR: SendMessage
-description:
- human:
- en_US: Send SMS or Twilio Messaging Channels messages.
- zh_Hans: 发送SMS或Twilio消息通道消息。
- pt_BR: Send SMS or Twilio Messaging Channels messages.
- llm: Send SMS or Twilio Messaging Channels messages. Supports different channels including WhatsApp.
-parameters:
- - name: message
- type: string
- required: true
- label:
- en_US: Message
- zh_Hans: 消息内容
- pt_BR: Message
- human_description:
- en_US: The content of the message to be sent.
- zh_Hans: 要发送的消息内容。
- pt_BR: The content of the message to be sent.
- llm_description: The content of the message to be sent.
- form: llm
- - name: to_number
- type: string
- required: true
- label:
- en_US: To Number
- zh_Hans: 收信号码
- pt_BR: Para Número
- human_description:
- en_US: The recipient's phone number. Prefix with 'whatsapp:' for WhatsApp messages, e.g., "whatsapp:+1234567890".
- zh_Hans: 收件人的电话号码。WhatsApp消息前缀为'whatsapp:',例如,"whatsapp:+1234567890"。
- pt_BR: The recipient's phone number. Prefix with 'whatsapp:' for WhatsApp messages, e.g., "whatsapp:+1234567890".
- llm_description: The recipient's phone number. Prefix with 'whatsapp:' for WhatsApp messages, e.g., "whatsapp:+1234567890".
- form: llm
diff --git a/api/core/tools/provider/builtin/twilio/twilio.py b/api/core/tools/provider/builtin/twilio/twilio.py
deleted file mode 100644
index 649e03d185121c..00000000000000
--- a/api/core/tools/provider/builtin/twilio/twilio.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Any
-
-from twilio.base.exceptions import TwilioRestException # type: ignore
-from twilio.rest import Client # type: ignore
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class TwilioProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- # Extract credentials
- account_sid = credentials["account_sid"]
- auth_token = credentials["auth_token"]
- from_number = credentials["from_number"]
-
- # Initialize twilio client
- client = Client(account_sid, auth_token)
-
- # fetch account
- client.api.accounts(account_sid).fetch()
-
- except TwilioRestException as e:
- raise ToolProviderCredentialValidationError(f"Twilio API error: {e.msg}") from e
- except KeyError as e:
- raise ToolProviderCredentialValidationError(f"Missing required credential: {e}") from e
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/twilio/twilio.yaml b/api/core/tools/provider/builtin/twilio/twilio.yaml
deleted file mode 100644
index 21867c1da5dc32..00000000000000
--- a/api/core/tools/provider/builtin/twilio/twilio.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-identity:
- author: Yash Parmar
- name: twilio
- label:
- en_US: Twilio
- zh_Hans: Twilio
- pt_BR: Twilio
- description:
- en_US: Send messages through SMS or Twilio Messaging Channels.
- zh_Hans: 通过SMS或Twilio消息通道发送消息。
- pt_BR: Send messages through SMS or Twilio Messaging Channels.
- icon: icon.svg
- tags:
- - social
-credentials_for_provider:
- account_sid:
- type: secret-input
- required: true
- label:
- en_US: Account SID
- zh_Hans: 账户SID
- pt_BR: Account SID
- placeholder:
- en_US: Please input your Twilio Account SID
- zh_Hans: 请输入您的Twilio账户SID
- pt_BR: Please input your Twilio Account SID
- auth_token:
- type: secret-input
- required: true
- label:
- en_US: Auth Token
- zh_Hans: 认证令牌
- pt_BR: Auth Token
- placeholder:
- en_US: Please input your Twilio Auth Token
- zh_Hans: 请输入您的Twilio认证令牌
- pt_BR: Please input your Twilio Auth Token
- from_number:
- type: secret-input
- required: true
- label:
- en_US: From Number
- zh_Hans: 发信号码
- pt_BR: De Número
- placeholder:
- en_US: Please input your Twilio phone number
- zh_Hans: 请输入您的Twilio电话号码
- pt_BR: Please input your Twilio phone number
diff --git a/api/core/tools/provider/builtin/vanna/_assets/icon.png b/api/core/tools/provider/builtin/vanna/_assets/icon.png
deleted file mode 100644
index 3a9011b54d8a07..00000000000000
Binary files a/api/core/tools/provider/builtin/vanna/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/vanna/tools/vanna.py b/api/core/tools/provider/builtin/vanna/tools/vanna.py
deleted file mode 100644
index a6afd2dddfc63a..00000000000000
--- a/api/core/tools/provider/builtin/vanna/tools/vanna.py
+++ /dev/null
@@ -1,134 +0,0 @@
-from typing import Any, Union
-
-from vanna.remote import VannaDefault # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class VannaTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # Ensure runtime and credentials
- if not self.runtime or not self.runtime.credentials:
- raise ToolProviderCredentialValidationError("Tool runtime or credentials are missing")
- api_key = self.runtime.credentials.get("api_key", None)
- if not api_key:
- raise ToolProviderCredentialValidationError("Please input api key")
-
- model = tool_parameters.get("model", "")
- if not model:
- return self.create_text_message("Please input RAG model")
-
- prompt = tool_parameters.get("prompt", "")
- if not prompt:
- return self.create_text_message("Please input prompt")
-
- url = tool_parameters.get("url", "")
- if not url:
- return self.create_text_message("Please input URL/Host/DSN")
-
- db_name = tool_parameters.get("db_name", "")
- username = tool_parameters.get("username", "")
- password = tool_parameters.get("password", "")
- port = tool_parameters.get("port", 0)
-
- base_url = self.runtime.credentials.get("base_url", None)
- vn = VannaDefault(model=model, api_key=api_key, config={"endpoint": base_url})
-
- db_type = tool_parameters.get("db_type", "")
- if db_type in {"Postgres", "MySQL", "Hive", "ClickHouse"}:
- if not db_name:
- return self.create_text_message("Please input database name")
- if not username:
- return self.create_text_message("Please input username")
- if port < 1:
- return self.create_text_message("Please input port")
-
- schema_sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS"
- match db_type:
- case "SQLite":
- schema_sql = "SELECT type, sql FROM sqlite_master WHERE sql is not null"
- vn.connect_to_sqlite(url)
- case "Postgres":
- vn.connect_to_postgres(host=url, dbname=db_name, user=username, password=password, port=port)
- case "DuckDB":
- vn.connect_to_duckdb(url=url)
- case "SQLServer":
- vn.connect_to_mssql(url)
- case "MySQL":
- vn.connect_to_mysql(host=url, dbname=db_name, user=username, password=password, port=port)
- case "Oracle":
- vn.connect_to_oracle(user=username, password=password, dsn=url)
- case "Hive":
- vn.connect_to_hive(host=url, dbname=db_name, user=username, password=password, port=port)
- case "ClickHouse":
- vn.connect_to_clickhouse(host=url, dbname=db_name, user=username, password=password, port=port)
-
- enable_training = tool_parameters.get("enable_training", False)
- reset_training_data = tool_parameters.get("reset_training_data", False)
- if enable_training:
- if reset_training_data:
- existing_training_data = vn.get_training_data()
- if len(existing_training_data) > 0:
- for _, training_data in existing_training_data.iterrows():
- vn.remove_training_data(training_data["id"])
-
- ddl = tool_parameters.get("ddl", "")
- question = tool_parameters.get("question", "")
- sql = tool_parameters.get("sql", "")
- memos = tool_parameters.get("memos", "")
- training_metadata = tool_parameters.get("training_metadata", False)
-
- if training_metadata:
- if db_type == "SQLite":
- df_ddl = vn.run_sql(schema_sql)
- for ddl in df_ddl["sql"].to_list():
- vn.train(ddl=ddl)
- else:
- df_information_schema = vn.run_sql(schema_sql)
- plan = vn.get_training_plan_generic(df_information_schema)
- vn.train(plan=plan)
-
- if ddl:
- vn.train(ddl=ddl)
-
- if sql:
- if question:
- vn.train(question=question, sql=sql)
- else:
- vn.train(sql=sql)
- if memos:
- vn.train(documentation=memos)
-
- #########################################################################################
- # Due to CVE-2024-5565, we have to disable the chart generation feature
- # The Vanna library uses a prompt function to present the user with visualized results,
- # it is possible to alter the prompt using prompt injection and run arbitrary Python code
- # instead of the intended visualization code.
- # Specifically - allowing external input to the library’s “ask” method
- # with "visualize" set to True (default behavior) leads to remote code execution.
- # Affected versions: <= 0.5.5
- #########################################################################################
- allow_llm_to_see_data = tool_parameters.get("allow_llm_to_see_data", False)
- res = vn.ask(
- prompt, print_results=False, auto_train=True, visualize=False, allow_llm_to_see_data=allow_llm_to_see_data
- )
-
- result = []
-
- if res is not None:
- result.append(self.create_text_message(res[0]))
- if len(res) > 1 and res[1] is not None:
- result.append(self.create_text_message(res[1].to_markdown()))
- if len(res) > 2 and res[2] is not None:
- result.append(
- self.create_blob_message(blob=res[2].to_image(format="svg"), meta={"mime_type": "image/svg+xml"})
- )
-
- return result
diff --git a/api/core/tools/provider/builtin/vanna/tools/vanna.yaml b/api/core/tools/provider/builtin/vanna/tools/vanna.yaml
deleted file mode 100644
index 309681321b1f3f..00000000000000
--- a/api/core/tools/provider/builtin/vanna/tools/vanna.yaml
+++ /dev/null
@@ -1,213 +0,0 @@
-identity:
- name: vanna
- author: QCTC
- label:
- en_US: Vanna.AI
- zh_Hans: Vanna.AI
-description:
- human:
- en_US: The fastest way to get actionable insights from your database just by asking questions.
- zh_Hans: 一个基于大模型和RAG的Text2SQL工具。
- llm: A tool for converting text to SQL.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- pt_BR: Prompt
- human_description:
- en_US: used for generating SQL
- zh_Hans: 用于生成SQL
- llm_description: key words for generating SQL
- form: llm
- - name: model
- type: string
- required: true
- label:
- en_US: RAG Model
- zh_Hans: RAG模型
- human_description:
- en_US: RAG Model for your database DDL
- zh_Hans: 存储数据库训练数据的RAG模型
- llm_description: RAG Model for generating SQL
- form: llm
- - name: db_type
- type: select
- required: true
- options:
- - value: SQLite
- label:
- en_US: SQLite
- zh_Hans: SQLite
- - value: Postgres
- label:
- en_US: Postgres
- zh_Hans: Postgres
- - value: DuckDB
- label:
- en_US: DuckDB
- zh_Hans: DuckDB
- - value: SQLServer
- label:
- en_US: Microsoft SQL Server
- zh_Hans: 微软 SQL Server
- - value: MySQL
- label:
- en_US: MySQL
- zh_Hans: MySQL
- - value: Oracle
- label:
- en_US: Oracle
- zh_Hans: Oracle
- - value: Hive
- label:
- en_US: Hive
- zh_Hans: Hive
- - value: ClickHouse
- label:
- en_US: ClickHouse
- zh_Hans: ClickHouse
- default: SQLite
- label:
- en_US: DB Type
- zh_Hans: 数据库类型
- human_description:
- en_US: Database type.
- zh_Hans: 选择要链接的数据库类型。
- form: form
- - name: url
- type: string
- required: true
- label:
- en_US: URL/Host/DSN
- zh_Hans: URL/Host/DSN
- human_description:
- en_US: Please input depending on DB type, visit https://vanna.ai/docs/ for more specification
- zh_Hans: 请根据数据库类型,填入对应值,详情参考https://vanna.ai/docs/
- form: form
- - name: db_name
- type: string
- required: false
- label:
- en_US: DB name
- zh_Hans: 数据库名
- human_description:
- en_US: Database name
- zh_Hans: 数据库名
- form: form
- - name: username
- type: string
- required: false
- label:
- en_US: Username
- zh_Hans: 用户名
- human_description:
- en_US: Username
- zh_Hans: 用户名
- form: form
- - name: password
- type: secret-input
- required: false
- label:
- en_US: Password
- zh_Hans: 密码
- human_description:
- en_US: Password
- zh_Hans: 密码
- form: form
- - name: port
- type: number
- required: false
- label:
- en_US: Port
- zh_Hans: 端口
- human_description:
- en_US: Port
- zh_Hans: 端口
- form: form
- - name: ddl
- type: string
- required: false
- label:
- en_US: Training DDL
- zh_Hans: 训练DDL
- human_description:
- en_US: DDL statements for training data
- zh_Hans: 用于训练RAG Model的建表语句
- form: llm
- - name: question
- type: string
- required: false
- label:
- en_US: Training Question
- zh_Hans: 训练问题
- human_description:
- en_US: Question-SQL Pairs
- zh_Hans: Question-SQL中的问题
- form: llm
- - name: sql
- type: string
- required: false
- label:
- en_US: Training SQL
- zh_Hans: 训练SQL
- human_description:
- en_US: SQL queries to your training data
- zh_Hans: 用于训练RAG Model的SQL语句
- form: llm
- - name: memos
- type: string
- required: false
- label:
- en_US: Training Memos
- zh_Hans: 训练说明
- human_description:
- en_US: Sometimes you may want to add documentation about your business terminology or definitions
- zh_Hans: 添加更多关于数据库的业务说明
- form: llm
- - name: enable_training
- type: boolean
- required: false
- default: false
- label:
- en_US: Training Data
- zh_Hans: 训练数据
- human_description:
- en_US: You only need to train once. Do not train again unless you want to add more training data
- zh_Hans: 训练数据无更新时,训练一次即可
- form: form
- - name: reset_training_data
- type: boolean
- required: false
- default: false
- label:
- en_US: Reset Training Data
- zh_Hans: 重置训练数据
- human_description:
- en_US: Remove all training data in the current RAG Model
- zh_Hans: 删除当前RAG Model中的所有训练数据
- form: form
- - name: training_metadata
- type: boolean
- required: false
- default: false
- label:
- en_US: Training Metadata
- zh_Hans: 训练元数据
- human_description:
- en_US: If enabled, it will attempt to train on the metadata of that database
- zh_Hans: 是否自动从数据库获取元数据来训练
- form: form
- - name: allow_llm_to_see_data
- type: boolean
- required: false
- default: false
- label:
- en_US: Whether to allow the LLM to see the data
- zh_Hans: 是否允许LLM查看数据
- human_description:
- en_US: Whether to allow the LLM to see the data
- zh_Hans: 是否允许LLM查看数据
- form: form
diff --git a/api/core/tools/provider/builtin/vanna/vanna.py b/api/core/tools/provider/builtin/vanna/vanna.py
deleted file mode 100644
index 4f9cac2beb01bb..00000000000000
--- a/api/core/tools/provider/builtin/vanna/vanna.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import re
-from typing import Any
-from urllib.parse import urlparse
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.vanna.tools.vanna import VannaTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class VannaProvider(BuiltinToolProviderController):
- def _get_protocol_and_main_domain(self, url):
- parsed_url = urlparse(url)
- protocol = parsed_url.scheme
- hostname = parsed_url.hostname
- port = f":{parsed_url.port}" if parsed_url.port else ""
-
- # Check if the hostname is an IP address
- is_ip = re.match(r"^\d{1,3}(\.\d{1,3}){3}$", hostname) is not None
-
- # Return the full hostname (with port if present) for IP addresses, otherwise return the main domain
- main_domain = f"{hostname}{port}" if is_ip else ".".join(hostname.split(".")[-2:]) + port
- return f"{protocol}://{main_domain}"
-
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- base_url = credentials.get("base_url")
- if not base_url:
- base_url = "https://ask.vanna.ai/rpc"
- else:
- base_url = base_url.removesuffix("/")
- credentials["base_url"] = base_url
- try:
- VannaTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "model": "chinook",
- "db_type": "SQLite",
- "url": f"{self._get_protocol_and_main_domain(credentials['base_url'])}/Chinook.sqlite",
- "query": "What are the top 10 customers by sales?",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/vanna/vanna.yaml b/api/core/tools/provider/builtin/vanna/vanna.yaml
deleted file mode 100644
index cf3fdca562c0b3..00000000000000
--- a/api/core/tools/provider/builtin/vanna/vanna.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-identity:
- author: QCTC
- name: vanna
- label:
- en_US: Vanna.AI
- zh_Hans: Vanna.AI
- description:
- en_US: The fastest way to get actionable insights from your database just by asking questions.
- zh_Hans: 一个基于大模型和RAG的Text2SQL工具。
- icon: icon.png
- tags:
- - utilities
- - productivity
-credentials_for_provider:
- api_key:
- type: secret-input
- required: true
- label:
- en_US: API key
- zh_Hans: API key
- placeholder:
- en_US: Please input your API key
- zh_Hans: 请输入你的 API key
- pt_BR: Please input your API key
- help:
- en_US: Get your API key from Vanna.AI
- zh_Hans: 从 Vanna.AI 获取你的 API key
- url: https://vanna.ai/account/profile
- base_url:
- type: text-input
- required: false
- label:
- en_US: Vanna.AI Endpoint Base URL
- placeholder:
- en_US: https://ask.vanna.ai/rpc
diff --git a/api/core/tools/provider/builtin/vectorizer/_assets/icon.png b/api/core/tools/provider/builtin/vectorizer/_assets/icon.png
deleted file mode 100644
index 52f18db84372dc..00000000000000
Binary files a/api/core/tools/provider/builtin/vectorizer/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.py b/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.py
deleted file mode 100644
index c722cd36c84e15..00000000000000
--- a/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from typing import Any, Union
-
-from httpx import post
-
-from core.file.enums import FileType
-from core.file.file_manager import download
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
-from core.tools.errors import ToolParameterValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class VectorizerTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- api_key_name = self.runtime.credentials.get("api_key_name")
- api_key_value = self.runtime.credentials.get("api_key_value")
- mode = tool_parameters.get("mode", "test")
-
- # image file for workflow mode
- image = tool_parameters.get("image")
- if image and image.type != FileType.IMAGE:
- raise ToolParameterValidationError("Not a valid image")
- # image_id for agent mode
- image_id = tool_parameters.get("image_id", "")
-
- if image_id:
- image_binary = self.get_variable_file(self.VariableKey.IMAGE)
- if not image_binary:
- return self.create_text_message("Image not found, please request user to generate image firstly.")
- elif image:
- image_binary = download(image)
- else:
- raise ToolParameterValidationError("Please provide either image or image_id")
-
- response = post(
- "https://vectorizer.ai/api/v1/vectorize",
- data={"mode": mode},
- files={"image": image_binary},
- auth=(api_key_name, api_key_value),
- timeout=30,
- )
-
- if response.status_code != 200:
- raise Exception(response.text)
-
- return [
- self.create_text_message("the vectorized svg is saved as an image."),
- self.create_blob_message(blob=response.content, meta={"mime_type": "image/svg+xml"}),
- ]
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- """
- override the runtime parameters
- """
- return [
- ToolParameter.get_simple_instance(
- name="image_id",
- llm_description=f"the image_id that you want to vectorize, \
- and the image_id should be specified in \
- {[i.name for i in self.list_default_image_variables()]}",
- type=ToolParameter.ToolParameterType.SELECT,
- required=False,
- options=[i.name for i in self.list_default_image_variables()],
- ),
- ToolParameter(
- name="image",
- label=I18nObject(en_US="image", zh_Hans="image"),
- human_description=I18nObject(
- en_US="The image to be converted.",
- zh_Hans="要转换的图片。",
- ),
- type=ToolParameter.ToolParameterType.FILE,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="you should not input this parameter. just input the image_id.",
- required=False,
- ),
- ]
diff --git a/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.yaml b/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.yaml
deleted file mode 100644
index 0afd1c201f9126..00000000000000
--- a/api/core/tools/provider/builtin/vectorizer/tools/vectorizer.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-identity:
- name: vectorizer
- author: Dify
- label:
- en_US: Vectorizer.AI
- zh_Hans: Vectorizer.AI
-description:
- human:
- en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
- zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
- llm: A tool for converting images to SVG vectors. you should input the image id as the input of this tool. the image id can be got from parameters.
-parameters:
- - name: image
- type: file
- label:
- en_US: image
- human_description:
- en_US: The image to be converted.
- zh_Hans: 要转换的图片。
- llm_description: you should not input this parameter. just input the image_id.
- form: llm
- - name: mode
- type: select
- required: true
- options:
- - value: production
- label:
- en_US: production
- zh_Hans: 生产模式
- - value: test
- label:
- en_US: test
- zh_Hans: 测试模式
- default: test
- label:
- en_US: Mode
- zh_Hans: 模式
- human_description:
- en_US: It is free to integrate with and test out the API in test mode, no subscription required.
- zh_Hans: 在测试模式下,可以免费测试API。
- form: form
diff --git a/api/core/tools/provider/builtin/vectorizer/vectorizer.py b/api/core/tools/provider/builtin/vectorizer/vectorizer.py
deleted file mode 100644
index 9d7613f8eaf170..00000000000000
--- a/api/core/tools/provider/builtin/vectorizer/vectorizer.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from typing import Any
-
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class VectorizerProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- return
diff --git a/api/core/tools/provider/builtin/vectorizer/vectorizer.yaml b/api/core/tools/provider/builtin/vectorizer/vectorizer.yaml
deleted file mode 100644
index 94dae2087609d4..00000000000000
--- a/api/core/tools/provider/builtin/vectorizer/vectorizer.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-identity:
- author: Dify
- name: vectorizer
- label:
- en_US: Vectorizer.AI
- zh_Hans: Vectorizer.AI
- description:
- en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
- zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
- icon: icon.png
- tags:
- - productivity
- - image
-credentials_for_provider:
- api_key_name:
- type: secret-input
- required: true
- label:
- en_US: Vectorizer.AI API Key name
- zh_Hans: Vectorizer.AI API Key name
- placeholder:
- en_US: Please input your Vectorizer.AI ApiKey name
- zh_Hans: 请输入你的 Vectorizer.AI ApiKey name
- help:
- en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
- zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
- url: https://vectorizer.ai/api
- api_key_value:
- type: secret-input
- required: true
- label:
- en_US: Vectorizer.AI API Key
- zh_Hans: Vectorizer.AI API Key
- placeholder:
- en_US: Please input your Vectorizer.AI ApiKey
- zh_Hans: 请输入你的 Vectorizer.AI ApiKey
- help:
- en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
- zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
diff --git a/api/core/tools/provider/builtin/webscraper/tools/webscraper.py b/api/core/tools/provider/builtin/webscraper/tools/webscraper.py
deleted file mode 100644
index 12670b4b8b9289..00000000000000
--- a/api/core/tools/provider/builtin/webscraper/tools/webscraper.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import Any, Union
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolInvokeError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class WebscraperTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- try:
- url = tool_parameters.get("url", "")
- user_agent = tool_parameters.get("user_agent", "")
- if not url:
- return self.create_text_message("Please input url")
-
- # get webpage
- result = self.get_url(url, user_agent=user_agent)
-
- if tool_parameters.get("generate_summary"):
- # summarize and return
- return self.create_text_message(self.summary(user_id=user_id, content=result))
- else:
- # return full webpage
- return self.create_text_message(result)
- except Exception as e:
- raise ToolInvokeError(str(e))
diff --git a/api/core/tools/provider/builtin/webscraper/tools/webscraper.yaml b/api/core/tools/provider/builtin/webscraper/tools/webscraper.yaml
deleted file mode 100644
index 0bb48a941dcffe..00000000000000
--- a/api/core/tools/provider/builtin/webscraper/tools/webscraper.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-identity:
- name: webscraper
- author: Dify
- label:
- en_US: Web Scraper
- zh_Hans: 网页爬虫
- pt_BR: Web Scraper
-description:
- human:
- en_US: A tool for scraping webpages.
- zh_Hans: 一个用于爬取网页的工具。
- pt_BR: A tool for scraping webpages.
- llm: A tool for scraping webpages. Input should be a URL.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: URL
- zh_Hans: 网页链接
- pt_BR: URL
- human_description:
- en_US: used for linking to webpages
- zh_Hans: 用于链接到网页
- pt_BR: used for linking to webpages
- llm_description: url for scraping
- form: llm
- - name: user_agent
- type: string
- required: false
- label:
- en_US: User Agent
- zh_Hans: User Agent
- pt_BR: User Agent
- human_description:
- en_US: used for identifying the browser.
- zh_Hans: 用于识别浏览器。
- pt_BR: used for identifying the browser.
- form: form
- default: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.1000.0 Safari/537.36
- - name: generate_summary
- type: boolean
- required: false
- label:
- en_US: Whether to generate summary
- zh_Hans: 是否生成摘要
- human_description:
- en_US: If true, the crawler will only return the page summary content.
- zh_Hans: 如果启用,爬虫将仅返回页面摘要内容。
- form: form
- options:
- - value: 'true'
- label:
- en_US: 'Yes'
- zh_Hans: 是
- - value: 'false'
- label:
- en_US: 'No'
- zh_Hans: 否
- default: 'false'
diff --git a/api/core/tools/provider/builtin/webscraper/webscraper.py b/api/core/tools/provider/builtin/webscraper/webscraper.py
deleted file mode 100644
index 3c51393ac64cc4..00000000000000
--- a/api/core/tools/provider/builtin/webscraper/webscraper.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.webscraper.tools.webscraper import WebscraperTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class WebscraperProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- WebscraperTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "url": "https://www.google.com",
- "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/webscraper/webscraper.yaml b/api/core/tools/provider/builtin/webscraper/webscraper.yaml
deleted file mode 100644
index 6c2eb97784e298..00000000000000
--- a/api/core/tools/provider/builtin/webscraper/webscraper.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: Dify
- name: webscraper
- label:
- en_US: WebScraper
- zh_Hans: 网页抓取
- pt_BR: WebScraper
- description:
- en_US: Web Scrapper tool kit is used to scrape web
- zh_Hans: 一个用于抓取网页的工具。
- pt_BR: Web Scrapper tool kit is used to scrape web
- icon: icon.svg
- tags:
- - productivity
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/websearch/_assets/icon.svg b/api/core/tools/provider/builtin/websearch/_assets/icon.svg
deleted file mode 100644
index d6ef5d878f8636..00000000000000
--- a/api/core/tools/provider/builtin/websearch/_assets/icon.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/websearch/tools/get_markdown.py b/api/core/tools/provider/builtin/websearch/tools/get_markdown.py
deleted file mode 100644
index 043879deeab18f..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/get_markdown.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from typing import Any, Union
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-BASE_URL = "https://api.serply.io/v1/request"
-
-
-class SerplyApi:
- """
- SerplyAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SerplyAPI tool provider."""
- self.serply_api_key = api_key
-
- def run(self, url: str, **kwargs: Any) -> str:
- """Run query through SerplyAPI and parse result."""
-
- location = kwargs.get("location", "US")
-
- headers = {
- "X-API-KEY": self.serply_api_key,
- "X-User-Agent": kwargs.get("device", "desktop"),
- "X-Proxy-Location": location,
- "User-Agent": "Dify",
- }
- data = {"url": url, "method": "GET", "response_type": "markdown"}
- res = requests.post(url, headers=headers, json=data)
- return res.text
-
-
-class GetMarkdownTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SerplyApi tool.
- """
- url = tool_parameters["url"]
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["serply_api_key"]
- result = SerplyApi(api_key).run(url, location=location)
-
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/websearch/tools/get_markdown.yaml b/api/core/tools/provider/builtin/websearch/tools/get_markdown.yaml
deleted file mode 100644
index 06a302bd14b82d..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/get_markdown.yaml
+++ /dev/null
@@ -1,96 +0,0 @@
-identity:
- name: get_markdown
- author: Dify
- label:
- en_US: Get Markdown API
- zh_Hans: Get Markdown API
-description:
- human:
- en_US: A tool to perform convert a webpage to markdown to make it easier for LLMs to understand.
- zh_Hans: 一个将网页转换为 Markdown 的工具,以便模型更容易理解
- llm: A tool to perform convert a webpage to markdown to make it easier for LLMs to understand.
-parameters:
- - name: url
- type: string
- required: true
- label:
- en_US: URL
- zh_Hans: URL
- human_description:
- en_US: URL that you want to grab the content from
- zh_Hans: 您要从中获取内容的 URL
- llm_description: Defines the link want to grab content from.
- form: llm
- - name: location
- type: string
- required: false
- default: US
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: form
- options:
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
diff --git a/api/core/tools/provider/builtin/websearch/tools/job_search.py b/api/core/tools/provider/builtin/websearch/tools/job_search.py
deleted file mode 100644
index 13eb40339153c9..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/job_search.py
+++ /dev/null
@@ -1,88 +0,0 @@
-from typing import Any, Union
-from urllib.parse import urlencode
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-BASE_URL = "https://api.serply.io/v1/news/"
-
-
-class SerplyApi:
- """
- SerplyAPI tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SerplyAPI tool provider."""
- self.serply_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SerplyAPI and parse result."""
- params = {"q": query, "hl": kwargs.get("hl", "en"), "gl": kwargs.get("gl", "US"), "num": kwargs.get("num", 10)}
- location = kwargs.get("location", "US")
-
- headers = {
- "X-API-KEY": self.serply_api_key,
- "X-User-Agent": kwargs.get("device", "desktop"),
- "X-Proxy-Location": location,
- "User-Agent": "Dify",
- }
-
- url = f"{BASE_URL}{urlencode(params)}"
- res = requests.get(
- url,
- headers=headers,
- )
- res = res.json()
-
- return self.parse_results(res)
-
- @staticmethod
- def parse_results(res: dict) -> str:
- """Process response from Serply Job Search."""
- jobs = res.get("jobs", [])
- if not res or "jobs" not in res:
- raise ValueError(f"Got error from Serply: {res}")
-
- string = []
- for job in jobs[:10]:
- try:
- string.append(
- "\n".join(
- [
- f"Position: {job['position']}",
- f"Employer: {job['employer']}",
- f"Location: {job['location']}",
- f"Link: {job['link']}",
- f"""Highest: {", ".join(list(job["highlights"]))}""",
- "---",
- ]
- )
- )
- except KeyError:
- continue
-
- content = "\n".join(string)
- return f"\nJobs results:\n {content}\n"
-
-
-class JobSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SerplyApi tool.
- """
- query = tool_parameters["query"]
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["serply_api_key"]
- result = SerplyApi(api_key).run(query, gl=gl, hl=hl, location=location)
-
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/websearch/tools/job_search.yaml b/api/core/tools/provider/builtin/websearch/tools/job_search.yaml
deleted file mode 100644
index b5ede3df46ab01..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/job_search.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-identity:
- name: job_search
- author: Dify
- label:
- en_US: Job Search API
- zh_Hans: Job Search API
-description:
- human:
- en_US: A tool to retrieve job titles, company names and description from Google Jobs engine.
- zh_Hans: 一个从 Google 招聘引擎检索职位名称、公司名称和描述的工具。
- llm: A tool to retrieve job titles, company names and description from Google Jobs engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: location
- type: string
- required: false
- default: US
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: form
- options:
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
diff --git a/api/core/tools/provider/builtin/websearch/tools/news_search.py b/api/core/tools/provider/builtin/websearch/tools/news_search.py
deleted file mode 100644
index 7a8a732ff3f246..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/news_search.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from typing import Any, Union
-from urllib.parse import urlencode
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-BASE_URL = "https://api.serply.io/v1/news/"
-
-
-class SerplyApi:
- """
- SerplyApi tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SerplyApi tool provider."""
- self.serply_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SerplyApi and parse result."""
- params = {"q": query, "hl": kwargs.get("hl", "en"), "gl": kwargs.get("gl", "US"), "num": kwargs.get("num", 10)}
- location = kwargs.get("location", "US")
-
- headers = {
- "X-API-KEY": self.serply_api_key,
- "X-User-Agent": kwargs.get("device", "desktop"),
- "X-Proxy-Location": location,
- "User-Agent": "Dify",
- }
-
- url = f"{BASE_URL}{urlencode(params)}"
- res = requests.get(
- url,
- headers=headers,
- )
- res = res.json()
-
- return self.parse_results(res)
-
- @staticmethod
- def parse_results(res: dict) -> str:
- """Process response from Serply News Search."""
- news = res.get("entries", [])
- if not res or "entries" not in res:
- raise ValueError(f"Got error from Serply: {res}")
-
- string = []
- for entry in news:
- try:
- # follow url
- r = requests.get(entry["link"])
- final_link = r.history[-1].headers["Location"]
- string.append(
- "\n".join(
- [
- f"Title: {entry['title']}",
- f"Link: {final_link}",
- f"Source: {entry['source']['title']}",
- f"Published: {entry['published']}",
- "---",
- ]
- )
- )
- except KeyError:
- continue
-
- content = "\n".join(string)
- return f"\nNews:\n {content}\n"
-
-
-class NewsSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SerplyApi tool.
- """
- query = tool_parameters["query"]
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["serply_api_key"]
- result = SerplyApi(api_key).run(query, gl=gl, hl=hl, location=location)
-
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/websearch/tools/news_search.yaml b/api/core/tools/provider/builtin/websearch/tools/news_search.yaml
deleted file mode 100644
index 126c610825ebbb..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/news_search.yaml
+++ /dev/null
@@ -1,501 +0,0 @@
-identity:
- name: news_search
- author: Dify
- label:
- en_US: News Search API
- zh_Hans: News Search API
-description:
- human:
- en_US: A tool to retrieve organic search results snippets and links from Google News engine.
- zh_Hans: 一种从 Google 新闻引擎检索有机搜索结果片段和链接的工具。
- llm: A tool to retrieve organic search results snippets and links from Google News engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: location
- type: string
- required: false
- default: US
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: form
- options:
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家/地区
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: AR
- label:
- en_US: Argentina
- zh_Hans: 阿根廷
- pt_BR: Argentina
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Austria
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Belgium
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colombia
- - value: CN
- label:
- en_US: China
- zh_Hans: 中国
- pt_BR: China
- - value: CZ
- label:
- en_US: Czech Republic
- zh_Hans: 捷克共和国
- pt_BR: Czech Republic
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Denmark
- - value: FI
- label:
- en_US: Finland
- zh_Hans: 芬兰
- pt_BR: Finland
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonesia
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Italy
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malaysia
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: Mexico
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Netherlands
- - value: NZ
- label:
- en_US: New Zealand
- zh_Hans: 新西兰
- pt_BR: New Zealand
- - value: NO
- label:
- en_US: Norway
- zh_Hans: 挪威
- pt_BR: Norway
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Philippines
- - value: PL
- label:
- en_US: Poland
- zh_Hans: 波兰
- pt_BR: Poland
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: RU
- label:
- en_US: Russia
- zh_Hans: 俄罗斯
- pt_BR: Russia
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Saudi Arabia
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: South Africa
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Spain
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Switzerland
- - value: TW
- label:
- en_US: Taiwan
- zh_Hans: 台湾
- pt_BR: Taiwan
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Thailand
- - value: TR
- label:
- en_US: Turkey
- zh_Hans: 土耳其
- pt_BR: Turkey
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
diff --git a/api/core/tools/provider/builtin/websearch/tools/scholar_search.py b/api/core/tools/provider/builtin/websearch/tools/scholar_search.py
deleted file mode 100644
index 32c5d39e5b8674..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/scholar_search.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from typing import Any, Union
-from urllib.parse import urlencode
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-BASE_URL = "https://api.serply.io/v1/scholar/"
-
-
-class SerplyApi:
- """
- SerplyApi tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize SerplyApi tool provider."""
- self.serply_api_key = api_key
-
- def run(self, query: str, **kwargs: Any) -> str:
- """Run query through SerplyApi and parse result."""
- params = {"q": query, "hl": kwargs.get("hl", "en"), "gl": kwargs.get("gl", "US"), "num": kwargs.get("num", 10)}
- location = kwargs.get("location", "US")
-
- headers = {
- "X-API-KEY": self.serply_api_key,
- "X-User-Agent": kwargs.get("device", "desktop"),
- "X-Proxy-Location": location,
- "User-Agent": "Dify",
- }
-
- url = f"{BASE_URL}{urlencode(params)}"
- res = requests.get(
- url,
- headers=headers,
- )
- res = res.json()
-
- return self.parse_results(res)
-
- @staticmethod
- def parse_results(res: dict) -> str:
- """Process response from Serply News Search."""
- articles = res.get("articles", [])
- if not res or "articles" not in res:
- raise ValueError(f"Got error from Serply: {res}")
-
- string = []
- for article in articles:
- try:
- if "doc" in article:
- link = article["doc"]["link"]
- else:
- link = article["link"]
- authors = [author["name"] for author in article["author"]["authors"]]
- string.append(
- "\n".join(
- [
- f"Title: {article['title']}",
- f"Link: {link}",
- f"Description: {article['description']}",
- f"Cite: {article['cite']}",
- f"Authors: {', '.join(authors)}",
- "---",
- ]
- )
- )
- except KeyError:
- continue
-
- content = "\n".join(string)
- return f"\nScholar results:\n {content}\n"
-
-
-class ScholarSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SerplyApi tool.
- """
- query = tool_parameters["query"]
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location")
-
- api_key = self.runtime.credentials["serply_api_key"]
- result = SerplyApi(api_key).run(query, gl=gl, hl=hl, location=location)
-
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/websearch/tools/scholar_search.yaml b/api/core/tools/provider/builtin/websearch/tools/scholar_search.yaml
deleted file mode 100644
index 63e79d7ebfaa49..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/scholar_search.yaml
+++ /dev/null
@@ -1,501 +0,0 @@
-identity:
- name: scholar_search
- author: Dify
- label:
- en_US: Scholar API
- zh_Hans: Scholar API
-description:
- human:
- en_US: A tool to retrieve scholarly literature.
- zh_Hans: 学术文献检索工具
- llm: A tool to retrieve scholarly literature.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: location
- type: string
- required: false
- default: US
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: form
- options:
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家/地区
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: AR
- label:
- en_US: Argentina
- zh_Hans: 阿根廷
- pt_BR: Argentina
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: AT
- label:
- en_US: Austria
- zh_Hans: 奥地利
- pt_BR: Austria
- - value: BE
- label:
- en_US: Belgium
- zh_Hans: 比利时
- pt_BR: Belgium
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: CL
- label:
- en_US: Chile
- zh_Hans: 智利
- pt_BR: Chile
- - value: CO
- label:
- en_US: Colombia
- zh_Hans: 哥伦比亚
- pt_BR: Colombia
- - value: CN
- label:
- en_US: China
- zh_Hans: 中国
- pt_BR: China
- - value: CZ
- label:
- en_US: Czech Republic
- zh_Hans: 捷克共和国
- pt_BR: Czech Republic
- - value: DK
- label:
- en_US: Denmark
- zh_Hans: 丹麦
- pt_BR: Denmark
- - value: FI
- label:
- en_US: Finland
- zh_Hans: 芬兰
- pt_BR: Finland
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: HK
- label:
- en_US: Hong Kong
- zh_Hans: 香港
- pt_BR: Hong Kong
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: ID
- label:
- en_US: Indonesia
- zh_Hans: 印度尼西亚
- pt_BR: Indonesia
- - value: IT
- label:
- en_US: Italy
- zh_Hans: 意大利
- pt_BR: Italy
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: MY
- label:
- en_US: Malaysia
- zh_Hans: 马来西亚
- pt_BR: Malaysia
- - value: MX
- label:
- en_US: Mexico
- zh_Hans: 墨西哥
- pt_BR: Mexico
- - value: NL
- label:
- en_US: Netherlands
- zh_Hans: 荷兰
- pt_BR: Netherlands
- - value: NZ
- label:
- en_US: New Zealand
- zh_Hans: 新西兰
- pt_BR: New Zealand
- - value: "NO"
- label:
- en_US: Norway
- zh_Hans: 挪威
- pt_BR: Norway
- - value: PH
- label:
- en_US: Philippines
- zh_Hans: 菲律宾
- pt_BR: Philippines
- - value: PL
- label:
- en_US: Poland
- zh_Hans: 波兰
- pt_BR: Poland
- - value: PT
- label:
- en_US: Portugal
- zh_Hans: 葡萄牙
- pt_BR: Portugal
- - value: RU
- label:
- en_US: Russia
- zh_Hans: 俄罗斯
- pt_BR: Russia
- - value: SA
- label:
- en_US: Saudi Arabia
- zh_Hans: 沙特阿拉伯
- pt_BR: Saudi Arabia
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: ZA
- label:
- en_US: South Africa
- zh_Hans: 南非
- pt_BR: South Africa
- - value: ES
- label:
- en_US: Spain
- zh_Hans: 西班牙
- pt_BR: Spain
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - value: CH
- label:
- en_US: Switzerland
- zh_Hans: 瑞士
- pt_BR: Switzerland
- - value: TW
- label:
- en_US: Taiwan
- zh_Hans: 台湾
- pt_BR: Taiwan
- - value: TH
- label:
- en_US: Thailand
- zh_Hans: 泰国
- pt_BR: Thailand
- - value: TR
- label:
- en_US: Turkey
- zh_Hans: 土耳其
- pt_BR: Turkey
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
diff --git a/api/core/tools/provider/builtin/websearch/tools/web_search.py b/api/core/tools/provider/builtin/websearch/tools/web_search.py
deleted file mode 100644
index d0e93cb0fa5681..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/web_search.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import typing
-from urllib.parse import urlencode
-
-import requests
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class SerplyApi:
- """
- SerplyApi tool provider.
- """
-
- def __init__(self, api_key: str) -> None:
- """Initialize Serply Web Search Tool provider."""
- self.serply_api_key = api_key
- self.base_url = "https://api.serply.io/v1/search/"
-
- def run(self, query: str, **kwargs: typing.Any) -> str:
- """Run query through Serply and parse result."""
- params = {"q": query, "hl": kwargs.get("hl", "en"), "gl": kwargs.get("gl", "US"), "num": kwargs.get("num", 10)}
- location = kwargs.get("location", "US")
-
- headers = {
- "X-API-KEY": self.serply_api_key,
- "X-User-Agent": kwargs.get("device", "desktop"),
- "X-Proxy-Location": location,
- "User-Agent": "Dify",
- }
-
- url = f"{self.base_url}{urlencode(params)}"
- res = requests.get(
- url,
- headers=headers,
- )
- res = res.json()
-
- return self.parse_results(res)
-
- @staticmethod
- def parse_results(res: dict) -> str:
- """Process response from Serply Web Search."""
- results = res.get("results", [])
- if not res or "results" not in res:
- raise ValueError(f"Got error from Serply: {res}")
-
- string = []
- for result in results:
- try:
- string.append(
- "\n".join(
- [
- f"Title: {result['title']}",
- f"Link: {result['link']}",
- f"Description: {result['description'].strip()}",
- "---",
- ]
- )
- )
- except KeyError:
- continue
-
- if related_questions := res.get("related_questions", []):
- string.append("---")
- string.append("Related Questions: ")
- string.append("\n".join(related_questions))
-
- content = "\n".join(string)
- return f"\nSearch results:\n {content}\n"
-
-
-class WebSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, typing.Any],
- ) -> typing.Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- Invoke the SerplyApi tool.
- """
- query = tool_parameters["query"]
- num = tool_parameters.get("num", 10)
- gl = tool_parameters.get("gl", "us")
- hl = tool_parameters.get("hl", "en")
- location = tool_parameters.get("location", "None")
-
- api_key = self.runtime.credentials["serply_api_key"]
- result = SerplyApi(api_key).run(query=query, num=num, gl=gl, hl=hl, location=location)
- return self.create_text_message(text=result)
diff --git a/api/core/tools/provider/builtin/websearch/tools/web_search.yaml b/api/core/tools/provider/builtin/websearch/tools/web_search.yaml
deleted file mode 100644
index 055029253c1753..00000000000000
--- a/api/core/tools/provider/builtin/websearch/tools/web_search.yaml
+++ /dev/null
@@ -1,376 +0,0 @@
-identity:
- name: web_search
- author: Dify
- label:
- en_US: Web Search API
- zh_Hans: Web Search API
-description:
- human:
- en_US: A tool to retrieve answer boxes, knowledge graphs, snippets, and webpages from Google Search engine.
- zh_Hans: 一种从 Google 搜索引擎检索答案框、知识图、片段和网页的工具。
- llm: A tool to retrieve answer boxes, knowledge graphs, snippets, and webpages from Google Search engine.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query
- zh_Hans: 询问
- human_description:
- en_US: Defines the query you want to search.
- zh_Hans: 定义您要搜索的查询。
- llm_description: Defines the search query you want to search.
- form: llm
- - name: location
- type: string
- required: false
- default: US
- label:
- en_US: Location
- zh_Hans: 询问
- human_description:
- en_US: Defines from where you want the search to originate. (For example - New York)
- zh_Hans: 定义您想要搜索的起始位置。 (例如 - 纽约)
- llm_description: Defines from where you want the search to originate. (For example - New York)
- form: form
- options:
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - value: JP
- label:
- en_US: Japan
- zh_Hans: 日本
- pt_BR: Japan
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - name: device
- type: select
- label:
- en_US: Device Type
- zh_Hans: 汉斯先生
- human_description:
- en_US: Defines the device to make interface search. Default is "desktop".
- zh_Hans: 定义进行接口搜索的设备。默认为“桌面”
- required: false
- default: desktop
- form: form
- options:
- - value: desktop
- label:
- en_US: Desktop
- zh_Hans: 桌面
- - value: mobile
- label:
- en_US: Mobile
- zh_Hans: 移动的
- - name: gl
- type: select
- label:
- en_US: Country
- zh_Hans: 国家/地区
- required: false
- human_description:
- en_US: Defines the country of the search. Default is "US".
- zh_Hans: 定义搜索的国家/地区。默认为“美国”。
- llm_description: Defines the gl parameter of the Google search.
- form: form
- default: US
- options:
- - value: AU
- label:
- en_US: Australia
- zh_Hans: 澳大利亚
- pt_BR: Australia
- - value: BR
- label:
- en_US: Brazil
- zh_Hans: 巴西
- pt_BR: Brazil
- - value: CA
- label:
- en_US: Canada
- zh_Hans: 加拿大
- pt_BR: Canada
- - value: DE
- label:
- en_US: Germany
- zh_Hans: 德国
- pt_BR: Germany
- - value: FR
- label:
- en_US: France
- zh_Hans: 法国
- pt_BR: France
- - value: GB
- label:
- en_US: United Kingdom
- zh_Hans: 英国
- pt_BR: United Kingdom
- - value: IN
- label:
- en_US: India
- zh_Hans: 印度
- pt_BR: India
- - value: KR
- label:
- en_US: Korea
- zh_Hans: 韩国
- pt_BR: Korea
- - value: SE
- label:
- en_US: Sweden
- zh_Hans: 瑞典
- pt_BR: Sweden
- - value: SG
- label:
- en_US: Singapore
- zh_Hans: 新加坡
- pt_BR: Singapore
- - value: US
- label:
- en_US: United States
- zh_Hans: 美国
- pt_BR: United States
- - name: hl
- type: select
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: Defines the interface language of the search. Default is "en".
- zh_Hans: 定义搜索的界面语言。默认为“en”。
- required: false
- default: en
- form: form
- options:
- - value: ar
- label:
- en_US: Arabic
- zh_Hans: 阿拉伯语
- - value: bg
- label:
- en_US: Bulgarian
- zh_Hans: 保加利亚语
- - value: ca
- label:
- en_US: Catalan
- zh_Hans: 加泰罗尼亚语
- - value: zh-cn
- label:
- en_US: Chinese (Simplified)
- zh_Hans: 中文(简体)
- - value: zh-tw
- label:
- en_US: Chinese (Traditional)
- zh_Hans: 中文(繁体)
- - value: cs
- label:
- en_US: Czech
- zh_Hans: 捷克语
- - value: da
- label:
- en_US: Danish
- zh_Hans: 丹麦语
- - value: nl
- label:
- en_US: Dutch
- zh_Hans: 荷兰语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: et
- label:
- en_US: Estonian
- zh_Hans: 爱沙尼亚语
- - value: fi
- label:
- en_US: Finnish
- zh_Hans: 芬兰语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: el
- label:
- en_US: Greek
- zh_Hans: 希腊语
- - value: iw
- label:
- en_US: Hebrew
- zh_Hans: 希伯来语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: hu
- label:
- en_US: Hungarian
- zh_Hans: 匈牙利语
- - value: id
- label:
- en_US: Indonesian
- zh_Hans: 印尼语
- - value: it
- label:
- en_US: Italian
- zh_Hans: 意大利语
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: kn
- label:
- en_US: Kannada
- zh_Hans: 卡纳达语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: lv
- label:
- en_US: Latvian
- zh_Hans: 拉脱维亚语
- - value: lt
- label:
- en_US: Lithuanian
- zh_Hans: 立陶宛语
- - value: my
- label:
- en_US: Malay
- zh_Hans: 马来语
- - value: ml
- label:
- en_US: Malayalam
- zh_Hans: 马拉雅拉姆语
- - value: mr
- label:
- en_US: Marathi
- zh_Hans: 马拉地语
- - value: "no"
- label:
- en_US: Norwegian
- zh_Hans: 挪威语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt-br
- label:
- en_US: Portuguese (Brazil)
- zh_Hans: 葡萄牙语(巴西)
- - value: pt-pt
- label:
- en_US: Portuguese (Portugal)
- zh_Hans: 葡萄牙语(葡萄牙)
- - value: pa
- label:
- en_US: Punjabi
- zh_Hans: 旁遮普语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: ru
- label:
- en_US: Russian
- zh_Hans: 俄语
- - value: sr
- label:
- en_US: Serbian
- zh_Hans: 塞尔维亚语
- - value: sk
- label:
- en_US: Slovak
- zh_Hans: 斯洛伐克语
- - value: sl
- label:
- en_US: Slovenian
- zh_Hans: 斯洛文尼亚语
- - value: es
- label:
- en_US: Spanish
- zh_Hans: 西班牙语
- - value: sv
- label:
- en_US: Swedish
- zh_Hans: 瑞典语
- - value: ta
- label:
- en_US: Tamil
- zh_Hans: 泰米尔语
- - value: te
- label:
- en_US: Telugu
- zh_Hans: 泰卢固语
- - value: th
- label:
- en_US: Thai
- zh_Hans: 泰语
- - value: tr
- label:
- en_US: Turkish
- zh_Hans: 土耳其语
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
diff --git a/api/core/tools/provider/builtin/websearch/websearch.py b/api/core/tools/provider/builtin/websearch/websearch.py
deleted file mode 100644
index 90cc0c573ac97e..00000000000000
--- a/api/core/tools/provider/builtin/websearch/websearch.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.websearch.tools.web_search import WebSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class WebSearchAPIProvider(BuiltinToolProviderController):
- # validate when saving the api_key
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- WebSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={"query": "what is llm"},
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/websearch/websearch.yaml b/api/core/tools/provider/builtin/websearch/websearch.yaml
deleted file mode 100644
index c4267e1022dfa1..00000000000000
--- a/api/core/tools/provider/builtin/websearch/websearch.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-identity:
- name: websearch
- author: Serply.io
- label:
- en_US: Serply.io
- zh_Hans: Serply.io
- pt_BR: Serply.io
- description:
- en_US: Serply.io is a robust real-time SERP API delivering structured data from a collection of search engines including Web Search, Jobs, News, and many more.
- zh_Hans: Serply.io 是一个强大的实时 SERP API,可提供来自 搜索 招聘 新闻等搜索引擎集合的结构化数据。
- pt_BR: Serply.io is a robust real-time SERP API delivering structured data from a collection of search engines including Web Search, Jobs, News, and many more.
- icon: icon.svg
- tags:
- - search
- - business
- - news
- - productivity
-credentials_for_provider:
- serply_api_key:
- type: secret-input
- required: true
- label:
- en_US: Serply.io API key
- zh_Hans: Serply.io API key
- pt_BR: Serply.io API key
- placeholder:
- en_US: Please input your Serply.io API key
- zh_Hans: 请输入你的 Serply.io API key
- pt_BR: Please input your Serply.io API key
- help:
- en_US: Get your Serply.io API key from https://Serply.io/
- zh_Hans: 从 Serply.io 获取您的 Serply.io API key
- pt_BR: Get your Serply.io API key from Serply.io
- url: https://Serply.io/
diff --git a/api/core/tools/provider/builtin/wecom/_assets/icon.png b/api/core/tools/provider/builtin/wecom/_assets/icon.png
deleted file mode 100644
index 8588c20d5781e5..00000000000000
Binary files a/api/core/tools/provider/builtin/wecom/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.py b/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.py
deleted file mode 100644
index 545d9f4f8d6335..00000000000000
--- a/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from typing import Any, Union
-
-import httpx
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.utils.uuid_utils import is_valid_uuid
-
-
-class WecomGroupBotTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- content = tool_parameters.get("content", "")
- if not content:
- return self.create_text_message("Invalid parameter content")
-
- hook_key = tool_parameters.get("hook_key", "")
- if not is_valid_uuid(hook_key):
- return self.create_text_message(f"Invalid parameter hook_key ${hook_key}, not a valid UUID")
-
- message_type = tool_parameters.get("message_type", "text")
- if message_type == "markdown":
- payload = {
- "msgtype": "markdown",
- "markdown": {
- "content": content,
- },
- }
- else:
- payload = {
- "msgtype": "text",
- "text": {
- "content": content,
- },
- }
- api_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send"
- headers = {
- "Content-Type": "application/json",
- }
- params = {
- "key": hook_key,
- }
-
- try:
- res = httpx.post(api_url, headers=headers, params=params, json=payload)
- if res.is_success:
- return self.create_text_message("Text message sent successfully")
- else:
- return self.create_text_message(
- f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
- )
- except Exception as e:
- return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
diff --git a/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.yaml b/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.yaml
deleted file mode 100644
index 379005a1021420..00000000000000
--- a/api/core/tools/provider/builtin/wecom/tools/wecom_group_bot.yaml
+++ /dev/null
@@ -1,64 +0,0 @@
-identity:
- name: wecom_group_bot
- author: Bowen Liang
- label:
- en_US: Send Group Message
- zh_Hans: 发送群消息
- pt_BR: Send Group Message
- icon: icon.svg
-description:
- human:
- en_US: Sending a group message on Wecom via the webhook of group bot
- zh_Hans: 通过企业微信的群机器人webhook发送群消息
- pt_BR: Sending a group message on Wecom via the webhook of group bot
- llm: A tool for sending messages to a chat group on Wecom(企业微信) .
-parameters:
- - name: hook_key
- type: secret-input
- required: true
- label:
- en_US: Wecom Group bot webhook key
- zh_Hans: 群机器人webhook的key
- pt_BR: Wecom Group bot webhook key
- human_description:
- en_US: Wecom Group bot webhook key
- zh_Hans: 群机器人webhook的key
- pt_BR: Wecom Group bot webhook key
- form: form
- - name: content
- type: string
- required: true
- label:
- en_US: content
- zh_Hans: 消息内容
- pt_BR: content
- human_description:
- en_US: Content to sent to the group.
- zh_Hans: 群消息文本
- pt_BR: Content to sent to the group.
- llm_description: Content of the message
- form: llm
- - name: message_type
- type: select
- default: text
- required: true
- label:
- en_US: Wecom Group bot message type
- zh_Hans: 群机器人webhook的消息类型
- pt_BR: Wecom Group bot message type
- human_description:
- en_US: Wecom Group bot message type
- zh_Hans: 群机器人webhook的消息类型
- pt_BR: Wecom Group bot message type
- options:
- - value: text
- label:
- en_US: Text
- zh_Hans: 文本
- pt_BR: Text
- - value: markdown
- label:
- en_US: Markdown
- zh_Hans: Markdown
- pt_BR: Markdown
- form: form
diff --git a/api/core/tools/provider/builtin/wecom/wecom.py b/api/core/tools/provider/builtin/wecom/wecom.py
deleted file mode 100644
index 573f76ee56da67..00000000000000
--- a/api/core/tools/provider/builtin/wecom/wecom.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from core.tools.provider.builtin.wecom.tools.wecom_group_bot import WecomGroupBotTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class WecomProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- WecomGroupBotTool()
diff --git a/api/core/tools/provider/builtin/wecom/wecom.yaml b/api/core/tools/provider/builtin/wecom/wecom.yaml
deleted file mode 100644
index a544055ba4cb67..00000000000000
--- a/api/core/tools/provider/builtin/wecom/wecom.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: Bowen Liang
- name: wecom
- label:
- en_US: Wecom
- zh_Hans: 企业微信
- pt_BR: Wecom
- description:
- en_US: Wecom group bot
- zh_Hans: 企业微信群机器人
- pt_BR: Wecom group bot
- icon: icon.png
- tags:
- - social
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/wikipedia/_assets/icon.svg b/api/core/tools/provider/builtin/wikipedia/_assets/icon.svg
deleted file mode 100644
index fe652aacf9c871..00000000000000
--- a/api/core/tools/provider/builtin/wikipedia/_assets/icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py b/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py
deleted file mode 100644
index edb96e722f7f33..00000000000000
--- a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py
+++ /dev/null
@@ -1,105 +0,0 @@
-from typing import Any, Optional, Union
-
-import wikipedia # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-WIKIPEDIA_MAX_QUERY_LENGTH = 300
-
-
-class WikipediaAPIWrapper:
- """Wrapper around WikipediaAPI.
-
- To use, you should have the ``wikipedia`` python package installed.
- This wrapper will use the Wikipedia API to conduct searches and
- fetch page summaries. By default, it will return the page summaries
- of the top-k results.
- It limits the Document content by doc_content_chars_max.
- """
-
- top_k_results: int = 3
- lang: str = "en"
- load_all_available_meta: bool = False
- doc_content_chars_max: int = 4000
-
- def __init__(self, doc_content_chars_max: int = 4000):
- self.doc_content_chars_max = doc_content_chars_max
-
- def run(self, query: str, lang: str = "") -> str:
- if lang in wikipedia.languages():
- self.lang = lang
-
- wikipedia.set_lang(self.lang)
- wiki_client = wikipedia
-
- """Run Wikipedia search and get page summaries."""
- page_titles = wiki_client.search(query[:WIKIPEDIA_MAX_QUERY_LENGTH])
- summaries = []
- for page_title in page_titles[: self.top_k_results]:
- if wiki_page := self._fetch_page(page_title):
- if summary := self._formatted_page_summary(page_title, wiki_page):
- summaries.append(summary)
- if not summaries:
- return "No good Wikipedia Search Result was found"
- return "\n\n".join(summaries)[: self.doc_content_chars_max]
-
- @staticmethod
- def _formatted_page_summary(page_title: str, wiki_page: Any) -> Optional[str]:
- return f"Page: {page_title}\nSummary: {wiki_page.summary}"
-
- def _fetch_page(self, page: str) -> Optional[str]:
- try:
- return wikipedia.page(title=page, auto_suggest=False)
- except (
- wikipedia.exceptions.PageError,
- wikipedia.exceptions.DisambiguationError,
- ):
- return None
-
-
-class WikipediaQueryRun:
- """Tool that searches the Wikipedia API."""
-
- name = "Wikipedia"
- description = (
- "A wrapper around Wikipedia. "
- "Useful for when you need to answer general questions about "
- "people, places, companies, facts, historical events, or other subjects. "
- "Input should be a search query."
- )
- api_wrapper: WikipediaAPIWrapper
-
- def __init__(self, api_wrapper: WikipediaAPIWrapper):
- self.api_wrapper = api_wrapper
-
- def _run(
- self,
- query: str,
- lang: str = "",
- ) -> str:
- """Use the Wikipedia tool."""
- return self.api_wrapper.run(query, lang)
-
-
-class WikiPediaSearchTool(BuiltinTool):
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- query = tool_parameters.get("query", "")
- lang = tool_parameters.get("language", "")
- if not query:
- return self.create_text_message("Please input query")
-
- tool = WikipediaQueryRun(
- api_wrapper=WikipediaAPIWrapper(doc_content_chars_max=4000),
- )
-
- result = tool._run(query, lang)
-
- return self.create_text_message(self.summary(user_id=user_id, content=result))
diff --git a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.yaml b/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.yaml
deleted file mode 100644
index 98d002df1c0daa..00000000000000
--- a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-identity:
- name: wikipedia_search
- author: Dify
- label:
- en_US: WikipediaSearch
- zh_Hans: 维基百科搜索
- pt_BR: WikipediaSearch
- icon: icon.svg
-description:
- human:
- en_US: A tool for performing a Wikipedia search and extracting snippets and webpages.
- zh_Hans: 一个用于执行维基百科搜索并提取片段和网页的工具。
- pt_BR: A tool for performing a Wikipedia search and extracting snippets and webpages.
- llm: A tool for performing a Wikipedia search and extracting snippets and webpages. Input should be a search query.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 查询语句
- pt_BR: Query string
- human_description:
- en_US: key words for searching
- zh_Hans: 查询关键词
- pt_BR: key words for searching
- llm_description: key words for searching, this should be in the language of "language" parameter
- form: llm
- - name: language
- type: string
- required: true
- label:
- en_US: Language
- zh_Hans: 语言
- human_description:
- en_US: The language of the Wikipedia to be searched
- zh_Hans: 要搜索的维基百科语言
- llm_description: >-
- language of the wikipedia to be searched,
- only "de" for German,
- "en" for English,
- "fr" for French,
- "hi" for Hindi,
- "ja" for Japanese,
- "ko" for Korean,
- "pl" for Polish,
- "pt" for Portuguese,
- "ro" for Romanian,
- "uk" for Ukrainian,
- "vi" for Vietnamese,
- and "zh" for Chinese are supported
- form: llm
- options:
- - value: de
- label:
- en_US: German
- zh_Hans: 德语
- - value: en
- label:
- en_US: English
- zh_Hans: 英语
- - value: fr
- label:
- en_US: French
- zh_Hans: 法语
- - value: hi
- label:
- en_US: Hindi
- zh_Hans: 印地语
- - value: ja
- label:
- en_US: Japanese
- zh_Hans: 日语
- - value: ko
- label:
- en_US: Korean
- zh_Hans: 韩语
- - value: pl
- label:
- en_US: Polish
- zh_Hans: 波兰语
- - value: pt
- label:
- en_US: Portuguese
- zh_Hans: 葡萄牙语
- - value: ro
- label:
- en_US: Romanian
- zh_Hans: 罗马尼亚语
- - value: uk
- label:
- en_US: Ukrainian
- zh_Hans: 乌克兰语
- - value: vi
- label:
- en_US: Vietnamese
- zh_Hans: 越南语
- - value: zh
- label:
- en_US: Chinese
- zh_Hans: 中文
diff --git a/api/core/tools/provider/builtin/wikipedia/wikipedia.py b/api/core/tools/provider/builtin/wikipedia/wikipedia.py
deleted file mode 100644
index 178bf7b0ceb2e9..00000000000000
--- a/api/core/tools/provider/builtin/wikipedia/wikipedia.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.wikipedia.tools.wikipedia_search import WikiPediaSearchTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class WikiPediaProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- WikiPediaSearchTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "misaka mikoto",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/wikipedia/wikipedia.yaml b/api/core/tools/provider/builtin/wikipedia/wikipedia.yaml
deleted file mode 100644
index c5828240225d00..00000000000000
--- a/api/core/tools/provider/builtin/wikipedia/wikipedia.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-identity:
- author: Dify
- name: wikipedia
- label:
- en_US: Wikipedia
- zh_Hans: 维基百科
- pt_BR: Wikipedia
- description:
- en_US: Wikipedia is a free online encyclopedia, created and edited by volunteers around the world.
- zh_Hans: 维基百科是一个由全世界的志愿者创建和编辑的免费在线百科全书。
- pt_BR: Wikipedia is a free online encyclopedia, created and edited by volunteers around the world.
- icon: icon.svg
- tags:
- - social
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/wolframalpha/_assets/icon.svg b/api/core/tools/provider/builtin/wolframalpha/_assets/icon.svg
deleted file mode 100644
index 2caf32ee67be0a..00000000000000
--- a/api/core/tools/provider/builtin/wolframalpha/_assets/icon.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.py b/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.py
deleted file mode 100644
index 9b24be7cab81eb..00000000000000
--- a/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from typing import Any, Union
-
-from httpx import get
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.errors import ToolInvokeError, ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class WolframAlphaTool(BuiltinTool):
- _base_url = "https://api.wolframalpha.com/v2/query"
-
- def _invoke(
- self,
- user_id: str,
- tool_parameters: dict[str, Any],
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- query = tool_parameters.get("query", "")
- if not query:
- return self.create_text_message("Please input query")
- appid = self.runtime.credentials.get("appid", "")
- if not appid:
- raise ToolProviderCredentialValidationError("Please input appid")
-
- params = {"appid": appid, "input": query, "includepodid": "Result", "format": "plaintext", "output": "json"}
-
- finished = False
- result = None
- # try 3 times at most
- counter = 0
-
- while not finished and counter < 3:
- counter += 1
- try:
- response = get(self._base_url, params=params, timeout=20)
- response.raise_for_status()
- response_data = response.json()
- except Exception as e:
- raise ToolInvokeError(str(e))
-
- if "success" not in response_data["queryresult"] or response_data["queryresult"]["success"] != True:
- query_result = response_data.get("queryresult", {})
- if query_result.get("error"):
- if "msg" in query_result["error"]:
- if query_result["error"]["msg"] == "Invalid appid":
- raise ToolProviderCredentialValidationError("Invalid appid")
- raise ToolInvokeError("Failed to invoke tool")
-
- if "didyoumeans" in response_data["queryresult"]:
- # get the most likely interpretation
- query = ""
- max_score = 0
- for didyoumean in response_data["queryresult"]["didyoumeans"]:
- if float(didyoumean["score"]) > max_score:
- query = didyoumean["val"]
- max_score = float(didyoumean["score"])
-
- params["input"] = query
- else:
- finished = True
- if "sources" in response_data["queryresult"]:
- return self.create_link_message(response_data["queryresult"]["sources"]["url"])
- elif "pods" in response_data["queryresult"]:
- result = response_data["queryresult"]["pods"][0]["subpods"][0]["plaintext"]
-
- if not finished or not result:
- return self.create_text_message("No result found")
-
- return self.create_text_message(result)
diff --git a/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.yaml b/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.yaml
deleted file mode 100644
index 08b5668691e23a..00000000000000
--- a/api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-identity:
- name: wolframalpha
- author: Dify
- label:
- en_US: WolframAlpha
- zh_Hans: WolframAlpha
- pt_BR: WolframAlpha
-description:
- human:
- en_US: WolframAlpha is a powerful computational knowledge engine.
- zh_Hans: WolframAlpha 是一个强大的计算知识引擎。
- pt_BR: WolframAlpha is a powerful computational knowledge engine.
- llm: WolframAlpha is a powerful computational knowledge engine. one single query can get the answer of a question.
-parameters:
- - name: query
- type: string
- required: true
- label:
- en_US: Query string
- zh_Hans: 计算语句
- pt_BR: Query string
- human_description:
- en_US: used for calculating
- zh_Hans: 用于计算最终结果
- pt_BR: used for calculating
- llm_description: a single query for calculating
- form: llm
diff --git a/api/core/tools/provider/builtin/wolframalpha/wolframalpha.py b/api/core/tools/provider/builtin/wolframalpha/wolframalpha.py
deleted file mode 100644
index 7be288b5387f34..00000000000000
--- a/api/core/tools/provider/builtin/wolframalpha/wolframalpha.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.wolframalpha.tools.wolframalpha import WolframAlphaTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class GoogleProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- try:
- WolframAlphaTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "query": "1+2+....+111",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/wolframalpha/wolframalpha.yaml b/api/core/tools/provider/builtin/wolframalpha/wolframalpha.yaml
deleted file mode 100644
index 91265eb3c00d0a..00000000000000
--- a/api/core/tools/provider/builtin/wolframalpha/wolframalpha.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-identity:
- author: Dify
- name: wolframalpha
- label:
- en_US: WolframAlpha
- zh_Hans: WolframAlpha
- pt_BR: WolframAlpha
- description:
- en_US: WolframAlpha is a powerful computational knowledge engine.
- zh_Hans: WolframAlpha 是一个强大的计算知识引擎。
- pt_BR: WolframAlpha is a powerful computational knowledge engine.
- icon: icon.svg
- tags:
- - productivity
- - utilities
-credentials_for_provider:
- appid:
- type: secret-input
- required: true
- label:
- en_US: WolframAlpha AppID
- zh_Hans: WolframAlpha AppID
- pt_BR: WolframAlpha AppID
- placeholder:
- en_US: Please input your WolframAlpha AppID
- zh_Hans: 请输入你的 WolframAlpha AppID
- pt_BR: Please input your WolframAlpha AppID
- help:
- en_US: Get your WolframAlpha AppID from WolframAlpha, please use "full results" api access.
- zh_Hans: 从 WolframAlpha 获取您的 WolframAlpha AppID,请使用 "full results" API。
- pt_BR: Get your WolframAlpha AppID from WolframAlpha, please use "full results" api access.
- url: https://products.wolframalpha.com/api
diff --git a/api/core/tools/provider/builtin/xinference/_assets/icon.png b/api/core/tools/provider/builtin/xinference/_assets/icon.png
deleted file mode 100644
index e58cacbd123b58..00000000000000
Binary files a/api/core/tools/provider/builtin/xinference/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.py b/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.py
deleted file mode 100644
index a44d3b730a84f9..00000000000000
--- a/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.py
+++ /dev/null
@@ -1,415 +0,0 @@
-import io
-import json
-from base64 import b64decode, b64encode
-from copy import deepcopy
-from typing import Any, Union
-
-from httpx import get, post
-from PIL import Image
-from yarl import URL
-
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import (
- ToolInvokeMessage,
- ToolParameter,
- ToolParameterOption,
-)
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.tool.builtin_tool import BuiltinTool
-
-# All commented out parameters default to null
-DRAW_TEXT_OPTIONS = {
- # Prompts
- "prompt": "",
- "negative_prompt": "",
- # "styles": [],
- # Seeds
- "seed": -1,
- "subseed": -1,
- "subseed_strength": 0,
- "seed_resize_from_h": -1,
- "seed_resize_from_w": -1,
- # Samplers
- "sampler_name": "DPM++ 2M",
- # "scheduler": "",
- # "sampler_index": "Automatic",
- # Latent Space Options
- "batch_size": 1,
- "n_iter": 1,
- "steps": 10,
- "cfg_scale": 7,
- "width": 512,
- "height": 512,
- # "restore_faces": True,
- # "tiling": True,
- "do_not_save_samples": False,
- "do_not_save_grid": False,
- # "eta": 0,
- # "denoising_strength": 0.75,
- # "s_min_uncond": 0,
- # "s_churn": 0,
- # "s_tmax": 0,
- # "s_tmin": 0,
- # "s_noise": 0,
- "override_settings": {},
- "override_settings_restore_afterwards": True,
- # Refinement Options
- "refiner_checkpoint": "",
- "refiner_switch_at": 0,
- "disable_extra_networks": False,
- # "firstpass_image": "",
- # "comments": "",
- # High-Resolution Options
- "enable_hr": False,
- "firstphase_width": 0,
- "firstphase_height": 0,
- "hr_scale": 2,
- # "hr_upscaler": "",
- "hr_second_pass_steps": 0,
- "hr_resize_x": 0,
- "hr_resize_y": 0,
- # "hr_checkpoint_name": "",
- # "hr_sampler_name": "",
- # "hr_scheduler": "",
- "hr_prompt": "",
- "hr_negative_prompt": "",
- # Task Options
- # "force_task_id": "",
- # Script Options
- # "script_name": "",
- "script_args": [],
- # Output Options
- "send_images": True,
- "save_images": False,
- "alwayson_scripts": {},
- # "infotext": "",
-}
-
-
-class StableDiffusionTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- # base url
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return self.create_text_message("Please input base_url")
-
- if tool_parameters.get("model"):
- self.runtime.credentials["model"] = tool_parameters["model"]
-
- model = self.runtime.credentials.get("model", None)
- if not model:
- return self.create_text_message("Please input model")
- api_key = self.runtime.credentials.get("api_key") or "abc"
- headers = {"Authorization": f"Bearer {api_key}"}
- # set model
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "options")
- response = post(
- url,
- json={"sd_model_checkpoint": model},
- headers=headers,
- )
- if response.status_code != 200:
- raise ToolProviderCredentialValidationError("Failed to set model, please tell user to set model")
- except Exception as e:
- raise ToolProviderCredentialValidationError("Failed to set model, please tell user to set model")
-
- # get image id and image variable
- image_id = tool_parameters.get("image_id", "")
- image_variable = self.get_default_image_variable()
- # Return text2img if there's no image ID or no image variable
- if not image_id or not image_variable:
- return self.text2img(base_url=base_url, tool_parameters=tool_parameters)
-
- # Proceed with image-to-image generation
- return self.img2img(base_url=base_url, tool_parameters=tool_parameters)
-
- def validate_models(self):
- """
- validate models
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- raise ToolProviderCredentialValidationError("Please input base_url")
- model = self.runtime.credentials.get("model", None)
- if not model:
- raise ToolProviderCredentialValidationError("Please input model")
-
- api_url = str(URL(base_url) / "sdapi" / "v1" / "sd-models")
- response = get(url=api_url, timeout=10)
- if response.status_code == 404:
- # try draw a picture
- self._invoke(
- user_id="test",
- tool_parameters={
- "prompt": "a cat",
- "width": 1024,
- "height": 1024,
- "steps": 1,
- "lora": "",
- },
- )
- elif response.status_code != 200:
- raise ToolProviderCredentialValidationError("Failed to get models")
- else:
- models = [d["model_name"] for d in response.json()]
- if len([d for d in models if d == model]) > 0:
- return self.create_text_message(json.dumps(models))
- else:
- raise ToolProviderCredentialValidationError(f"model {model} does not exist")
- except Exception as e:
- raise ToolProviderCredentialValidationError(f"Failed to get models, {e}")
-
- def get_sd_models(self) -> list[str]:
- """
- get sd models
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "sdapi" / "v1" / "sd-models")
- response = get(url=api_url, timeout=120)
- if response.status_code != 200:
- return []
- else:
- return [d["model_name"] for d in response.json()]
- except Exception as e:
- return []
-
- def get_sample_methods(self) -> list[str]:
- """
- get sample method
- """
- try:
- base_url = self.runtime.credentials.get("base_url", None)
- if not base_url:
- return []
- api_url = str(URL(base_url) / "sdapi" / "v1" / "samplers")
- response = get(url=api_url, timeout=120)
- if response.status_code != 200:
- return []
- else:
- return [d["name"] for d in response.json()]
- except Exception as e:
- return []
-
- def img2img(
- self, base_url: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- generate image
- """
-
- # Fetch the binary data of the image
- image_variable = self.get_default_image_variable()
- image_binary = self.get_variable_file(image_variable.name)
- if not image_binary:
- return self.create_text_message("Image not found, please request user to generate image firstly.")
-
- # Convert image to RGB and save as PNG
- try:
- with Image.open(io.BytesIO(image_binary)) as image, io.BytesIO() as buffer:
- image.convert("RGB").save(buffer, format="PNG")
- image_binary = buffer.getvalue()
- except Exception as e:
- return self.create_text_message(f"Failed to process the image: {str(e)}")
-
- # copy draw options
- draw_options = deepcopy(DRAW_TEXT_OPTIONS)
- # set image options
- model = tool_parameters.get("model", "")
- draw_options_image = {
- "init_images": [b64encode(image_binary).decode("utf-8")],
- "denoising_strength": 0.9,
- "restore_faces": False,
- "script_args": [],
- "override_settings": {"sd_model_checkpoint": model},
- "resize_mode": 0,
- "image_cfg_scale": 0,
- # "mask": None,
- "mask_blur_x": 4,
- "mask_blur_y": 4,
- "mask_blur": 0,
- "mask_round": True,
- "inpainting_fill": 0,
- "inpaint_full_res": True,
- "inpaint_full_res_padding": 0,
- "inpainting_mask_invert": 0,
- "initial_noise_multiplier": 0,
- # "latent_mask": None,
- "include_init_images": True,
- }
- # update key and values
- draw_options.update(draw_options_image)
- draw_options.update(tool_parameters)
-
- # get prompt lora model
- prompt = tool_parameters.get("prompt", "")
- lora = tool_parameters.get("lora", "")
- model = tool_parameters.get("model", "")
- if lora:
- draw_options["prompt"] = f"{lora},{prompt}"
- else:
- draw_options["prompt"] = prompt
- api_key = self.runtime.credentials.get("api_key") or "abc"
- headers = {"Authorization": f"Bearer {api_key}"}
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "img2img")
- response = post(
- url,
- json=draw_options,
- timeout=120,
- headers=headers,
- )
- if response.status_code != 200:
- return self.create_text_message("Failed to generate image")
-
- image = response.json()["images"][0]
-
- return self.create_blob_message(
- blob=b64decode(image),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- )
-
- except Exception as e:
- return self.create_text_message("Failed to generate image")
-
- def text2img(
- self, base_url: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- generate image
- """
- # copy draw options
- draw_options = deepcopy(DRAW_TEXT_OPTIONS)
- draw_options.update(tool_parameters)
- # get prompt lora model
- prompt = tool_parameters.get("prompt", "")
- lora = tool_parameters.get("lora", "")
- model = tool_parameters.get("model", "")
- if lora:
- draw_options["prompt"] = f"{lora},{prompt}"
- else:
- draw_options["prompt"] = prompt
- draw_options["override_settings"]["sd_model_checkpoint"] = model
- api_key = self.runtime.credentials.get("api_key") or "abc"
- headers = {"Authorization": f"Bearer {api_key}"}
- try:
- url = str(URL(base_url) / "sdapi" / "v1" / "txt2img")
- response = post(
- url,
- json=draw_options,
- timeout=120,
- headers=headers,
- )
- if response.status_code != 200:
- return self.create_text_message("Failed to generate image")
-
- image = response.json()["images"][0]
-
- return self.create_blob_message(
- blob=b64decode(image),
- meta={"mime_type": "image/png"},
- save_as=self.VariableKey.IMAGE.value,
- )
-
- except Exception as e:
- return self.create_text_message("Failed to generate image")
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- parameters = [
- ToolParameter(
- name="prompt",
- label=I18nObject(en_US="Prompt", zh_Hans="Prompt"),
- human_description=I18nObject(
- en_US="Image prompt, you can check the official documentation of Stable Diffusion",
- zh_Hans="图像提示词,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Image prompt of Stable Diffusion, you should describe the image you want to generate"
- " as a list of words as possible as detailed, the prompt must be written in English.",
- required=True,
- ),
- ]
- if len(self.list_default_image_variables()) != 0:
- parameters.append(
- ToolParameter(
- name="image_id",
- label=I18nObject(en_US="image_id", zh_Hans="image_id"),
- human_description=I18nObject(
- en_US="Image id of the image you want to generate based on, if you want to generate image based"
- " on the default image, you can leave this field empty.",
- zh_Hans="您想要生成的图像的图像 ID,如果您想要基于默认图像生成图像,则可以将此字段留空。",
- ),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Image id of the original image, you can leave this field empty if you want to"
- " generate a new image.",
- required=True,
- options=[
- ToolParameterOption(value=i.name, label=I18nObject(en_US=i.name, zh_Hans=i.name))
- for i in self.list_default_image_variables()
- ],
- )
- )
-
- if self.runtime.credentials:
- try:
- models = self.get_sd_models()
- if len(models) != 0:
- parameters.append(
- ToolParameter(
- name="model",
- label=I18nObject(en_US="Model", zh_Hans="Model"),
- human_description=I18nObject(
- en_US="Model of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- zh_Hans="Stable Diffusion 的模型,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Model of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- required=True,
- default=models[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in models
- ],
- )
- )
-
- except:
- pass
-
- sample_methods = self.get_sample_methods()
- if len(sample_methods) != 0:
- parameters.append(
- ToolParameter(
- name="sampler_name",
- label=I18nObject(en_US="Sampling method", zh_Hans="Sampling method"),
- human_description=I18nObject(
- en_US="Sampling method of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- zh_Hans="Stable Diffusion 的Sampling method,您可以查看 Stable Diffusion 的官方文档",
- ),
- type=ToolParameter.ToolParameterType.SELECT,
- form=ToolParameter.ToolParameterForm.FORM,
- llm_description="Sampling method of Stable Diffusion, you can check the official documentation"
- " of Stable Diffusion",
- required=True,
- default=sample_methods[0],
- options=[
- ToolParameterOption(value=i, label=I18nObject(en_US=i, zh_Hans=i)) for i in sample_methods
- ],
- )
- )
- return parameters
diff --git a/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.yaml b/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.yaml
deleted file mode 100644
index 4f1d17f175c567..00000000000000
--- a/api/core/tools/provider/builtin/xinference/tools/stable_diffusion.yaml
+++ /dev/null
@@ -1,87 +0,0 @@
-identity:
- name: stable_diffusion
- author: xinference
- label:
- en_US: Stable Diffusion
- zh_Hans: Stable Diffusion
-description:
- human:
- en_US: Generate images using Stable Diffusion models.
- zh_Hans: 使用 Stable Diffusion 模型生成图片。
- llm: draw the image you want based on your prompt.
-parameters:
- - name: prompt
- type: string
- required: true
- label:
- en_US: Prompt
- zh_Hans: 提示词
- human_description:
- en_US: Image prompt
- zh_Hans: 图像提示词
- llm_description: Image prompt of Stable Diffusion, you should describe the image you want to generate as a list of words as possible as detailed, the prompt must be written in English.
- form: llm
- - name: model
- type: string
- required: false
- label:
- en_US: Model Name
- zh_Hans: 模型名称
- human_description:
- en_US: Model Name
- zh_Hans: 模型名称
- form: form
- - name: lora
- type: string
- required: false
- label:
- en_US: Lora
- zh_Hans: Lora
- human_description:
- en_US: Lora
- zh_Hans: Lora
- form: form
- - name: steps
- type: number
- required: false
- label:
- en_US: Steps
- zh_Hans: Steps
- human_description:
- en_US: Steps
- zh_Hans: Steps
- form: form
- default: 10
- - name: width
- type: number
- required: false
- label:
- en_US: Width
- zh_Hans: Width
- human_description:
- en_US: Width
- zh_Hans: Width
- form: form
- default: 1024
- - name: height
- type: number
- required: false
- label:
- en_US: Height
- zh_Hans: Height
- human_description:
- en_US: Height
- zh_Hans: Height
- form: form
- default: 1024
- - name: negative_prompt
- type: string
- required: false
- label:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- human_description:
- en_US: Negative prompt
- zh_Hans: Negative prompt
- form: form
- default: bad art, ugly, deformed, watermark, duplicated, discontinuous lines
diff --git a/api/core/tools/provider/builtin/xinference/xinference.py b/api/core/tools/provider/builtin/xinference/xinference.py
deleted file mode 100644
index 9692e4060e8a87..00000000000000
--- a/api/core/tools/provider/builtin/xinference/xinference.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import requests
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class XinferenceProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- base_url = credentials.get("base_url", "").removesuffix("/")
- api_key = credentials.get("api_key", "")
- if not api_key:
- api_key = "abc"
- credentials["api_key"] = api_key
- model = credentials.get("model", "")
- if not base_url or not model:
- raise ToolProviderCredentialValidationError("Xinference base_url and model is required")
- headers = {"Authorization": f"Bearer {api_key}"}
- res = requests.post(
- f"{base_url}/sdapi/v1/options",
- headers=headers,
- json={"sd_model_checkpoint": model},
- )
- if res.status_code != 200:
- raise ToolProviderCredentialValidationError("Xinference API key is invalid")
diff --git a/api/core/tools/provider/builtin/xinference/xinference.yaml b/api/core/tools/provider/builtin/xinference/xinference.yaml
deleted file mode 100644
index b0c02b9cbcb01a..00000000000000
--- a/api/core/tools/provider/builtin/xinference/xinference.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-identity:
- author: xinference
- name: xinference
- label:
- en_US: Xinference
- zh_Hans: Xinference
- description:
- zh_Hans: Xinference 提供的兼容 Stable Diffusion web ui 的图片生成 API。
- en_US: Stable Diffusion web ui compatible API provided by Xinference.
- icon: icon.png
- tags:
- - image
-credentials_for_provider:
- base_url:
- type: secret-input
- required: true
- label:
- en_US: Base URL
- zh_Hans: Xinference 服务器的 Base URL
- placeholder:
- en_US: Please input Xinference server's Base URL
- zh_Hans: 请输入 Xinference 服务器的 Base URL
- model:
- type: text-input
- required: true
- label:
- en_US: Model
- zh_Hans: 模型
- placeholder:
- en_US: Please input your model name
- zh_Hans: 请输入你的模型名称
- api_key:
- type: secret-input
- required: false
- label:
- en_US: API Key
- zh_Hans: Xinference 服务器的 API Key
- placeholder:
- en_US: Please input Xinference server's API Key
- zh_Hans: 请输入 Xinference 服务器的 API Key
diff --git a/api/core/tools/provider/builtin/yahoo/_assets/icon.png b/api/core/tools/provider/builtin/yahoo/_assets/icon.png
deleted file mode 100644
index 35d756f75410db..00000000000000
Binary files a/api/core/tools/provider/builtin/yahoo/_assets/icon.png and /dev/null differ
diff --git a/api/core/tools/provider/builtin/yahoo/tools/analytics.py b/api/core/tools/provider/builtin/yahoo/tools/analytics.py
deleted file mode 100644
index 95a65ba22fc8af..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/analytics.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from datetime import datetime
-from typing import Any, Union
-
-import pandas as pd
-from requests.exceptions import HTTPError, ReadTimeout
-from yfinance import download # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class YahooFinanceAnalyticsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- symbol = tool_parameters.get("symbol", "")
- if not symbol:
- return self.create_text_message("Please input symbol")
-
- time_range = [None, None]
- start_date = tool_parameters.get("start_date", "")
- if start_date:
- time_range[0] = start_date
- else:
- time_range[0] = "1800-01-01"
-
- end_date = tool_parameters.get("end_date", "")
- if end_date:
- time_range[1] = end_date
- else:
- time_range[1] = datetime.now().strftime("%Y-%m-%d")
-
- stock_data = download(symbol, start=time_range[0], end=time_range[1])
- max_segments = min(15, len(stock_data))
- rows_per_segment = len(stock_data) // (max_segments or 1)
- summary_data = []
- for i in range(max_segments):
- start_idx = i * rows_per_segment
- end_idx = (i + 1) * rows_per_segment if i < max_segments - 1 else len(stock_data)
- segment_data = stock_data.iloc[start_idx:end_idx]
- segment_summary = {
- "Start Date": segment_data.index[0],
- "End Date": segment_data.index[-1],
- "Average Close": segment_data["Close"].mean(),
- "Average Volume": segment_data["Volume"].mean(),
- "Average Open": segment_data["Open"].mean(),
- "Average High": segment_data["High"].mean(),
- "Average Low": segment_data["Low"].mean(),
- "Average Adj Close": segment_data["Adj Close"].mean(),
- "Max Close": segment_data["Close"].max(),
- "Min Close": segment_data["Close"].min(),
- "Max Volume": segment_data["Volume"].max(),
- "Min Volume": segment_data["Volume"].min(),
- "Max Open": segment_data["Open"].max(),
- "Min Open": segment_data["Open"].min(),
- "Max High": segment_data["High"].max(),
- "Min High": segment_data["High"].min(),
- }
-
- summary_data.append(segment_summary)
-
- summary_df = pd.DataFrame(summary_data)
-
- try:
- return self.create_text_message(str(summary_df.to_dict()))
- except (HTTPError, ReadTimeout):
- return self.create_text_message("There is a internet connection problem. Please try again later.")
diff --git a/api/core/tools/provider/builtin/yahoo/tools/analytics.yaml b/api/core/tools/provider/builtin/yahoo/tools/analytics.yaml
deleted file mode 100644
index 89e66fb5814908..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/analytics.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-identity:
- name: yahoo_finance_analytics
- author: Dify
- label:
- en_US: Analytics
- zh_Hans: 分析
- pt_BR: Análises
- icon: icon.svg
-description:
- human:
- en_US: A tool for get analytics about a ticker from Yahoo Finance.
- zh_Hans: 一个用于从雅虎财经获取分析数据的工具。
- pt_BR: Uma ferramenta para obter análises sobre um ticker do Yahoo Finance.
- llm: A tool for get analytics from Yahoo Finance. Input should be the ticker symbol like AAPL.
-parameters:
- - name: symbol
- type: string
- required: true
- label:
- en_US: Ticker symbol
- zh_Hans: 股票代码
- pt_BR: Símbolo do ticker
- human_description:
- en_US: The ticker symbol of the company you want to analyze.
- zh_Hans: 你想要搜索的公司的股票代码。
- pt_BR: O símbolo do ticker da empresa que você deseja analisar.
- llm_description: The ticker symbol of the company you want to analyze.
- form: llm
- - name: start_date
- type: string
- required: false
- label:
- en_US: Start date
- zh_Hans: 开始日期
- pt_BR: Data de início
- human_description:
- en_US: The start date of the analytics.
- zh_Hans: 分析的开始日期。
- pt_BR: A data de início das análises.
- llm_description: The start date of the analytics, the format of the date must be YYYY-MM-DD like 2020-01-01.
- form: llm
- - name: end_date
- type: string
- required: false
- label:
- en_US: End date
- zh_Hans: 结束日期
- pt_BR: Data de término
- human_description:
- en_US: The end date of the analytics.
- zh_Hans: 分析的结束日期。
- pt_BR: A data de término das análises.
- llm_description: The end date of the analytics, the format of the date must be YYYY-MM-DD like 2024-01-01.
- form: llm
diff --git a/api/core/tools/provider/builtin/yahoo/tools/news.py b/api/core/tools/provider/builtin/yahoo/tools/news.py
deleted file mode 100644
index c9ae0c4ca7fcc6..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/news.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Any, Union
-
-import yfinance # type: ignore
-from requests.exceptions import HTTPError, ReadTimeout
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class YahooFinanceSearchTickerTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
-
- query = tool_parameters.get("symbol", "")
- if not query:
- return self.create_text_message("Please input symbol")
-
- try:
- return self.run(ticker=query, user_id=user_id)
- except (HTTPError, ReadTimeout):
- return self.create_text_message("There is a internet connection problem. Please try again later.")
-
- def run(self, ticker: str, user_id: str) -> ToolInvokeMessage:
- company = yfinance.Ticker(ticker)
- try:
- if company.isin is None:
- return self.create_text_message(f"Company ticker {ticker} not found.")
- except (HTTPError, ReadTimeout, ConnectionError):
- return self.create_text_message(f"Company ticker {ticker} not found.")
-
- links = []
- try:
- links = [n["link"] for n in company.news if n["type"] == "STORY"]
- except (HTTPError, ReadTimeout, ConnectionError):
- if not links:
- return self.create_text_message(f"There is nothing about {ticker} ticker")
- if not links:
- return self.create_text_message(f"No news found for company that searched with {ticker} ticker.")
-
- result = "\n\n".join([self.get_url(link) for link in links])
-
- return self.create_text_message(self.summary(user_id=user_id, content=result))
diff --git a/api/core/tools/provider/builtin/yahoo/tools/news.yaml b/api/core/tools/provider/builtin/yahoo/tools/news.yaml
deleted file mode 100644
index 4118c1a82f280f..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/news.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-identity:
- name: yahoo_finance_news
- author: Dify
- label:
- en_US: News
- zh_Hans: 新闻
- pt_BR: Notícias
- icon: icon.svg
-description:
- human:
- en_US: A tool for get news about a ticker from Yahoo Finance.
- zh_Hans: 一个用于从雅虎财经获取新闻的工具。
- pt_BR: Uma ferramenta para obter notícias sobre um ticker da Yahoo Finance.
- llm: A tool for get news from Yahoo Finance. Input should be the ticker symbol like AAPL.
-parameters:
- - name: symbol
- type: string
- required: true
- label:
- en_US: Ticker symbol
- zh_Hans: 股票代码
- pt_BR: Símbolo do ticker
- human_description:
- en_US: The ticker symbol of the company you want to search.
- zh_Hans: 你想要搜索的公司的股票代码。
- pt_BR: O símbolo do ticker da empresa que você deseja pesquisar.
- llm_description: The ticker symbol of the company you want to search.
- form: llm
diff --git a/api/core/tools/provider/builtin/yahoo/tools/ticker.py b/api/core/tools/provider/builtin/yahoo/tools/ticker.py
deleted file mode 100644
index 74d0d25addf04b..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/ticker.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from typing import Any, Union
-
-from requests.exceptions import HTTPError, ReadTimeout
-from yfinance import Ticker # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class YahooFinanceSearchTickerTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- query = tool_parameters.get("symbol", "")
- if not query:
- return self.create_text_message("Please input symbol")
-
- try:
- return self.create_text_message(self.run(ticker=query))
- except (HTTPError, ReadTimeout):
- return self.create_text_message("There is a internet connection problem. Please try again later.")
-
- def run(self, ticker: str) -> str:
- return str(Ticker(ticker).info)
diff --git a/api/core/tools/provider/builtin/yahoo/tools/ticker.yaml b/api/core/tools/provider/builtin/yahoo/tools/ticker.yaml
deleted file mode 100644
index 3c1ee9cf316be9..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/tools/ticker.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-identity:
- name: yahoo_finance_ticker
- author: Dify
- label:
- en_US: Ticker
- zh_Hans: 股票信息
- pt_BR: Ticker
- icon: icon.svg
-description:
- human:
- en_US: A tool for search ticker information from Yahoo Finance.
- zh_Hans: 一个用于从雅虎财经搜索股票信息的工具。
- pt_BR: Uma ferramenta para buscar informações de ticker do Yahoo Finance.
- llm: A tool for search ticker information from Yahoo Finance. Input should be the ticker symbol like AAPL.
-parameters:
- - name: symbol
- type: string
- required: true
- label:
- en_US: Ticker symbol
- zh_Hans: 股票代码
- pt_BR: Símbolo do ticker
- human_description:
- en_US: The ticker symbol of the company you want to search.
- zh_Hans: 你想要搜索的公司的股票代码。
- pt_BR: O símbolo do ticker da empresa que você deseja pesquisar.
- llm_description: The ticker symbol of the company you want to search.
- form: llm
diff --git a/api/core/tools/provider/builtin/yahoo/yahoo.py b/api/core/tools/provider/builtin/yahoo/yahoo.py
deleted file mode 100644
index 8d82084e769703..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/yahoo.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.yahoo.tools.ticker import YahooFinanceSearchTickerTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class YahooFinanceProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- YahooFinanceSearchTickerTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "ticker": "MSFT",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/yahoo/yahoo.yaml b/api/core/tools/provider/builtin/yahoo/yahoo.yaml
deleted file mode 100644
index f1e82952c09ba4..00000000000000
--- a/api/core/tools/provider/builtin/yahoo/yahoo.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-identity:
- author: Dify
- name: yahoo
- label:
- en_US: YahooFinance
- zh_Hans: 雅虎财经
- pt_BR: YahooFinance
- description:
- en_US: Finance, and Yahoo! get the latest news, stock quotes, and interactive chart with Yahoo!
- zh_Hans: 雅虎财经,获取并整理出最新的新闻、股票报价等一切你想要的财经信息。
- pt_BR: Finance, and Yahoo! get the latest news, stock quotes, and interactive chart with Yahoo!
- icon: icon.png
- tags:
- - business
- - finance
-credentials_for_provider:
diff --git a/api/core/tools/provider/builtin/youtube/_assets/icon.svg b/api/core/tools/provider/builtin/youtube/_assets/icon.svg
deleted file mode 100644
index 83b0700fecbf30..00000000000000
--- a/api/core/tools/provider/builtin/youtube/_assets/icon.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/api/core/tools/provider/builtin/youtube/tools/videos.py b/api/core/tools/provider/builtin/youtube/tools/videos.py
deleted file mode 100644
index a24fe89679b29b..00000000000000
--- a/api/core/tools/provider/builtin/youtube/tools/videos.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from datetime import datetime
-from typing import Any, Union
-
-from googleapiclient.discovery import build # type: ignore
-
-from core.tools.entities.tool_entities import ToolInvokeMessage
-from core.tools.tool.builtin_tool import BuiltinTool
-
-
-class YoutubeVideosAnalyticsTool(BuiltinTool):
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke tools
- """
- channel = tool_parameters.get("channel", "")
- if not channel:
- return self.create_text_message("Please input symbol")
-
- time_range = [None, None]
- start_date = tool_parameters.get("start_date", "")
- if start_date:
- time_range[0] = start_date
- else:
- time_range[0] = "1800-01-01"
-
- end_date = tool_parameters.get("end_date", "")
- if end_date:
- time_range[1] = end_date
- else:
- time_range[1] = datetime.now().strftime("%Y-%m-%d")
-
- if "google_api_key" not in self.runtime.credentials or not self.runtime.credentials["google_api_key"]:
- return self.create_text_message("Please input api key")
-
- youtube = build("youtube", "v3", developerKey=self.runtime.credentials["google_api_key"])
-
- # try to get channel id
- search_results = youtube.search().list(q=channel, type="channel", order="relevance", part="id").execute()
- channel_id = search_results["items"][0]["id"]["channelId"]
-
- start_date, end_date = time_range
-
- start_date = datetime.strptime(start_date, "%Y-%m-%d").strftime("%Y-%m-%dT%H:%M:%SZ")
- end_date = datetime.strptime(end_date, "%Y-%m-%d").strftime("%Y-%m-%dT%H:%M:%SZ")
-
- # get videos
- time_range_videos = (
- youtube.search()
- .list(
- part="snippet",
- channelId=channel_id,
- order="date",
- type="video",
- publishedAfter=start_date,
- publishedBefore=end_date,
- )
- .execute()
- )
-
- def extract_video_data(video_list):
- data = []
- for video in video_list["items"]:
- video_id = video["id"]["videoId"]
- video_info = youtube.videos().list(part="snippet,statistics", id=video_id).execute()
- title = video_info["items"][0]["snippet"]["title"]
- views = video_info["items"][0]["statistics"]["viewCount"]
- data.append({"Title": title, "Views": views})
- return data
-
- summary = extract_video_data(time_range_videos)
-
- return self.create_text_message(str(summary))
diff --git a/api/core/tools/provider/builtin/youtube/tools/videos.yaml b/api/core/tools/provider/builtin/youtube/tools/videos.yaml
deleted file mode 100644
index 976699eb627910..00000000000000
--- a/api/core/tools/provider/builtin/youtube/tools/videos.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-identity:
- name: youtube_video_statistics
- author: Dify
- label:
- en_US: Video statistics
- zh_Hans: 视频统计
- pt_BR: Estatísticas de vídeo
- icon: icon.svg
-description:
- human:
- en_US: A tool for get statistics about a channel's videos.
- zh_Hans: 一个用于获取油管频道视频统计数据的工具。
- pt_BR: Uma ferramenta para obter estatísticas sobre os vídeos de um canal.
- llm: A tool for get statistics about a channel's videos. Input should be the name of the channel like PewDiePie.
-parameters:
- - name: channel
- type: string
- required: true
- label:
- en_US: Channel name
- zh_Hans: 频道名
- pt_BR: Nome do canal
- human_description:
- en_US: The name of the channel you want to search.
- zh_Hans: 你想要搜索的油管频道名。
- pt_BR: O nome do canal que você deseja pesquisar.
- llm_description: The name of the channel you want to search.
- form: llm
- - name: start_date
- type: string
- required: false
- label:
- en_US: Start date
- zh_Hans: 开始日期
- pt_BR: Data de início
- human_description:
- en_US: The start date of the analytics.
- zh_Hans: 分析的开始日期。
- pt_BR: A data de início da análise.
- llm_description: The start date of the analytics, the format of the date must be YYYY-MM-DD like 2020-01-01.
- form: llm
- - name: end_date
- type: string
- required: false
- label:
- en_US: End date
- zh_Hans: 结束日期
- pt_BR: Data de término
- human_description:
- en_US: The end date of the analytics.
- zh_Hans: 分析的结束日期。
- pt_BR: A data de término da análise.
- llm_description: The end date of the analytics, the format of the date must be YYYY-MM-DD like 2024-01-01.
- form: llm
diff --git a/api/core/tools/provider/builtin/youtube/youtube.py b/api/core/tools/provider/builtin/youtube/youtube.py
deleted file mode 100644
index 07e430bcbf27e1..00000000000000
--- a/api/core/tools/provider/builtin/youtube/youtube.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from core.tools.errors import ToolProviderCredentialValidationError
-from core.tools.provider.builtin.youtube.tools.videos import YoutubeVideosAnalyticsTool
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-
-
-class YahooFinanceProvider(BuiltinToolProviderController):
- def _validate_credentials(self, credentials: dict) -> None:
- try:
- YoutubeVideosAnalyticsTool().fork_tool_runtime(
- runtime={
- "credentials": credentials,
- }
- ).invoke(
- user_id="",
- tool_parameters={
- "channel": "UC2JZCsZSOudXA08cMMRCL9g",
- "start_date": "2020-01-01",
- "end_date": "2024-12-31",
- },
- )
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
diff --git a/api/core/tools/provider/builtin/youtube/youtube.yaml b/api/core/tools/provider/builtin/youtube/youtube.yaml
deleted file mode 100644
index d6915b9a324767..00000000000000
--- a/api/core/tools/provider/builtin/youtube/youtube.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-identity:
- author: Dify
- name: youtube
- label:
- en_US: YouTube
- zh_Hans: YouTube
- pt_BR: YouTube
- description:
- en_US: YouTube
- zh_Hans: YouTube(油管)是全球最大的视频分享网站,用户可以在上面上传、观看和分享视频。
- pt_BR: YouTube é o maior site de compartilhamento de vídeos do mundo, onde os usuários podem fazer upload, assistir e compartilhar vídeos.
- icon: icon.svg
- tags:
- - videos
-credentials_for_provider:
- google_api_key:
- type: secret-input
- required: true
- label:
- en_US: Google API key
- zh_Hans: Google API key
- pt_BR: Chave da API do Google
- placeholder:
- en_US: Please input your Google API key
- zh_Hans: 请输入你的 Google API key
- pt_BR: Insira sua chave da API do Google
- help:
- en_US: Get your Google API key from Google
- zh_Hans: 从 Google 获取您的 Google API key
- pt_BR: Obtenha sua chave da API do Google no Google
- url: https://console.developers.google.com/apis/credentials
diff --git a/api/core/tools/provider/builtin_tool_provider.py b/api/core/tools/provider/builtin_tool_provider.py
deleted file mode 100644
index 61de75ac5e2ccd..00000000000000
--- a/api/core/tools/provider/builtin_tool_provider.py
+++ /dev/null
@@ -1,244 +0,0 @@
-from abc import abstractmethod
-from os import listdir, path
-from typing import Any, Optional
-
-from core.helper.module_import_helper import load_single_subclass_from_source
-from core.tools.entities.tool_entities import ToolParameter, ToolProviderCredentials, ToolProviderType
-from core.tools.entities.values import ToolLabelEnum, default_tool_label_dict
-from core.tools.errors import (
- ToolNotFoundError,
- ToolParameterValidationError,
- ToolProviderNotFoundError,
-)
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.tool.tool import Tool
-from core.tools.utils.yaml_utils import load_yaml_file
-
-
-class BuiltinToolProviderController(ToolProviderController):
- def __init__(self, **data: Any) -> None:
- if self.provider_type in {ToolProviderType.API, ToolProviderType.APP}:
- super().__init__(**data)
- return
-
- # load provider yaml
- provider = self.__class__.__module__.split(".")[-1]
- yaml_path = path.join(path.dirname(path.realpath(__file__)), "builtin", provider, f"{provider}.yaml")
- try:
- provider_yaml = load_yaml_file(yaml_path, ignore_error=False)
- except Exception as e:
- raise ToolProviderNotFoundError(f"can not load provider yaml for {provider}: {e}")
-
- if "credentials_for_provider" in provider_yaml and provider_yaml["credentials_for_provider"] is not None:
- # set credentials name
- for credential_name in provider_yaml["credentials_for_provider"]:
- provider_yaml["credentials_for_provider"][credential_name]["name"] = credential_name
-
- super().__init__(
- **{
- "identity": provider_yaml["identity"],
- "credentials_schema": provider_yaml.get("credentials_for_provider", None),
- }
- )
-
- def _get_builtin_tools(self) -> list[Tool]:
- """
- returns a list of tools that the provider can provide
-
- :return: list of tools
- """
- if self.tools:
- return self.tools
- if not self.identity:
- return []
-
- provider = self.identity.name
- tool_path = path.join(path.dirname(path.realpath(__file__)), "builtin", provider, "tools")
- # get all the yaml files in the tool path
- tool_files = list(filter(lambda x: x.endswith(".yaml") and not x.startswith("__"), listdir(tool_path)))
- tools = []
- for tool_file in tool_files:
- # get tool name
- tool_name = tool_file.split(".")[0]
- tool = load_yaml_file(path.join(tool_path, tool_file), ignore_error=False)
-
- # get tool class, import the module
- assistant_tool_class = load_single_subclass_from_source(
- module_name=f"core.tools.provider.builtin.{provider}.tools.{tool_name}",
- script_path=path.join(
- path.dirname(path.realpath(__file__)), "builtin", provider, "tools", f"{tool_name}.py"
- ),
- parent_type=BuiltinTool,
- )
- tool["identity"]["provider"] = provider
- tools.append(assistant_tool_class(**tool))
-
- self.tools = tools
- return tools
-
- def get_credentials_schema(self) -> dict[str, ToolProviderCredentials]:
- """
- returns the credentials schema of the provider
-
- :return: the credentials schema
- """
- if not self.credentials_schema:
- return {}
-
- return self.credentials_schema.copy()
-
- def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]:
- """
- returns a list of tools that the provider can provide
-
- :return: list of tools
- """
- return self._get_builtin_tools()
-
- def get_tool(self, tool_name: str) -> Optional[Tool]:
- """
- returns the tool that the provider can provide
- """
- tools = self.get_tools()
- if tools is None:
- raise ValueError("tools not found")
- return next((t for t in tools if t.identity and t.identity.name == tool_name), None)
-
- def get_parameters(self, tool_name: str) -> list[ToolParameter]:
- """
- returns the parameters of the tool
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :return: list of parameters
- """
- tools = self.get_tools()
- if tools is None:
- raise ToolNotFoundError(f"tool {tool_name} not found")
- tool = next((t for t in tools if t.identity and t.identity.name == tool_name), None)
- if tool is None:
- raise ToolNotFoundError(f"tool {tool_name} not found")
- return tool.parameters or []
-
- @property
- def need_credentials(self) -> bool:
- """
- returns whether the provider needs credentials
-
- :return: whether the provider needs credentials
- """
- return self.credentials_schema is not None and len(self.credentials_schema) != 0
-
- @property
- def provider_type(self) -> ToolProviderType:
- """
- returns the type of the provider
-
- :return: type of the provider
- """
- return ToolProviderType.BUILT_IN
-
- @property
- def tool_labels(self) -> list[str]:
- """
- returns the labels of the provider
-
- :return: labels of the provider
- """
- label_enums = self._get_tool_labels()
- return [default_tool_label_dict[label].name for label in label_enums]
-
- def _get_tool_labels(self) -> list[ToolLabelEnum]:
- """
- returns the labels of the provider
- """
- if self.identity is None:
- return []
- return self.identity.tags or []
-
- def validate_parameters(self, tool_id: int, tool_name: str, tool_parameters: dict[str, Any]) -> None:
- """
- validate the parameters of the tool and set the default value if needed
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :param tool_parameters: the parameters of the tool
- """
- tool_parameters_schema = self.get_parameters(tool_name)
-
- tool_parameters_need_to_validate: dict[str, ToolParameter] = {}
- for parameter in tool_parameters_schema:
- tool_parameters_need_to_validate[parameter.name] = parameter
-
- for parameter_name in tool_parameters:
- if parameter_name not in tool_parameters_need_to_validate:
- raise ToolParameterValidationError(f"parameter {parameter_name} not found in tool {tool_name}")
-
- # check type
- parameter_schema = tool_parameters_need_to_validate[parameter_name]
- if parameter_schema.type == ToolParameter.ToolParameterType.STRING:
- if not isinstance(tool_parameters[parameter_name], str):
- raise ToolParameterValidationError(f"parameter {parameter_name} should be string")
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.NUMBER:
- if not isinstance(tool_parameters[parameter_name], int | float):
- raise ToolParameterValidationError(f"parameter {parameter_name} should be number")
-
- if parameter_schema.min is not None and tool_parameters[parameter_name] < parameter_schema.min:
- raise ToolParameterValidationError(
- f"parameter {parameter_name} should be greater than {parameter_schema.min}"
- )
-
- if parameter_schema.max is not None and tool_parameters[parameter_name] > parameter_schema.max:
- raise ToolParameterValidationError(
- f"parameter {parameter_name} should be less than {parameter_schema.max}"
- )
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.BOOLEAN:
- if not isinstance(tool_parameters[parameter_name], bool):
- raise ToolParameterValidationError(f"parameter {parameter_name} should be boolean")
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.SELECT:
- if not isinstance(tool_parameters[parameter_name], str):
- raise ToolParameterValidationError(f"parameter {parameter_name} should be string")
-
- options = parameter_schema.options
- if not isinstance(options, list):
- raise ToolParameterValidationError(f"parameter {parameter_name} options should be list")
-
- if tool_parameters[parameter_name] not in [x.value for x in options]:
- raise ToolParameterValidationError(f"parameter {parameter_name} should be one of {options}")
-
- tool_parameters_need_to_validate.pop(parameter_name)
-
- for parameter_name in tool_parameters_need_to_validate:
- parameter_schema = tool_parameters_need_to_validate[parameter_name]
- if parameter_schema.required:
- raise ToolParameterValidationError(f"parameter {parameter_name} is required")
-
- # the parameter is not set currently, set the default value if needed
- if parameter_schema.default is not None:
- default_value = parameter_schema.type.cast_value(parameter_schema.default)
- tool_parameters[parameter_name] = default_value
-
- def validate_credentials(self, credentials: dict[str, Any]) -> None:
- """
- validate the credentials of the provider
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :param credentials: the credentials of the tool
- """
- # validate credentials format
- self.validate_credentials_format(credentials)
-
- # validate credentials
- self._validate_credentials(credentials)
-
- @abstractmethod
- def _validate_credentials(self, credentials: dict[str, Any]) -> None:
- """
- validate the credentials of the provider
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :param credentials: the credentials of the tool
- """
- pass
diff --git a/api/core/tools/provider/tool_provider.py b/api/core/tools/provider/tool_provider.py
deleted file mode 100644
index e35207e4f06404..00000000000000
--- a/api/core/tools/provider/tool_provider.py
+++ /dev/null
@@ -1,201 +0,0 @@
-from abc import ABC, abstractmethod
-from typing import Any, Optional
-
-from pydantic import BaseModel
-
-from core.tools.entities.tool_entities import (
- ToolParameter,
- ToolProviderCredentials,
- ToolProviderIdentity,
- ToolProviderType,
-)
-from core.tools.errors import ToolNotFoundError, ToolParameterValidationError, ToolProviderCredentialValidationError
-from core.tools.tool.tool import Tool
-
-
-class ToolProviderController(BaseModel, ABC):
- identity: Optional[ToolProviderIdentity] = None
- tools: Optional[list[Tool]] = None
- credentials_schema: Optional[dict[str, ToolProviderCredentials]] = None
-
- def get_credentials_schema(self) -> dict[str, ToolProviderCredentials]:
- """
- returns the credentials schema of the provider
-
- :return: the credentials schema
- """
- if self.credentials_schema is None:
- return {}
- return self.credentials_schema.copy()
-
- @abstractmethod
- def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]:
- """
- returns a list of tools that the provider can provide
-
- :return: list of tools
- """
- pass
-
- @abstractmethod
- def get_tool(self, tool_name: str) -> Optional[Tool]:
- """
- returns a tool that the provider can provide
-
- :return: tool
- """
- pass
-
- def get_parameters(self, tool_name: str) -> list[ToolParameter]:
- """
- returns the parameters of the tool
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :return: list of parameters
- """
- tools = self.get_tools()
- if tools is None:
- raise ToolNotFoundError(f"tool {tool_name} not found")
- tool = next((t for t in tools if t.identity and t.identity.name == tool_name), None)
- if tool is None:
- raise ToolNotFoundError(f"tool {tool_name} not found")
- return tool.parameters or []
-
- @property
- def provider_type(self) -> ToolProviderType:
- """
- returns the type of the provider
-
- :return: type of the provider
- """
- return ToolProviderType.BUILT_IN
-
- def validate_parameters(self, tool_id: int, tool_name: str, tool_parameters: dict[str, Any]) -> None:
- """
- validate the parameters of the tool and set the default value if needed
-
- :param tool_name: the name of the tool, defined in `get_tools`
- :param tool_parameters: the parameters of the tool
- """
- tool_parameters_schema = self.get_parameters(tool_name)
-
- tool_parameters_need_to_validate: dict[str, ToolParameter] = {}
- for parameter in tool_parameters_schema:
- tool_parameters_need_to_validate[parameter.name] = parameter
-
- for tool_parameter in tool_parameters:
- if tool_parameter not in tool_parameters_need_to_validate:
- raise ToolParameterValidationError(f"parameter {tool_parameter} not found in tool {tool_name}")
-
- # check type
- parameter_schema = tool_parameters_need_to_validate[tool_parameter]
- if parameter_schema.type == ToolParameter.ToolParameterType.STRING:
- if not isinstance(tool_parameters[tool_parameter], str):
- raise ToolParameterValidationError(f"parameter {tool_parameter} should be string")
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.NUMBER:
- if not isinstance(tool_parameters[tool_parameter], int | float):
- raise ToolParameterValidationError(f"parameter {tool_parameter} should be number")
-
- if parameter_schema.min is not None and tool_parameters[tool_parameter] < parameter_schema.min:
- raise ToolParameterValidationError(
- f"parameter {tool_parameter} should be greater than {parameter_schema.min}"
- )
-
- if parameter_schema.max is not None and tool_parameters[tool_parameter] > parameter_schema.max:
- raise ToolParameterValidationError(
- f"parameter {tool_parameter} should be less than {parameter_schema.max}"
- )
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.BOOLEAN:
- if not isinstance(tool_parameters[tool_parameter], bool):
- raise ToolParameterValidationError(f"parameter {tool_parameter} should be boolean")
-
- elif parameter_schema.type == ToolParameter.ToolParameterType.SELECT:
- if not isinstance(tool_parameters[tool_parameter], str):
- raise ToolParameterValidationError(f"parameter {tool_parameter} should be string")
-
- options = parameter_schema.options
- if not isinstance(options, list):
- raise ToolParameterValidationError(f"parameter {tool_parameter} options should be list")
-
- if tool_parameters[tool_parameter] not in [x.value for x in options]:
- raise ToolParameterValidationError(f"parameter {tool_parameter} should be one of {options}")
-
- tool_parameters_need_to_validate.pop(tool_parameter)
-
- for tool_parameter_validate in tool_parameters_need_to_validate:
- parameter_schema = tool_parameters_need_to_validate[tool_parameter_validate]
- if parameter_schema.required:
- raise ToolParameterValidationError(f"parameter {tool_parameter_validate} is required")
-
- # the parameter is not set currently, set the default value if needed
- if parameter_schema.default is not None:
- tool_parameters[tool_parameter_validate] = parameter_schema.type.cast_value(parameter_schema.default)
-
- def validate_credentials_format(self, credentials: dict[str, Any]) -> None:
- """
- validate the format of the credentials of the provider and set the default value if needed
-
- :param credentials: the credentials of the tool
- """
- credentials_schema = self.credentials_schema
- if credentials_schema is None:
- return
-
- credentials_need_to_validate: dict[str, ToolProviderCredentials] = {}
- for credential_name in credentials_schema:
- credentials_need_to_validate[credential_name] = credentials_schema[credential_name]
-
- for credential_name in credentials:
- if credential_name not in credentials_need_to_validate:
- if self.identity is None:
- raise ValueError("identity is not set")
- raise ToolProviderCredentialValidationError(
- f"credential {credential_name} not found in provider {self.identity.name}"
- )
-
- # check type
- credential_schema = credentials_need_to_validate[credential_name]
- if not credential_schema.required and credentials[credential_name] is None:
- continue
-
- if credential_schema.type in {
- ToolProviderCredentials.CredentialsType.SECRET_INPUT,
- ToolProviderCredentials.CredentialsType.TEXT_INPUT,
- }:
- if not isinstance(credentials[credential_name], str):
- raise ToolProviderCredentialValidationError(f"credential {credential_name} should be string")
-
- elif credential_schema.type == ToolProviderCredentials.CredentialsType.SELECT:
- if not isinstance(credentials[credential_name], str):
- raise ToolProviderCredentialValidationError(f"credential {credential_name} should be string")
-
- options = credential_schema.options
- if not isinstance(options, list):
- raise ToolProviderCredentialValidationError(f"credential {credential_name} options should be list")
-
- if credentials[credential_name] not in [x.value for x in options]:
- raise ToolProviderCredentialValidationError(
- f"credential {credential_name} should be one of {options}"
- )
-
- credentials_need_to_validate.pop(credential_name)
-
- for credential_name in credentials_need_to_validate:
- credential_schema = credentials_need_to_validate[credential_name]
- if credential_schema.required:
- raise ToolProviderCredentialValidationError(f"credential {credential_name} is required")
-
- # the credential is not set currently, set the default value if needed
- if credential_schema.default is not None:
- default_value = credential_schema.default
- # parse default value into the correct type
- if credential_schema.type in {
- ToolProviderCredentials.CredentialsType.SECRET_INPUT,
- ToolProviderCredentials.CredentialsType.TEXT_INPUT,
- ToolProviderCredentials.CredentialsType.SELECT,
- }:
- default_value = str(default_value)
-
- credentials[credential_name] = default_value
diff --git a/api/core/tools/provider/workflow_tool_provider.py b/api/core/tools/provider/workflow_tool_provider.py
deleted file mode 100644
index 17fe2e20cf282e..00000000000000
--- a/api/core/tools/provider/workflow_tool_provider.py
+++ /dev/null
@@ -1,208 +0,0 @@
-from typing import Optional
-
-from core.app.app_config.entities import VariableEntityType
-from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import (
- ToolDescription,
- ToolIdentity,
- ToolParameter,
- ToolParameterOption,
- ToolProviderType,
-)
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.tool.tool import Tool
-from core.tools.tool.workflow_tool import WorkflowTool
-from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils
-from extensions.ext_database import db
-from models.model import App, AppMode
-from models.tools import WorkflowToolProvider
-from models.workflow import Workflow
-
-VARIABLE_TO_PARAMETER_TYPE_MAPPING = {
- VariableEntityType.TEXT_INPUT: ToolParameter.ToolParameterType.STRING,
- VariableEntityType.PARAGRAPH: ToolParameter.ToolParameterType.STRING,
- VariableEntityType.SELECT: ToolParameter.ToolParameterType.SELECT,
- VariableEntityType.NUMBER: ToolParameter.ToolParameterType.NUMBER,
- VariableEntityType.FILE: ToolParameter.ToolParameterType.FILE,
- VariableEntityType.FILE_LIST: ToolParameter.ToolParameterType.FILES,
-}
-
-
-class WorkflowToolProviderController(ToolProviderController):
- provider_id: str
-
- @classmethod
- def from_db(cls, db_provider: WorkflowToolProvider) -> "WorkflowToolProviderController":
- app = db_provider.app
-
- if not app:
- raise ValueError("app not found")
-
- controller = WorkflowToolProviderController.model_validate(
- {
- "identity": {
- "author": db_provider.user.name if db_provider.user_id and db_provider.user else "",
- "name": db_provider.label,
- "label": {"en_US": db_provider.label, "zh_Hans": db_provider.label},
- "description": {"en_US": db_provider.description, "zh_Hans": db_provider.description},
- "icon": db_provider.icon,
- },
- "credentials_schema": {},
- "provider_id": db_provider.id or "",
- }
- )
-
- # init tools
-
- controller.tools = [controller._get_db_provider_tool(db_provider, app)]
-
- return controller
-
- @property
- def provider_type(self) -> ToolProviderType:
- return ToolProviderType.WORKFLOW
-
- def _get_db_provider_tool(self, db_provider: WorkflowToolProvider, app: App) -> WorkflowTool:
- """
- get db provider tool
- :param db_provider: the db provider
- :param app: the app
- :return: the tool
- """
- workflow = (
- db.session.query(Workflow)
- .filter(Workflow.app_id == db_provider.app_id, Workflow.version == db_provider.version)
- .first()
- )
- if not workflow:
- raise ValueError("workflow not found")
-
- # fetch start node
- graph = workflow.graph_dict
- features_dict = workflow.features_dict
- features = WorkflowAppConfigManager.convert_features(config_dict=features_dict, app_mode=AppMode.WORKFLOW)
-
- parameters = db_provider.parameter_configurations
- variables = WorkflowToolConfigurationUtils.get_workflow_graph_variables(graph)
-
- def fetch_workflow_variable(variable_name: str):
- return next(filter(lambda x: x.variable == variable_name, variables), None)
-
- user = db_provider.user
-
- workflow_tool_parameters = []
- for parameter in parameters:
- variable = fetch_workflow_variable(parameter.name)
- if variable:
- parameter_type = None
- options = None
- if variable.type not in VARIABLE_TO_PARAMETER_TYPE_MAPPING:
- raise ValueError(f"unsupported variable type {variable.type}")
- parameter_type = VARIABLE_TO_PARAMETER_TYPE_MAPPING[variable.type]
-
- if variable.type == VariableEntityType.SELECT and variable.options:
- options = [
- ToolParameterOption(value=option, label=I18nObject(en_US=option, zh_Hans=option))
- for option in variable.options
- ]
-
- workflow_tool_parameters.append(
- ToolParameter(
- name=parameter.name,
- label=I18nObject(en_US=variable.label, zh_Hans=variable.label),
- human_description=I18nObject(en_US=parameter.description, zh_Hans=parameter.description),
- type=parameter_type,
- form=parameter.form,
- llm_description=parameter.description,
- required=variable.required,
- options=options,
- placeholder=I18nObject(en_US="", zh_Hans=""),
- )
- )
- elif features.file_upload:
- workflow_tool_parameters.append(
- ToolParameter(
- name=parameter.name,
- label=I18nObject(en_US=parameter.name, zh_Hans=parameter.name),
- human_description=I18nObject(en_US=parameter.description, zh_Hans=parameter.description),
- type=ToolParameter.ToolParameterType.SYSTEM_FILES,
- llm_description=parameter.description,
- required=False,
- form=parameter.form,
- placeholder=I18nObject(en_US="", zh_Hans=""),
- )
- )
- else:
- raise ValueError("variable not found")
-
- return WorkflowTool(
- identity=ToolIdentity(
- author=user.name if user else "",
- name=db_provider.name,
- label=I18nObject(en_US=db_provider.label, zh_Hans=db_provider.label),
- provider=self.provider_id,
- icon=db_provider.icon,
- ),
- description=ToolDescription(
- human=I18nObject(en_US=db_provider.description, zh_Hans=db_provider.description),
- llm=db_provider.description,
- ),
- parameters=workflow_tool_parameters,
- is_team_authorization=True,
- workflow_app_id=app.id,
- workflow_entities={
- "app": app,
- "workflow": workflow,
- },
- version=db_provider.version,
- workflow_call_depth=0,
- label=db_provider.label,
- )
-
- def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]:
- """
- fetch tools from database
-
- :param user_id: the user id
- :param tenant_id: the tenant id
- :return: the tools
- """
- if self.tools is not None:
- return self.tools
-
- db_providers: Optional[WorkflowToolProvider] = (
- db.session.query(WorkflowToolProvider)
- .filter(
- WorkflowToolProvider.tenant_id == tenant_id,
- WorkflowToolProvider.app_id == self.provider_id,
- )
- .first()
- )
-
- if not db_providers:
- return []
- if not db_providers.app:
- raise ValueError("app not found")
-
- self.tools = [self._get_db_provider_tool(db_providers, db_providers.app)]
-
- return self.tools
-
- def get_tool(self, tool_name: str) -> Optional[Tool]:
- """
- get tool by name
-
- :param tool_name: the name of the tool
- :return: the tool
- """
- if self.tools is None:
- return None
-
- for tool in self.tools:
- if tool.identity is None:
- continue
- if tool.identity.name == tool_name:
- return tool
-
- return None
diff --git a/api/core/tools/tool/api_tool.py b/api/core/tools/tool/api_tool.py
deleted file mode 100644
index 6904fecb46698b..00000000000000
--- a/api/core/tools/tool/api_tool.py
+++ /dev/null
@@ -1,322 +0,0 @@
-import json
-from os import getenv
-from typing import Any
-from urllib.parse import urlencode
-
-import httpx
-
-from core.file.file_manager import download
-from core.helper import ssrf_proxy
-from core.tools.entities.tool_bundle import ApiToolBundle
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType
-from core.tools.errors import ToolInvokeError, ToolParameterValidationError, ToolProviderCredentialValidationError
-from core.tools.tool.tool import Tool
-
-API_TOOL_DEFAULT_TIMEOUT = (
- int(getenv("API_TOOL_DEFAULT_CONNECT_TIMEOUT", "10")),
- int(getenv("API_TOOL_DEFAULT_READ_TIMEOUT", "60")),
-)
-
-
-class ApiTool(Tool):
- api_bundle: ApiToolBundle
-
- """
- Api tool
- """
-
- def fork_tool_runtime(self, runtime: dict[str, Any]) -> "Tool":
- """
- fork a new tool with meta data
-
- :param meta: the meta data of a tool call processing, tenant_id is required
- :return: the new tool
- """
- if self.api_bundle is None:
- raise ValueError("api_bundle is required")
- return self.__class__(
- identity=self.identity.model_copy() if self.identity else None,
- parameters=self.parameters.copy() if self.parameters else None,
- description=self.description.model_copy() if self.description else None,
- api_bundle=self.api_bundle.model_copy(),
- runtime=Tool.Runtime(**runtime),
- )
-
- def validate_credentials(
- self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False
- ) -> str:
- """
- validate the credentials for Api tool
- """
- # assemble validate request and request parameters
- headers = self.assembling_request(parameters)
-
- if format_only:
- return ""
-
- response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, parameters)
- # validate response
- return self.validate_and_parse_response(response)
-
- def tool_provider_type(self) -> ToolProviderType:
- return ToolProviderType.API
-
- def assembling_request(self, parameters: dict[str, Any]) -> dict[str, Any]:
- headers = {}
- if self.runtime is None:
- raise ValueError("runtime is required")
- credentials = self.runtime.credentials or {}
-
- if "auth_type" not in credentials:
- raise ToolProviderCredentialValidationError("Missing auth_type")
-
- if credentials["auth_type"] == "api_key":
- api_key_header = "api_key"
-
- if "api_key_header" in credentials:
- api_key_header = credentials["api_key_header"]
-
- if "api_key_value" not in credentials:
- raise ToolProviderCredentialValidationError("Missing api_key_value")
- elif not isinstance(credentials["api_key_value"], str):
- raise ToolProviderCredentialValidationError("api_key_value must be a string")
-
- if "api_key_header_prefix" in credentials:
- api_key_header_prefix = credentials["api_key_header_prefix"]
- if api_key_header_prefix == "basic" and credentials["api_key_value"]:
- credentials["api_key_value"] = f"Basic {credentials['api_key_value']}"
- elif api_key_header_prefix == "bearer" and credentials["api_key_value"]:
- credentials["api_key_value"] = f"Bearer {credentials['api_key_value']}"
- elif api_key_header_prefix == "custom":
- pass
-
- headers[api_key_header] = credentials["api_key_value"]
-
- needed_parameters = [parameter for parameter in (self.api_bundle.parameters or []) if parameter.required]
- for parameter in needed_parameters:
- if parameter.required and parameter.name not in parameters:
- raise ToolParameterValidationError(f"Missing required parameter {parameter.name}")
-
- if parameter.default is not None and parameter.name not in parameters:
- parameters[parameter.name] = parameter.default
-
- return headers
-
- def validate_and_parse_response(self, response: httpx.Response) -> str:
- """
- validate the response
- """
- if isinstance(response, httpx.Response):
- if response.status_code >= 400:
- raise ToolInvokeError(f"Request failed with status code {response.status_code} and {response.text}")
- if not response.content:
- return "Empty response from the tool, please check your parameters and try again."
- try:
- response = response.json()
- try:
- return json.dumps(response, ensure_ascii=False)
- except Exception as e:
- return json.dumps(response)
- except Exception as e:
- return response.text
- else:
- raise ValueError(f"Invalid response type {type(response)}")
-
- @staticmethod
- def get_parameter_value(parameter, parameters):
- if parameter["name"] in parameters:
- return parameters[parameter["name"]]
- elif parameter.get("required", False):
- raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
- else:
- return (parameter.get("schema", {}) or {}).get("default", "")
-
- def do_http_request(
- self, url: str, method: str, headers: dict[str, Any], parameters: dict[str, Any]
- ) -> httpx.Response:
- """
- do http request depending on api bundle
- """
- method = method.lower()
-
- params = {}
- path_params = {}
- # FIXME: body should be a dict[str, Any] but it changed a lot in this function
- body: Any = {}
- cookies = {}
- files = []
-
- # check parameters
- for parameter in self.api_bundle.openapi.get("parameters", []):
- value = self.get_parameter_value(parameter, parameters)
- if parameter["in"] == "path":
- path_params[parameter["name"]] = value
-
- elif parameter["in"] == "query":
- if value != "":
- params[parameter["name"]] = value
-
- elif parameter["in"] == "cookie":
- cookies[parameter["name"]] = value
-
- elif parameter["in"] == "header":
- headers[parameter["name"]] = value
-
- # check if there is a request body and handle it
- if "requestBody" in self.api_bundle.openapi and self.api_bundle.openapi["requestBody"] is not None:
- # handle json request body
- if "content" in self.api_bundle.openapi["requestBody"]:
- for content_type in self.api_bundle.openapi["requestBody"]["content"]:
- headers["Content-Type"] = content_type
- body_schema = self.api_bundle.openapi["requestBody"]["content"][content_type]["schema"]
- required = body_schema.get("required", [])
- properties = body_schema.get("properties", {})
- for name, property in properties.items():
- if name in parameters:
- if property.get("format") == "binary":
- f = parameters[name]
- files.append((name, (f.filename, download(f), f.mime_type)))
- else:
- # convert type
- body[name] = self._convert_body_property_type(property, parameters[name])
- elif name in required:
- raise ToolParameterValidationError(
- f"Missing required parameter {name} in operation {self.api_bundle.operation_id}"
- )
- elif "default" in property:
- body[name] = property["default"]
- else:
- body[name] = None
- break
-
- # replace path parameters
- for name, value in path_params.items():
- url = url.replace(f"{{{name}}}", f"{value}")
-
- # parse http body data if needed
- if "Content-Type" in headers:
- if headers["Content-Type"] == "application/json":
- body = json.dumps(body)
- elif headers["Content-Type"] == "application/x-www-form-urlencoded":
- body = urlencode(body)
- else:
- body = body
-
- if method in {
- "get",
- "head",
- "post",
- "put",
- "delete",
- "patch",
- "options",
- "GET",
- "POST",
- "PUT",
- "PATCH",
- "DELETE",
- "HEAD",
- "OPTIONS",
- }:
- response: httpx.Response = getattr(ssrf_proxy, method.lower())(
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- data=body,
- files=files,
- timeout=API_TOOL_DEFAULT_TIMEOUT,
- follow_redirects=True,
- )
- return response
- else:
- raise ValueError(f"Invalid http method {method}")
-
- def _convert_body_property_any_of(
- self, property: dict[str, Any], value: Any, any_of: list[dict[str, Any]], max_recursive=10
- ) -> Any:
- if max_recursive <= 0:
- raise Exception("Max recursion depth reached")
- for option in any_of or []:
- try:
- if "type" in option:
- # Attempt to convert the value based on the type.
- if option["type"] == "integer" or option["type"] == "int":
- return int(value)
- elif option["type"] == "number":
- if "." in str(value):
- return float(value)
- else:
- return int(value)
- elif option["type"] == "string":
- return str(value)
- elif option["type"] == "boolean":
- if str(value).lower() in {"true", "1"}:
- return True
- elif str(value).lower() in {"false", "0"}:
- return False
- else:
- continue # Not a boolean, try next option
- elif option["type"] == "null" and not value:
- return None
- else:
- continue # Unsupported type, try next option
- elif "anyOf" in option and isinstance(option["anyOf"], list):
- # Recursive call to handle nested anyOf
- return self._convert_body_property_any_of(property, value, option["anyOf"], max_recursive - 1)
- except ValueError:
- continue # Conversion failed, try next option
- # If no option succeeded, you might want to return the value as is or raise an error
- return value # or raise ValueError(f"Cannot convert value '{value}' to any specified type in anyOf")
-
- def _convert_body_property_type(self, property: dict[str, Any], value: Any) -> Any:
- try:
- if "type" in property:
- if property["type"] == "integer" or property["type"] == "int":
- return int(value)
- elif property["type"] == "number":
- # check if it is a float
- if "." in str(value):
- return float(value)
- else:
- return int(value)
- elif property["type"] == "string":
- return str(value)
- elif property["type"] == "boolean":
- return bool(value)
- elif property["type"] == "null":
- if value is None:
- return None
- elif property["type"] == "object" or property["type"] == "array":
- if isinstance(value, str):
- try:
- return json.loads(value)
- except ValueError:
- return value
- elif isinstance(value, dict):
- return value
- else:
- return value
- else:
- raise ValueError(f"Invalid type {property['type']} for property {property}")
- elif "anyOf" in property and isinstance(property["anyOf"], list):
- return self._convert_body_property_any_of(property, value, property["anyOf"])
- except ValueError as e:
- return value
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- invoke http request
- """
- response: httpx.Response | str = ""
- # assemble request
- headers = self.assembling_request(tool_parameters)
-
- # do http request
- response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, tool_parameters)
-
- # validate response
- response = self.validate_and_parse_response(response)
-
- # assemble invoke message
- return self.create_text_message(response)
diff --git a/api/core/tools/tool/builtin_tool.py b/api/core/tools/tool/builtin_tool.py
deleted file mode 100644
index adda4297f38e8a..00000000000000
--- a/api/core/tools/tool/builtin_tool.py
+++ /dev/null
@@ -1,144 +0,0 @@
-from typing import Optional, cast
-
-from core.model_runtime.entities.llm_entities import LLMResult
-from core.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage
-from core.tools.entities.tool_entities import ToolProviderType
-from core.tools.tool.tool import Tool
-from core.tools.utils.model_invocation_utils import ModelInvocationUtils
-from core.tools.utils.web_reader_tool import get_url
-
-_SUMMARY_PROMPT = """You are a professional language researcher, you are interested in the language
-and you can quickly aimed at the main point of an webpage and reproduce it in your own words but
-retain the original meaning and keep the key points.
-however, the text you got is too long, what you got is possible a part of the text.
-Please summarize the text you got.
-"""
-
-
-class BuiltinTool(Tool):
- """
- Builtin tool
-
- :param meta: the meta data of a tool call processing
- """
-
- def invoke_model(self, user_id: str, prompt_messages: list[PromptMessage], stop: list[str]) -> LLMResult:
- """
- invoke model
-
- :param model_config: the model config
- :param prompt_messages: the prompt messages
- :param stop: the stop words
- :return: the model result
- """
- # invoke model
- if self.runtime is None or self.identity is None:
- raise ValueError("runtime and identity are required")
-
- return ModelInvocationUtils.invoke(
- user_id=user_id,
- tenant_id=self.runtime.tenant_id or "",
- tool_type="builtin",
- tool_name=self.identity.name,
- prompt_messages=prompt_messages,
- )
-
- def tool_provider_type(self) -> ToolProviderType:
- return ToolProviderType.BUILT_IN
-
- def get_max_tokens(self) -> int:
- """
- get max tokens
-
- :param model_config: the model config
- :return: the max tokens
- """
- if self.runtime is None:
- raise ValueError("runtime is required")
-
- return ModelInvocationUtils.get_max_llm_context_tokens(
- tenant_id=self.runtime.tenant_id or "",
- )
-
- def get_prompt_tokens(self, prompt_messages: list[PromptMessage]) -> int:
- """
- get prompt tokens
-
- :param prompt_messages: the prompt messages
- :return: the tokens
- """
- if self.runtime is None:
- raise ValueError("runtime is required")
-
- return ModelInvocationUtils.calculate_tokens(
- tenant_id=self.runtime.tenant_id or "", prompt_messages=prompt_messages
- )
-
- def summary(self, user_id: str, content: str) -> str:
- max_tokens = self.get_max_tokens()
-
- if self.get_prompt_tokens(prompt_messages=[UserPromptMessage(content=content)]) < max_tokens * 0.6:
- return content
-
- def get_prompt_tokens(content: str) -> int:
- return self.get_prompt_tokens(
- prompt_messages=[SystemPromptMessage(content=_SUMMARY_PROMPT), UserPromptMessage(content=content)]
- )
-
- def summarize(content: str) -> str:
- summary = self.invoke_model(
- user_id=user_id,
- prompt_messages=[SystemPromptMessage(content=_SUMMARY_PROMPT), UserPromptMessage(content=content)],
- stop=[],
- )
-
- return cast(str, summary.message.content)
-
- lines = content.split("\n")
- new_lines = []
- # split long line into multiple lines
- for i in range(len(lines)):
- line = lines[i]
- if not line.strip():
- continue
- if len(line) < max_tokens * 0.5:
- new_lines.append(line)
- elif get_prompt_tokens(line) > max_tokens * 0.7:
- while get_prompt_tokens(line) > max_tokens * 0.7:
- new_lines.append(line[: int(max_tokens * 0.5)])
- line = line[int(max_tokens * 0.5) :]
- new_lines.append(line)
- else:
- new_lines.append(line)
-
- # merge lines into messages with max tokens
- messages: list[str] = []
- for j in new_lines:
- if len(messages) == 0:
- messages.append(j)
- else:
- if len(messages[-1]) + len(j) < max_tokens * 0.5:
- messages[-1] += j
- if get_prompt_tokens(messages[-1] + j) > max_tokens * 0.7:
- messages.append(j)
- else:
- messages[-1] += j
-
- summaries = []
- for i in range(len(messages)):
- message = messages[i]
- summary = summarize(message)
- summaries.append(summary)
-
- result = "\n".join(summaries)
-
- if self.get_prompt_tokens(prompt_messages=[UserPromptMessage(content=result)]) > max_tokens * 0.7:
- return self.summary(user_id=user_id, content=result)
-
- return result
-
- def get_url(self, url: str, user_agent: Optional[str] = None) -> str:
- """
- get url
- """
- return get_url(url, user_agent=user_agent)
diff --git a/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py b/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py
deleted file mode 100644
index b382016473055d..00000000000000
--- a/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py
+++ /dev/null
@@ -1,196 +0,0 @@
-from typing import Any
-
-from pydantic import BaseModel, Field
-
-from core.rag.datasource.retrieval_service import RetrievalService
-from core.rag.models.document import Document as RetrievalDocument
-from core.rag.retrieval.retrieval_methods import RetrievalMethod
-from core.tools.tool.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
-from extensions.ext_database import db
-from models.dataset import Dataset, Document, DocumentSegment
-from services.external_knowledge_service import ExternalDatasetService
-
-default_retrieval_model = {
- "search_method": RetrievalMethod.SEMANTIC_SEARCH.value,
- "reranking_enable": False,
- "reranking_model": {"reranking_provider_name": "", "reranking_model_name": ""},
- "reranking_mode": "reranking_model",
- "top_k": 2,
- "score_threshold_enabled": False,
-}
-
-
-class DatasetRetrieverToolInput(BaseModel):
- query: str = Field(..., description="Query for the dataset to be used to retrieve the dataset.")
-
-
-class DatasetRetrieverTool(DatasetRetrieverBaseTool):
- """Tool for querying a Dataset."""
-
- name: str = "dataset"
- args_schema: type[BaseModel] = DatasetRetrieverToolInput
- description: str = "use this to retrieve a dataset. "
- dataset_id: str
-
- @classmethod
- def from_dataset(cls, dataset: Dataset, **kwargs):
- description = dataset.description
- if not description:
- description = "useful for when you want to answer queries about the " + dataset.name
-
- description = description.replace("\n", "").replace("\r", "")
- return cls(
- name=f"dataset_{dataset.id.replace('-', '_')}",
- tenant_id=dataset.tenant_id,
- dataset_id=dataset.id,
- description=description,
- **kwargs,
- )
-
- def _run(self, query: str) -> str:
- dataset = (
- db.session.query(Dataset).filter(Dataset.tenant_id == self.tenant_id, Dataset.id == self.dataset_id).first()
- )
-
- if not dataset:
- return ""
-
- for hit_callback in self.hit_callbacks:
- hit_callback.on_query(query, dataset.id)
- if dataset.provider == "external":
- results = []
- external_documents = ExternalDatasetService.fetch_external_knowledge_retrieval(
- tenant_id=dataset.tenant_id,
- dataset_id=dataset.id,
- query=query,
- external_retrieval_parameters=dataset.retrieval_model,
- )
- for external_document in external_documents:
- document = RetrievalDocument(
- page_content=external_document.get("content"),
- metadata=external_document.get("metadata"),
- provider="external",
- )
- if document.metadata is not None:
- document.metadata["score"] = external_document.get("score")
- document.metadata["title"] = external_document.get("title")
- document.metadata["dataset_id"] = dataset.id
- document.metadata["dataset_name"] = dataset.name
- results.append(document)
- # deal with external documents
- context_list = []
- for position, item in enumerate(results, start=1):
- if item.metadata is not None:
- source = {
- "position": position,
- "dataset_id": item.metadata.get("dataset_id"),
- "dataset_name": item.metadata.get("dataset_name"),
- "document_name": item.metadata.get("title"),
- "data_source_type": "external",
- "retriever_from": self.retriever_from,
- "score": item.metadata.get("score"),
- "title": item.metadata.get("title"),
- "content": item.page_content,
- }
- context_list.append(source)
- for hit_callback in self.hit_callbacks:
- hit_callback.return_retriever_resource_info(context_list)
-
- return str("\n".join([item.page_content for item in results]))
- else:
- # get retrieval model , if the model is not setting , using default
- retrieval_model: dict[str, Any] = dataset.retrieval_model or default_retrieval_model
- if dataset.indexing_technique == "economy":
- # use keyword table query
- documents = RetrievalService.retrieve(
- retrieval_method="keyword_search", dataset_id=dataset.id, query=query, top_k=self.top_k
- )
- return str("\n".join([document.page_content for document in documents]))
- else:
- if self.top_k > 0:
- # retrieval source
- documents = RetrievalService.retrieve(
- retrieval_method=retrieval_model.get("search_method", "semantic_search"),
- dataset_id=dataset.id,
- query=query,
- top_k=self.top_k,
- score_threshold=retrieval_model.get("score_threshold", 0.0)
- if retrieval_model["score_threshold_enabled"]
- else 0.0,
- reranking_model=retrieval_model.get("reranking_model")
- if retrieval_model["reranking_enable"]
- else None,
- reranking_mode=retrieval_model.get("reranking_mode") or "reranking_model",
- weights=retrieval_model.get("weights"),
- )
- else:
- documents = []
-
- for hit_callback in self.hit_callbacks:
- hit_callback.on_tool_end(documents)
- document_score_list = {}
- if dataset.indexing_technique != "economy":
- for item in documents:
- if item.metadata is not None and item.metadata.get("score"):
- document_score_list[item.metadata["doc_id"]] = item.metadata["score"]
- document_context_list = []
- index_node_ids = [document.metadata["doc_id"] for document in documents]
- segments = DocumentSegment.query.filter(
- DocumentSegment.dataset_id == self.dataset_id,
- DocumentSegment.completed_at.isnot(None),
- DocumentSegment.status == "completed",
- DocumentSegment.enabled == True,
- DocumentSegment.index_node_id.in_(index_node_ids),
- ).all()
-
- if segments:
- index_node_id_to_position = {id: position for position, id in enumerate(index_node_ids)}
- sorted_segments = sorted(
- segments, key=lambda segment: index_node_id_to_position.get(segment.index_node_id, float("inf"))
- )
- for segment in sorted_segments:
- if segment.answer:
- document_context_list.append(
- f"question:{segment.get_sign_content()} answer:{segment.answer}"
- )
- else:
- document_context_list.append(segment.get_sign_content())
- if self.return_resource:
- context_list = []
- resource_number = 1
- for segment in sorted_segments:
- document_segment = Document.query.filter(
- Document.id == segment.document_id,
- Document.enabled == True,
- Document.archived == False,
- ).first()
- if not document_segment:
- continue
- if dataset and document_segment:
- source = {
- "position": resource_number,
- "dataset_id": dataset.id,
- "dataset_name": dataset.name,
- "document_id": document_segment.id,
- "document_name": document_segment.name,
- "data_source_type": document_segment.data_source_type,
- "segment_id": segment.id,
- "retriever_from": self.retriever_from,
- "score": document_score_list.get(segment.index_node_id, None),
- }
- if self.retriever_from == "dev":
- source["hit_count"] = segment.hit_count
- source["word_count"] = segment.word_count
- source["segment_position"] = segment.position
- source["index_node_hash"] = segment.index_node_hash
- if segment.answer:
- source["content"] = f"question:{segment.content} \nanswer:{segment.answer}"
- else:
- source["content"] = segment.content
- context_list.append(source)
- resource_number += 1
-
- for hit_callback in self.hit_callbacks:
- hit_callback.return_retriever_resource_info(context_list)
-
- return str("\n".join(document_context_list))
diff --git a/api/core/tools/tool/dataset_retriever_tool.py b/api/core/tools/tool/dataset_retriever_tool.py
deleted file mode 100644
index 2d7e193e152645..00000000000000
--- a/api/core/tools/tool/dataset_retriever_tool.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from typing import Any, Optional
-
-from core.app.app_config.entities import DatasetRetrieveConfigEntity
-from core.app.entities.app_invoke_entities import InvokeFrom
-from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCallbackHandler
-from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
-from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import (
- ToolDescription,
- ToolIdentity,
- ToolInvokeMessage,
- ToolParameter,
- ToolProviderType,
-)
-from core.tools.tool.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
-from core.tools.tool.tool import Tool
-
-
-class DatasetRetrieverTool(Tool):
- retrieval_tool: DatasetRetrieverBaseTool
-
- @staticmethod
- def get_dataset_tools(
- tenant_id: str,
- dataset_ids: list[str],
- retrieve_config: Optional[DatasetRetrieveConfigEntity],
- return_resource: bool,
- invoke_from: InvokeFrom,
- hit_callback: DatasetIndexToolCallbackHandler,
- ) -> list["DatasetRetrieverTool"]:
- """
- get dataset tool
- """
- # check if retrieve_config is valid
- if dataset_ids is None or len(dataset_ids) == 0:
- return []
- if retrieve_config is None:
- return []
-
- feature = DatasetRetrieval()
-
- # save original retrieve strategy, and set retrieve strategy to SINGLE
- # Agent only support SINGLE mode
- original_retriever_mode = retrieve_config.retrieve_strategy
- retrieve_config.retrieve_strategy = DatasetRetrieveConfigEntity.RetrieveStrategy.SINGLE
- retrieval_tools = feature.to_dataset_retriever_tool(
- tenant_id=tenant_id,
- dataset_ids=dataset_ids,
- retrieve_config=retrieve_config,
- return_resource=return_resource,
- invoke_from=invoke_from,
- hit_callback=hit_callback,
- )
- if retrieval_tools is None:
- return []
- # restore retrieve strategy
- retrieve_config.retrieve_strategy = original_retriever_mode
-
- # convert retrieval tools to Tools
- tools = []
- for retrieval_tool in retrieval_tools:
- tool = DatasetRetrieverTool(
- retrieval_tool=retrieval_tool,
- identity=ToolIdentity(
- provider="", author="", name=retrieval_tool.name, label=I18nObject(en_US="", zh_Hans="")
- ),
- parameters=[],
- is_team_authorization=True,
- description=ToolDescription(human=I18nObject(en_US="", zh_Hans=""), llm=retrieval_tool.description),
- runtime=DatasetRetrieverTool.Runtime(),
- )
-
- tools.append(tool)
-
- return tools
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- return [
- ToolParameter(
- name="query",
- label=I18nObject(en_US="", zh_Hans=""),
- human_description=I18nObject(en_US="", zh_Hans=""),
- type=ToolParameter.ToolParameterType.STRING,
- form=ToolParameter.ToolParameterForm.LLM,
- llm_description="Query for the dataset to be used to retrieve the dataset.",
- required=True,
- default="",
- placeholder=I18nObject(en_US="", zh_Hans=""),
- ),
- ]
-
- def tool_provider_type(self) -> ToolProviderType:
- return ToolProviderType.DATASET_RETRIEVAL
-
- def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
- """
- invoke dataset retriever tool
- """
- query = tool_parameters.get("query")
- if not query:
- return self.create_text_message(text="please input query")
-
- # invoke dataset retriever tool
- result = self.retrieval_tool._run(query=query)
-
- return self.create_text_message(text=result)
-
- def validate_credentials(
- self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False
- ) -> str | None:
- """
- validate the credentials for dataset retriever tool
- """
- pass
diff --git a/api/core/tools/tool/tool.py b/api/core/tools/tool/tool.py
deleted file mode 100644
index 4094207beb5c9f..00000000000000
--- a/api/core/tools/tool/tool.py
+++ /dev/null
@@ -1,355 +0,0 @@
-from abc import ABC, abstractmethod
-from collections.abc import Mapping
-from copy import deepcopy
-from enum import Enum, StrEnum
-from typing import TYPE_CHECKING, Any, Optional, Union
-
-from pydantic import BaseModel, ConfigDict, field_validator
-from pydantic_core.core_schema import ValidationInfo
-
-from core.app.entities.app_invoke_entities import InvokeFrom
-from core.tools.entities.tool_entities import (
- ToolDescription,
- ToolIdentity,
- ToolInvokeFrom,
- ToolInvokeMessage,
- ToolParameter,
- ToolProviderType,
- ToolRuntimeImageVariable,
- ToolRuntimeVariable,
- ToolRuntimeVariablePool,
-)
-from core.tools.tool_file_manager import ToolFileManager
-
-if TYPE_CHECKING:
- from core.file.models import File
-
-
-class Tool(BaseModel, ABC):
- identity: Optional[ToolIdentity] = None
- parameters: Optional[list[ToolParameter]] = None
- description: Optional[ToolDescription] = None
- is_team_authorization: bool = False
-
- # pydantic configs
- model_config = ConfigDict(protected_namespaces=())
-
- @field_validator("parameters", mode="before")
- @classmethod
- def set_parameters(cls, v, validation_info: ValidationInfo) -> list[ToolParameter]:
- return v or []
-
- class Runtime(BaseModel):
- """
- Meta data of a tool call processing
- """
-
- def __init__(self, **data: Any):
- super().__init__(**data)
- if not self.runtime_parameters:
- self.runtime_parameters = {}
-
- tenant_id: Optional[str] = None
- tool_id: Optional[str] = None
- invoke_from: Optional[InvokeFrom] = None
- tool_invoke_from: Optional[ToolInvokeFrom] = None
- credentials: Optional[dict[str, Any]] = None
- runtime_parameters: Optional[dict[str, Any]] = None
-
- runtime: Optional[Runtime] = None
- variables: Optional[ToolRuntimeVariablePool] = None
-
- def __init__(self, **data: Any):
- super().__init__(**data)
-
- class VariableKey(StrEnum):
- IMAGE = "image"
- DOCUMENT = "document"
- VIDEO = "video"
- AUDIO = "audio"
- CUSTOM = "custom"
-
- def fork_tool_runtime(self, runtime: dict[str, Any]) -> "Tool":
- """
- fork a new tool with meta data
-
- :param meta: the meta data of a tool call processing, tenant_id is required
- :return: the new tool
- """
- return self.__class__(
- identity=self.identity.model_copy() if self.identity else None,
- parameters=self.parameters.copy() if self.parameters else None,
- description=self.description.model_copy() if self.description else None,
- runtime=Tool.Runtime(**runtime),
- )
-
- @abstractmethod
- def tool_provider_type(self) -> ToolProviderType:
- """
- get the tool provider type
-
- :return: the tool provider type
- """
-
- def load_variables(self, variables: ToolRuntimeVariablePool | None) -> None:
- """
- load variables from database
-
- :param conversation_id: the conversation id
- """
- self.variables = variables
-
- def set_image_variable(self, variable_name: str, image_key: str) -> None:
- """
- set an image variable
- """
- if not self.variables:
- return
- if self.identity is None:
- return
-
- self.variables.set_file(self.identity.name, variable_name, image_key)
-
- def set_text_variable(self, variable_name: str, text: str) -> None:
- """
- set a text variable
- """
- if not self.variables:
- return
- if self.identity is None:
- return
-
- self.variables.set_text(self.identity.name, variable_name, text)
-
- def get_variable(self, name: Union[str, Enum]) -> Optional[ToolRuntimeVariable]:
- """
- get a variable
-
- :param name: the name of the variable
- :return: the variable
- """
- if not self.variables:
- return None
-
- if isinstance(name, Enum):
- name = name.value
-
- for variable in self.variables.pool:
- if variable.name == name:
- return variable
-
- return None
-
- def get_default_image_variable(self) -> Optional[ToolRuntimeVariable]:
- """
- get the default image variable
-
- :return: the image variable
- """
- if not self.variables:
- return None
-
- return self.get_variable(self.VariableKey.IMAGE)
-
- def get_variable_file(self, name: Union[str, Enum]) -> Optional[bytes]:
- """
- get a variable file
-
- :param name: the name of the variable
- :return: the variable file
- """
- variable = self.get_variable(name)
- if not variable:
- return None
-
- if not isinstance(variable, ToolRuntimeImageVariable):
- return None
-
- message_file_id = variable.value
- # get file binary
- file_binary = ToolFileManager.get_file_binary_by_message_file_id(message_file_id)
- if not file_binary:
- return None
-
- return file_binary[0]
-
- def list_variables(self) -> list[ToolRuntimeVariable]:
- """
- list all variables
-
- :return: the variables
- """
- if not self.variables:
- return []
-
- return self.variables.pool
-
- def list_default_image_variables(self) -> list[ToolRuntimeVariable]:
- """
- list all image variables
-
- :return: the image variables
- """
- if not self.variables:
- return []
-
- result = []
-
- for variable in self.variables.pool:
- if variable.name.startswith(self.VariableKey.IMAGE.value):
- result.append(variable)
-
- return result
-
- def invoke(self, user_id: str, tool_parameters: Mapping[str, Any]) -> list[ToolInvokeMessage]:
- # update tool_parameters
- # TODO: Fix type error.
- if self.runtime is None:
- return []
- if self.runtime.runtime_parameters:
- # Convert Mapping to dict before updating
- tool_parameters = dict(tool_parameters)
- tool_parameters.update(self.runtime.runtime_parameters)
-
- # try parse tool parameters into the correct type
- tool_parameters = self._transform_tool_parameters_type(tool_parameters)
-
- result = self._invoke(
- user_id=user_id,
- tool_parameters=tool_parameters,
- )
-
- if not isinstance(result, list):
- result = [result]
-
- if not all(isinstance(message, ToolInvokeMessage) for message in result):
- raise ValueError(
- f"Invalid return type from {self.__class__.__name__}._invoke method. "
- "Expected ToolInvokeMessage or list of ToolInvokeMessage."
- )
-
- return result
-
- def _transform_tool_parameters_type(self, tool_parameters: Mapping[str, Any]) -> dict[str, Any]:
- """
- Transform tool parameters type
- """
- # Temp fix for the issue that the tool parameters will be converted to empty while validating the credentials
- result: dict[str, Any] = deepcopy(dict(tool_parameters))
- for parameter in self.parameters or []:
- if parameter.name in tool_parameters:
- result[parameter.name] = parameter.type.cast_value(tool_parameters[parameter.name])
-
- return result
-
- @abstractmethod
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- pass
-
- def validate_credentials(
- self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False
- ) -> str | None:
- """
- validate the credentials
-
- :param credentials: the credentials
- :param parameters: the parameters
- :param format_only: only return the formatted
- """
- pass
-
- def get_runtime_parameters(self) -> list[ToolParameter]:
- """
- get the runtime parameters
-
- interface for developer to dynamic change the parameters of a tool depends on the variables pool
-
- :return: the runtime parameters
- """
- return self.parameters or []
-
- def get_all_runtime_parameters(self) -> list[ToolParameter]:
- """
- get all runtime parameters
-
- :return: all runtime parameters
- """
- parameters = self.parameters or []
- parameters = parameters.copy()
- user_parameters = self.get_runtime_parameters()
- user_parameters = user_parameters.copy()
-
- # override parameters
- for parameter in user_parameters:
- # check if parameter in tool parameters
- found = False
- for tool_parameter in parameters:
- if tool_parameter.name == parameter.name:
- found = True
- break
-
- if found:
- # override parameter
- tool_parameter.type = parameter.type
- tool_parameter.form = parameter.form
- tool_parameter.required = parameter.required
- tool_parameter.default = parameter.default
- tool_parameter.options = parameter.options
- tool_parameter.llm_description = parameter.llm_description
- else:
- # add new parameter
- parameters.append(parameter)
-
- return parameters
-
- def create_image_message(self, image: str, save_as: str = "") -> ToolInvokeMessage:
- """
- create an image message
-
- :param image: the url of the image
- :return: the image message
- """
- return ToolInvokeMessage(type=ToolInvokeMessage.MessageType.IMAGE, message=image, save_as=save_as)
-
- def create_file_message(self, file: "File") -> ToolInvokeMessage:
- return ToolInvokeMessage(type=ToolInvokeMessage.MessageType.FILE, message="", meta={"file": file}, save_as="")
-
- def create_link_message(self, link: str, save_as: str = "") -> ToolInvokeMessage:
- """
- create a link message
-
- :param link: the url of the link
- :return: the link message
- """
- return ToolInvokeMessage(type=ToolInvokeMessage.MessageType.LINK, message=link, save_as=save_as)
-
- def create_text_message(self, text: str, save_as: str = "") -> ToolInvokeMessage:
- """
- create a text message
-
- :param text: the text
- :return: the text message
- """
- return ToolInvokeMessage(type=ToolInvokeMessage.MessageType.TEXT, message=text, save_as=save_as)
-
- def create_blob_message(self, blob: bytes, meta: Optional[dict] = None, save_as: str = "") -> ToolInvokeMessage:
- """
- create a blob message
-
- :param blob: the blob
- :return: the blob message
- """
- return ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.BLOB,
- message=blob,
- meta=meta or {},
- save_as=save_as,
- )
-
- def create_json_message(self, object: dict) -> ToolInvokeMessage:
- """
- create a json message
- """
- return ToolInvokeMessage(type=ToolInvokeMessage.MessageType.JSON, message=object)
diff --git a/api/core/tools/tool/workflow_tool.py b/api/core/tools/tool/workflow_tool.py
deleted file mode 100644
index eea66ee4ed7447..00000000000000
--- a/api/core/tools/tool/workflow_tool.py
+++ /dev/null
@@ -1,213 +0,0 @@
-import json
-import logging
-from copy import deepcopy
-from typing import Any, Optional, Union, cast
-
-from core.file import FILE_MODEL_IDENTITY, File, FileTransferMethod
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolProviderType
-from core.tools.tool.tool import Tool
-from extensions.ext_database import db
-from factories.file_factory import build_from_mapping
-from models.account import Account
-from models.model import App, EndUser
-from models.workflow import Workflow
-
-logger = logging.getLogger(__name__)
-
-
-class WorkflowTool(Tool):
- workflow_app_id: str
- version: str
- workflow_entities: dict[str, Any]
- workflow_call_depth: int
- thread_pool_id: Optional[str] = None
-
- label: str
-
- """
- Workflow tool.
- """
-
- def tool_provider_type(self) -> ToolProviderType:
- """
- get the tool provider type
-
- :return: the tool provider type
- """
- return ToolProviderType.WORKFLOW
-
- def _invoke(
- self, user_id: str, tool_parameters: dict[str, Any]
- ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
- """
- invoke the tool
- """
- app = self._get_app(app_id=self.workflow_app_id)
- workflow = self._get_workflow(app_id=self.workflow_app_id, version=self.version)
-
- # transform the tool parameters
- tool_parameters, files = self._transform_args(tool_parameters=tool_parameters)
-
- from core.app.apps.workflow.app_generator import WorkflowAppGenerator
-
- generator = WorkflowAppGenerator()
- assert self.runtime is not None
- assert self.runtime.invoke_from is not None
- result = generator.generate(
- app_model=app,
- workflow=workflow,
- user=self._get_user(user_id),
- args={"inputs": tool_parameters, "files": files},
- invoke_from=self.runtime.invoke_from,
- streaming=False,
- call_depth=self.workflow_call_depth + 1,
- workflow_thread_pool_id=self.thread_pool_id,
- )
- assert isinstance(result, dict)
- data = result.get("data", {})
-
- if data.get("error"):
- raise Exception(data.get("error"))
-
- r = []
-
- outputs = data.get("outputs")
- if outputs == None:
- outputs = {}
- else:
- outputs, extracted_files = self._extract_files(outputs)
- for f in extracted_files:
- r.append(self.create_file_message(f))
-
- r.append(self.create_text_message(json.dumps(outputs, ensure_ascii=False)))
- r.append(self.create_json_message(outputs))
-
- return r
-
- def _get_user(self, user_id: str) -> Union[EndUser, Account]:
- """
- get the user by user id
- """
-
- user = db.session.query(EndUser).filter(EndUser.id == user_id).first()
- if not user:
- user = db.session.query(Account).filter(Account.id == user_id).first()
-
- if not user:
- raise ValueError("user not found")
-
- return user
-
- def fork_tool_runtime(self, runtime: dict[str, Any]) -> "WorkflowTool":
- """
- fork a new tool with meta data
-
- :param meta: the meta data of a tool call processing, tenant_id is required
- :return: the new tool
- """
- return self.__class__(
- identity=deepcopy(self.identity),
- parameters=deepcopy(self.parameters),
- description=deepcopy(self.description),
- runtime=Tool.Runtime(**runtime),
- workflow_app_id=self.workflow_app_id,
- workflow_entities=self.workflow_entities,
- workflow_call_depth=self.workflow_call_depth,
- version=self.version,
- label=self.label,
- )
-
- def _get_workflow(self, app_id: str, version: str) -> Workflow:
- """
- get the workflow by app id and version
- """
- if not version:
- workflow = (
- db.session.query(Workflow)
- .filter(Workflow.app_id == app_id, Workflow.version != "draft")
- .order_by(Workflow.created_at.desc())
- .first()
- )
- else:
- workflow = db.session.query(Workflow).filter(Workflow.app_id == app_id, Workflow.version == version).first()
-
- if not workflow:
- raise ValueError("workflow not found or not published")
-
- return workflow
-
- def _get_app(self, app_id: str) -> App:
- """
- get the app by app id
- """
- app = db.session.query(App).filter(App.id == app_id).first()
- if not app:
- raise ValueError("app not found")
-
- return app
-
- def _transform_args(self, tool_parameters: dict) -> tuple[dict, list[dict]]:
- """
- transform the tool parameters
-
- :param tool_parameters: the tool parameters
- :return: tool_parameters, files
- """
- parameter_rules = self.get_all_runtime_parameters()
- parameters_result = {}
- files = []
- for parameter in parameter_rules:
- if parameter.type == ToolParameter.ToolParameterType.SYSTEM_FILES:
- file = tool_parameters.get(parameter.name)
- if file:
- try:
- file_var_list = [File.model_validate(f) for f in file]
- for file in file_var_list:
- file_dict: dict[str, str | None] = {
- "transfer_method": file.transfer_method.value,
- "type": file.type.value,
- }
- if file.transfer_method == FileTransferMethod.TOOL_FILE:
- file_dict["tool_file_id"] = file.related_id
- elif file.transfer_method == FileTransferMethod.LOCAL_FILE:
- file_dict["upload_file_id"] = file.related_id
- elif file.transfer_method == FileTransferMethod.REMOTE_URL:
- file_dict["url"] = file.generate_url()
-
- files.append(file_dict)
- except Exception as e:
- logger.exception(f"Failed to transform file {file}")
- else:
- parameters_result[parameter.name] = tool_parameters.get(parameter.name)
-
- return parameters_result, files
-
- def _extract_files(self, outputs: dict) -> tuple[dict, list[File]]:
- """
- extract files from the result
-
- :param result: the result
- :return: the result, files
- """
- files = []
- result = {}
- for key, value in outputs.items():
- if isinstance(value, list):
- for item in value:
- if isinstance(item, dict) and item.get("dify_model_identity") == FILE_MODEL_IDENTITY:
- item["tool_file_id"] = item.get("related_id")
- file = build_from_mapping(
- mapping=item,
- tenant_id=str(cast(Tool.Runtime, self.runtime).tenant_id),
- )
- files.append(file)
- elif isinstance(value, dict) and value.get("dify_model_identity") == FILE_MODEL_IDENTITY:
- value["tool_file_id"] = value.get("related_id")
- file = build_from_mapping(
- mapping=value,
- tenant_id=str(cast(Tool.Runtime, self.runtime).tenant_id),
- )
- files.append(file)
-
- result[key] = value
- return result, files
diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py
index f7a8ed63f401d5..37b4582381e9a0 100644
--- a/api/core/tools/tool_engine.py
+++ b/api/core/tools/tool_engine.py
@@ -1,5 +1,5 @@
import json
-from collections.abc import Mapping
+from collections.abc import Generator, Iterable
from copy import deepcopy
from datetime import UTC, datetime
from mimetypes import guess_type
@@ -13,7 +13,13 @@
from core.file import FileType
from core.file.models import FileTransferMethod
from core.ops.ops_trace_manager import TraceQueueManager
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolInvokeMessageBinary, ToolInvokeMeta, ToolParameter
+from core.tools.__base.tool import Tool
+from core.tools.entities.tool_entities import (
+ ToolInvokeMessage,
+ ToolInvokeMessageBinary,
+ ToolInvokeMeta,
+ ToolParameter,
+)
from core.tools.errors import (
ToolEngineInvokeError,
ToolInvokeError,
@@ -23,9 +29,8 @@
ToolProviderCredentialValidationError,
ToolProviderNotFoundError,
)
-from core.tools.tool.tool import Tool
-from core.tools.tool.workflow_tool import WorkflowTool
from core.tools.utils.message_transformer import ToolFileMessageTransformer
+from core.tools.workflow_as_tool.tool import WorkflowTool
from extensions.ext_database import db
from models.enums import CreatedByRole
from models.model import Message, MessageFile
@@ -46,7 +51,10 @@ def agent_invoke(
invoke_from: InvokeFrom,
agent_tool_callback: DifyAgentCallbackHandler,
trace_manager: Optional[TraceQueueManager] = None,
- ) -> tuple[str, list[tuple[MessageFile, str]], ToolInvokeMeta]:
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> tuple[str, list[str], ToolInvokeMeta]:
"""
Agent invokes the tool with the given arguments.
"""
@@ -63,35 +71,50 @@ def agent_invoke(
else:
try:
tool_parameters = json.loads(tool_parameters)
- except Exception as e:
+ except Exception:
pass
if not isinstance(tool_parameters, dict):
raise ValueError(f"tool_parameters should be a dict, but got a string: {tool_parameters}")
- # invoke the tool
- if tool.identity is None:
- raise ValueError("tool identity is not set")
try:
# hit the callback handler
- agent_tool_callback.on_tool_start(tool_name=tool.identity.name, tool_inputs=tool_parameters)
-
- meta, response = ToolEngine._invoke(tool, tool_parameters, user_id)
- response = ToolFileMessageTransformer.transform_tool_invoke_messages(
- messages=response, user_id=user_id, tenant_id=tenant_id, conversation_id=message.conversation_id
+ agent_tool_callback.on_tool_start(tool_name=tool.entity.identity.name, tool_inputs=tool_parameters)
+
+ messages = ToolEngine._invoke(tool, tool_parameters, user_id, conversation_id, app_id, message_id)
+ invocation_meta_dict: dict[str, ToolInvokeMeta] = {}
+
+ def message_callback(
+ invocation_meta_dict: dict, messages: Generator[ToolInvokeMessage | ToolInvokeMeta, None, None]
+ ):
+ for message in messages:
+ if isinstance(message, ToolInvokeMeta):
+ invocation_meta_dict["meta"] = message
+ else:
+ yield message
+
+ messages = ToolFileMessageTransformer.transform_tool_invoke_messages(
+ messages=message_callback(invocation_meta_dict, messages),
+ user_id=user_id,
+ tenant_id=tenant_id,
+ conversation_id=message.conversation_id,
)
+ message_list = list(messages)
+
# extract binary data from tool invoke message
- binary_files = ToolEngine._extract_tool_response_binary(response)
+ binary_files = ToolEngine._extract_tool_response_binary_and_text(message_list)
# create message file
message_files = ToolEngine._create_message_files(
tool_messages=binary_files, agent_message=message, invoke_from=invoke_from, user_id=user_id
)
- plain_text = ToolEngine._convert_tool_response_to_str(response)
+ plain_text = ToolEngine._convert_tool_response_to_str(message_list)
+
+ meta = invocation_meta_dict["meta"]
# hit the callback handler
agent_tool_callback.on_tool_end(
- tool_name=tool.identity.name,
+ tool_name=tool.entity.identity.name,
tool_inputs=tool_parameters,
tool_outputs=plain_text,
message_id=message.id,
@@ -104,7 +127,7 @@ def agent_invoke(
error_response = "Please check your tool provider credentials"
agent_tool_callback.on_tool_error(e)
except (ToolNotFoundError, ToolNotSupportedError, ToolProviderNotFoundError) as e:
- error_response = f"there is not a tool named {tool.identity.name}"
+ error_response = f"there is not a tool named {tool.entity.identity.name}"
agent_tool_callback.on_tool_error(e)
except ToolParameterValidationError as e:
error_response = f"tool parameters validation error: {e}, please check your tool parameters"
@@ -124,21 +147,23 @@ def agent_invoke(
return error_response, [], ToolInvokeMeta.error_instance(error_response)
@staticmethod
- def workflow_invoke(
+ def generic_invoke(
tool: Tool,
- tool_parameters: Mapping[str, Any],
+ tool_parameters: dict[str, Any],
user_id: str,
workflow_tool_callback: DifyWorkflowCallbackHandler,
workflow_call_depth: int,
thread_pool_id: Optional[str] = None,
- ) -> list[ToolInvokeMessage]:
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[ToolInvokeMessage, None, None]:
"""
Workflow invokes the tool with the given arguments.
"""
try:
# hit the callback handler
- assert tool.identity is not None
- workflow_tool_callback.on_tool_start(tool_name=tool.identity.name, tool_inputs=tool_parameters)
+ workflow_tool_callback.on_tool_start(tool_name=tool.entity.identity.name, tool_inputs=tool_parameters)
if isinstance(tool, WorkflowTool):
tool.workflow_call_depth = workflow_call_depth + 1
@@ -146,11 +171,18 @@ def workflow_invoke(
if tool.runtime and tool.runtime.runtime_parameters:
tool_parameters = {**tool.runtime.runtime_parameters, **tool_parameters}
- response = tool.invoke(user_id=user_id, tool_parameters=tool_parameters)
+
+ response = tool.invoke(
+ user_id=user_id,
+ tool_parameters=tool_parameters,
+ conversation_id=conversation_id,
+ app_id=app_id,
+ message_id=message_id,
+ )
# hit the callback handler
- workflow_tool_callback.on_tool_end(
- tool_name=tool.identity.name,
+ response = workflow_tool_callback.on_tool_execution(
+ tool_name=tool.entity.identity.name,
tool_inputs=tool_parameters,
tool_outputs=response,
)
@@ -161,34 +193,38 @@ def workflow_invoke(
raise e
@staticmethod
- def _invoke(tool: Tool, tool_parameters: dict, user_id: str) -> tuple[ToolInvokeMeta, list[ToolInvokeMessage]]:
+ def _invoke(
+ tool: Tool,
+ tool_parameters: dict,
+ user_id: str,
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[ToolInvokeMessage | ToolInvokeMeta, None, None]:
"""
Invoke the tool with the given arguments.
"""
- if tool.identity is None:
- raise ValueError("tool identity is not set")
started_at = datetime.now(UTC)
meta = ToolInvokeMeta(
time_cost=0.0,
error=None,
tool_config={
- "tool_name": tool.identity.name,
- "tool_provider": tool.identity.provider,
+ "tool_name": tool.entity.identity.name,
+ "tool_provider": tool.entity.identity.provider,
"tool_provider_type": tool.tool_provider_type().value,
- "tool_parameters": deepcopy(tool.runtime.runtime_parameters) if tool.runtime else {},
- "tool_icon": tool.identity.icon,
+ "tool_parameters": deepcopy(tool.runtime.runtime_parameters),
+ "tool_icon": tool.entity.identity.icon,
},
)
try:
- response = tool.invoke(user_id, tool_parameters)
+ yield from tool.invoke(user_id, tool_parameters, conversation_id, app_id, message_id)
except Exception as e:
meta.error = str(e)
raise ToolEngineInvokeError(meta)
finally:
ended_at = datetime.now(UTC)
meta.time_cost = (ended_at - started_at).total_seconds()
-
- return meta, response
+ yield meta
@staticmethod
def _convert_tool_response_to_str(tool_response: list[ToolInvokeMessage]) -> str:
@@ -198,36 +234,43 @@ def _convert_tool_response_to_str(tool_response: list[ToolInvokeMessage]) -> str
result = ""
for response in tool_response:
if response.type == ToolInvokeMessage.MessageType.TEXT:
- result += str(response.message) if response.message is not None else ""
+ result += cast(ToolInvokeMessage.TextMessage, response.message).text
elif response.type == ToolInvokeMessage.MessageType.LINK:
- result += f"result link: {response.message!r}. please tell user to check it."
+ result += (
+ f"result link: {cast(ToolInvokeMessage.TextMessage, response.message).text}."
+ + " please tell user to check it."
+ )
elif response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}:
result += (
- "image has been created and sent to user already, you do not need to create it,"
- " just tell the user to check it now."
+ "image has been created and sent to user already, "
+ + "you do not need to create it, just tell the user to check it now."
)
elif response.type == ToolInvokeMessage.MessageType.JSON:
- result += f"tool response: {json.dumps(response.message, ensure_ascii=False)}."
+ result = json.dumps(
+ cast(ToolInvokeMessage.JsonMessage, response.message).json_object, ensure_ascii=False
+ )
else:
- result += f"tool response: {response.message!r}."
+ result += str(response.message)
return result
@staticmethod
- def _extract_tool_response_binary(tool_response: list[ToolInvokeMessage]) -> list[ToolInvokeMessageBinary]:
+ def _extract_tool_response_binary_and_text(
+ tool_response: list[ToolInvokeMessage],
+ ) -> Generator[ToolInvokeMessageBinary, None, None]:
"""
Extract tool response binary
"""
- result = []
-
for response in tool_response:
if response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}:
mimetype = None
+ if not response.meta:
+ raise ValueError("missing meta data")
if response.meta.get("mime_type"):
mimetype = response.meta.get("mime_type")
else:
try:
- url = URL(cast(str, response.message))
+ url = URL(cast(ToolInvokeMessage.TextMessage, response.message).text)
extension = url.suffix
guess_type_result, _ = guess_type(f"a{extension}")
if guess_type_result:
@@ -238,48 +281,38 @@ def _extract_tool_response_binary(tool_response: list[ToolInvokeMessage]) -> lis
if not mimetype:
mimetype = "image/jpeg"
- result.append(
- ToolInvokeMessageBinary(
- mimetype=response.meta.get("mime_type", "image/jpeg"),
- url=cast(str, response.message),
- save_as=response.save_as,
- )
+ yield ToolInvokeMessageBinary(
+ mimetype=response.meta.get("mime_type", "image/jpeg"),
+ url=cast(ToolInvokeMessage.TextMessage, response.message).text,
)
elif response.type == ToolInvokeMessage.MessageType.BLOB:
- result.append(
- ToolInvokeMessageBinary(
- mimetype=response.meta.get("mime_type", "octet/stream"),
- url=cast(str, response.message),
- save_as=response.save_as,
- )
+ if not response.meta:
+ raise ValueError("missing meta data")
+
+ yield ToolInvokeMessageBinary(
+ mimetype=response.meta.get("mime_type", "octet/stream"),
+ url=cast(ToolInvokeMessage.TextMessage, response.message).text,
)
elif response.type == ToolInvokeMessage.MessageType.LINK:
# check if there is a mime type in meta
if response.meta and "mime_type" in response.meta:
- result.append(
- ToolInvokeMessageBinary(
- mimetype=response.meta.get("mime_type", "octet/stream")
- if response.meta
- else "octet/stream",
- url=cast(str, response.message),
- save_as=response.save_as,
- )
+ yield ToolInvokeMessageBinary(
+ mimetype=response.meta.get("mime_type", "octet/stream") if response.meta else "octet/stream",
+ url=cast(ToolInvokeMessage.TextMessage, response.message).text,
)
- return result
-
@staticmethod
def _create_message_files(
- tool_messages: list[ToolInvokeMessageBinary],
+ tool_messages: Iterable[ToolInvokeMessageBinary],
agent_message: Message,
invoke_from: InvokeFrom,
user_id: str,
- ) -> list[tuple[Any, str]]:
+ ) -> list[str]:
"""
Create message file
:param messages: messages
- :return: message files, should save as variable
+ :return: message file ids
"""
result = []
@@ -316,7 +349,7 @@ def _create_message_files(
db.session.commit()
db.session.refresh(message_file)
- result.append((message_file.id, message.save_as))
+ result.append(message_file.id)
db.session.close()
diff --git a/api/core/tools/tool_file_manager.py b/api/core/tools/tool_file_manager.py
index 2aaca6d82e36b1..967cddac6cfae0 100644
--- a/api/core/tools/tool_file_manager.py
+++ b/api/core/tools/tool_file_manager.py
@@ -90,15 +90,15 @@ def create_file_by_raw(
def create_file_by_url(
user_id: str,
tenant_id: str,
- conversation_id: str | None,
file_url: str,
+ conversation_id: Optional[str] = None,
) -> ToolFile:
# try to download image
try:
response = ssrf_proxy.get(file_url)
response.raise_for_status()
blob = response.content
- except httpx.TimeoutException as e:
+ except httpx.TimeoutException:
raise ValueError(f"timeout when downloading file from {file_url}")
mimetype = guess_type(file_url)[0] or "octet/stream"
@@ -133,7 +133,7 @@ def get_file_binary(id: str) -> Union[tuple[bytes, str], None]:
:return: the binary of the file, mime type
"""
- tool_file = (
+ tool_file: ToolFile | None = (
db.session.query(ToolFile)
.filter(
ToolFile.id == id,
@@ -157,7 +157,7 @@ def get_file_binary_by_message_file_id(id: str) -> Union[tuple[bytes, str], None
:return: the binary of the file, mime type
"""
- message_file = (
+ message_file: MessageFile | None = (
db.session.query(MessageFile)
.filter(
MessageFile.id == id,
@@ -177,7 +177,7 @@ def get_file_binary_by_message_file_id(id: str) -> Union[tuple[bytes, str], None
else:
tool_file_id = None
- tool_file = (
+ tool_file: ToolFile | None = (
db.session.query(ToolFile)
.filter(
ToolFile.id == tool_file_id,
@@ -201,7 +201,7 @@ def get_file_generator_by_tool_file_id(tool_file_id: str):
:return: the binary of the file, mime type
"""
- tool_file = (
+ tool_file: ToolFile | None = (
db.session.query(ToolFile)
.filter(
ToolFile.id == tool_file_id,
diff --git a/api/core/tools/tool_label_manager.py b/api/core/tools/tool_label_manager.py
index e53985951b0627..4787d7d79cba89 100644
--- a/api/core/tools/tool_label_manager.py
+++ b/api/core/tools/tool_label_manager.py
@@ -1,8 +1,8 @@
+from core.tools.__base.tool_provider import ToolProviderController
+from core.tools.builtin_tool.provider import BuiltinToolProviderController
+from core.tools.custom_tool.provider import ApiToolProviderController
from core.tools.entities.values import default_tool_label_name_list
-from core.tools.provider.api_tool_provider import ApiToolProviderController
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController
+from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
from extensions.ext_database import db
from models.tools import ToolLabelBinding
@@ -55,7 +55,7 @@ def get_tool_labels(cls, controller: ToolProviderController) -> list[str]:
else:
raise ValueError("Unsupported tool type")
- labels: list[ToolLabelBinding] = (
+ labels = (
db.session.query(ToolLabelBinding.label_name)
.filter(
ToolLabelBinding.tool_id == provider_id,
@@ -84,11 +84,10 @@ def get_tools_labels(cls, tool_providers: list[ToolProviderController]) -> dict[
if not isinstance(controller, ApiToolProviderController | WorkflowToolProviderController):
raise ValueError("Unsupported tool type")
- provider_ids = [
- controller.provider_id
- for controller in tool_providers
- if isinstance(controller, (ApiToolProviderController, WorkflowToolProviderController))
- ]
+ provider_ids = []
+ for controller in tool_providers:
+ assert isinstance(controller, ApiToolProviderController | WorkflowToolProviderController)
+ provider_ids.append(controller.provider_id)
labels: list[ToolLabelBinding] = (
db.session.query(ToolLabelBinding).filter(ToolLabelBinding.tool_id.in_(provider_ids)).all()
diff --git a/api/core/tools/tool_manager.py b/api/core/tools/tool_manager.py
index 5b2173a4d0ad69..e97f0ca15729bd 100644
--- a/api/core/tools/tool_manager.py
+++ b/api/core/tools/tool_manager.py
@@ -3,8 +3,23 @@
import mimetypes
from collections.abc import Generator
from os import listdir, path
-from threading import Lock, Thread
-from typing import Any, Optional, Union, cast
+from threading import Lock
+from typing import TYPE_CHECKING, Any, Union, cast
+
+from yarl import URL
+
+import contexts
+from core.plugin.entities.plugin import ToolProviderID
+from core.plugin.manager.tool import PluginToolManager
+from core.tools.__base.tool_provider import ToolProviderController
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.plugin_tool.provider import PluginToolProviderController
+from core.tools.plugin_tool.tool import PluginTool
+from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
+
+if TYPE_CHECKING:
+ from core.workflow.nodes.tool.entities import ToolEntity
+
from configs import dify_config
from core.agent.entities import AgentToolEntity
@@ -12,21 +27,27 @@
from core.helper.module_import_helper import load_single_subclass_from_source
from core.helper.position_helper import is_filtered
from core.model_runtime.utils.encoders import jsonable_encoder
-from core.tools.entities.api_entities import UserToolProvider, UserToolProviderTypeLiteral
+from core.tools.__base.tool import Tool
+from core.tools.builtin_tool.provider import BuiltinToolProviderController
+from core.tools.builtin_tool.providers._positions import BuiltinToolProviderSort
+from core.tools.builtin_tool.tool import BuiltinTool
+from core.tools.custom_tool.provider import ApiToolProviderController
+from core.tools.custom_tool.tool import ApiTool
+from core.tools.entities.api_entities import ToolProviderApiEntity, ToolProviderTypeApiLiteral
from core.tools.entities.common_entities import I18nObject
-from core.tools.entities.tool_entities import ApiProviderAuthType, ToolInvokeFrom, ToolParameter
+from core.tools.entities.tool_entities import (
+ ApiProviderAuthType,
+ ToolInvokeFrom,
+ ToolParameter,
+ ToolProviderType,
+)
from core.tools.errors import ToolNotFoundError, ToolProviderNotFoundError
-from core.tools.provider.api_tool_provider import ApiToolProviderController
-from core.tools.provider.builtin._positions import BuiltinToolProviderSort
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController
-from core.tools.tool.api_tool import ApiTool
-from core.tools.tool.builtin_tool import BuiltinTool
-from core.tools.tool.tool import Tool
from core.tools.tool_label_manager import ToolLabelManager
-from core.tools.utils.configuration import ToolConfigurationManager, ToolParameterConfigurationManager
-from core.workflow.nodes.tool.entities import ToolEntity
+from core.tools.utils.configuration import (
+ ProviderConfigEncrypter,
+ ToolParameterConfigurationManager,
+)
+from core.tools.workflow_as_tool.tool import WorkflowTool
from extensions.ext_database import db
from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider
from services.tools.tools_transform_service import ToolTransformService
@@ -36,79 +57,106 @@
class ToolManager:
_builtin_provider_lock = Lock()
- _builtin_providers: dict[str, BuiltinToolProviderController] = {}
+ _hardcoded_providers: dict[str, BuiltinToolProviderController] = {}
_builtin_providers_loaded = False
_builtin_tools_labels: dict[str, Union[I18nObject, None]] = {}
@classmethod
- def get_builtin_provider(cls, provider: str) -> BuiltinToolProviderController:
+ def get_hardcoded_provider(cls, provider: str) -> BuiltinToolProviderController:
+ """
+ get the hardcoded provider
+ """
+ if len(cls._hardcoded_providers) == 0:
+ # init the builtin providers
+ cls.load_hardcoded_providers_cache()
+
+ return cls._hardcoded_providers[provider]
+
+ @classmethod
+ def get_builtin_provider(
+ cls, provider: str, tenant_id: str
+ ) -> BuiltinToolProviderController | PluginToolProviderController:
"""
get the builtin provider
:param provider: the name of the provider
+ :param tenant_id: the id of the tenant
:return: the provider
"""
- if len(cls._builtin_providers) == 0:
+ # split provider to
+
+ if len(cls._hardcoded_providers) == 0:
# init the builtin providers
- cls.load_builtin_providers_cache()
+ cls.load_hardcoded_providers_cache()
+
+ if provider not in cls._hardcoded_providers:
+ # get plugin provider
+ plugin_provider = cls.get_plugin_provider(provider, tenant_id)
+ if plugin_provider:
+ return plugin_provider
+
+ return cls._hardcoded_providers[provider]
+
+ @classmethod
+ def get_plugin_provider(cls, provider: str, tenant_id: str) -> PluginToolProviderController:
+ """
+ get the plugin provider
+ """
+ # check if context is set
+ try:
+ contexts.plugin_tool_providers.get()
+ except LookupError:
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(Lock())
+
+ with contexts.plugin_tool_providers_lock.get():
+ plugin_tool_providers = contexts.plugin_tool_providers.get()
+ if provider in plugin_tool_providers:
+ return plugin_tool_providers[provider]
+
+ manager = PluginToolManager()
+ provider_entity = manager.fetch_tool_provider(tenant_id, provider)
+ if not provider_entity:
+ raise ToolProviderNotFoundError(f"plugin provider {provider} not found")
+
+ controller = PluginToolProviderController(
+ entity=provider_entity.declaration,
+ plugin_id=provider_entity.plugin_id,
+ plugin_unique_identifier=provider_entity.plugin_unique_identifier,
+ tenant_id=tenant_id,
+ )
- if provider not in cls._builtin_providers:
- raise ToolProviderNotFoundError(f"builtin provider {provider} not found")
+ plugin_tool_providers[provider] = controller
- return cls._builtin_providers[provider]
+ return controller
@classmethod
- def get_builtin_tool(cls, provider: str, tool_name: str) -> Union[BuiltinTool, Tool]:
+ def get_builtin_tool(cls, provider: str, tool_name: str, tenant_id: str) -> BuiltinTool | PluginTool | None:
"""
get the builtin tool
:param provider: the name of the provider
:param tool_name: the name of the tool
-
+ :param tenant_id: the id of the tenant
:return: the provider, the tool
"""
- provider_controller = cls.get_builtin_provider(provider)
+ provider_controller = cls.get_builtin_provider(provider, tenant_id)
tool = provider_controller.get_tool(tool_name)
if tool is None:
raise ToolNotFoundError(f"tool {tool_name} not found")
return tool
- @classmethod
- def get_tool(
- cls, provider_type: str, provider_id: str, tool_name: str, tenant_id: Optional[str] = None
- ) -> Union[BuiltinTool, ApiTool, Tool]:
- """
- get the tool
-
- :param provider_type: the type of the provider
- :param provider_name: the name of the provider
- :param tool_name: the name of the tool
-
- :return: the tool
- """
- if provider_type == "builtin":
- return cls.get_builtin_tool(provider_id, tool_name)
- elif provider_type == "api":
- if tenant_id is None:
- raise ValueError("tenant id is required for api provider")
- api_provider, _ = cls.get_api_provider_controller(tenant_id, provider_id)
- return api_provider.get_tool(tool_name)
- elif provider_type == "app":
- raise NotImplementedError("app provider not implemented")
- else:
- raise ToolProviderNotFoundError(f"provider type {provider_type} not found")
-
@classmethod
def get_tool_runtime(
cls,
- provider_type: str,
+ provider_type: ToolProviderType,
provider_id: str,
tool_name: str,
tenant_id: str,
invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
tool_invoke_from: ToolInvokeFrom = ToolInvokeFrom.AGENT,
- ) -> Union[BuiltinTool, ApiTool, Tool]:
+ ) -> Union[BuiltinTool, PluginTool, ApiTool, WorkflowTool]:
"""
get the tool runtime
@@ -118,72 +166,101 @@ def get_tool_runtime(
:return: the tool
"""
- controller: Union[BuiltinToolProviderController, ApiToolProviderController, WorkflowToolProviderController]
- if provider_type == "builtin":
- builtin_tool = cls.get_builtin_tool(provider_id, tool_name)
-
+ if provider_type == ToolProviderType.BUILT_IN:
# check if the builtin tool need credentials
- provider_controller = cls.get_builtin_provider(provider_id)
+ provider_controller = cls.get_builtin_provider(provider_id, tenant_id)
+
+ builtin_tool = provider_controller.get_tool(tool_name)
+ if not builtin_tool:
+ raise ToolProviderNotFoundError(f"builtin tool {tool_name} not found")
+
if not provider_controller.need_credentials:
- return builtin_tool.fork_tool_runtime(
- runtime={
- "tenant_id": tenant_id,
- "credentials": {},
- "invoke_from": invoke_from,
- "tool_invoke_from": tool_invoke_from,
- }
+ return cast(
+ BuiltinTool,
+ builtin_tool.fork_tool_runtime(
+ runtime=ToolRuntime(
+ tenant_id=tenant_id,
+ credentials={},
+ invoke_from=invoke_from,
+ tool_invoke_from=tool_invoke_from,
+ )
+ ),
+ )
+
+ if isinstance(provider_controller, PluginToolProviderController):
+ provider_id_entity = ToolProviderID(provider_id)
+ # get credentials
+ builtin_provider: BuiltinToolProvider | None = (
+ db.session.query(BuiltinToolProvider)
+ .filter(
+ BuiltinToolProvider.tenant_id == tenant_id,
+ (BuiltinToolProvider.provider == provider_id)
+ | (BuiltinToolProvider.provider == provider_id_entity.provider_name),
+ )
+ .first()
)
- # get credentials
- builtin_provider: Optional[BuiltinToolProvider] = (
- db.session.query(BuiltinToolProvider)
- .filter(
- BuiltinToolProvider.tenant_id == tenant_id,
- BuiltinToolProvider.provider == provider_id,
+ if builtin_provider is None:
+ raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
+ else:
+ builtin_provider = (
+ db.session.query(BuiltinToolProvider)
+ .filter(BuiltinToolProvider.tenant_id == tenant_id, (BuiltinToolProvider.provider == provider_id))
+ .first()
)
- .first()
- )
- if builtin_provider is None:
- raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
+ if builtin_provider is None:
+ raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
# decrypt the credentials
credentials = builtin_provider.credentials
- controller = cls.get_builtin_provider(provider_id)
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
-
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
-
- return builtin_tool.fork_tool_runtime(
- runtime={
- "tenant_id": tenant_id,
- "credentials": decrypted_credentials,
- "runtime_parameters": {},
- "invoke_from": invoke_from,
- "tool_invoke_from": tool_invoke_from,
- }
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
)
- elif provider_type == "api":
- if tenant_id is None:
- raise ValueError("tenant id is required for api provider")
+ decrypted_credentials = tool_configuration.decrypt(credentials)
+
+ return cast(
+ BuiltinTool,
+ builtin_tool.fork_tool_runtime(
+ runtime=ToolRuntime(
+ tenant_id=tenant_id,
+ credentials=decrypted_credentials,
+ runtime_parameters={},
+ invoke_from=invoke_from,
+ tool_invoke_from=tool_invoke_from,
+ )
+ ),
+ )
+ elif provider_type == ToolProviderType.API:
api_provider, credentials = cls.get_api_provider_controller(tenant_id, provider_id)
# decrypt the credentials
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=api_provider)
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
-
- return api_provider.get_tool(tool_name).fork_tool_runtime(
- runtime={
- "tenant_id": tenant_id,
- "credentials": decrypted_credentials,
- "invoke_from": invoke_from,
- "tool_invoke_from": tool_invoke_from,
- }
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in api_provider.get_credentials_schema()],
+ provider_type=api_provider.provider_type.value,
+ provider_identity=api_provider.entity.identity.name,
)
- elif provider_type == "workflow":
- workflow_provider: Optional[WorkflowToolProvider] = (
+ decrypted_credentials = tool_configuration.decrypt(credentials)
+
+ return cast(
+ ApiTool,
+ api_provider.get_tool(tool_name).fork_tool_runtime(
+ runtime=ToolRuntime(
+ tenant_id=tenant_id,
+ credentials=decrypted_credentials,
+ invoke_from=invoke_from,
+ tool_invoke_from=tool_invoke_from,
+ )
+ ),
+ )
+ elif provider_type == ToolProviderType.WORKFLOW:
+ workflow_provider = (
db.session.query(WorkflowToolProvider)
.filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
.first()
@@ -193,50 +270,35 @@ def get_tool_runtime(
raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
controller = ToolTransformService.workflow_provider_to_controller(db_provider=workflow_provider)
- controller_tools: Optional[list[Tool]] = controller.get_tools(
- user_id="", tenant_id=workflow_provider.tenant_id
- )
+ controller_tools: list[WorkflowTool] = controller.get_tools(tenant_id=workflow_provider.tenant_id)
if controller_tools is None or len(controller_tools) == 0:
raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
- return controller_tools[0].fork_tool_runtime(
- runtime={
- "tenant_id": tenant_id,
- "credentials": {},
- "invoke_from": invoke_from,
- "tool_invoke_from": tool_invoke_from,
- }
+ return cast(
+ WorkflowTool,
+ controller.get_tools(tenant_id=workflow_provider.tenant_id)[0].fork_tool_runtime(
+ runtime=ToolRuntime(
+ tenant_id=tenant_id,
+ credentials={},
+ invoke_from=invoke_from,
+ tool_invoke_from=tool_invoke_from,
+ )
+ ),
)
- elif provider_type == "app":
+ elif provider_type == ToolProviderType.APP:
raise NotImplementedError("app provider not implemented")
+ elif provider_type == ToolProviderType.PLUGIN:
+ return cls.get_plugin_provider(provider_id, tenant_id).get_tool(tool_name)
else:
- raise ToolProviderNotFoundError(f"provider type {provider_type} not found")
-
- @classmethod
- def _init_runtime_parameter(cls, parameter_rule: ToolParameter, parameters: dict):
- """
- init runtime parameter
- """
- parameter_value = parameters.get(parameter_rule.name)
- if not parameter_value and parameter_value != 0:
- # get default value
- parameter_value = parameter_rule.default
- if not parameter_value and parameter_rule.required:
- raise ValueError(f"tool parameter {parameter_rule.name} not found in tool config")
-
- if parameter_rule.type == ToolParameter.ToolParameterType.SELECT:
- # check if tool_parameter_config in options
- options = [x.value for x in parameter_rule.options or []]
- if parameter_value is not None and parameter_value not in options:
- raise ValueError(
- f"tool parameter {parameter_rule.name} value {parameter_value} not in options {options}"
- )
-
- return parameter_rule.type.cast_value(parameter_value)
+ raise ToolProviderNotFoundError(f"provider type {provider_type.value} not found")
@classmethod
def get_agent_tool_runtime(
- cls, tenant_id: str, app_id: str, agent_tool: AgentToolEntity, invoke_from: InvokeFrom = InvokeFrom.DEBUGGER
+ cls,
+ tenant_id: str,
+ app_id: str,
+ agent_tool: AgentToolEntity,
+ invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
) -> Tool:
"""
get the agent tool runtime
@@ -250,7 +312,7 @@ def get_agent_tool_runtime(
tool_invoke_from=ToolInvokeFrom.AGENT,
)
runtime_parameters = {}
- parameters = tool_entity.get_all_runtime_parameters()
+ parameters = tool_entity.get_merged_runtime_parameters()
for parameter in parameters:
# check file types
if (
@@ -266,7 +328,7 @@ def get_agent_tool_runtime(
if parameter.form == ToolParameter.ToolParameterForm.FORM:
# save tool parameter to tool entity memory
- value = cls._init_runtime_parameter(parameter, agent_tool.tool_parameters)
+ value = parameter.init_frontend_parameter(agent_tool.tool_parameters.get(parameter.name))
runtime_parameters[parameter.name] = value
# decrypt runtime parameters
@@ -296,7 +358,7 @@ def get_workflow_tool_runtime(
"""
get the workflow tool runtime
"""
- tool_entity = cls.get_tool_runtime(
+ tool_runtime = cls.get_tool_runtime(
provider_type=workflow_tool.provider_type,
provider_id=workflow_tool.provider_id,
tool_name=workflow_tool.tool_name,
@@ -305,18 +367,18 @@ def get_workflow_tool_runtime(
tool_invoke_from=ToolInvokeFrom.WORKFLOW,
)
runtime_parameters = {}
- parameters = tool_entity.get_all_runtime_parameters()
+ parameters = tool_runtime.get_merged_runtime_parameters()
for parameter in parameters:
# save tool parameter to tool entity memory
if parameter.form == ToolParameter.ToolParameterForm.FORM:
- value = cls._init_runtime_parameter(parameter, workflow_tool.tool_configurations)
+ value = parameter.init_frontend_parameter(workflow_tool.tool_configurations.get(parameter.name))
runtime_parameters[parameter.name] = value
# decrypt runtime parameters
encryption_manager = ToolParameterConfigurationManager(
tenant_id=tenant_id,
- tool_runtime=tool_entity,
+ tool_runtime=tool_runtime,
provider_name=workflow_tool.provider_id,
provider_type=workflow_tool.provider_type,
identity_id=f"WORKFLOW.{app_id}.{node_id}",
@@ -325,33 +387,60 @@ def get_workflow_tool_runtime(
if runtime_parameters:
runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
- if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None:
- raise ValueError("runtime not found or runtime parameters not found")
+ tool_runtime.runtime.runtime_parameters.update(runtime_parameters)
+ return tool_runtime
+
+ @classmethod
+ def get_tool_runtime_from_plugin(
+ cls,
+ tool_type: ToolProviderType,
+ tenant_id: str,
+ provider: str,
+ tool_name: str,
+ tool_parameters: dict[str, Any],
+ ) -> Tool:
+ """
+ get tool runtime from plugin
+ """
+ tool_entity = cls.get_tool_runtime(
+ provider_type=tool_type,
+ provider_id=provider,
+ tool_name=tool_name,
+ tenant_id=tenant_id,
+ invoke_from=InvokeFrom.SERVICE_API,
+ tool_invoke_from=ToolInvokeFrom.PLUGIN,
+ )
+ runtime_parameters = {}
+ parameters = tool_entity.get_merged_runtime_parameters()
+ for parameter in parameters:
+ if parameter.form == ToolParameter.ToolParameterForm.FORM:
+ # save tool parameter to tool entity memory
+ value = parameter.init_frontend_parameter(tool_parameters.get(parameter.name))
+ runtime_parameters[parameter.name] = value
tool_entity.runtime.runtime_parameters.update(runtime_parameters)
return tool_entity
@classmethod
- def get_builtin_provider_icon(cls, provider: str) -> tuple[str, str]:
+ def get_hardcoded_provider_icon(cls, provider: str) -> tuple[str, str]:
"""
- get the absolute path of the icon of the builtin provider
+ get the absolute path of the icon of the hardcoded provider
:param provider: the name of the provider
+ :param tenant_id: the id of the tenant
:return: the absolute path of the icon, the mime type of the icon
"""
# get provider
- provider_controller = cls.get_builtin_provider(provider)
- if provider_controller.identity is None:
- raise ToolProviderNotFoundError(f"builtin provider {provider} not found")
+ provider_controller = cls.get_hardcoded_provider(provider)
absolute_path = path.join(
path.dirname(path.realpath(__file__)),
- "provider",
- "builtin",
+ "builtin_tool",
+ "providers",
provider,
"_assets",
- provider_controller.identity.icon,
+ provider_controller.entity.identity.icon,
)
# check if the icon exists
if not path.exists(absolute_path):
@@ -364,65 +453,93 @@ def get_builtin_provider_icon(cls, provider: str) -> tuple[str, str]:
return absolute_path, mime_type
@classmethod
- def list_builtin_providers(cls) -> Generator[BuiltinToolProviderController, None, None]:
+ def list_hardcoded_providers(cls):
# use cache first
if cls._builtin_providers_loaded:
- yield from list(cls._builtin_providers.values())
+ yield from list(cls._hardcoded_providers.values())
return
with cls._builtin_provider_lock:
if cls._builtin_providers_loaded:
- yield from list(cls._builtin_providers.values())
+ yield from list(cls._hardcoded_providers.values())
return
- yield from cls._list_builtin_providers()
+ yield from cls._list_hardcoded_providers()
@classmethod
- def _list_builtin_providers(cls) -> Generator[BuiltinToolProviderController, None, None]:
+ def list_plugin_providers(cls, tenant_id: str) -> list[PluginToolProviderController]:
+ """
+ list all the plugin providers
+ """
+ manager = PluginToolManager()
+ provider_entities = manager.fetch_tool_providers(tenant_id)
+ return [
+ PluginToolProviderController(
+ entity=provider.declaration,
+ plugin_id=provider.plugin_id,
+ plugin_unique_identifier=provider.plugin_unique_identifier,
+ tenant_id=tenant_id,
+ )
+ for provider in provider_entities
+ ]
+
+ @classmethod
+ def list_builtin_providers(
+ cls, tenant_id: str
+ ) -> Generator[BuiltinToolProviderController | PluginToolProviderController, None, None]:
"""
list all the builtin providers
"""
- for provider in listdir(path.join(path.dirname(path.realpath(__file__)), "provider", "builtin")):
- if provider.startswith("__"):
+ yield from cls.list_hardcoded_providers()
+ # get plugin providers
+ yield from cls.list_plugin_providers(tenant_id)
+
+ @classmethod
+ def _list_hardcoded_providers(cls) -> Generator[BuiltinToolProviderController, None, None]:
+ """
+ list all the builtin providers
+ """
+ for provider_path in listdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers")):
+ if provider_path.startswith("__"):
continue
- if path.isdir(path.join(path.dirname(path.realpath(__file__)), "provider", "builtin", provider)):
- if provider.startswith("__"):
+ if path.isdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers", provider_path)):
+ if provider_path.startswith("__"):
continue
# init provider
try:
provider_class = load_single_subclass_from_source(
- module_name=f"core.tools.provider.builtin.{provider}.{provider}",
+ module_name=f"core.tools.builtin_tool.providers.{provider_path}.{provider_path}",
script_path=path.join(
- path.dirname(path.realpath(__file__)), "provider", "builtin", provider, f"{provider}.py"
+ path.dirname(path.realpath(__file__)),
+ "builtin_tool",
+ "providers",
+ provider_path,
+ f"{provider_path}.py",
),
parent_type=BuiltinToolProviderController,
)
- provider_controller: BuiltinToolProviderController = provider_class()
- if provider_controller.identity is None:
- continue
- cls._builtin_providers[provider_controller.identity.name] = provider_controller
- for tool in provider_controller.get_tools() or []:
- if tool.identity is None:
- continue
- cls._builtin_tools_labels[tool.identity.name] = tool.identity.label
- yield provider_controller
-
- except Exception as e:
+ provider: BuiltinToolProviderController = provider_class()
+ cls._hardcoded_providers[provider.entity.identity.name] = provider
+ for tool in provider.get_tools():
+ cls._builtin_tools_labels[tool.entity.identity.name] = tool.entity.identity.label
+ yield provider
+
+ except Exception:
logger.exception(f"load builtin provider {provider}")
continue
# set builtin providers loaded
cls._builtin_providers_loaded = True
@classmethod
- def load_builtin_providers_cache(cls):
- for _ in cls.list_builtin_providers():
+ def load_hardcoded_providers_cache(cls):
+ for _ in cls.list_hardcoded_providers():
pass
@classmethod
- def clear_builtin_providers_cache(cls):
- cls._builtin_providers = {}
+ def clear_hardcoded_providers_cache(cls):
+ cls._hardcoded_providers = {}
cls._builtin_providers_loaded = False
@classmethod
@@ -436,7 +553,7 @@ def get_tool_label(cls, tool_name: str) -> Union[I18nObject, None]:
"""
if len(cls._builtin_tools_labels) == 0:
# init the builtin providers
- cls.load_builtin_providers_cache()
+ cls.load_hardcoded_providers_cache()
if tool_name not in cls._builtin_tools_labels:
return None
@@ -444,10 +561,10 @@ def get_tool_label(cls, tool_name: str) -> Union[I18nObject, None]:
return cls._builtin_tools_labels[tool_name]
@classmethod
- def user_list_providers(
- cls, user_id: str, tenant_id: str, typ: UserToolProviderTypeLiteral
- ) -> list[UserToolProvider]:
- result_providers: dict[str, UserToolProvider] = {}
+ def list_providers_from_api(
+ cls, user_id: str, tenant_id: str, typ: ToolProviderTypeApiLiteral
+ ) -> list[ToolProviderApiEntity]:
+ result_providers: dict[str, ToolProviderApiEntity] = {}
filters = []
if not typ:
@@ -455,90 +572,96 @@ def user_list_providers(
else:
filters.append(typ)
- if "builtin" in filters:
- # get builtin providers
- builtin_providers = cls.list_builtin_providers()
-
- # get db builtin providers
- db_builtin_providers: list[BuiltinToolProvider] = (
- db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
- )
-
- find_db_builtin_provider = lambda provider: next(
- (x for x in db_builtin_providers if x.provider == provider), None
- )
-
- # append builtin providers
- for provider in builtin_providers:
- # handle include, exclude
- if provider.identity is None:
- continue
- if is_filtered(
- include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET),
- exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET),
- data=provider,
- name_func=lambda x: x.identity.name,
- ):
- continue
+ with db.session.no_autoflush:
+ if "builtin" in filters:
+ # get builtin providers
+ builtin_providers = cls.list_builtin_providers(tenant_id)
- user_provider = ToolTransformService.builtin_provider_to_user_provider(
- provider_controller=provider,
- db_provider=find_db_builtin_provider(provider.identity.name),
- decrypt_credentials=False,
+ # get db builtin providers
+ db_builtin_providers: list[BuiltinToolProvider] = (
+ db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
)
- result_providers[provider.identity.name] = user_provider
-
- # get db api providers
+ # rewrite db_builtin_providers
+ for db_provider in db_builtin_providers:
+ tool_provider_id = str(ToolProviderID(db_provider.provider))
+ db_provider.provider = tool_provider_id
+
+ def find_db_builtin_provider(provider):
+ return next((x for x in db_builtin_providers if x.provider == provider), None)
+
+ # append builtin providers
+ for provider in builtin_providers:
+ # handle include, exclude
+ if is_filtered(
+ include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET),
+ exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET),
+ data=provider,
+ name_func=lambda x: x.identity.name,
+ ):
+ continue
- if "api" in filters:
- db_api_providers: list[ApiToolProvider] = (
- db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all()
- )
+ user_provider = ToolTransformService.builtin_provider_to_user_provider(
+ provider_controller=provider,
+ db_provider=find_db_builtin_provider(provider.entity.identity.name),
+ decrypt_credentials=False,
+ )
- api_provider_controllers: list[dict[str, Any]] = [
- {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)}
- for provider in db_api_providers
- ]
+ if isinstance(provider, PluginToolProviderController):
+ result_providers[f"plugin_provider.{user_provider.name}"] = user_provider
+ else:
+ result_providers[f"builtin_provider.{user_provider.name}"] = user_provider
- # get labels
- labels = ToolLabelManager.get_tools_labels([x["controller"] for x in api_provider_controllers])
+ # get db api providers
- for api_provider_controller in api_provider_controllers:
- user_provider = ToolTransformService.api_provider_to_user_provider(
- provider_controller=api_provider_controller["controller"],
- db_provider=api_provider_controller["provider"],
- decrypt_credentials=False,
- labels=labels.get(api_provider_controller["controller"].provider_id, []),
+ if "api" in filters:
+ db_api_providers: list[ApiToolProvider] = (
+ db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all()
)
- result_providers[f"api_provider.{user_provider.name}"] = user_provider
- if "workflow" in filters:
- # get workflow providers
- workflow_providers: list[WorkflowToolProvider] = (
- db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
- )
+ api_provider_controllers: list[dict[str, Any]] = [
+ {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)}
+ for provider in db_api_providers
+ ]
- workflow_provider_controllers: list[WorkflowToolProviderController] = []
- for provider in workflow_providers:
- try:
- workflow_provider_controllers.append(
- ToolTransformService.workflow_provider_to_controller(db_provider=provider)
+ # get labels
+ labels = ToolLabelManager.get_tools_labels([x["controller"] for x in api_provider_controllers])
+
+ for api_provider_controller in api_provider_controllers:
+ user_provider = ToolTransformService.api_provider_to_user_provider(
+ provider_controller=api_provider_controller["controller"],
+ db_provider=api_provider_controller["provider"],
+ decrypt_credentials=False,
+ labels=labels.get(api_provider_controller["controller"].provider_id, []),
)
- except Exception as e:
- # app has been deleted
- pass
+ result_providers[f"api_provider.{user_provider.name}"] = user_provider
- labels = ToolLabelManager.get_tools_labels(
- [cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
- )
+ if "workflow" in filters:
+ # get workflow providers
+ workflow_providers: list[WorkflowToolProvider] = (
+ db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
+ )
- for provider_controller in workflow_provider_controllers:
- user_provider = ToolTransformService.workflow_provider_to_user_provider(
- provider_controller=provider_controller,
- labels=labels.get(provider_controller.provider_id, []),
+ workflow_provider_controllers: list[WorkflowToolProviderController] = []
+ for provider in workflow_providers:
+ try:
+ workflow_provider_controllers.append(
+ ToolTransformService.workflow_provider_to_controller(db_provider=provider)
+ )
+ except Exception:
+ # app has been deleted
+ pass
+
+ labels = ToolLabelManager.get_tools_labels(
+ [cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
)
- result_providers[f"workflow_provider.{user_provider.name}"] = user_provider
+
+ for provider_controller in workflow_provider_controllers:
+ user_provider = ToolTransformService.workflow_provider_to_user_provider(
+ provider_controller=provider_controller,
+ labels=labels.get(provider_controller.provider_id, []),
+ )
+ result_providers[f"workflow_provider.{user_provider.name}"] = user_provider
return BuiltinToolProviderSort.sort(list(result_providers.values()))
@@ -553,7 +676,7 @@ def get_api_provider_controller(
:return: the provider controller, the credentials
"""
- provider: Optional[ApiToolProvider] = (
+ provider: ApiToolProvider | None = (
db.session.query(ApiToolProvider)
.filter(
ApiToolProvider.id == provider_id,
@@ -582,7 +705,7 @@ def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
get tool provider
"""
provider_name = provider
- provider_tool: Optional[ApiToolProvider] = (
+ provider_obj: ApiToolProvider | None = (
db.session.query(ApiToolProvider)
.filter(
ApiToolProvider.tenant_id == tenant_id,
@@ -591,28 +714,33 @@ def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
.first()
)
- if provider_tool is None:
+ if provider_obj is None:
raise ValueError(f"you have not added provider {provider_name}")
try:
- credentials = json.loads(provider_tool.credentials_str) or {}
- except:
+ credentials = json.loads(provider_obj.credentials_str) or {}
+ except Exception:
credentials = {}
# package tool provider controller
controller = ApiToolProviderController.from_db(
- provider_tool,
+ provider_obj,
ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
)
# init tool configuration
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in controller.get_credentials_schema()],
+ provider_type=controller.provider_type.value,
+ provider_identity=controller.entity.identity.name,
+ )
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
+ decrypted_credentials = tool_configuration.decrypt(credentials)
masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
try:
- icon = json.loads(provider_tool.icon)
- except:
+ icon = json.loads(provider_obj.icon)
+ except Exception:
icon = {"background": "#252525", "content": "\ud83d\ude01"}
# add tool labels
@@ -622,21 +750,82 @@ def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
dict,
jsonable_encoder(
{
- "schema_type": provider_tool.schema_type,
- "schema": provider_tool.schema,
- "tools": provider_tool.tools,
+ "schema_type": provider_obj.schema_type,
+ "schema": provider_obj.schema,
+ "tools": provider_obj.tools,
"icon": icon,
- "description": provider_tool.description,
+ "description": provider_obj.description,
"credentials": masked_credentials,
- "privacy_policy": provider_tool.privacy_policy,
- "custom_disclaimer": provider_tool.custom_disclaimer,
+ "privacy_policy": provider_obj.privacy_policy,
+ "custom_disclaimer": provider_obj.custom_disclaimer,
"labels": labels,
}
),
)
@classmethod
- def get_tool_icon(cls, tenant_id: str, provider_type: str, provider_id: str) -> Union[str, dict]:
+ def generate_builtin_tool_icon_url(cls, provider_id: str) -> str:
+ return (
+ dify_config.CONSOLE_API_URL
+ + "/console/api/workspaces/current/tool-provider/builtin/"
+ + provider_id
+ + "/icon"
+ )
+
+ @classmethod
+ def generate_plugin_tool_icon_url(cls, tenant_id: str, filename: str) -> str:
+ return str(
+ URL(dify_config.CONSOLE_API_URL)
+ / "console"
+ / "api"
+ / "workspaces"
+ / "current"
+ / "plugin"
+ / "icon"
+ % {"tenant_id": tenant_id, "filename": filename}
+ )
+
+ @classmethod
+ def generate_workflow_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
+ try:
+ workflow_provider: WorkflowToolProvider | None = (
+ db.session.query(WorkflowToolProvider)
+ .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
+ .first()
+ )
+
+ if workflow_provider is None:
+ raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
+
+ icon: dict = json.loads(workflow_provider.icon)
+ return icon
+ except Exception:
+ return {"background": "#252525", "content": "\ud83d\ude01"}
+
+ @classmethod
+ def generate_api_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
+ try:
+ api_provider: ApiToolProvider | None = (
+ db.session.query(ApiToolProvider)
+ .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id)
+ .first()
+ )
+
+ if api_provider is None:
+ raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
+
+ icon: dict = json.loads(api_provider.icon)
+ return icon
+ except Exception:
+ return {"background": "#252525", "content": "\ud83d\ude01"}
+
+ @classmethod
+ def get_tool_icon(
+ cls,
+ tenant_id: str,
+ provider_type: ToolProviderType,
+ provider_id: str,
+ ) -> Union[str, dict]:
"""
get the tool icon
@@ -647,48 +836,28 @@ def get_tool_icon(cls, tenant_id: str, provider_type: str, provider_id: str) ->
"""
provider_type = provider_type
provider_id = provider_id
- provider: Optional[Union[BuiltinToolProvider, ApiToolProvider, WorkflowToolProvider]] = None
- if provider_type == "builtin":
- return (
- dify_config.CONSOLE_API_URL
- + "/console/api/workspaces/current/tool-provider/builtin/"
- + provider_id
- + "/icon"
- )
- elif provider_type == "api":
- try:
- provider = (
- db.session.query(ApiToolProvider)
- .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id)
- .first()
- )
- if provider is None:
- raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
- icon = json.loads(provider.icon)
- if isinstance(icon, (str, dict)):
- return icon
- return {"background": "#252525", "content": "\ud83d\ude01"}
- except:
- return {"background": "#252525", "content": "\ud83d\ude01"}
- elif provider_type == "workflow":
- provider = (
- db.session.query(WorkflowToolProvider)
- .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
- .first()
- )
- if provider is None:
- raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
-
- try:
- icon = json.loads(provider.icon)
- if isinstance(icon, (str, dict)):
- return icon
- return {"background": "#252525", "content": "\ud83d\ude01"}
- except:
- return {"background": "#252525", "content": "\ud83d\ude01"}
+ if provider_type == ToolProviderType.BUILT_IN:
+ provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
+ if isinstance(provider, PluginToolProviderController):
+ try:
+ return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
+ except Exception:
+ return {"background": "#252525", "content": "\ud83d\ude01"}
+ return cls.generate_builtin_tool_icon_url(provider_id)
+ elif provider_type == ToolProviderType.API:
+ return cls.generate_api_tool_icon_url(tenant_id, provider_id)
+ elif provider_type == ToolProviderType.WORKFLOW:
+ return cls.generate_workflow_tool_icon_url(tenant_id, provider_id)
+ elif provider_type == ToolProviderType.PLUGIN:
+ provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
+ if isinstance(provider, PluginToolProviderController):
+ try:
+ return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
+ except Exception:
+ return {"background": "#252525", "content": "\ud83d\ude01"}
+ raise ValueError(f"plugin provider {provider_id} not found")
else:
raise ValueError(f"provider type {provider_type} not found")
-# preload builtin tool providers
-Thread(target=ToolManager.load_builtin_providers_cache, name="pre_load_builtin_providers_cache", daemon=True).start()
+ToolManager.load_hardcoded_providers_cache()
diff --git a/api/core/tools/utils/configuration.py b/api/core/tools/utils/configuration.py
index d7720928644701..6a5fba65bd92e0 100644
--- a/api/core/tools/utils/configuration.py
+++ b/api/core/tools/utils/configuration.py
@@ -3,115 +3,120 @@
from pydantic import BaseModel
+from core.entities.provider_entities import BasicProviderConfig
from core.helper import encrypter
from core.helper.tool_parameter_cache import ToolParameterCache, ToolParameterCacheType
from core.helper.tool_provider_cache import ToolProviderCredentialsCache, ToolProviderCredentialsCacheType
+from core.tools.__base.tool import Tool
from core.tools.entities.tool_entities import (
ToolParameter,
- ToolProviderCredentials,
+ ToolProviderType,
)
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.tool.tool import Tool
-class ToolConfigurationManager(BaseModel):
+class ProviderConfigEncrypter(BaseModel):
tenant_id: str
- provider_controller: ToolProviderController
+ config: list[BasicProviderConfig]
+ provider_type: str
+ provider_identity: str
- def _deep_copy(self, credentials: dict[str, str]) -> dict[str, str]:
+ def _deep_copy(self, data: dict[str, str]) -> dict[str, str]:
"""
- deep copy credentials
+ deep copy data
"""
- return deepcopy(credentials)
+ return deepcopy(data)
- def encrypt_tool_credentials(self, credentials: dict[str, str]) -> dict[str, str]:
+ def encrypt(self, data: dict[str, str]) -> dict[str, str]:
"""
encrypt tool credentials with tenant id
return a deep copy of credentials with encrypted values
"""
- credentials = self._deep_copy(credentials)
+ data = self._deep_copy(data)
# get fields need to be decrypted
- fields = self.provider_controller.get_credentials_schema()
+ fields = dict[str, BasicProviderConfig]()
+ for credential in self.config:
+ fields[credential.name] = credential
+
for field_name, field in fields.items():
- if field.type == ToolProviderCredentials.CredentialsType.SECRET_INPUT:
- if field_name in credentials:
- encrypted = encrypter.encrypt_token(self.tenant_id, credentials[field_name])
- credentials[field_name] = encrypted
+ if field.type == BasicProviderConfig.Type.SECRET_INPUT:
+ if field_name in data:
+ encrypted = encrypter.encrypt_token(self.tenant_id, data[field_name] or "")
+ data[field_name] = encrypted
- return credentials
+ return data
- def mask_tool_credentials(self, credentials: dict[str, Any]) -> dict[str, Any]:
+ def mask_tool_credentials(self, data: dict[str, Any]) -> dict[str, Any]:
"""
mask tool credentials
return a deep copy of credentials with masked values
"""
- credentials = self._deep_copy(credentials)
+ data = self._deep_copy(data)
# get fields need to be decrypted
- fields = self.provider_controller.get_credentials_schema()
+ fields = dict[str, BasicProviderConfig]()
+ for credential in self.config:
+ fields[credential.name] = credential
+
for field_name, field in fields.items():
- if field.type == ToolProviderCredentials.CredentialsType.SECRET_INPUT:
- if field_name in credentials:
- if len(credentials[field_name]) > 6:
- credentials[field_name] = (
- credentials[field_name][:2]
- + "*" * (len(credentials[field_name]) - 4)
- + credentials[field_name][-2:]
+ if field.type == BasicProviderConfig.Type.SECRET_INPUT:
+ if field_name in data:
+ if len(data[field_name]) > 6:
+ data[field_name] = (
+ data[field_name][:2] + "*" * (len(data[field_name]) - 4) + data[field_name][-2:]
)
else:
- credentials[field_name] = "*" * len(credentials[field_name])
+ data[field_name] = "*" * len(data[field_name])
- return credentials
+ return data
- def decrypt_tool_credentials(self, credentials: dict[str, str]) -> dict[str, str]:
+ def decrypt(self, data: dict[str, str]) -> dict[str, str]:
"""
decrypt tool credentials with tenant id
return a deep copy of credentials with decrypted values
"""
- identity_id = ""
- if self.provider_controller.identity:
- identity_id = f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}"
-
cache = ToolProviderCredentialsCache(
tenant_id=self.tenant_id,
- identity_id=identity_id,
+ identity_id=f"{self.provider_type}.{self.provider_identity}",
cache_type=ToolProviderCredentialsCacheType.PROVIDER,
)
cached_credentials = cache.get()
if cached_credentials:
return cached_credentials
- credentials = self._deep_copy(credentials)
+ data = self._deep_copy(data)
# get fields need to be decrypted
- fields = self.provider_controller.get_credentials_schema()
+ fields = dict[str, BasicProviderConfig]()
+ for credential in self.config:
+ fields[credential.name] = credential
+
for field_name, field in fields.items():
- if field.type == ToolProviderCredentials.CredentialsType.SECRET_INPUT:
- if field_name in credentials:
+ if field.type == BasicProviderConfig.Type.SECRET_INPUT:
+ if field_name in data:
try:
- credentials[field_name] = encrypter.decrypt_token(self.tenant_id, credentials[field_name])
- except:
+ # if the value is None or empty string, skip decrypt
+ if not data[field_name]:
+ continue
+
+ data[field_name] = encrypter.decrypt_token(self.tenant_id, data[field_name])
+ except Exception:
pass
- cache.set(credentials)
- return credentials
+ cache.set(data)
+ return data
def delete_tool_credentials_cache(self):
- identity_id = ""
- if self.provider_controller.identity:
- identity_id = f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}"
-
cache = ToolProviderCredentialsCache(
tenant_id=self.tenant_id,
- identity_id=identity_id,
+ identity_id=f"{self.provider_type}.{self.provider_identity}",
cache_type=ToolProviderCredentialsCacheType.PROVIDER,
)
cache.delete()
-class ToolParameterConfigurationManager(BaseModel):
+class ToolParameterConfigurationManager:
"""
Tool parameter configuration manager
"""
@@ -119,9 +124,18 @@ class ToolParameterConfigurationManager(BaseModel):
tenant_id: str
tool_runtime: Tool
provider_name: str
- provider_type: str
+ provider_type: ToolProviderType
identity_id: str
+ def __init__(
+ self, tenant_id: str, tool_runtime: Tool, provider_name: str, provider_type: ToolProviderType, identity_id: str
+ ) -> None:
+ self.tenant_id = tenant_id
+ self.tool_runtime = tool_runtime
+ self.provider_name = provider_name
+ self.provider_type = provider_type
+ self.identity_id = identity_id
+
def _deep_copy(self, parameters: dict[str, Any]) -> dict[str, Any]:
"""
deep copy parameters
@@ -133,7 +147,7 @@ def _merge_parameters(self) -> list[ToolParameter]:
merge parameters
"""
# get tool parameters
- tool_parameters = self.tool_runtime.parameters or []
+ tool_parameters = self.tool_runtime.entity.parameters or []
# get tool runtime parameters
runtime_parameters = self.tool_runtime.get_runtime_parameters()
# override parameters
@@ -207,13 +221,11 @@ def decrypt_tool_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
return a deep copy of parameters with decrypted values
"""
- if self.tool_runtime is None or self.tool_runtime.identity is None:
- raise ValueError("tool_runtime is required")
cache = ToolParameterCache(
tenant_id=self.tenant_id,
- provider=f"{self.provider_type}.{self.provider_name}",
- tool_name=self.tool_runtime.identity.name,
+ provider=f"{self.provider_type.value}.{self.provider_name}",
+ tool_name=self.tool_runtime.entity.identity.name,
cache_type=ToolParameterCacheType.PARAMETER,
identity_id=self.identity_id,
)
@@ -234,7 +246,7 @@ def decrypt_tool_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
try:
has_secret_input = True
parameters[parameter.name] = encrypter.decrypt_token(self.tenant_id, parameters[parameter.name])
- except:
+ except Exception:
pass
if has_secret_input:
@@ -243,13 +255,10 @@ def decrypt_tool_parameters(self, parameters: dict[str, Any]) -> dict[str, Any]:
return parameters
def delete_tool_parameters_cache(self):
- if self.tool_runtime is None or self.tool_runtime.identity is None:
- raise ValueError("tool_runtime is required")
-
cache = ToolParameterCache(
tenant_id=self.tenant_id,
- provider=f"{self.provider_type}.{self.provider_name}",
- tool_name=self.tool_runtime.identity.name,
+ provider=f"{self.provider_type.value}.{self.provider_name}",
+ tool_name=self.tool_runtime.entity.identity.name,
cache_type=ToolParameterCacheType.PARAMETER,
identity_id=self.identity_id,
)
diff --git a/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py b/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py
similarity index 98%
rename from api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py
rename to api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py
index a4afea4b9df429..802bee42176af5 100644
--- a/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py
+++ b/api/core/tools/utils/dataset_retriever/dataset_multi_retriever_tool.py
@@ -11,7 +11,7 @@
from core.rag.models.document import Document as RagDocument
from core.rag.rerank.rerank_model import RerankModelRunner
from core.rag.retrieval.retrieval_methods import RetrievalMethod
-from core.tools.tool.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
+from core.tools.utils.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
from extensions.ext_database import db
from models.dataset import Dataset, Document, DocumentSegment
@@ -143,6 +143,8 @@ def _run(self, query: str) -> str:
return str("\n".join(document_context_list))
return ""
+ raise RuntimeError("not segments found")
+
def _retriever(
self,
flask_app: Flask,
diff --git a/api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py b/api/core/tools/utils/dataset_retriever/dataset_retriever_base_tool.py
similarity index 100%
rename from api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py
rename to api/core/tools/utils/dataset_retriever/dataset_retriever_base_tool.py
diff --git a/api/core/tools/utils/dataset_retriever/dataset_retriever_tool.py b/api/core/tools/utils/dataset_retriever/dataset_retriever_tool.py
new file mode 100644
index 00000000000000..946d99ef6ffd87
--- /dev/null
+++ b/api/core/tools/utils/dataset_retriever/dataset_retriever_tool.py
@@ -0,0 +1,201 @@
+from typing import Any
+
+from pydantic import BaseModel, Field
+
+from core.rag.datasource.retrieval_service import RetrievalService
+from core.rag.entities.context_entities import DocumentContext
+from core.rag.models.document import Document as RetrievalDocument
+from core.rag.retrieval.retrieval_methods import RetrievalMethod
+from core.tools.utils.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
+from extensions.ext_database import db
+from models.dataset import Dataset
+from models.dataset import Document as DatasetDocument
+from services.external_knowledge_service import ExternalDatasetService
+
+default_retrieval_model = {
+ "search_method": RetrievalMethod.SEMANTIC_SEARCH.value,
+ "reranking_enable": False,
+ "reranking_model": {"reranking_provider_name": "", "reranking_model_name": ""},
+ "reranking_mode": "reranking_model",
+ "top_k": 2,
+ "score_threshold_enabled": False,
+}
+
+
+class DatasetRetrieverToolInput(BaseModel):
+ query: str = Field(..., description="Query for the dataset to be used to retrieve the dataset.")
+
+
+class DatasetRetrieverTool(DatasetRetrieverBaseTool):
+ """Tool for querying a Dataset."""
+
+ name: str = "dataset"
+ args_schema: type[BaseModel] = DatasetRetrieverToolInput
+ description: str = "use this to retrieve a dataset. "
+ dataset_id: str
+
+ @classmethod
+ def from_dataset(cls, dataset: Dataset, **kwargs):
+ description = dataset.description
+ if not description:
+ description = "useful for when you want to answer queries about the " + dataset.name
+
+ description = description.replace("\n", "").replace("\r", "")
+ return cls(
+ name=f"dataset_{dataset.id.replace('-', '_')}",
+ tenant_id=dataset.tenant_id,
+ dataset_id=dataset.id,
+ description=description,
+ **kwargs,
+ )
+
+ def _run(self, query: str) -> str:
+ dataset = (
+ db.session.query(Dataset).filter(Dataset.tenant_id == self.tenant_id, Dataset.id == self.dataset_id).first()
+ )
+
+ if not dataset:
+ return ""
+ for hit_callback in self.hit_callbacks:
+ hit_callback.on_query(query, dataset.id)
+ if dataset.provider == "external":
+ results = []
+ external_documents = ExternalDatasetService.fetch_external_knowledge_retrieval(
+ tenant_id=dataset.tenant_id,
+ dataset_id=dataset.id,
+ query=query,
+ external_retrieval_parameters=dataset.retrieval_model,
+ )
+ for external_document in external_documents:
+ document = RetrievalDocument(
+ page_content=external_document.get("content"),
+ metadata=external_document.get("metadata"),
+ provider="external",
+ )
+ if document.metadata is not None:
+ document.metadata["score"] = external_document.get("score")
+ document.metadata["title"] = external_document.get("title")
+ document.metadata["dataset_id"] = dataset.id
+ document.metadata["dataset_name"] = dataset.name
+ results.append(document)
+ # deal with external documents
+ context_list = []
+ for position, item in enumerate(results, start=1):
+ if item.metadata is not None:
+ source = {
+ "position": position,
+ "dataset_id": item.metadata.get("dataset_id"),
+ "dataset_name": item.metadata.get("dataset_name"),
+ "document_name": item.metadata.get("title"),
+ "data_source_type": "external",
+ "retriever_from": self.retriever_from,
+ "score": item.metadata.get("score"),
+ "title": item.metadata.get("title"),
+ "content": item.page_content,
+ }
+ context_list.append(source)
+ for hit_callback in self.hit_callbacks:
+ hit_callback.return_retriever_resource_info(context_list)
+
+ return str("\n".join([item.page_content for item in results]))
+ else:
+ # get retrieval model , if the model is not setting , using default
+ retrieval_model: dict[str, Any] = dataset.retrieval_model or default_retrieval_model
+ if dataset.indexing_technique == "economy":
+ # use keyword table query
+ documents = RetrievalService.retrieve(
+ retrieval_method="keyword_search", dataset_id=dataset.id, query=query, top_k=self.top_k
+ )
+ return str("\n".join([document.page_content for document in documents]))
+ else:
+ if self.top_k > 0:
+ # retrieval source
+ documents = RetrievalService.retrieve(
+ retrieval_method=retrieval_model.get("search_method", "semantic_search"),
+ dataset_id=dataset.id,
+ query=query,
+ top_k=self.top_k,
+ score_threshold=retrieval_model.get("score_threshold", 0.0)
+ if retrieval_model["score_threshold_enabled"]
+ else 0.0,
+ reranking_model=retrieval_model.get("reranking_model")
+ if retrieval_model["reranking_enable"]
+ else None,
+ reranking_mode=retrieval_model.get("reranking_mode") or "reranking_model",
+ weights=retrieval_model.get("weights"),
+ )
+ else:
+ documents = []
+ for hit_callback in self.hit_callbacks:
+ hit_callback.on_tool_end(documents)
+ document_score_list = {}
+ if dataset.indexing_technique != "economy":
+ for item in documents:
+ if item.metadata is not None and item.metadata.get("score"):
+ document_score_list[item.metadata["doc_id"]] = item.metadata["score"]
+ document_context_list = []
+ records = RetrievalService.format_retrieval_documents(documents)
+ if records:
+ for record in records:
+ segment = record.segment
+ if segment.answer:
+ document_context_list.append(
+ DocumentContext(
+ content=f"question:{segment.get_sign_content()} answer:{segment.answer}",
+ score=record.score,
+ )
+ )
+ else:
+ document_context_list.append(
+ DocumentContext(
+ content=segment.get_sign_content(),
+ score=record.score,
+ )
+ )
+ retrieval_resource_list = []
+ if self.return_resource:
+ for record in records:
+ segment = record.segment
+ dataset = Dataset.query.filter_by(id=segment.dataset_id).first()
+ document = DatasetDocument.query.filter(
+ DatasetDocument.id == segment.document_id,
+ DatasetDocument.enabled == True,
+ DatasetDocument.archived == False,
+ ).first()
+ if dataset and document:
+ source = {
+ "dataset_id": dataset.id,
+ "dataset_name": dataset.name,
+ "document_id": document.id, # type: ignore
+ "document_name": document.name, # type: ignore
+ "data_source_type": document.data_source_type, # type: ignore
+ "segment_id": segment.id,
+ "retriever_from": self.retriever_from,
+ "score": record.score or 0.0,
+ }
+
+ if self.retriever_from == "dev":
+ source["hit_count"] = segment.hit_count
+ source["word_count"] = segment.word_count
+ source["segment_position"] = segment.position
+ source["index_node_hash"] = segment.index_node_hash
+ if segment.answer:
+ source["content"] = f"question:{segment.content} \nanswer:{segment.answer}"
+ else:
+ source["content"] = segment.content
+ retrieval_resource_list.append(source)
+
+ if self.return_resource and retrieval_resource_list:
+ retrieval_resource_list = sorted(
+ retrieval_resource_list,
+ key=lambda x: x.get("score") or 0.0,
+ reverse=True,
+ )
+ for position, item in enumerate(retrieval_resource_list, start=1): # type: ignore
+ item["position"] = position # type: ignore
+ for hit_callback in self.hit_callbacks:
+ hit_callback.return_retriever_resource_info(retrieval_resource_list)
+ if document_context_list:
+ document_context_list = sorted(document_context_list, key=lambda x: x.score or 0.0, reverse=True)
+ return str("\n".join([document_context.content for document_context in document_context_list]))
+ return ""
diff --git a/api/core/tools/utils/dataset_retriever_tool.py b/api/core/tools/utils/dataset_retriever_tool.py
new file mode 100644
index 00000000000000..b73dec4ebc5b28
--- /dev/null
+++ b/api/core/tools/utils/dataset_retriever_tool.py
@@ -0,0 +1,134 @@
+from collections.abc import Generator
+from typing import Any, Optional
+
+from core.app.app_config.entities import DatasetRetrieveConfigEntity
+from core.app.entities.app_invoke_entities import InvokeFrom
+from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCallbackHandler
+from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
+from core.tools.__base.tool import Tool
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.entities.common_entities import I18nObject
+from core.tools.entities.tool_entities import (
+ ToolDescription,
+ ToolEntity,
+ ToolIdentity,
+ ToolInvokeMessage,
+ ToolParameter,
+ ToolProviderType,
+)
+from core.tools.utils.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool
+
+
+class DatasetRetrieverTool(Tool):
+ retrieval_tool: DatasetRetrieverBaseTool
+
+ def __init__(self, entity: ToolEntity, runtime: ToolRuntime, retrieval_tool: DatasetRetrieverBaseTool) -> None:
+ super().__init__(entity, runtime)
+ self.retrieval_tool = retrieval_tool
+
+ @staticmethod
+ def get_dataset_tools(
+ tenant_id: str,
+ dataset_ids: list[str],
+ retrieve_config: DatasetRetrieveConfigEntity | None,
+ return_resource: bool,
+ invoke_from: InvokeFrom,
+ hit_callback: DatasetIndexToolCallbackHandler,
+ ) -> list["DatasetRetrieverTool"]:
+ """
+ get dataset tool
+ """
+ # check if retrieve_config is valid
+ if dataset_ids is None or len(dataset_ids) == 0:
+ return []
+ if retrieve_config is None:
+ return []
+
+ feature = DatasetRetrieval()
+
+ # save original retrieve strategy, and set retrieve strategy to SINGLE
+ # Agent only support SINGLE mode
+ original_retriever_mode = retrieve_config.retrieve_strategy
+ retrieve_config.retrieve_strategy = DatasetRetrieveConfigEntity.RetrieveStrategy.SINGLE
+ retrieval_tools = feature.to_dataset_retriever_tool(
+ tenant_id=tenant_id,
+ dataset_ids=dataset_ids,
+ retrieve_config=retrieve_config,
+ return_resource=return_resource,
+ invoke_from=invoke_from,
+ hit_callback=hit_callback,
+ )
+ if retrieval_tools is None or len(retrieval_tools) == 0:
+ return []
+
+ # restore retrieve strategy
+ retrieve_config.retrieve_strategy = original_retriever_mode
+
+ # convert retrieval tools to Tools
+ tools = []
+ for retrieval_tool in retrieval_tools:
+ tool = DatasetRetrieverTool(
+ retrieval_tool=retrieval_tool,
+ entity=ToolEntity(
+ identity=ToolIdentity(
+ provider="", author="", name=retrieval_tool.name, label=I18nObject(en_US="", zh_Hans="")
+ ),
+ parameters=[],
+ description=ToolDescription(human=I18nObject(en_US="", zh_Hans=""), llm=retrieval_tool.description),
+ ),
+ runtime=ToolRuntime(tenant_id=tenant_id),
+ )
+
+ tools.append(tool)
+
+ return tools
+
+ def get_runtime_parameters(
+ self,
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> list[ToolParameter]:
+ return [
+ ToolParameter(
+ name="query",
+ label=I18nObject(en_US="", zh_Hans=""),
+ human_description=I18nObject(en_US="", zh_Hans=""),
+ type=ToolParameter.ToolParameterType.STRING,
+ form=ToolParameter.ToolParameterForm.LLM,
+ llm_description="Query for the dataset to be used to retrieve the dataset.",
+ required=True,
+ default="",
+ placeholder=I18nObject(en_US="", zh_Hans=""),
+ ),
+ ]
+
+ def tool_provider_type(self) -> ToolProviderType:
+ return ToolProviderType.DATASET_RETRIEVAL
+
+ def _invoke(
+ self,
+ user_id: str,
+ tool_parameters: dict[str, Any],
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[ToolInvokeMessage, None, None]:
+ """
+ invoke dataset retriever tool
+ """
+ query = tool_parameters.get("query")
+ if not query:
+ yield self.create_text_message(text="please input query")
+ else:
+ # invoke dataset retriever tool
+ result = self.retrieval_tool._run(query=query)
+ yield self.create_text_message(text=result)
+
+ def validate_credentials(
+ self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False
+ ) -> str | None:
+ """
+ validate the credentials for dataset retriever tool
+ """
+ pass
diff --git a/api/core/tools/utils/feishu_api_utils.py b/api/core/tools/utils/feishu_api_utils.py
deleted file mode 100644
index ecf60045aa8dc5..00000000000000
--- a/api/core/tools/utils/feishu_api_utils.py
+++ /dev/null
@@ -1,919 +0,0 @@
-import json
-from typing import Any, Optional, cast
-
-import httpx
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from extensions.ext_redis import redis_client
-
-
-def auth(credentials):
- app_id = credentials.get("app_id")
- app_secret = credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ToolProviderCredentialValidationError("app_id and app_secret is required")
- try:
- assert FeishuRequest(app_id, app_secret).tenant_access_token is not None
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
-
-
-def convert_add_records(json_str):
- try:
- data = json.loads(json_str)
- if not isinstance(data, list):
- raise ValueError("Parsed data must be a list")
- converted_data = [{"fields": json.dumps(item, ensure_ascii=False)} for item in data]
- return converted_data
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- except Exception as e:
- raise ValueError(f"An error occurred while processing the data: {e}")
-
-
-def convert_update_records(json_str):
- try:
- data = json.loads(json_str)
- if not isinstance(data, list):
- raise ValueError("Parsed data must be a list")
-
- converted_data = [
- {"fields": json.dumps(record["fields"], ensure_ascii=False), "record_id": record["record_id"]}
- for record in data
- if "fields" in record and "record_id" in record
- ]
-
- if len(converted_data) != len(data):
- raise ValueError("Each record must contain 'fields' and 'record_id'")
-
- return converted_data
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- except Exception as e:
- raise ValueError(f"An error occurred while processing the data: {e}")
-
-
-class FeishuRequest:
- API_BASE_URL = "https://lark-plugin-api.solutionsuite.cn/lark-plugin"
-
- def __init__(self, app_id: str, app_secret: str):
- self.app_id = app_id
- self.app_secret = app_secret
-
- @property
- def tenant_access_token(self):
- feishu_tenant_access_token = f"tools:{self.app_id}:feishu_tenant_access_token"
- if redis_client.exists(feishu_tenant_access_token):
- return redis_client.get(feishu_tenant_access_token).decode()
- res = self.get_tenant_access_token(self.app_id, self.app_secret)
- redis_client.setex(feishu_tenant_access_token, res.get("expire"), res.get("tenant_access_token"))
- return res.get("tenant_access_token")
-
- def _send_request(
- self,
- url: str,
- method: str = "post",
- require_token: bool = True,
- payload: Optional[dict] = None,
- params: Optional[dict] = None,
- ):
- headers = {
- "Content-Type": "application/json",
- "user-agent": "Dify",
- }
- if require_token:
- headers["tenant-access-token"] = f"{self.tenant_access_token}"
- res = httpx.request(method=method, url=url, headers=headers, json=payload, params=params, timeout=30).json()
- if res.get("code") != 0:
- raise Exception(res)
- return res
-
- def get_tenant_access_token(self, app_id: str, app_secret: str) -> dict:
- """
- API url: https://open.feishu.cn/document/server-docs/authentication-management/access-token/tenant_access_token_internal
- Example Response:
- {
- "code": 0,
- "msg": "ok",
- "tenant_access_token": "t-caecc734c2e3328a62489fe0648c4b98779515d3",
- "expire": 7200
- }
- """
- url = f"{self.API_BASE_URL}/access_token/get_tenant_access_token"
- payload = {"app_id": app_id, "app_secret": app_secret}
- res: dict = self._send_request(url, require_token=False, payload=payload)
- return res
-
- def create_document(self, title: str, content: str, folder_token: str) -> dict:
- """
- API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/create
- Example Response:
- {
- "data": {
- "title": "title",
- "url": "https://svi136aogf123.feishu.cn/docx/VWbvd4fEdoW0WSxaY1McQTz8n7d",
- "type": "docx",
- "token": "VWbvd4fEdoW0WSxaY1McQTz8n7d"
- },
- "log_id": "021721281231575fdbddc0200ff00060a9258ec0000103df61b5d",
- "code": 0,
- "msg": "创建飞书文档成功,请查看"
- }
- """
- url = f"{self.API_BASE_URL}/document/create_document"
- payload = {
- "title": title,
- "content": content,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def write_document(self, document_id: str, content: str, position: str = "end") -> dict:
- url = f"{self.API_BASE_URL}/document/write_document"
- payload = {"document_id": document_id, "content": content, "position": position}
- res: dict = self._send_request(url, payload=payload)
- return res
-
- def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> str:
- """
- API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/raw_content
- Example Response:
- {
- "code": 0,
- "msg": "success",
- "data": {
- "content": "云文档\n多人实时协同,插入一切元素。不仅是在线文档,更是强大的创作和互动工具\n云文档:专为协作而生\n"
- }
- }
- """ # noqa: E501
- params = {
- "document_id": document_id,
- "mode": mode,
- "lang": lang,
- }
- url = f"{self.API_BASE_URL}/document/get_document_content"
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- return cast(str, res.get("data", {}).get("content"))
- return ""
-
- def list_document_blocks(
- self, document_id: str, page_token: str, user_id_type: str = "open_id", page_size: int = 500
- ) -> dict:
- """
- API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/list
- """
- params = {
- "user_id_type": user_id_type,
- "document_id": document_id,
- "page_size": page_size,
- "page_token": page_token,
- }
- url = f"{self.API_BASE_URL}/document/list_document_blocks"
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict:
- """
- API url: https://open.larkoffice.com/document/server-docs/im-v1/message/create
- """
- url = f"{self.API_BASE_URL}/message/send_bot_message"
- params = {
- "receive_id_type": receive_id_type,
- }
- payload = {
- "receive_id": receive_id,
- "msg_type": msg_type,
- "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict:
- url = f"{self.API_BASE_URL}/message/send_webhook_message"
- payload = {
- "webhook": webhook,
- "msg_type": msg_type,
- "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
- }
- res: dict = self._send_request(url, require_token=False, payload=payload)
- return res
-
- def get_chat_messages(
- self,
- container_id: str,
- start_time: str,
- end_time: str,
- page_token: str,
- sort_type: str = "ByCreateTimeAsc",
- page_size: int = 20,
- ) -> dict:
- """
- API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
- """
- url = f"{self.API_BASE_URL}/message/get_chat_messages"
- params = {
- "container_id": container_id,
- "start_time": start_time,
- "end_time": end_time,
- "sort_type": sort_type,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_thread_messages(
- self, container_id: str, page_token: str, sort_type: str = "ByCreateTimeAsc", page_size: int = 20
- ) -> dict:
- """
- API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
- """
- url = f"{self.API_BASE_URL}/message/get_thread_messages"
- params = {
- "container_id": container_id,
- "sort_type": sort_type,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict:
- # 创建任务
- url = f"{self.API_BASE_URL}/task/create_task"
- payload = {
- "summary": summary,
- "start_time": start_time,
- "end_time": end_time,
- "completed_at": completed_time,
- "description": description,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_task(
- self, task_guid: str, summary: str, start_time: str, end_time: str, completed_time: str, description: str
- ) -> dict:
- # 更新任务
- url = f"{self.API_BASE_URL}/task/update_task"
- payload = {
- "task_guid": task_guid,
- "summary": summary,
- "start_time": start_time,
- "end_time": end_time,
- "completed_time": completed_time,
- "description": description,
- }
- res: dict = self._send_request(url, method="PATCH", payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_task(self, task_guid: str) -> dict:
- # 删除任务
- url = f"{self.API_BASE_URL}/task/delete_task"
- payload = {
- "task_guid": task_guid,
- }
- res: dict = self._send_request(url, method="DELETE", payload=payload)
- return res
-
- def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict:
- # 删除任务
- url = f"{self.API_BASE_URL}/task/add_members"
- payload = {
- "task_guid": task_guid,
- "member_phone_or_email": member_phone_or_email,
- "member_role": member_role,
- }
- res: dict = self._send_request(url, payload=payload)
- return res
-
- def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict:
- # 获取知识库全部子节点列表
- url = f"{self.API_BASE_URL}/wiki/get_wiki_nodes"
- payload = {
- "space_id": space_id,
- "parent_node_token": parent_node_token,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_primary_calendar(self, user_id_type: str = "open_id") -> dict:
- url = f"{self.API_BASE_URL}/calendar/get_primary_calendar"
- params = {
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_event(
- self,
- summary: str,
- description: str,
- start_time: str,
- end_time: str,
- attendee_ability: str,
- need_notification: bool = True,
- auto_record: bool = False,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/create_event"
- payload = {
- "summary": summary,
- "description": description,
- "need_notification": need_notification,
- "start_time": start_time,
- "end_time": end_time,
- "auto_record": auto_record,
- "attendee_ability": attendee_ability,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_event(
- self,
- event_id: str,
- summary: str,
- description: str,
- need_notification: bool,
- start_time: str,
- end_time: str,
- auto_record: bool,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}"
- payload: dict[str, Any] = {}
- if summary:
- payload["summary"] = summary
- if description:
- payload["description"] = description
- if start_time:
- payload["start_time"] = start_time
- if end_time:
- payload["end_time"] = end_time
- if need_notification:
- payload["need_notification"] = need_notification
- if auto_record:
- payload["auto_record"] = auto_record
- res: dict = self._send_request(url, method="PATCH", payload=payload)
- return res
-
- def delete_event(self, event_id: str, need_notification: bool = True) -> dict:
- url = f"{self.API_BASE_URL}/calendar/delete_event/{event_id}"
- params = {
- "need_notification": need_notification,
- }
- res: dict = self._send_request(url, method="DELETE", params=params)
- return res
-
- def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict:
- url = f"{self.API_BASE_URL}/calendar/list_events"
- params = {
- "start_time": start_time,
- "end_time": end_time,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def search_events(
- self,
- query: str,
- start_time: str,
- end_time: str,
- page_token: str,
- user_id_type: str = "open_id",
- page_size: int = 20,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/search_events"
- payload = {
- "query": query,
- "start_time": start_time,
- "end_time": end_time,
- "page_token": page_token,
- "user_id_type": user_id_type,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict:
- # 参加日程参会人
- url = f"{self.API_BASE_URL}/calendar/add_event_attendees"
- payload = {
- "event_id": event_id,
- "attendee_phone_or_email": attendee_phone_or_email,
- "need_notification": need_notification,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_spreadsheet(
- self,
- title: str,
- folder_token: str,
- ) -> dict:
- # 创建电子表格
- url = f"{self.API_BASE_URL}/spreadsheet/create_spreadsheet"
- payload = {
- "title": title,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_spreadsheet(
- self,
- spreadsheet_token: str,
- user_id_type: str = "open_id",
- ) -> dict:
- # 获取电子表格信息
- url = f"{self.API_BASE_URL}/spreadsheet/get_spreadsheet"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def list_spreadsheet_sheets(
- self,
- spreadsheet_token: str,
- ) -> dict:
- # 列出电子表格的所有工作表
- url = f"{self.API_BASE_URL}/spreadsheet/list_spreadsheet_sheets"
- params = {
- "spreadsheet_token": spreadsheet_token,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_rows(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- length: int,
- values: str,
- ) -> dict:
- # 增加行,在工作表最后添加
- url = f"{self.API_BASE_URL}/spreadsheet/add_rows"
- payload = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "length": length,
- "values": values,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_cols(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- length: int,
- values: str,
- ) -> dict:
- # 增加列,在工作表最后添加
- url = f"{self.API_BASE_URL}/spreadsheet/add_cols"
- payload = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "length": length,
- "values": values,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_rows(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- start_row: int,
- num_rows: int,
- user_id_type: str = "open_id",
- ) -> dict:
- # 读取工作表行数据
- url = f"{self.API_BASE_URL}/spreadsheet/read_rows"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "start_row": start_row,
- "num_rows": num_rows,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_cols(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- start_col: int,
- num_cols: int,
- user_id_type: str = "open_id",
- ) -> dict:
- # 读取工作表列数据
- url = f"{self.API_BASE_URL}/spreadsheet/read_cols"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "start_col": start_col,
- "num_cols": num_cols,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_table(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- num_range: str,
- query: str,
- user_id_type: str = "open_id",
- ) -> dict:
- # 自定义读取行列数据
- url = f"{self.API_BASE_URL}/spreadsheet/read_table"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "range": num_range,
- "query": query,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_base(
- self,
- name: str,
- folder_token: str,
- ) -> dict:
- # 创建多维表格
- url = f"{self.API_BASE_URL}/base/create_base"
- payload = {
- "name": name,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- records: str,
- user_id_type: str = "open_id",
- ) -> dict:
- # 新增多条记录
- url = f"{self.API_BASE_URL}/base/add_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- }
- payload = {
- "records": convert_add_records(records),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- records: str,
- user_id_type: str,
- ) -> dict:
- # 更新多条记录
- url = f"{self.API_BASE_URL}/base/update_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- }
- payload = {
- "records": convert_update_records(records),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- record_ids: str,
- ) -> dict:
- # 删除多条记录
- url = f"{self.API_BASE_URL}/base/delete_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- }
- if not record_ids:
- record_id_list = []
- else:
- try:
- record_id_list = json.loads(record_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "records": record_id_list,
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def search_record(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- view_id: str,
- field_names: str,
- sort: str,
- filters: str,
- page_token: str,
- automatic_fields: bool = False,
- user_id_type: str = "open_id",
- page_size: int = 20,
- ) -> dict:
- # 查询记录,单次最多查询 500 行记录。
- url = f"{self.API_BASE_URL}/base/search_record"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- "page_token": page_token,
- "page_size": page_size,
- }
-
- if not field_names:
- field_name_list = []
- else:
- try:
- field_name_list = json.loads(field_names)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not sort:
- sort_list = []
- else:
- try:
- sort_list = json.loads(sort)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not filters:
- filter_dict = {}
- else:
- try:
- filter_dict = json.loads(filters)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- payload: dict[str, Any] = {}
-
- if view_id:
- payload["view_id"] = view_id
- if field_names:
- payload["field_names"] = field_name_list
- if sort:
- payload["sort"] = sort_list
- if filters:
- payload["filter"] = filter_dict
- if automatic_fields:
- payload["automatic_fields"] = automatic_fields
- res: dict = self._send_request(url, params=params, payload=payload)
-
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_base_info(
- self,
- app_token: str,
- ) -> dict:
- # 获取多维表格元数据
- url = f"{self.API_BASE_URL}/base/get_base_info"
- params = {
- "app_token": app_token,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_table(
- self,
- app_token: str,
- table_name: str,
- default_view_name: str,
- fields: str,
- ) -> dict:
- # 新增一个数据表
- url = f"{self.API_BASE_URL}/base/create_table"
- params = {
- "app_token": app_token,
- }
- if not fields:
- fields_list = []
- else:
- try:
- fields_list = json.loads(fields)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "name": table_name,
- "fields": fields_list,
- }
- if default_view_name:
- payload["default_view_name"] = default_view_name
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_tables(
- self,
- app_token: str,
- table_ids: str,
- table_names: str,
- ) -> dict:
- # 删除多个数据表
- url = f"{self.API_BASE_URL}/base/delete_tables"
- params = {
- "app_token": app_token,
- }
- if not table_ids:
- table_id_list = []
- else:
- try:
- table_id_list = json.loads(table_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not table_names:
- table_name_list = []
- else:
- try:
- table_name_list = json.loads(table_names)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- payload = {
- "table_ids": table_id_list,
- "table_names": table_name_list,
- }
-
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def list_tables(
- self,
- app_token: str,
- page_token: str,
- page_size: int = 20,
- ) -> dict:
- # 列出多维表格下的全部数据表
- url = f"{self.API_BASE_URL}/base/list_tables"
- params = {
- "app_token": app_token,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- record_ids: str,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/read_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- }
- if not record_ids:
- record_id_list = []
- else:
- try:
- record_id_list = json.loads(record_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "record_ids": record_id_list,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
diff --git a/api/core/tools/utils/lark_api_utils.py b/api/core/tools/utils/lark_api_utils.py
deleted file mode 100644
index de394a39bf5a00..00000000000000
--- a/api/core/tools/utils/lark_api_utils.py
+++ /dev/null
@@ -1,851 +0,0 @@
-import json
-from typing import Any, Optional, cast
-
-import httpx
-
-from core.tools.errors import ToolProviderCredentialValidationError
-from extensions.ext_redis import redis_client
-
-
-def lark_auth(credentials):
- app_id = credentials.get("app_id")
- app_secret = credentials.get("app_secret")
- if not app_id or not app_secret:
- raise ToolProviderCredentialValidationError("app_id and app_secret is required")
- try:
- assert LarkRequest(app_id, app_secret).tenant_access_token is not None
- except Exception as e:
- raise ToolProviderCredentialValidationError(str(e))
-
-
-class LarkRequest:
- API_BASE_URL = "https://lark-plugin-api.solutionsuite.ai/lark-plugin"
-
- def __init__(self, app_id: str, app_secret: str):
- self.app_id = app_id
- self.app_secret = app_secret
-
- def convert_add_records(self, json_str):
- try:
- data = json.loads(json_str)
- if not isinstance(data, list):
- raise ValueError("Parsed data must be a list")
- converted_data = [{"fields": json.dumps(item, ensure_ascii=False)} for item in data]
- return converted_data
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- except Exception as e:
- raise ValueError(f"An error occurred while processing the data: {e}")
-
- def convert_update_records(self, json_str):
- try:
- data = json.loads(json_str)
- if not isinstance(data, list):
- raise ValueError("Parsed data must be a list")
-
- converted_data = [
- {"fields": json.dumps(record["fields"], ensure_ascii=False), "record_id": record["record_id"]}
- for record in data
- if "fields" in record and "record_id" in record
- ]
-
- if len(converted_data) != len(data):
- raise ValueError("Each record must contain 'fields' and 'record_id'")
-
- return converted_data
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- except Exception as e:
- raise ValueError(f"An error occurred while processing the data: {e}")
-
- @property
- def tenant_access_token(self) -> str:
- feishu_tenant_access_token = f"tools:{self.app_id}:feishu_tenant_access_token"
- if redis_client.exists(feishu_tenant_access_token):
- return str(redis_client.get(feishu_tenant_access_token).decode())
- res: dict[str, str] = self.get_tenant_access_token(self.app_id, self.app_secret)
- redis_client.setex(feishu_tenant_access_token, res.get("expire"), res.get("tenant_access_token"))
- return res.get("tenant_access_token", "")
-
- def _send_request(
- self,
- url: str,
- method: str = "post",
- require_token: bool = True,
- payload: Optional[dict] = None,
- params: Optional[dict] = None,
- ):
- headers = {
- "Content-Type": "application/json",
- "user-agent": "Dify",
- }
- if require_token:
- headers["tenant-access-token"] = f"{self.tenant_access_token}"
- res = httpx.request(method=method, url=url, headers=headers, json=payload, params=params, timeout=30).json()
- if res.get("code") != 0:
- raise Exception(res)
- return res
-
- def get_tenant_access_token(self, app_id: str, app_secret: str) -> dict:
- url = f"{self.API_BASE_URL}/access_token/get_tenant_access_token"
- payload = {"app_id": app_id, "app_secret": app_secret}
- res: dict = self._send_request(url, require_token=False, payload=payload)
- return res
-
- def create_document(self, title: str, content: str, folder_token: str) -> dict:
- url = f"{self.API_BASE_URL}/document/create_document"
- payload = {
- "title": title,
- "content": content,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def write_document(self, document_id: str, content: str, position: str = "end") -> dict:
- url = f"{self.API_BASE_URL}/document/write_document"
- payload = {"document_id": document_id, "content": content, "position": position}
- res: dict = self._send_request(url, payload=payload)
- return res
-
- def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> str | dict:
- params = {
- "document_id": document_id,
- "mode": mode,
- "lang": lang,
- }
- url = f"{self.API_BASE_URL}/document/get_document_content"
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- return cast(dict, res.get("data", {}).get("content"))
- return ""
-
- def list_document_blocks(
- self, document_id: str, page_token: str, user_id_type: str = "open_id", page_size: int = 500
- ) -> dict:
- params = {
- "user_id_type": user_id_type,
- "document_id": document_id,
- "page_size": page_size,
- "page_token": page_token,
- }
- url = f"{self.API_BASE_URL}/document/list_document_blocks"
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict:
- url = f"{self.API_BASE_URL}/message/send_bot_message"
- params = {
- "receive_id_type": receive_id_type,
- }
- payload = {
- "receive_id": receive_id,
- "msg_type": msg_type,
- "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict:
- url = f"{self.API_BASE_URL}/message/send_webhook_message"
- payload = {
- "webhook": webhook,
- "msg_type": msg_type,
- "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
- }
- res: dict = self._send_request(url, require_token=False, payload=payload)
- return res
-
- def get_chat_messages(
- self,
- container_id: str,
- start_time: str,
- end_time: str,
- page_token: str,
- sort_type: str = "ByCreateTimeAsc",
- page_size: int = 20,
- ) -> dict:
- url = f"{self.API_BASE_URL}/message/get_chat_messages"
- params = {
- "container_id": container_id,
- "start_time": start_time,
- "end_time": end_time,
- "sort_type": sort_type,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_thread_messages(
- self, container_id: str, page_token: str, sort_type: str = "ByCreateTimeAsc", page_size: int = 20
- ) -> dict:
- url = f"{self.API_BASE_URL}/message/get_thread_messages"
- params = {
- "container_id": container_id,
- "sort_type": sort_type,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict:
- url = f"{self.API_BASE_URL}/task/create_task"
- payload = {
- "summary": summary,
- "start_time": start_time,
- "end_time": end_time,
- "completed_at": completed_time,
- "description": description,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_task(
- self, task_guid: str, summary: str, start_time: str, end_time: str, completed_time: str, description: str
- ) -> dict:
- url = f"{self.API_BASE_URL}/task/update_task"
- payload = {
- "task_guid": task_guid,
- "summary": summary,
- "start_time": start_time,
- "end_time": end_time,
- "completed_time": completed_time,
- "description": description,
- }
- res: dict = self._send_request(url, method="PATCH", payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_task(self, task_guid: str) -> dict:
- url = f"{self.API_BASE_URL}/task/delete_task"
- payload = {
- "task_guid": task_guid,
- }
- res: dict = self._send_request(url, method="DELETE", payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict:
- url = f"{self.API_BASE_URL}/task/add_members"
- payload = {
- "task_guid": task_guid,
- "member_phone_or_email": member_phone_or_email,
- "member_role": member_role,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict:
- url = f"{self.API_BASE_URL}/wiki/get_wiki_nodes"
- payload = {
- "space_id": space_id,
- "parent_node_token": parent_node_token,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_primary_calendar(self, user_id_type: str = "open_id") -> dict:
- url = f"{self.API_BASE_URL}/calendar/get_primary_calendar"
- params = {
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_event(
- self,
- summary: str,
- description: str,
- start_time: str,
- end_time: str,
- attendee_ability: str,
- need_notification: bool = True,
- auto_record: bool = False,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/create_event"
- payload = {
- "summary": summary,
- "description": description,
- "need_notification": need_notification,
- "start_time": start_time,
- "end_time": end_time,
- "auto_record": auto_record,
- "attendee_ability": attendee_ability,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_event(
- self,
- event_id: str,
- summary: str,
- description: str,
- need_notification: bool,
- start_time: str,
- end_time: str,
- auto_record: bool,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}"
- payload: dict[str, Any] = {}
- if summary:
- payload["summary"] = summary
- if description:
- payload["description"] = description
- if start_time:
- payload["start_time"] = start_time
- if end_time:
- payload["end_time"] = end_time
- if need_notification:
- payload["need_notification"] = need_notification
- if auto_record:
- payload["auto_record"] = auto_record
- res: dict = self._send_request(url, method="PATCH", payload=payload)
- return res
-
- def delete_event(self, event_id: str, need_notification: bool = True) -> dict:
- url = f"{self.API_BASE_URL}/calendar/delete_event/{event_id}"
- params = {
- "need_notification": need_notification,
- }
- res: dict = self._send_request(url, method="DELETE", params=params)
- return res
-
- def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict:
- url = f"{self.API_BASE_URL}/calendar/list_events"
- params = {
- "start_time": start_time,
- "end_time": end_time,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def search_events(
- self,
- query: str,
- start_time: str,
- end_time: str,
- page_token: str,
- user_id_type: str = "open_id",
- page_size: int = 20,
- ) -> dict:
- url = f"{self.API_BASE_URL}/calendar/search_events"
- payload = {
- "query": query,
- "start_time": start_time,
- "end_time": end_time,
- "page_token": page_token,
- "user_id_type": user_id_type,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict:
- url = f"{self.API_BASE_URL}/calendar/add_event_attendees"
- payload = {
- "event_id": event_id,
- "attendee_phone_or_email": attendee_phone_or_email,
- "need_notification": need_notification,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_spreadsheet(
- self,
- title: str,
- folder_token: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/create_spreadsheet"
- payload = {
- "title": title,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_spreadsheet(
- self,
- spreadsheet_token: str,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/get_spreadsheet"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def list_spreadsheet_sheets(
- self,
- spreadsheet_token: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/list_spreadsheet_sheets"
- params = {
- "spreadsheet_token": spreadsheet_token,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_rows(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- length: int,
- values: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/add_rows"
- payload = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "length": length,
- "values": values,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_cols(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- length: int,
- values: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/add_cols"
- payload = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "length": length,
- "values": values,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_rows(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- start_row: int,
- num_rows: int,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/read_rows"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "start_row": start_row,
- "num_rows": num_rows,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_cols(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- start_col: int,
- num_cols: int,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/read_cols"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "start_col": start_col,
- "num_cols": num_cols,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_table(
- self,
- spreadsheet_token: str,
- sheet_id: str,
- sheet_name: str,
- num_range: str,
- query: str,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/spreadsheet/read_table"
- params = {
- "spreadsheet_token": spreadsheet_token,
- "sheet_id": sheet_id,
- "sheet_name": sheet_name,
- "range": num_range,
- "query": query,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_base(
- self,
- name: str,
- folder_token: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/create_base"
- payload = {
- "name": name,
- "folder_token": folder_token,
- }
- res: dict = self._send_request(url, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def add_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- records: str,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/add_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- }
- payload = {
- "records": self.convert_add_records(records),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def update_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- records: str,
- user_id_type: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/update_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- }
- payload = {
- "records": self.convert_update_records(records),
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- record_ids: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/delete_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- }
- if not record_ids:
- record_id_list = []
- else:
- try:
- record_id_list = json.loads(record_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "records": record_id_list,
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def search_record(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- view_id: str,
- field_names: str,
- sort: str,
- filters: str,
- page_token: str,
- automatic_fields: bool = False,
- user_id_type: str = "open_id",
- page_size: int = 20,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/search_record"
-
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- "user_id_type": user_id_type,
- "page_token": page_token,
- "page_size": page_size,
- }
-
- if not field_names:
- field_name_list = []
- else:
- try:
- field_name_list = json.loads(field_names)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not sort:
- sort_list = []
- else:
- try:
- sort_list = json.loads(sort)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not filters:
- filter_dict = {}
- else:
- try:
- filter_dict = json.loads(filters)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- payload: dict[str, Any] = {}
-
- if view_id:
- payload["view_id"] = view_id
- if field_names:
- payload["field_names"] = field_name_list
- if sort:
- payload["sort"] = sort_list
- if filters:
- payload["filter"] = filter_dict
- if automatic_fields:
- payload["automatic_fields"] = automatic_fields
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def get_base_info(
- self,
- app_token: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/get_base_info"
- params = {
- "app_token": app_token,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def create_table(
- self,
- app_token: str,
- table_name: str,
- default_view_name: str,
- fields: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/create_table"
- params = {
- "app_token": app_token,
- }
- if not fields:
- fields_list = []
- else:
- try:
- fields_list = json.loads(fields)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "name": table_name,
- "fields": fields_list,
- }
- if default_view_name:
- payload["default_view_name"] = default_view_name
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def delete_tables(
- self,
- app_token: str,
- table_ids: str,
- table_names: str,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/delete_tables"
- params = {
- "app_token": app_token,
- }
- if not table_ids:
- table_id_list = []
- else:
- try:
- table_id_list = json.loads(table_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- if not table_names:
- table_name_list = []
- else:
- try:
- table_name_list = json.loads(table_names)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
-
- payload = {
- "table_ids": table_id_list,
- "table_names": table_name_list,
- }
- res: dict = self._send_request(url, params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def list_tables(
- self,
- app_token: str,
- page_token: str,
- page_size: int = 20,
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/list_tables"
- params = {
- "app_token": app_token,
- "page_token": page_token,
- "page_size": page_size,
- }
- res: dict = self._send_request(url, method="GET", params=params)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
-
- def read_records(
- self,
- app_token: str,
- table_id: str,
- table_name: str,
- record_ids: str,
- user_id_type: str = "open_id",
- ) -> dict:
- url = f"{self.API_BASE_URL}/base/read_records"
- params = {
- "app_token": app_token,
- "table_id": table_id,
- "table_name": table_name,
- }
- if not record_ids:
- record_id_list = []
- else:
- try:
- record_id_list = json.loads(record_ids)
- except json.JSONDecodeError:
- raise ValueError("The input string is not valid JSON")
- payload = {
- "record_ids": record_id_list,
- "user_id_type": user_id_type,
- }
- res: dict = self._send_request(url, method="POST", params=params, payload=payload)
- if "data" in res:
- data: dict = res.get("data", {})
- return data
- return res
diff --git a/api/core/tools/utils/message_transformer.py b/api/core/tools/utils/message_transformer.py
index b28953264cfc79..d92aa5ee6000b8 100644
--- a/api/core/tools/utils/message_transformer.py
+++ b/api/core/tools/utils/message_transformer.py
@@ -1,4 +1,5 @@
import logging
+from collections.abc import Generator
from mimetypes import guess_extension
from typing import Optional
@@ -12,58 +13,64 @@
class ToolFileMessageTransformer:
@classmethod
def transform_tool_invoke_messages(
- cls, messages: list[ToolInvokeMessage], user_id: str, tenant_id: str, conversation_id: str | None
- ) -> list[ToolInvokeMessage]:
+ cls,
+ messages: Generator[ToolInvokeMessage, None, None],
+ user_id: str,
+ tenant_id: str,
+ conversation_id: Optional[str] = None,
+ ) -> Generator[ToolInvokeMessage, None, None]:
"""
Transform tool message and handle file download
"""
- result = []
-
for message in messages:
if message.type in {ToolInvokeMessage.MessageType.TEXT, ToolInvokeMessage.MessageType.LINK}:
- result.append(message)
- elif message.type == ToolInvokeMessage.MessageType.IMAGE and isinstance(message.message, str):
+ yield message
+ elif message.type == ToolInvokeMessage.MessageType.IMAGE and isinstance(
+ message.message, ToolInvokeMessage.TextMessage
+ ):
# try to download image
try:
+ assert isinstance(message.message, ToolInvokeMessage.TextMessage)
+
file = ToolFileManager.create_file_by_url(
- user_id=user_id, tenant_id=tenant_id, conversation_id=conversation_id, file_url=message.message
+ user_id=user_id,
+ tenant_id=tenant_id,
+ file_url=message.message.text,
+ conversation_id=conversation_id,
)
url = f"/files/tools/{file.id}{guess_extension(file.mimetype) or '.png'}"
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.IMAGE_LINK,
- message=url,
- save_as=message.save_as,
- meta=message.meta.copy() if message.meta is not None else {},
- )
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.IMAGE_LINK,
+ message=ToolInvokeMessage.TextMessage(text=url),
+ meta=message.meta.copy() if message.meta is not None else {},
)
except Exception as e:
- logger.exception(f"Failed to download image from {url}")
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.TEXT,
- message=f"Failed to download image: {message.message}, please try to download it manually.",
- meta=message.meta.copy() if message.meta is not None else {},
- save_as=message.save_as,
- )
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.TEXT,
+ message=ToolInvokeMessage.TextMessage(
+ text=f"Failed to download image: {message.message.text}: {e}"
+ ),
+ meta=message.meta.copy() if message.meta is not None else {},
)
elif message.type == ToolInvokeMessage.MessageType.BLOB:
# get mime type and save blob to storage
- assert message.meta is not None
- mimetype = message.meta.get("mime_type", "octet/stream")
+ meta = message.meta or {}
+
+ mimetype = meta.get("mime_type", "octet/stream")
# if message is str, encode it to bytes
- if isinstance(message.message, str):
- message.message = message.message.encode("utf-8")
+
+ if not isinstance(message.message, ToolInvokeMessage.BlobMessage):
+ raise ValueError("unexpected message type")
# FIXME: should do a type check here.
- assert isinstance(message.message, bytes)
+ assert isinstance(message.message.blob, bytes)
file = ToolFileManager.create_file_by_raw(
user_id=user_id,
tenant_id=tenant_id,
conversation_id=conversation_id,
- file_binary=message.message,
+ file_binary=message.message.blob,
mimetype=mimetype,
)
@@ -71,54 +78,40 @@ def transform_tool_invoke_messages(
# check if file is image
if "image" in mimetype:
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.IMAGE_LINK,
- message=url,
- save_as=message.save_as,
- meta=message.meta.copy() if message.meta is not None else {},
- )
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.IMAGE_LINK,
+ message=ToolInvokeMessage.TextMessage(text=url),
+ meta=meta.copy() if meta is not None else {},
)
else:
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.LINK,
- message=url,
- save_as=message.save_as,
- meta=message.meta.copy() if message.meta is not None else {},
- )
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.BINARY_LINK,
+ message=ToolInvokeMessage.TextMessage(text=url),
+ meta=meta.copy() if meta is not None else {},
)
elif message.type == ToolInvokeMessage.MessageType.FILE:
- assert message.meta is not None
- file_mata = message.meta.get("file")
- if isinstance(file_mata, File):
- if file_mata.transfer_method == FileTransferMethod.TOOL_FILE:
- assert file_mata.related_id is not None
- url = cls.get_tool_file_url(tool_file_id=file_mata.related_id, extension=file_mata.extension)
- if file_mata.type == FileType.IMAGE:
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.IMAGE_LINK,
- message=url,
- save_as=message.save_as,
- meta=message.meta.copy() if message.meta is not None else {},
- )
+ meta = message.meta or {}
+ file = meta.get("file", None)
+ if isinstance(file, File):
+ if file.transfer_method == FileTransferMethod.TOOL_FILE:
+ assert file.related_id is not None
+ url = cls.get_tool_file_url(tool_file_id=file.related_id, extension=file.extension)
+ if file.type == FileType.IMAGE:
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.IMAGE_LINK,
+ message=ToolInvokeMessage.TextMessage(text=url),
+ meta=meta.copy() if meta is not None else {},
)
else:
- result.append(
- ToolInvokeMessage(
- type=ToolInvokeMessage.MessageType.LINK,
- message=url,
- save_as=message.save_as,
- meta=message.meta.copy() if message.meta is not None else {},
- )
+ yield ToolInvokeMessage(
+ type=ToolInvokeMessage.MessageType.LINK,
+ message=ToolInvokeMessage.TextMessage(text=url),
+ meta=meta.copy() if meta is not None else {},
)
else:
- result.append(message)
+ yield message
else:
- result.append(message)
-
- return result
+ yield message
@classmethod
def get_tool_file_url(cls, tool_file_id: str, extension: Optional[str]) -> str:
diff --git a/api/core/tools/utils/model_invocation_utils.py b/api/core/tools/utils/model_invocation_utils.py
index 3689dcc9e5ebfd..245470ea491dc4 100644
--- a/api/core/tools/utils/model_invocation_utils.py
+++ b/api/core/tools/utils/model_invocation_utils.py
@@ -10,7 +10,7 @@
from core.model_manager import ModelManager
from core.model_runtime.entities.llm_entities import LLMResult
from core.model_runtime.entities.message_entities import PromptMessage
-from core.model_runtime.entities.model_entities import ModelType
+from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.model_runtime.errors.invoke import (
InvokeAuthorizationError,
InvokeBadRequestError,
@@ -18,7 +18,7 @@
InvokeRateLimitError,
InvokeServerUnavailableError,
)
-from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel, ModelPropertyKey
+from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.model_runtime.utils.encoders import jsonable_encoder
from extensions.ext_database import db
from models.tools import ToolModelInvoke
diff --git a/api/core/tools/utils/parser.py b/api/core/tools/utils/parser.py
index b15a86b5c0a5cf..c69fc1f825325a 100644
--- a/api/core/tools/utils/parser.py
+++ b/api/core/tools/utils/parser.py
@@ -18,7 +18,7 @@
class ApiBasedToolSchemaParser:
@staticmethod
def parse_openapi_to_tool_bundle(
- openapi: dict, extra_info: Optional[dict], warning: Optional[dict]
+ openapi: dict, extra_info: dict | None = None, warning: dict | None = None
) -> list[ApiToolBundle]:
warning = warning if warning is not None else {}
extra_info = extra_info if extra_info is not None else {}
@@ -191,7 +191,7 @@ def _get_tool_parameter_type(parameter: dict) -> Optional[ToolParameter.ToolPara
@staticmethod
def parse_openapi_yaml_to_tool_bundle(
- yaml: str, extra_info: Optional[dict], warning: Optional[dict]
+ yaml: str, extra_info: dict | None = None, warning: dict | None = None
) -> list[ApiToolBundle]:
"""
parse openapi yaml to tool bundle
@@ -208,7 +208,8 @@ def parse_openapi_yaml_to_tool_bundle(
return ApiBasedToolSchemaParser.parse_openapi_to_tool_bundle(openapi, extra_info=extra_info, warning=warning)
@staticmethod
- def parse_swagger_to_openapi(swagger: dict, extra_info: Optional[dict], warning: Optional[dict]) -> dict:
+ def parse_swagger_to_openapi(swagger: dict, extra_info: dict | None = None, warning: dict | None = None) -> dict:
+ warning = warning or {}
"""
parse swagger to openapi
@@ -271,7 +272,7 @@ def parse_swagger_to_openapi(swagger: dict, extra_info: Optional[dict], warning:
@staticmethod
def parse_openai_plugin_json_to_tool_bundle(
- json: str, extra_info: Optional[dict], warning: Optional[dict]
+ json: str, extra_info: dict | None = None, warning: dict | None = None
) -> list[ApiToolBundle]:
"""
parse openapi plugin yaml to tool bundle
@@ -287,7 +288,7 @@ def parse_openai_plugin_json_to_tool_bundle(
api = openai_plugin["api"]
api_url = api["url"]
api_type = api["type"]
- except:
+ except JSONDecodeError:
raise ToolProviderNotFoundError("Invalid openai plugin json.")
if api_type != "openapi":
@@ -305,7 +306,7 @@ def parse_openai_plugin_json_to_tool_bundle(
@staticmethod
def auto_parse_to_tool_bundle(
- content: str, extra_info: Optional[dict] = None, warning: Optional[dict] = None
+ content: str, extra_info: dict | None = None, warning: dict | None = None
) -> tuple[list[ApiToolBundle], str]:
"""
auto parse to tool bundle
diff --git a/api/core/tools/utils/rag_web_reader.py b/api/core/tools/utils/rag_web_reader.py
new file mode 100644
index 00000000000000..22c47fa814f5a8
--- /dev/null
+++ b/api/core/tools/utils/rag_web_reader.py
@@ -0,0 +1,17 @@
+import re
+
+
+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/tools/utils/workflow_configuration_sync.py b/api/core/tools/utils/workflow_configuration_sync.py
index 08a112cfdb2b91..d16d6fc576fa61 100644
--- a/api/core/tools/utils/workflow_configuration_sync.py
+++ b/api/core/tools/utils/workflow_configuration_sync.py
@@ -27,7 +27,7 @@ def get_workflow_graph_variables(cls, graph: Mapping[str, Any]) -> Sequence[Vari
@classmethod
def check_is_synced(
cls, variables: list[VariableEntity], tool_configurations: list[WorkflowToolParameterConfiguration]
- ) -> bool:
+ ):
"""
check is synced
@@ -41,5 +41,3 @@ def check_is_synced(
for parameter in tool_configurations:
if parameter.name not in variable_names:
raise ValueError("parameter configuration mismatch, please republish the tool to update")
-
- return True
diff --git a/api/core/tools/workflow_as_tool/provider.py b/api/core/tools/workflow_as_tool/provider.py
new file mode 100644
index 00000000000000..4777a019e41113
--- /dev/null
+++ b/api/core/tools/workflow_as_tool/provider.py
@@ -0,0 +1,228 @@
+from collections.abc import Mapping
+from typing import Optional
+
+from pydantic import Field
+
+from core.app.app_config.entities import VariableEntity, VariableEntityType
+from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
+from core.plugin.entities.parameters import PluginParameterOption
+from core.tools.__base.tool_provider import ToolProviderController
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.entities.common_entities import I18nObject
+from core.tools.entities.tool_entities import (
+ ToolDescription,
+ ToolEntity,
+ ToolIdentity,
+ ToolParameter,
+ ToolProviderEntity,
+ ToolProviderIdentity,
+ ToolProviderType,
+)
+from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils
+from core.tools.workflow_as_tool.tool import WorkflowTool
+from extensions.ext_database import db
+from models.model import App, AppMode
+from models.tools import WorkflowToolProvider
+from models.workflow import Workflow
+
+VARIABLE_TO_PARAMETER_TYPE_MAPPING = {
+ VariableEntityType.TEXT_INPUT: ToolParameter.ToolParameterType.STRING,
+ VariableEntityType.PARAGRAPH: ToolParameter.ToolParameterType.STRING,
+ VariableEntityType.SELECT: ToolParameter.ToolParameterType.SELECT,
+ VariableEntityType.NUMBER: ToolParameter.ToolParameterType.NUMBER,
+ VariableEntityType.FILE: ToolParameter.ToolParameterType.FILE,
+ VariableEntityType.FILE_LIST: ToolParameter.ToolParameterType.FILES,
+}
+
+
+class WorkflowToolProviderController(ToolProviderController):
+ provider_id: str
+ tools: list[WorkflowTool] = Field(default_factory=list)
+
+ def __init__(self, entity: ToolProviderEntity, provider_id: str):
+ super().__init__(entity=entity)
+ self.provider_id = provider_id
+
+ @classmethod
+ def from_db(cls, db_provider: WorkflowToolProvider) -> "WorkflowToolProviderController":
+ app = db_provider.app
+
+ if not app:
+ raise ValueError("app not found")
+
+ controller = WorkflowToolProviderController(
+ entity=ToolProviderEntity(
+ identity=ToolProviderIdentity(
+ author=db_provider.user.name if db_provider.user_id and db_provider.user else "",
+ name=db_provider.label,
+ label=I18nObject(en_US=db_provider.label, zh_Hans=db_provider.label),
+ description=I18nObject(en_US=db_provider.description, zh_Hans=db_provider.description),
+ icon=db_provider.icon,
+ ),
+ credentials_schema=[],
+ plugin_id=None,
+ ),
+ provider_id=db_provider.id or "",
+ )
+
+ # init tools
+
+ controller.tools = [controller._get_db_provider_tool(db_provider, app)]
+
+ return controller
+
+ @property
+ def provider_type(self) -> ToolProviderType:
+ return ToolProviderType.WORKFLOW
+
+ def _get_db_provider_tool(self, db_provider: WorkflowToolProvider, app: App) -> WorkflowTool:
+ """
+ get db provider tool
+ :param db_provider: the db provider
+ :param app: the app
+ :return: the tool
+ """
+ workflow: Workflow | None = (
+ db.session.query(Workflow)
+ .filter(Workflow.app_id == db_provider.app_id, Workflow.version == db_provider.version)
+ .first()
+ )
+
+ if not workflow:
+ raise ValueError("workflow not found")
+
+ # fetch start node
+ graph: Mapping = workflow.graph_dict
+ features_dict: Mapping = workflow.features_dict
+ features = WorkflowAppConfigManager.convert_features(config_dict=features_dict, app_mode=AppMode.WORKFLOW)
+
+ parameters = db_provider.parameter_configurations
+ variables = WorkflowToolConfigurationUtils.get_workflow_graph_variables(graph)
+
+ def fetch_workflow_variable(variable_name: str) -> VariableEntity | None:
+ return next(filter(lambda x: x.variable == variable_name, variables), None) # type: ignore
+
+ user = db_provider.user
+
+ workflow_tool_parameters = []
+ for parameter in parameters:
+ variable = fetch_workflow_variable(parameter.name)
+ if variable:
+ parameter_type = None
+ options = []
+ if variable.type not in VARIABLE_TO_PARAMETER_TYPE_MAPPING:
+ raise ValueError(f"unsupported variable type {variable.type}")
+ parameter_type = VARIABLE_TO_PARAMETER_TYPE_MAPPING[variable.type]
+
+ if variable.type == VariableEntityType.SELECT and variable.options:
+ options = [
+ PluginParameterOption(value=option, label=I18nObject(en_US=option, zh_Hans=option))
+ for option in variable.options
+ ]
+
+ workflow_tool_parameters.append(
+ ToolParameter(
+ name=parameter.name,
+ label=I18nObject(en_US=variable.label, zh_Hans=variable.label),
+ human_description=I18nObject(en_US=parameter.description, zh_Hans=parameter.description),
+ type=parameter_type,
+ form=parameter.form,
+ llm_description=parameter.description,
+ required=variable.required,
+ options=options,
+ placeholder=I18nObject(en_US="", zh_Hans=""),
+ )
+ )
+ elif features.file_upload:
+ workflow_tool_parameters.append(
+ ToolParameter(
+ name=parameter.name,
+ label=I18nObject(en_US=parameter.name, zh_Hans=parameter.name),
+ human_description=I18nObject(en_US=parameter.description, zh_Hans=parameter.description),
+ type=ToolParameter.ToolParameterType.SYSTEM_FILES,
+ llm_description=parameter.description,
+ required=False,
+ form=parameter.form,
+ placeholder=I18nObject(en_US="", zh_Hans=""),
+ )
+ )
+ else:
+ raise ValueError("variable not found")
+
+ return WorkflowTool(
+ workflow_as_tool_id=db_provider.id,
+ entity=ToolEntity(
+ identity=ToolIdentity(
+ author=user.name if user else "",
+ name=db_provider.name,
+ label=I18nObject(en_US=db_provider.label, zh_Hans=db_provider.label),
+ provider=self.provider_id,
+ icon=db_provider.icon,
+ ),
+ description=ToolDescription(
+ human=I18nObject(en_US=db_provider.description, zh_Hans=db_provider.description),
+ llm=db_provider.description,
+ ),
+ parameters=workflow_tool_parameters,
+ ),
+ runtime=ToolRuntime(
+ tenant_id=db_provider.tenant_id,
+ ),
+ workflow_app_id=app.id,
+ workflow_entities={
+ "app": app,
+ "workflow": workflow,
+ },
+ version=db_provider.version,
+ workflow_call_depth=0,
+ label=db_provider.label,
+ )
+
+ def get_tools(self, tenant_id: str) -> list[WorkflowTool]:
+ """
+ fetch tools from database
+
+ :param user_id: the user id
+ :param tenant_id: the tenant id
+ :return: the tools
+ """
+ if self.tools is not None:
+ return self.tools
+
+ db_providers: WorkflowToolProvider | None = (
+ db.session.query(WorkflowToolProvider)
+ .filter(
+ WorkflowToolProvider.tenant_id == tenant_id,
+ WorkflowToolProvider.app_id == self.provider_id,
+ )
+ .first()
+ )
+
+ if not db_providers:
+ return []
+ if not db_providers.app:
+ raise ValueError("app not found")
+
+ app = db_providers.app
+ if not app:
+ raise ValueError("can not read app of workflow")
+
+ self.tools = [self._get_db_provider_tool(db_providers, app)]
+
+ return self.tools
+
+ def get_tool(self, tool_name: str) -> Optional[WorkflowTool]: # type: ignore
+ """
+ get tool by name
+
+ :param tool_name: the name of the tool
+ :return: the tool
+ """
+ if self.tools is None:
+ return None
+
+ for tool in self.tools:
+ if tool.entity.identity.name == tool_name:
+ return tool
+
+ return None
diff --git a/api/core/tools/workflow_as_tool/tool.py b/api/core/tools/workflow_as_tool/tool.py
new file mode 100644
index 00000000000000..cb9b804c77b81d
--- /dev/null
+++ b/api/core/tools/workflow_as_tool/tool.py
@@ -0,0 +1,250 @@
+import json
+import logging
+from collections.abc import Generator
+from typing import Any, Optional, Union, cast
+
+from core.file import FILE_MODEL_IDENTITY, File, FileTransferMethod
+from core.tools.__base.tool import Tool
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.entities.tool_entities import ToolEntity, ToolInvokeMessage, ToolParameter, ToolProviderType
+from extensions.ext_database import db
+from factories.file_factory import build_from_mapping
+from models.account import Account
+from models.model import App, EndUser
+from models.workflow import Workflow
+
+logger = logging.getLogger(__name__)
+
+
+class WorkflowTool(Tool):
+ workflow_app_id: str
+ version: str
+ workflow_entities: dict[str, Any]
+ workflow_call_depth: int
+ thread_pool_id: Optional[str] = None
+ workflow_as_tool_id: str
+
+ label: str
+
+ """
+ Workflow tool.
+ """
+
+ def __init__(
+ self,
+ workflow_app_id: str,
+ workflow_as_tool_id: str,
+ version: str,
+ workflow_entities: dict[str, Any],
+ workflow_call_depth: int,
+ entity: ToolEntity,
+ runtime: ToolRuntime,
+ label: str = "Workflow",
+ thread_pool_id: Optional[str] = None,
+ ):
+ self.workflow_app_id = workflow_app_id
+ self.workflow_as_tool_id = workflow_as_tool_id
+ self.version = version
+ self.workflow_entities = workflow_entities
+ self.workflow_call_depth = workflow_call_depth
+ self.thread_pool_id = thread_pool_id
+ self.label = label
+
+ super().__init__(entity=entity, runtime=runtime)
+
+ def tool_provider_type(self) -> ToolProviderType:
+ """
+ get the tool provider type
+
+ :return: the tool provider type
+ """
+ return ToolProviderType.WORKFLOW
+
+ def _invoke(
+ self,
+ user_id: str,
+ tool_parameters: dict[str, Any],
+ conversation_id: Optional[str] = None,
+ app_id: Optional[str] = None,
+ message_id: Optional[str] = None,
+ ) -> Generator[ToolInvokeMessage, None, None]:
+ """
+ invoke the tool
+ """
+ app = self._get_app(app_id=self.workflow_app_id)
+ workflow = self._get_workflow(app_id=self.workflow_app_id, version=self.version)
+
+ # transform the tool parameters
+ tool_parameters, files = self._transform_args(tool_parameters=tool_parameters)
+
+ from core.app.apps.workflow.app_generator import WorkflowAppGenerator
+
+ generator = WorkflowAppGenerator()
+ assert self.runtime is not None
+ assert self.runtime.invoke_from is not None
+
+ result = generator.generate(
+ app_model=app,
+ workflow=workflow,
+ user=self._get_user(user_id),
+ args={"inputs": tool_parameters, "files": files},
+ invoke_from=self.runtime.invoke_from,
+ streaming=False,
+ call_depth=self.workflow_call_depth + 1,
+ workflow_thread_pool_id=self.thread_pool_id,
+ )
+ assert isinstance(result, dict)
+ data = result.get("data", {})
+
+ if data.get("error"):
+ raise Exception(data.get("error"))
+
+ if data.get("error"):
+ raise Exception(data.get("error"))
+
+ outputs = data.get("outputs")
+ if outputs is None:
+ outputs = {}
+ else:
+ outputs, files = self._extract_files(outputs) # type: ignore
+ for file in files:
+ yield self.create_file_message(file) # type: ignore
+
+ yield self.create_text_message(json.dumps(outputs, ensure_ascii=False))
+ yield self.create_json_message(outputs)
+
+ def _get_user(self, user_id: str) -> Union[EndUser, Account]:
+ """
+ get the user by user id
+ """
+
+ user = db.session.query(EndUser).filter(EndUser.id == user_id).first()
+ if not user:
+ user = db.session.query(Account).filter(Account.id == user_id).first()
+
+ if not user:
+ raise ValueError("user not found")
+
+ return user
+
+ def fork_tool_runtime(self, runtime: ToolRuntime) -> "WorkflowTool":
+ """
+ fork a new tool with meta data
+
+ :param meta: the meta data of a tool call processing, tenant_id is required
+ :return: the new tool
+ """
+ return self.__class__(
+ entity=self.entity.model_copy(),
+ runtime=runtime,
+ workflow_app_id=self.workflow_app_id,
+ workflow_as_tool_id=self.workflow_as_tool_id,
+ workflow_entities=self.workflow_entities,
+ workflow_call_depth=self.workflow_call_depth,
+ version=self.version,
+ label=self.label,
+ )
+
+ def _get_workflow(self, app_id: str, version: str) -> Workflow:
+ """
+ get the workflow by app id and version
+ """
+ if not version:
+ workflow = (
+ db.session.query(Workflow)
+ .filter(Workflow.app_id == app_id, Workflow.version != "draft")
+ .order_by(Workflow.created_at.desc())
+ .first()
+ )
+ else:
+ workflow = db.session.query(Workflow).filter(Workflow.app_id == app_id, Workflow.version == version).first()
+
+ if not workflow:
+ raise ValueError("workflow not found or not published")
+
+ return workflow
+
+ def _get_app(self, app_id: str) -> App:
+ """
+ get the app by app id
+ """
+ app = db.session.query(App).filter(App.id == app_id).first()
+ if not app:
+ raise ValueError("app not found")
+
+ return app
+
+ def _transform_args(self, tool_parameters: dict) -> tuple[dict, list[dict]]:
+ """
+ transform the tool parameters
+
+ :param tool_parameters: the tool parameters
+ :return: tool_parameters, files
+ """
+ parameter_rules = self.get_merged_runtime_parameters()
+ parameters_result = {}
+ files = []
+ for parameter in parameter_rules:
+ if parameter.type == ToolParameter.ToolParameterType.SYSTEM_FILES:
+ file = tool_parameters.get(parameter.name)
+ if file:
+ try:
+ file_var_list = [File.model_validate(f) for f in file]
+ for file in file_var_list:
+ file_dict: dict[str, str | None] = {
+ "transfer_method": file.transfer_method.value,
+ "type": file.type.value,
+ }
+ if file.transfer_method == FileTransferMethod.TOOL_FILE:
+ file_dict["tool_file_id"] = file.related_id
+ elif file.transfer_method == FileTransferMethod.LOCAL_FILE:
+ file_dict["upload_file_id"] = file.related_id
+ elif file.transfer_method == FileTransferMethod.REMOTE_URL:
+ file_dict["url"] = file.generate_url()
+
+ files.append(file_dict)
+ except Exception:
+ logger.exception(f"Failed to transform file {file}")
+ else:
+ parameters_result[parameter.name] = tool_parameters.get(parameter.name)
+
+ return parameters_result, files
+
+ def _extract_files(self, outputs: dict) -> tuple[dict, list[File]]:
+ """
+ extract files from the result
+
+ :param result: the result
+ :return: the result, files
+ """
+ files: list[File] = []
+ result = {}
+ for key, value in outputs.items():
+ if isinstance(value, list):
+ for item in value:
+ if isinstance(item, dict) and item.get("dify_model_identity") == FILE_MODEL_IDENTITY:
+ item = self._update_file_mapping(item)
+ file = build_from_mapping(
+ mapping=item,
+ tenant_id=str(cast(ToolRuntime, self.runtime).tenant_id),
+ )
+ files.append(file)
+ elif isinstance(value, dict) and value.get("dify_model_identity") == FILE_MODEL_IDENTITY:
+ value = self._update_file_mapping(value)
+ file = build_from_mapping(
+ mapping=value,
+ tenant_id=str(cast(ToolRuntime, self.runtime).tenant_id),
+ )
+ files.append(file)
+
+ result[key] = value
+
+ return result, files
+
+ def _update_file_mapping(self, file_dict: dict) -> dict:
+ transfer_method = FileTransferMethod.value_of(file_dict.get("transfer_method"))
+ if transfer_method == FileTransferMethod.TOOL_FILE:
+ file_dict["tool_file_id"] = file_dict.get("related_id")
+ elif transfer_method == FileTransferMethod.LOCAL_FILE:
+ file_dict["upload_file_id"] = file_dict.get("related_id")
+ return file_dict
diff --git a/api/core/workflow/entities/node_entities.py b/api/core/workflow/entities/node_entities.py
index ae5f117bf9b121..27c0e6702a5107 100644
--- a/api/core/workflow/entities/node_entities.py
+++ b/api/core/workflow/entities/node_entities.py
@@ -17,6 +17,7 @@ class NodeRunMetadataKey(StrEnum):
TOTAL_PRICE = "total_price"
CURRENCY = "currency"
TOOL_INFO = "tool_info"
+ AGENT_LOG = "agent_log"
ITERATION_ID = "iteration_id"
ITERATION_INDEX = "iteration_index"
PARALLEL_ID = "parallel_id"
@@ -48,3 +49,8 @@ class NodeRunResult(BaseModel):
# single step node run retry
retry_index: int = 0
+
+
+class AgentNodeStrategyInit(BaseModel):
+ name: str
+ icon: str | None = None
diff --git a/api/core/workflow/graph_engine/entities/event.py b/api/core/workflow/graph_engine/entities/event.py
index d591b68e7e72be..3130bb25da75e7 100644
--- a/api/core/workflow/graph_engine/entities/event.py
+++ b/api/core/workflow/graph_engine/entities/event.py
@@ -4,6 +4,7 @@
from pydantic import BaseModel, Field
+from core.workflow.entities.node_entities import AgentNodeStrategyInit
from core.workflow.graph_engine.entities.runtime_route_state import RouteNodeState
from core.workflow.nodes import NodeType
from core.workflow.nodes.base import BaseNodeData
@@ -66,8 +67,10 @@ class BaseNodeEvent(GraphEngineEvent):
class NodeRunStartedEvent(BaseNodeEvent):
predecessor_node_id: Optional[str] = None
- parallel_mode_run_id: Optional[str] = None
"""predecessor node id"""
+ parallel_mode_run_id: Optional[str] = None
+ """iteration node parallel mode run id"""
+ agent_strategy: Optional[AgentNodeStrategyInit] = None
class NodeRunStreamChunkEvent(BaseNodeEvent):
@@ -164,8 +167,8 @@ 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")
+ pre_iteration_output: Optional[Any] = None
+ duration: Optional[float] = None
class IterationRunSucceededEvent(BaseIterationEvent):
@@ -186,4 +189,24 @@ class IterationRunFailedEvent(BaseIterationEvent):
error: str = Field(..., description="failed reason")
-InNodeEvent = BaseNodeEvent | BaseParallelBranchEvent | BaseIterationEvent
+###########################################
+# Agent Events
+###########################################
+
+
+class BaseAgentEvent(GraphEngineEvent):
+ pass
+
+
+class AgentLogEvent(BaseAgentEvent):
+ id: str = Field(..., description="id")
+ label: str = Field(..., description="label")
+ node_execution_id: str = Field(..., description="node execution id")
+ parent_id: str | None = Field(..., description="parent id")
+ error: str | None = Field(..., description="error")
+ status: str = Field(..., description="status")
+ data: Mapping[str, Any] = Field(..., description="data")
+ metadata: Optional[Mapping[str, Any]] = Field(default=None, description="metadata")
+
+
+InNodeEvent = BaseNodeEvent | BaseParallelBranchEvent | BaseIterationEvent | BaseAgentEvent
diff --git a/api/core/workflow/graph_engine/graph_engine.py b/api/core/workflow/graph_engine/graph_engine.py
index db1e01f14fda59..937f4a2a597611 100644
--- a/api/core/workflow/graph_engine/graph_engine.py
+++ b/api/core/workflow/graph_engine/graph_engine.py
@@ -1,3 +1,4 @@
+import contextvars
import logging
import queue
import time
@@ -13,7 +14,7 @@
from configs import dify_config
from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError
from core.app.entities.app_invoke_entities import InvokeFrom
-from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult
+from core.workflow.entities.node_entities import AgentNodeStrategyInit, NodeRunMetadataKey, NodeRunResult
from core.workflow.entities.variable_pool import VariablePool, VariableValue
from core.workflow.graph_engine.condition_handlers.condition_manager import ConditionManager
from core.workflow.graph_engine.entities.event import (
@@ -39,6 +40,8 @@
from core.workflow.graph_engine.entities.graph_runtime_state import GraphRuntimeState
from core.workflow.graph_engine.entities.runtime_route_state import RouteNodeState
from core.workflow.nodes import NodeType
+from core.workflow.nodes.agent.agent_node import AgentNode
+from core.workflow.nodes.agent.entities import AgentNodeData
from core.workflow.nodes.answer.answer_stream_processor import AnswerStreamProcessor
from core.workflow.nodes.answer.base_stream_processor import StreamProcessor
from core.workflow.nodes.base import BaseNode
@@ -477,6 +480,7 @@ def _run_parallel_branches(
**{
"flask_app": current_app._get_current_object(), # type: ignore[attr-defined]
"q": q,
+ "context": contextvars.copy_context(),
"parallel_id": parallel_id,
"parallel_start_node_id": edge.target_node_id,
"parent_parallel_id": in_parallel_id,
@@ -520,6 +524,7 @@ def _run_parallel_branches(
def _run_parallel_node(
self,
flask_app: Flask,
+ context: contextvars.Context,
q: queue.Queue,
parallel_id: str,
parallel_start_node_id: str,
@@ -530,6 +535,9 @@ def _run_parallel_node(
"""
Run parallel nodes
"""
+ for var, val in context.items():
+ var.set(val)
+
with flask_app.app_context():
try:
q.put(
@@ -600,6 +608,14 @@ def _run_node(
Run node
"""
# trigger node run start event
+ agent_strategy = (
+ AgentNodeStrategyInit(
+ name=cast(AgentNodeData, node_instance.node_data).agent_strategy_name,
+ icon=cast(AgentNode, node_instance).agent_strategy_icon,
+ )
+ if node_instance.node_type == NodeType.AGENT
+ else None
+ )
yield NodeRunStartedEvent(
id=node_instance.id,
node_id=node_instance.node_id,
@@ -611,6 +627,7 @@ def _run_node(
parallel_start_node_id=parallel_start_node_id,
parent_parallel_id=parent_parallel_id,
parent_parallel_start_node_id=parent_parallel_start_node_id,
+ agent_strategy=agent_strategy,
)
db.session.close()
@@ -648,7 +665,7 @@ def _run_node(
retries += 1
route_node_state.node_run_result = run_result
yield NodeRunRetryEvent(
- id=node_instance.id,
+ id=str(uuid.uuid4()),
node_id=node_instance.node_id,
node_type=node_instance.node_type,
node_data=node_instance.node_data,
@@ -663,7 +680,7 @@ def _run_node(
start_at=retry_start_at,
)
time.sleep(retry_interval)
- continue
+ break
route_node_state.set_finished(run_result=run_result)
if run_result.status == WorkflowNodeExecutionStatus.FAILED:
diff --git a/api/core/workflow/nodes/agent/__init__.py b/api/core/workflow/nodes/agent/__init__.py
new file mode 100644
index 00000000000000..95e7cf895be959
--- /dev/null
+++ b/api/core/workflow/nodes/agent/__init__.py
@@ -0,0 +1,3 @@
+from .agent_node import AgentNode
+
+__all__ = ["AgentNode"]
diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py
new file mode 100644
index 00000000000000..db846249612b56
--- /dev/null
+++ b/api/core/workflow/nodes/agent/agent_node.py
@@ -0,0 +1,254 @@
+from ast import literal_eval
+from collections.abc import Generator, Mapping, Sequence
+from typing import Any, cast
+
+from core.agent.entities import AgentToolEntity
+from core.agent.plugin_entities import AgentStrategyParameter
+from core.model_manager import ModelManager
+from core.model_runtime.entities.model_entities import ModelType
+from core.plugin.manager.exc import PluginDaemonClientSideError
+from core.plugin.manager.plugin import PluginInstallationManager
+from core.tools.entities.tool_entities import ToolProviderType
+from core.tools.tool_manager import ToolManager
+from core.workflow.entities.node_entities import NodeRunResult
+from core.workflow.entities.variable_pool import VariablePool
+from core.workflow.enums import SystemVariableKey
+from core.workflow.nodes.agent.entities import AgentNodeData
+from core.workflow.nodes.base.entities import BaseNodeData
+from core.workflow.nodes.enums import NodeType
+from core.workflow.nodes.event.event import RunCompletedEvent
+from core.workflow.nodes.tool.tool_node import ToolNode
+from core.workflow.utils.variable_template_parser import VariableTemplateParser
+from factories.agent_factory import get_plugin_agent_strategy
+from models.workflow import WorkflowNodeExecutionStatus
+
+
+class AgentNode(ToolNode):
+ """
+ Agent Node
+ """
+
+ _node_data_cls = AgentNodeData # type: ignore
+ _node_type = NodeType.AGENT
+
+ def _run(self) -> Generator:
+ """
+ Run the agent node
+ """
+ node_data = cast(AgentNodeData, self.node_data)
+
+ try:
+ strategy = get_plugin_agent_strategy(
+ tenant_id=self.tenant_id,
+ agent_strategy_provider_name=node_data.agent_strategy_provider_name,
+ agent_strategy_name=node_data.agent_strategy_name,
+ )
+ except Exception as e:
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs={},
+ error=f"Failed to get agent strategy: {str(e)}",
+ )
+ )
+ return
+
+ agent_parameters = strategy.get_parameters()
+
+ # get parameters
+ parameters = self._generate_agent_parameters(
+ agent_parameters=agent_parameters,
+ variable_pool=self.graph_runtime_state.variable_pool,
+ node_data=node_data,
+ )
+ parameters_for_log = self._generate_agent_parameters(
+ agent_parameters=agent_parameters,
+ variable_pool=self.graph_runtime_state.variable_pool,
+ node_data=node_data,
+ for_log=True,
+ )
+
+ # get conversation id
+ conversation_id = self.graph_runtime_state.variable_pool.get(["sys", SystemVariableKey.CONVERSATION_ID])
+
+ try:
+ message_stream = strategy.invoke(
+ params=parameters,
+ user_id=self.user_id,
+ app_id=self.app_id,
+ conversation_id=conversation_id.text if conversation_id else None,
+ )
+ except Exception as e:
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs=parameters_for_log,
+ error=f"Failed to invoke agent: {str(e)}",
+ )
+ )
+ return
+
+ try:
+ # convert tool messages
+
+ yield from self._transform_message(
+ message_stream,
+ {
+ "icon": self.agent_strategy_icon,
+ "agent_strategy": cast(AgentNodeData, self.node_data).agent_strategy_name,
+ },
+ parameters_for_log,
+ )
+ except PluginDaemonClientSideError as e:
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs=parameters_for_log,
+ error=f"Failed to transform agent message: {str(e)}",
+ )
+ )
+
+ def _generate_agent_parameters(
+ self,
+ *,
+ agent_parameters: Sequence[AgentStrategyParameter],
+ variable_pool: VariablePool,
+ node_data: AgentNodeData,
+ for_log: bool = False,
+ ) -> dict[str, Any]:
+ """
+ Generate parameters based on the given tool parameters, variable pool, and node data.
+
+ Args:
+ agent_parameters (Sequence[AgentParameter]): The list of agent parameters.
+ variable_pool (VariablePool): The variable pool containing the variables.
+ node_data (AgentNodeData): The data associated with the agent node.
+
+ Returns:
+ Mapping[str, Any]: A dictionary containing the generated parameters.
+
+ """
+ agent_parameters_dictionary = {parameter.name: parameter for parameter in agent_parameters}
+
+ result: dict[str, Any] = {}
+ for parameter_name in node_data.agent_parameters:
+ parameter = agent_parameters_dictionary.get(parameter_name)
+ if not parameter:
+ result[parameter_name] = None
+ continue
+ agent_input = node_data.agent_parameters[parameter_name]
+ if agent_input.type == "variable":
+ variable = variable_pool.get(agent_input.value) # type: ignore
+ if variable is None:
+ raise ValueError(f"Variable {agent_input.value} does not exist")
+ parameter_value = variable.value
+ elif agent_input.type in {"mixed", "constant"}:
+ segment_group = variable_pool.convert_template(str(agent_input.value))
+ parameter_value = segment_group.log if for_log else segment_group.text
+ else:
+ raise ValueError(f"Unknown agent input type '{agent_input.type}'")
+ value = parameter_value.strip()
+ if (parameter_value.startswith("{") and parameter_value.endswith("}")) or (
+ parameter_value.startswith("[") and parameter_value.endswith("]")
+ ):
+ value = literal_eval(parameter_value) # transform string to python object
+ if parameter.type == "array[tools]":
+ value = cast(list[dict[str, Any]], value)
+ value = [tool for tool in value if tool.get("enabled", False)]
+
+ if not for_log:
+ if parameter.type == "array[tools]":
+ value = cast(list[dict[str, Any]], value)
+ tool_value = []
+ for tool in value:
+ entity = AgentToolEntity(
+ provider_id=tool.get("provider_name", ""),
+ provider_type=ToolProviderType.BUILT_IN,
+ tool_name=tool.get("tool_name", ""),
+ tool_parameters=tool.get("parameters", {}),
+ plugin_unique_identifier=tool.get("plugin_unique_identifier", None),
+ )
+
+ extra = tool.get("extra", {})
+
+ tool_runtime = ToolManager.get_agent_tool_runtime(
+ self.tenant_id, self.app_id, entity, self.invoke_from
+ )
+ if tool_runtime.entity.description:
+ tool_runtime.entity.description.llm = (
+ extra.get("descrption", "") or tool_runtime.entity.description.llm
+ )
+
+ tool_value.append(
+ {
+ **tool_runtime.entity.model_dump(mode="json"),
+ "runtime_parameters": tool_runtime.runtime.runtime_parameters,
+ }
+ )
+ value = tool_value
+ if parameter.type == "model-selector":
+ value = cast(dict[str, Any], value)
+ model_instance = ModelManager().get_model_instance(
+ tenant_id=self.tenant_id,
+ provider=value.get("provider", ""),
+ model_type=ModelType(value.get("model_type", "")),
+ model=value.get("model", ""),
+ )
+ models = model_instance.model_type_instance.plugin_model_provider.declaration.models
+ finded_model = next((model for model in models if model.model == value.get("model", "")), None)
+
+ value["entity"] = finded_model.model_dump(mode="json") if finded_model else None
+
+ result[parameter_name] = value
+
+ return result
+
+ @classmethod
+ def _extract_variable_selector_to_variable_mapping(
+ cls,
+ *,
+ graph_config: Mapping[str, Any],
+ node_id: str,
+ node_data: BaseNodeData,
+ ) -> Mapping[str, Sequence[str]]:
+ """
+ Extract variable selector to variable mapping
+ :param graph_config: graph config
+ :param node_id: node id
+ :param node_data: node data
+ :return:
+ """
+ node_data = cast(AgentNodeData, node_data)
+ result: dict[str, Any] = {}
+ for parameter_name in node_data.agent_parameters:
+ input = node_data.agent_parameters[parameter_name]
+ if input.type in ["mixed", "constant"]:
+ selectors = VariableTemplateParser(str(input.value)).extract_variable_selectors()
+ for selector in selectors:
+ result[selector.variable] = selector.value_selector
+ elif input.type == "variable":
+ result[parameter_name] = input.value
+
+ result = {node_id + "." + key: value for key, value in result.items()}
+
+ return result
+
+ @property
+ def agent_strategy_icon(self) -> str | None:
+ """
+ Get agent strategy icon
+ :return:
+ """
+ manager = PluginInstallationManager()
+ plugins = manager.list_plugins(self.tenant_id)
+ try:
+ current_plugin = next(
+ plugin
+ for plugin in plugins
+ if f"{plugin.plugin_id}/{plugin.name}"
+ == cast(AgentNodeData, self.node_data).agent_strategy_provider_name
+ )
+ icon = current_plugin.declaration.icon
+ except StopIteration:
+ icon = None
+ return icon
diff --git a/api/core/workflow/nodes/agent/entities.py b/api/core/workflow/nodes/agent/entities.py
new file mode 100644
index 00000000000000..066dcd5666ea85
--- /dev/null
+++ b/api/core/workflow/nodes/agent/entities.py
@@ -0,0 +1,18 @@
+from typing import Any, Literal, Union
+
+from pydantic import BaseModel
+
+from core.tools.entities.tool_entities import ToolSelector
+from core.workflow.nodes.base.entities import BaseNodeData
+
+
+class AgentNodeData(BaseNodeData):
+ agent_strategy_provider_name: str # redundancy
+ agent_strategy_name: str
+ agent_strategy_label: str # redundancy
+
+ class AgentInput(BaseModel):
+ value: Union[list[str], list[ToolSelector], Any]
+ type: Literal["mixed", "variable", "constant"]
+
+ agent_parameters: dict[str, AgentInput]
diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py
index 2f82bf8c382b55..b1a50c2dcc2c3f 100644
--- a/api/core/workflow/nodes/code/code_node.py
+++ b/api/core/workflow/nodes/code/code_node.py
@@ -195,7 +195,7 @@ def _transform_result(
if output_config.type == "object":
# check if output is object
if not isinstance(result.get(output_name), dict):
- if isinstance(result.get(output_name), type(None)):
+ if result[output_name] is None:
transformed_result[output_name] = None
else:
raise OutputValidationError(
@@ -223,7 +223,7 @@ def _transform_result(
elif output_config.type == "array[number]":
# check if array of number available
if not isinstance(result[output_name], list):
- if isinstance(result[output_name], type(None)):
+ if result[output_name] is None:
transformed_result[output_name] = None
else:
raise OutputValidationError(
@@ -244,7 +244,7 @@ def _transform_result(
elif output_config.type == "array[string]":
# check if array of string available
if not isinstance(result[output_name], list):
- if isinstance(result[output_name], type(None)):
+ if result[output_name] is None:
transformed_result[output_name] = None
else:
raise OutputValidationError(
@@ -265,7 +265,7 @@ def _transform_result(
elif output_config.type == "array[object]":
# check if array of object available
if not isinstance(result[output_name], list):
- if isinstance(result[output_name], type(None)):
+ if result[output_name] is None:
transformed_result[output_name] = None
else:
raise OutputValidationError(
diff --git a/api/core/workflow/nodes/document_extractor/node.py b/api/core/workflow/nodes/document_extractor/node.py
index c0d8c6409982e6..07abe345dddbfe 100644
--- a/api/core/workflow/nodes/document_extractor/node.py
+++ b/api/core/workflow/nodes/document_extractor/node.py
@@ -107,8 +107,10 @@ def _extract_text_by_mime_type(*, file_content: bytes, mime_type: str) -> str:
return _extract_text_from_plain_text(file_content)
case "application/pdf":
return _extract_text_from_pdf(file_content)
- case "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/msword":
+ case "application/msword":
return _extract_text_from_doc(file_content)
+ case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
+ return _extract_text_from_docx(file_content)
case "text/csv":
return _extract_text_from_csv(file_content)
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.ms-excel":
@@ -142,8 +144,10 @@ def _extract_text_by_file_extension(*, file_content: bytes, file_extension: str)
return _extract_text_from_yaml(file_content)
case ".pdf":
return _extract_text_from_pdf(file_content)
- case ".doc" | ".docx":
+ case ".doc":
return _extract_text_from_doc(file_content)
+ case ".docx":
+ return _extract_text_from_docx(file_content)
case ".csv":
return _extract_text_from_csv(file_content)
case ".xls" | ".xlsx":
@@ -203,7 +207,33 @@ def _extract_text_from_pdf(file_content: bytes) -> str:
def _extract_text_from_doc(file_content: bytes) -> str:
"""
- Extract text from a DOC/DOCX file.
+ Extract text from a DOC file.
+ """
+ from unstructured.partition.api import partition_via_api
+
+ if not (dify_config.UNSTRUCTURED_API_URL and dify_config.UNSTRUCTURED_API_KEY):
+ raise TextExtractionError("UNSTRUCTURED_API_URL and UNSTRUCTURED_API_KEY must be set")
+
+ try:
+ with tempfile.NamedTemporaryFile(suffix=".doc", delete=False) as temp_file:
+ temp_file.write(file_content)
+ temp_file.flush()
+ with open(temp_file.name, "rb") as file:
+ elements = partition_via_api(
+ file=file,
+ metadata_filename=temp_file.name,
+ api_url=dify_config.UNSTRUCTURED_API_URL,
+ api_key=dify_config.UNSTRUCTURED_API_KEY,
+ )
+ os.unlink(temp_file.name)
+ return "\n".join([getattr(element, "text", "") for element in elements])
+ except Exception as e:
+ raise TextExtractionError(f"Failed to extract text from DOC: {str(e)}") from e
+
+
+def _extract_text_from_docx(file_content: bytes) -> str:
+ """
+ Extract text from a DOCX file.
For now support only paragraph and table add more if needed
"""
try:
@@ -255,13 +285,13 @@ def _extract_text_from_doc(file_content: bytes) -> str:
text.append(markdown_table)
except Exception as e:
- logger.warning(f"Failed to extract table from DOC/DOCX: {e}")
+ logger.warning(f"Failed to extract table from DOC: {e}")
continue
return "\n".join(text)
except Exception as e:
- raise TextExtractionError(f"Failed to extract text from DOC/DOCX: {str(e)}") from e
+ raise TextExtractionError(f"Failed to extract text from DOCX: {str(e)}") from e
def _download_file_content(file: File) -> bytes:
@@ -329,14 +359,29 @@ def _extract_text_from_excel(file_content: bytes) -> str:
def _extract_text_from_ppt(file_content: bytes) -> str:
+ from unstructured.partition.api import partition_via_api
from unstructured.partition.ppt import partition_ppt
try:
- with io.BytesIO(file_content) as file:
- elements = partition_ppt(file=file)
+ if dify_config.UNSTRUCTURED_API_URL and dify_config.UNSTRUCTURED_API_KEY:
+ with tempfile.NamedTemporaryFile(suffix=".ppt", delete=False) as temp_file:
+ temp_file.write(file_content)
+ temp_file.flush()
+ with open(temp_file.name, "rb") as file:
+ elements = partition_via_api(
+ file=file,
+ metadata_filename=temp_file.name,
+ api_url=dify_config.UNSTRUCTURED_API_URL,
+ api_key=dify_config.UNSTRUCTURED_API_KEY,
+ )
+ os.unlink(temp_file.name)
+ else:
+ with io.BytesIO(file_content) as file:
+ elements = partition_ppt(file=file)
return "\n".join([getattr(element, "text", "") for element in elements])
+
except Exception as e:
- raise TextExtractionError(f"Failed to extract text from PPT: {str(e)}") from e
+ raise TextExtractionError(f"Failed to extract text from PPTX: {str(e)}") from e
def _extract_text_from_pptx(file_content: bytes) -> str:
diff --git a/api/core/workflow/nodes/end/end_node.py b/api/core/workflow/nodes/end/end_node.py
index 2398e4e89d59fa..6acc915ab568cf 100644
--- a/api/core/workflow/nodes/end/end_node.py
+++ b/api/core/workflow/nodes/end/end_node.py
@@ -1,6 +1,3 @@
-from collections.abc import Mapping, Sequence
-from typing import Any
-
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.end.entities import EndNodeData
@@ -30,20 +27,3 @@ def _run(self) -> NodeRunResult:
inputs=outputs,
outputs=outputs,
)
-
- @classmethod
- def _extract_variable_selector_to_variable_mapping(
- cls,
- *,
- graph_config: Mapping[str, Any],
- node_id: str,
- node_data: EndNodeData,
- ) -> Mapping[str, Sequence[str]]:
- """
- Extract variable selector to variable mapping
- :param graph_config: graph config
- :param node_id: node id
- :param node_data: node data
- :return:
- """
- return {}
diff --git a/api/core/workflow/nodes/enums.py b/api/core/workflow/nodes/enums.py
index 7970a49aa42df4..25e049577ac817 100644
--- a/api/core/workflow/nodes/enums.py
+++ b/api/core/workflow/nodes/enums.py
@@ -22,6 +22,7 @@ class NodeType(StrEnum):
VARIABLE_ASSIGNER = "assigner"
DOCUMENT_EXTRACTOR = "document-extractor"
LIST_OPERATOR = "list-operator"
+ AGENT = "agent"
class ErrorStrategy(StrEnum):
diff --git a/api/core/workflow/nodes/if_else/if_else_node.py b/api/core/workflow/nodes/if_else/if_else_node.py
index a1dc0f0664a32d..cb51b1ddd5c3c3 100644
--- a/api/core/workflow/nodes/if_else/if_else_node.py
+++ b/api/core/workflow/nodes/if_else/if_else_node.py
@@ -1,5 +1,4 @@
-from collections.abc import Mapping, Sequence
-from typing import Any, Literal
+from typing import Literal
from typing_extensions import deprecated
@@ -88,23 +87,6 @@ def _run(self) -> NodeRunResult:
return data
- @classmethod
- def _extract_variable_selector_to_variable_mapping(
- cls,
- *,
- graph_config: Mapping[str, Any],
- node_id: str,
- node_data: IfElseNodeData,
- ) -> Mapping[str, Sequence[str]]:
- """
- Extract variable selector to variable mapping
- :param graph_config: graph config
- :param node_id: node id
- :param node_data: node data
- :return:
- """
- return {}
-
@deprecated("This function is deprecated. You should use the new cases structure.")
def _should_not_use_old_function(
diff --git a/api/core/workflow/nodes/iteration/iteration_node.py b/api/core/workflow/nodes/iteration/iteration_node.py
index f1289558fffa82..16fccf8bf32c5b 100644
--- a/api/core/workflow/nodes/iteration/iteration_node.py
+++ b/api/core/workflow/nodes/iteration/iteration_node.py
@@ -1,3 +1,4 @@
+import contextvars
import logging
import uuid
from collections.abc import Generator, Mapping, Sequence
@@ -174,6 +175,7 @@ def _run(self) -> Generator[NodeEvent | InNodeEvent, None, None]:
self._run_single_iter_parallel,
flask_app=current_app._get_current_object(), # type: ignore
q=q,
+ context=contextvars.copy_context(),
iterator_list_value=iterator_list_value,
inputs=inputs,
outputs=outputs,
@@ -568,6 +570,7 @@ def _run_single_iter_parallel(
self,
*,
flask_app: Flask,
+ context: contextvars.Context,
q: Queue,
iterator_list_value: Sequence[str],
inputs: Mapping[str, list],
@@ -582,9 +585,12 @@ def _run_single_iter_parallel(
"""
run single iteration in parallel mode
"""
+ for var, val in context.items():
+ var.set(val)
with flask_app.app_context():
parallel_mode_run_id = uuid.uuid4().hex
graph_engine_copy = graph_engine.create_copy()
+ graph_engine_copy.graph_runtime_state.total_tokens = 0
variable_pool_copy = graph_engine_copy.graph_runtime_state.variable_pool
variable_pool_copy.add([self.node_id, "index"], index)
variable_pool_copy.add([self.node_id, "item"], item)
diff --git a/api/core/workflow/nodes/iteration/iteration_start_node.py b/api/core/workflow/nodes/iteration/iteration_start_node.py
index 6ab7c301066d93..deb5066a14346d 100644
--- a/api/core/workflow/nodes/iteration/iteration_start_node.py
+++ b/api/core/workflow/nodes/iteration/iteration_start_node.py
@@ -1,10 +1,7 @@
-from collections.abc import Mapping, Sequence
-from typing import Any
-
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.enums import NodeType
-from core.workflow.nodes.iteration.entities import IterationNodeData, IterationStartNodeData
+from core.workflow.nodes.iteration.entities import IterationStartNodeData
from models.workflow import WorkflowNodeExecutionStatus
@@ -21,16 +18,3 @@ def _run(self) -> NodeRunResult:
Run the node.
"""
return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED)
-
- @classmethod
- def _extract_variable_selector_to_variable_mapping(
- cls, graph_config: Mapping[str, Any], node_id: str, node_data: IterationNodeData
- ) -> Mapping[str, Sequence[str]]:
- """
- Extract variable selector to variable mapping
- :param graph_config: graph config
- :param node_id: node id
- :param node_data: node data
- :return:
- """
- return {}
diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py
index 7e28aa7a3ffb3d..aff3d2b7c68464 100644
--- a/api/core/workflow/nodes/llm/node.py
+++ b/api/core/workflow/nodes/llm/node.py
@@ -1,6 +1,7 @@
import json
import logging
from collections.abc import Generator, Mapping, Sequence
+from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any, Optional, cast
from configs import dify_config
@@ -29,6 +30,7 @@
from core.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey, ModelType
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.model_runtime.utils.encoders import jsonable_encoder
+from core.plugin.entities.plugin import ModelProviderID
from core.prompt.entities.advanced_prompt_entities import CompletionModelPromptTemplate, MemoryConfig
from core.prompt.utils.prompt_message_util import PromptMessageUtil
from core.variables import (
@@ -236,9 +238,9 @@ def _invoke_llm(
db.session.close()
invoke_result = model_instance.invoke_llm(
- prompt_messages=prompt_messages,
+ prompt_messages=list(prompt_messages),
model_parameters=node_data_model.completion_params,
- stop=stop,
+ stop=list(stop or []),
stream=True,
user=self.user_id,
)
@@ -247,6 +249,24 @@ def _invoke_llm(
def _handle_invoke_result(self, invoke_result: LLMResult | Generator) -> Generator[NodeEvent, None, None]:
if isinstance(invoke_result, LLMResult):
+ content = invoke_result.message.content
+ if content is None:
+ message_text = ""
+ elif isinstance(content, str):
+ message_text = content
+ elif isinstance(content, list):
+ # Assuming the list contains PromptMessageContent objects with a "data" attribute
+ message_text = "".join(
+ item.data if hasattr(item, "data") and isinstance(item.data, str) else str(item) for item in content
+ )
+ else:
+ message_text = str(content)
+
+ yield ModelInvokeCompletedEvent(
+ text=message_text,
+ usage=invoke_result.usage,
+ finish_reason=None,
+ )
return
model = None
@@ -740,11 +760,17 @@ def deduct_llm_quota(cls, tenant_id: str, model_instance: ModelInstance, usage:
if used_quota is not None and system_configuration.current_quota_type is not None:
db.session.query(Provider).filter(
Provider.tenant_id == tenant_id,
- Provider.provider_name == model_instance.provider,
+ # TODO: Use provider name with prefix after the data migration.
+ Provider.provider_name == ModelProviderID(model_instance.provider).provider_name,
Provider.provider_type == ProviderType.SYSTEM.value,
Provider.quota_type == system_configuration.current_quota_type.value,
Provider.quota_limit > Provider.quota_used,
- ).update({"quota_used": Provider.quota_used + used_quota})
+ ).update(
+ {
+ "quota_used": Provider.quota_used + used_quota,
+ "last_used": datetime.now(tz=UTC).replace(tzinfo=None),
+ }
+ )
db.session.commit()
@classmethod
diff --git a/api/core/workflow/nodes/node_mapping.py b/api/core/workflow/nodes/node_mapping.py
index 51fc5129cdd875..6341b9455c5943 100644
--- a/api/core/workflow/nodes/node_mapping.py
+++ b/api/core/workflow/nodes/node_mapping.py
@@ -1,5 +1,6 @@
from collections.abc import Mapping
+from core.workflow.nodes.agent.agent_node import AgentNode
from core.workflow.nodes.answer import AnswerNode
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.code import CodeNode
@@ -101,4 +102,8 @@
LATEST_VERSION: ListOperatorNode,
"1": ListOperatorNode,
},
+ NodeType.AGENT: {
+ LATEST_VERSION: AgentNode,
+ "1": AgentNode,
+ },
}
diff --git a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
index 9c88047f2c8e57..e147caacf3ecea 100644
--- a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
+++ b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
@@ -244,8 +244,8 @@ def _invoke(
if not isinstance(invoke_result, LLMResult):
raise InvalidInvokeResultError(f"Invalid invoke result: {invoke_result}")
- text = invoke_result.message.content
- if not isinstance(text, str | None):
+ text = invoke_result.message.content or ""
+ if not isinstance(text, str):
raise InvalidTextContentTypeError(f"Invalid text content type: {type(text)}. Expected str.")
usage = invoke_result.usage
diff --git a/api/core/workflow/nodes/start/start_node.py b/api/core/workflow/nodes/start/start_node.py
index a7b91e82bbdd92..1b47b81517b601 100644
--- a/api/core/workflow/nodes/start/start_node.py
+++ b/api/core/workflow/nodes/start/start_node.py
@@ -1,6 +1,3 @@
-from collections.abc import Mapping, Sequence
-from typing import Any
-
from core.workflow.constants import SYSTEM_VARIABLE_NODE_ID
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.base import BaseNode
@@ -23,13 +20,3 @@ def _run(self) -> NodeRunResult:
node_inputs[SYSTEM_VARIABLE_NODE_ID + "." + var] = system_inputs[var]
return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=node_inputs, outputs=node_inputs)
-
- @classmethod
- def _extract_variable_selector_to_variable_mapping(
- cls,
- *,
- graph_config: Mapping[str, Any],
- node_id: str,
- node_data: StartNodeData,
- ) -> Mapping[str, Sequence[str]]:
- return {}
diff --git a/api/core/workflow/nodes/tool/entities.py b/api/core/workflow/nodes/tool/entities.py
index 9e29791481436e..21023d4ab76195 100644
--- a/api/core/workflow/nodes/tool/entities.py
+++ b/api/core/workflow/nodes/tool/entities.py
@@ -3,16 +3,18 @@
from pydantic import BaseModel, field_validator
from pydantic_core.core_schema import ValidationInfo
-from core.workflow.nodes.base import BaseNodeData
+from core.tools.entities.tool_entities import ToolProviderType
+from core.workflow.nodes.base.entities import BaseNodeData
class ToolEntity(BaseModel):
provider_id: str
- provider_type: Literal["builtin", "api", "workflow"]
+ provider_type: ToolProviderType
provider_name: str # redundancy
tool_name: str
tool_label: str # redundancy
tool_configurations: dict[str, Any]
+ plugin_unique_identifier: str | None = None # redundancy
@field_validator("tool_configurations", mode="before")
@classmethod
diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py
index 01d07e494944b4..8518db5d8073ca 100644
--- a/api/core/workflow/nodes/tool/tool_node.py
+++ b/api/core/workflow/nodes/tool/tool_node.py
@@ -1,24 +1,31 @@
-from collections.abc import Mapping, Sequence
-from typing import Any
-from uuid import UUID
+from collections.abc import Generator, Mapping, Sequence
+from typing import Any, cast
from sqlalchemy import select
from sqlalchemy.orm import Session
from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCallbackHandler
-from core.file import File, FileTransferMethod, FileType
+from core.file import File, FileTransferMethod
+from core.plugin.manager.exc import PluginDaemonClientSideError
+from core.plugin.manager.plugin import PluginInstallationManager
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
from core.tools.tool_engine import ToolEngine
from core.tools.utils.message_transformer import ToolFileMessageTransformer
+from core.variables.segments import ArrayAnySegment
+from core.variables.variables import ArrayAnyVariable
from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult
from core.workflow.entities.variable_pool import VariablePool
+from core.workflow.enums import SystemVariableKey
+from core.workflow.graph_engine.entities.event import AgentLogEvent
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.enums import NodeType
+from core.workflow.nodes.event import RunCompletedEvent, RunStreamChunkEvent
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
+from services.tools.builtin_tools_manage_service import BuiltinToolManageService
from .entities import ToolNodeData
from .exc import (
@@ -36,11 +43,18 @@ class ToolNode(BaseNode[ToolNodeData]):
_node_data_cls = ToolNodeData
_node_type = NodeType.TOOL
- def _run(self) -> NodeRunResult:
+ def _run(self) -> Generator:
+ """
+ Run the tool node
+ """
+
+ node_data = cast(ToolNodeData, self.node_data)
+
# fetch tool icon
tool_info = {
- "provider_type": self.node_data.provider_type,
- "provider_id": self.node_data.provider_id,
+ "provider_type": node_data.provider_type.value,
+ "provider_id": node_data.provider_id,
+ "plugin_unique_identifier": node_data.plugin_unique_identifier,
}
# get tool runtime
@@ -51,18 +65,19 @@ def _run(self) -> NodeRunResult:
self.tenant_id, self.app_id, self.node_id, self.node_data, self.invoke_from
)
except ToolNodeError as e:
- return NodeRunResult(
- status=WorkflowNodeExecutionStatus.FAILED,
- inputs={},
- metadata={
- NodeRunMetadataKey.TOOL_INFO: tool_info,
- },
- error=f"Failed to get tool runtime: {str(e)}",
- error_type=type(e).__name__,
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs={},
+ metadata={NodeRunMetadataKey.TOOL_INFO: tool_info},
+ error=f"Failed to get tool runtime: {str(e)}",
+ error_type=type(e).__name__,
+ )
)
+ return
# get parameters
- tool_parameters = tool_runtime.parameters or []
+ tool_parameters = tool_runtime.get_merged_runtime_parameters() or []
parameters = self._generate_parameters(
tool_parameters=tool_parameters,
variable_pool=self.graph_runtime_state.variable_pool,
@@ -75,51 +90,44 @@ def _run(self) -> NodeRunResult:
for_log=True,
)
+ # get conversation id
+ conversation_id = self.graph_runtime_state.variable_pool.get(["sys", SystemVariableKey.CONVERSATION_ID])
+
try:
- messages = ToolEngine.workflow_invoke(
+ message_stream = ToolEngine.generic_invoke(
tool=tool_runtime,
tool_parameters=parameters,
user_id=self.user_id,
workflow_tool_callback=DifyWorkflowCallbackHandler(),
workflow_call_depth=self.workflow_call_depth,
thread_pool_id=self.thread_pool_id,
+ app_id=self.app_id,
+ conversation_id=conversation_id.text if conversation_id else None,
)
except ToolNodeError as e:
- return NodeRunResult(
- status=WorkflowNodeExecutionStatus.FAILED,
- inputs=parameters_for_log,
- metadata={
- NodeRunMetadataKey.TOOL_INFO: tool_info,
- },
- error=f"Failed to invoke tool: {str(e)}",
- error_type=type(e).__name__,
- )
- except Exception as e:
- return NodeRunResult(
- status=WorkflowNodeExecutionStatus.FAILED,
- inputs=parameters_for_log,
- metadata={
- NodeRunMetadataKey.TOOL_INFO: tool_info,
- },
- error=f"Failed to invoke tool: {str(e)}",
- error_type="UnknownError",
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs=parameters_for_log,
+ metadata={NodeRunMetadataKey.TOOL_INFO: tool_info},
+ error=f"Failed to invoke tool: {str(e)}",
+ error_type=type(e).__name__,
+ )
)
+ return
- # convert tool messages
- plain_text, files, json = self._convert_tool_messages(messages)
-
- return NodeRunResult(
- status=WorkflowNodeExecutionStatus.SUCCEEDED,
- outputs={
- "text": plain_text,
- "files": files,
- "json": json,
- },
- metadata={
- NodeRunMetadataKey.TOOL_INFO: tool_info,
- },
- inputs=parameters_for_log,
- )
+ try:
+ # convert tool messages
+ yield from self._transform_message(message_stream, tool_info, parameters_for_log)
+ except PluginDaemonClientSideError as e:
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.FAILED,
+ inputs=parameters_for_log,
+ metadata={NodeRunMetadataKey.TOOL_INFO: tool_info},
+ error=f"Failed to transform tool message: {str(e)}",
+ )
+ )
def _generate_parameters(
self,
@@ -128,7 +136,7 @@ def _generate_parameters(
variable_pool: VariablePool,
node_data: ToolNodeData,
for_log: bool = False,
- ) -> Mapping[str, Any]:
+ ) -> dict[str, Any]:
"""
Generate parameters based on the given tool parameters, variable pool, and node data.
@@ -164,37 +172,52 @@ def _generate_parameters(
return result
- def _convert_tool_messages(
+ def _fetch_files(self, variable_pool: VariablePool) -> list[File]:
+ variable = variable_pool.get(["sys", SystemVariableKey.FILES.value])
+ assert isinstance(variable, ArrayAnyVariable | ArrayAnySegment)
+ return list(variable.value) if variable else []
+
+ def _transform_message(
self,
- messages: list[ToolInvokeMessage],
- ):
+ messages: Generator[ToolInvokeMessage, None, None],
+ tool_info: Mapping[str, Any],
+ parameters_for_log: dict[str, Any],
+ ) -> Generator:
"""
Convert ToolInvokeMessages into tuple[plain_text, files]
"""
# transform message and handle file storage
- messages = ToolFileMessageTransformer.transform_tool_invoke_messages(
+ message_stream = ToolFileMessageTransformer.transform_tool_invoke_messages(
messages=messages,
user_id=self.user_id,
tenant_id=self.tenant_id,
conversation_id=None,
)
- # extract plain text and files
- files = self._extract_tool_response_binary(messages)
- plain_text = self._extract_tool_response_text(messages)
- json = self._extract_tool_response_json(messages)
- return plain_text, files, json
+ text = ""
+ files: list[File] = []
+ json: list[dict] = []
+
+ agent_logs: list[AgentLogEvent] = []
+ agent_execution_metadata: Mapping[NodeRunMetadataKey, Any] = {}
+
+ variables: dict[str, Any] = {}
+
+ for message in message_stream:
+ if message.type in {
+ ToolInvokeMessage.MessageType.IMAGE_LINK,
+ ToolInvokeMessage.MessageType.BINARY_LINK,
+ ToolInvokeMessage.MessageType.IMAGE,
+ }:
+ assert isinstance(message.message, ToolInvokeMessage.TextMessage)
+
+ url = message.message.text
+ if message.meta:
+ transfer_method = message.meta.get("transfer_method", FileTransferMethod.TOOL_FILE)
+ else:
+ transfer_method = FileTransferMethod.TOOL_FILE
- def _extract_tool_response_binary(self, tool_response: list[ToolInvokeMessage]) -> list[File]:
- """
- Extract tool response binary
- """
- result = []
- 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
tool_file_id = str(url).split("/")[-1].split(".")[0]
- transfer_method = response.meta.get("transfer_method", FileTransferMethod.TOOL_FILE)
with Session(db.engine) as session:
stmt = select(ToolFile).where(ToolFile.id == tool_file_id)
@@ -204,7 +227,7 @@ def _extract_tool_response_binary(self, tool_response: list[ToolInvokeMessage])
mapping = {
"tool_file_id": tool_file_id,
- "type": FileType.IMAGE,
+ "type": file_factory.get_file_type_by_mime_type(tool_file.mimetype),
"transfer_method": transfer_method,
"url": url,
}
@@ -212,69 +235,138 @@ def _extract_tool_response_binary(self, tool_response: list[ToolInvokeMessage])
mapping=mapping,
tenant_id=self.tenant_id,
)
- result.append(file)
- elif response.type == ToolInvokeMessage.MessageType.BLOB:
- tool_file_id = str(response.message).split("/")[-1].split(".")[0]
+ files.append(file)
+ elif message.type == ToolInvokeMessage.MessageType.BLOB:
+ # get tool file id
+ assert isinstance(message.message, ToolInvokeMessage.TextMessage)
+ assert message.meta
+
+ tool_file_id = message.message.text.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 ValueError(f"tool file {tool_file_id} not exists")
+ raise ToolFileError(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,
+
+ files.append(
+ 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
- tool_file_id = url.split("/")[-1].split(".")[0]
- try:
- UUID(tool_file_id)
- except ValueError:
- raise ToolFileError(f"cannot extract tool file id from url {url}")
- 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")
- 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,
+ elif message.type == ToolInvokeMessage.MessageType.TEXT:
+ assert isinstance(message.message, ToolInvokeMessage.TextMessage)
+ text += message.message.text
+ yield RunStreamChunkEvent(
+ chunk_content=message.message.text, from_variable_selector=[self.node_id, "text"]
)
- result.append(file)
+ elif message.type == ToolInvokeMessage.MessageType.JSON:
+ assert isinstance(message.message, ToolInvokeMessage.JsonMessage)
+ if self.node_type == NodeType.AGENT:
+ msg_metadata = message.message.json_object.pop("execution_metadata", {})
+ agent_execution_metadata = {
+ key: value for key, value in msg_metadata.items() if key in NodeRunMetadataKey
+ }
+ json.append(message.message.json_object)
+ elif message.type == ToolInvokeMessage.MessageType.LINK:
+ assert isinstance(message.message, ToolInvokeMessage.TextMessage)
+ stream_text = f"Link: {message.message.text}\n"
+ text += stream_text
+ yield RunStreamChunkEvent(chunk_content=stream_text, from_variable_selector=[self.node_id, "text"])
+ elif message.type == ToolInvokeMessage.MessageType.VARIABLE:
+ assert isinstance(message.message, ToolInvokeMessage.VariableMessage)
+ variable_name = message.message.variable_name
+ variable_value = message.message.variable_value
+ if message.message.stream:
+ if not isinstance(variable_value, str):
+ raise ValueError("When 'stream' is True, 'variable_value' must be a string.")
+ if variable_name not in variables:
+ variables[variable_name] = ""
+ variables[variable_name] += variable_value
- elif response.type == ToolInvokeMessage.MessageType.FILE:
- assert response.meta is not None
- result.append(response.meta["file"])
+ yield RunStreamChunkEvent(
+ chunk_content=variable_value, from_variable_selector=[self.node_id, variable_name]
+ )
+ else:
+ variables[variable_name] = variable_value
+ elif message.type == ToolInvokeMessage.MessageType.FILE:
+ assert message.meta is not None
+ files.append(message.meta["file"])
+ elif message.type == ToolInvokeMessage.MessageType.LOG:
+ assert isinstance(message.message, ToolInvokeMessage.LogMessage)
+ if message.message.metadata:
+ icon = tool_info.get("icon", "")
+ dict_metadata = dict(message.message.metadata)
+ if dict_metadata.get("provider"):
+ manager = PluginInstallationManager()
+ plugins = manager.list_plugins(self.tenant_id)
+ try:
+ current_plugin = next(
+ plugin
+ for plugin in plugins
+ if f"{plugin.plugin_id}/{plugin.name}" == dict_metadata["provider"]
+ )
+ icon = current_plugin.declaration.icon
+ except StopIteration:
+ pass
+ try:
+ builtin_tool = next(
+ provider
+ for provider in BuiltinToolManageService.list_builtin_tools(
+ self.user_id,
+ self.tenant_id,
+ )
+ if provider.name == dict_metadata["provider"]
+ )
+ icon = builtin_tool.icon
+ except StopIteration:
+ pass
- return result
+ dict_metadata["icon"] = icon
+ message.message.metadata = dict_metadata
+ agent_log = AgentLogEvent(
+ id=message.message.id,
+ node_execution_id=self.id,
+ parent_id=message.message.parent_id,
+ error=message.message.error,
+ status=message.message.status.value,
+ data=message.message.data,
+ label=message.message.label,
+ metadata=message.message.metadata,
+ )
- def _extract_tool_response_text(self, tool_response: list[ToolInvokeMessage]) -> str:
- """
- Extract tool response text
- """
- return "\n".join(
- [
- str(message.message)
- if message.type == ToolInvokeMessage.MessageType.TEXT
- else f"Link: {str(message.message)}"
- for message in tool_response
- if message.type in {ToolInvokeMessage.MessageType.TEXT, ToolInvokeMessage.MessageType.LINK}
- ]
- )
+ # check if the agent log is already in the list
+ for log in agent_logs:
+ if log.id == agent_log.id:
+ # update the log
+ log.data = agent_log.data
+ log.status = agent_log.status
+ log.error = agent_log.error
+ log.label = agent_log.label
+ log.metadata = agent_log.metadata
+ break
+ else:
+ agent_logs.append(agent_log)
- def _extract_tool_response_json(self, tool_response: list[ToolInvokeMessage]):
- return [message.message for message in tool_response if message.type == ToolInvokeMessage.MessageType.JSON]
+ yield agent_log
+
+ yield RunCompletedEvent(
+ run_result=NodeRunResult(
+ status=WorkflowNodeExecutionStatus.SUCCEEDED,
+ outputs={"text": text, "files": files, "json": json, **variables},
+ metadata={
+ **agent_execution_metadata,
+ NodeRunMetadataKey.TOOL_INFO: tool_info,
+ NodeRunMetadataKey.AGENT_LOG: agent_logs,
+ },
+ inputs=parameters_for_log,
+ )
+ )
@classmethod
def _extract_variable_selector_to_variable_mapping(
@@ -295,7 +387,8 @@ def _extract_variable_selector_to_variable_mapping(
for parameter_name in node_data.tool_parameters:
input = node_data.tool_parameters[parameter_name]
if input.type == "mixed":
- selectors = VariableTemplateParser(str(input.value)).extract_variable_selectors()
+ assert isinstance(input.value, str)
+ selectors = VariableTemplateParser(input.value).extract_variable_selectors()
for selector in selectors:
result[selector.variable] = selector.value_selector
elif input.type == "variable":
diff --git a/api/core/workflow/nodes/variable_aggregator/variable_aggregator_node.py b/api/core/workflow/nodes/variable_aggregator/variable_aggregator_node.py
index 031a7b83095541..372496a8fabd57 100644
--- a/api/core/workflow/nodes/variable_aggregator/variable_aggregator_node.py
+++ b/api/core/workflow/nodes/variable_aggregator/variable_aggregator_node.py
@@ -1,6 +1,3 @@
-from collections.abc import Mapping, Sequence
-from typing import Any
-
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.enums import NodeType
@@ -36,16 +33,3 @@ def _run(self) -> NodeRunResult:
break
return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, outputs=outputs, inputs=inputs)
-
- @classmethod
- def _extract_variable_selector_to_variable_mapping(
- cls, *, graph_config: Mapping[str, Any], node_id: str, node_data: VariableAssignerNodeData
- ) -> Mapping[str, Sequence[str]]:
- """
- Extract variable selector to variable mapping
- :param graph_config: graph config
- :param node_id: node id
- :param node_data: node data
- :return:
- """
- return {}
diff --git a/api/core/workflow/utils/condition/processor.py b/api/core/workflow/utils/condition/processor.py
index 19473f39d2299a..c61b3d1861cc57 100644
--- a/api/core/workflow/utils/condition/processor.py
+++ b/api/core/workflow/utils/condition/processor.py
@@ -64,6 +64,10 @@ def process_conditions(
expected=expected_value,
)
group_results.append(result)
+ # Implemented short-circuit evaluation for logical conditions
+ if (operator == "and" and not result) or (operator == "or" and result):
+ final_result = result
+ return input_conditions, group_results, final_result
final_result = all(group_results) if operator == "and" else any(group_results)
return input_conditions, group_results, final_result
diff --git a/api/core/workflow/workflow_entry.py b/api/core/workflow/workflow_entry.py
index f622d0b2d01f28..5a7d5373c1ba9d 100644
--- a/api/core/workflow/workflow_entry.py
+++ b/api/core/workflow/workflow_entry.py
@@ -2,7 +2,7 @@
import time
import uuid
from collections.abc import Generator, Mapping, Sequence
-from typing import Any, Optional
+from typing import Any, Optional, cast
from configs import dify_config
from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError
@@ -194,6 +194,105 @@ def single_step_run(
raise WorkflowNodeRunFailedError(node_instance=node_instance, error=str(e))
return node_instance, generator
+ @classmethod
+ def run_free_node(
+ cls, node_data: dict, node_id: str, tenant_id: str, user_id: str, user_inputs: dict[str, Any]
+ ) -> tuple[BaseNode, Generator[NodeEvent | InNodeEvent, None, None]]:
+ """
+ Run free node
+
+ NOTE: only parameter_extractor/question_classifier are supported
+
+ :param node_data: node data
+ :param user_id: user id
+ :param user_inputs: user inputs
+ :return:
+ """
+ # generate a fake graph
+ node_config = {"id": node_id, "width": 114, "height": 514, "type": "custom", "data": node_data}
+ start_node_config = {
+ "id": "start",
+ "width": 114,
+ "height": 514,
+ "type": "custom",
+ "data": {
+ "type": NodeType.START.value,
+ "title": "Start",
+ "desc": "Start",
+ },
+ }
+ graph_dict = {
+ "nodes": [start_node_config, node_config],
+ "edges": [
+ {
+ "source": "start",
+ "target": node_id,
+ "sourceHandle": "source",
+ "targetHandle": "target",
+ }
+ ],
+ }
+
+ node_type = NodeType(node_data.get("type", ""))
+ if node_type not in {NodeType.PARAMETER_EXTRACTOR, NodeType.QUESTION_CLASSIFIER}:
+ raise ValueError(f"Node type {node_type} not supported")
+
+ node_cls = NODE_TYPE_CLASSES_MAPPING[node_type]["1"]
+ if not node_cls:
+ raise ValueError(f"Node class not found for node type {node_type}")
+
+ graph = Graph.init(graph_config=graph_dict)
+
+ # init variable pool
+ variable_pool = VariablePool(
+ system_variables={},
+ user_inputs={},
+ environment_variables=[],
+ )
+
+ node_cls = cast(type[BaseNode], node_cls)
+ # init workflow run state
+ node_instance: BaseNode = node_cls(
+ id=str(uuid.uuid4()),
+ config=node_config,
+ graph_init_params=GraphInitParams(
+ tenant_id=tenant_id,
+ app_id="",
+ workflow_type=WorkflowType.WORKFLOW,
+ workflow_id="",
+ graph_config=graph_dict,
+ user_id=user_id,
+ user_from=UserFrom.ACCOUNT,
+ invoke_from=InvokeFrom.DEBUGGER,
+ call_depth=0,
+ ),
+ graph=graph,
+ graph_runtime_state=GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()),
+ )
+
+ try:
+ # variable selector to variable mapping
+ try:
+ variable_mapping = node_cls.extract_variable_selector_to_variable_mapping(
+ graph_config=graph_dict, config=node_config
+ )
+ except NotImplementedError:
+ variable_mapping = {}
+
+ cls.mapping_user_inputs_to_variable_pool(
+ variable_mapping=variable_mapping,
+ user_inputs=user_inputs,
+ variable_pool=variable_pool,
+ tenant_id=tenant_id,
+ )
+
+ # run node
+ generator = node_instance.run()
+
+ return node_instance, generator
+ except Exception as e:
+ raise WorkflowNodeRunFailedError(node_instance=node_instance, error=str(e))
+
@staticmethod
def handle_special_values(value: Optional[Mapping[str, Any]]) -> Mapping[str, Any] | None:
result = WorkflowEntry._handle_special_values(value)
diff --git a/api/events/event_handlers/deduct_quota_when_message_created.py b/api/events/event_handlers/deduct_quota_when_message_created.py
index d196a4862013b7..b8e7019446cff9 100644
--- a/api/events/event_handlers/deduct_quota_when_message_created.py
+++ b/api/events/event_handlers/deduct_quota_when_message_created.py
@@ -1,6 +1,9 @@
+from datetime import UTC, datetime
+
from configs import dify_config
from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity, ChatAppGenerateEntity
from core.entities.provider_entities import QuotaUnit
+from core.plugin.entities.plugin import ModelProviderID
from events.message_event import message_was_created
from extensions.ext_database import db
from models.provider import Provider, ProviderType
@@ -23,6 +26,9 @@ def handle(sender, **kwargs):
system_configuration = provider_configuration.system_configuration
+ if not system_configuration.current_quota_type:
+ return
+
quota_unit = None
for quota_configuration in system_configuration.quota_configurations:
if quota_configuration.quota_type == system_configuration.current_quota_type:
@@ -45,9 +51,15 @@ def handle(sender, **kwargs):
if used_quota is not None and system_configuration.current_quota_type is not None:
db.session.query(Provider).filter(
Provider.tenant_id == application_generate_entity.app_config.tenant_id,
- Provider.provider_name == model_config.provider,
+ # TODO: Use provider name with prefix after the data migration.
+ Provider.provider_name == ModelProviderID(model_config.provider).provider_name,
Provider.provider_type == ProviderType.SYSTEM.value,
Provider.quota_type == system_configuration.current_quota_type.value,
Provider.quota_limit > Provider.quota_used,
- ).update({"quota_used": Provider.quota_used + used_quota})
+ ).update(
+ {
+ "quota_used": Provider.quota_used + used_quota,
+ "last_used": datetime.now(tz=UTC).replace(tzinfo=None),
+ }
+ )
db.session.commit()
diff --git a/api/extensions/ext_commands.py b/api/extensions/ext_commands.py
index ccf0d316ca486e..3f5ae539c5b694 100644
--- a/api/extensions/ext_commands.py
+++ b/api/extensions/ext_commands.py
@@ -6,7 +6,11 @@ def init_app(app: DifyApp):
add_qdrant_doc_id_index,
convert_to_agent_apps,
create_tenant,
+ extract_plugins,
+ extract_unique_plugins,
fix_app_site_missing,
+ install_plugins,
+ migrate_data_for_plugin,
reset_email,
reset_encrypt_key_pair,
reset_password,
@@ -24,6 +28,10 @@ def init_app(app: DifyApp):
create_tenant,
upgrade_db,
fix_app_site_missing,
+ migrate_data_for_plugin,
+ extract_plugins,
+ extract_unique_plugins,
+ install_plugins,
]
for cmd in cmds_to_register:
app.cli.add_command(cmd)
diff --git a/api/extensions/ext_proxy_fix.py b/api/extensions/ext_proxy_fix.py
index 514e0658257293..c085aed98643d3 100644
--- a/api/extensions/ext_proxy_fix.py
+++ b/api/extensions/ext_proxy_fix.py
@@ -6,4 +6,4 @@ def init_app(app: DifyApp):
if dify_config.RESPECT_XFORWARD_HEADERS_ENABLED:
from werkzeug.middleware.proxy_fix import ProxyFix
- app.wsgi_app = ProxyFix(app.wsgi_app) # type: ignore
+ app.wsgi_app = ProxyFix(app.wsgi_app, x_port=1) # type: ignore
diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py
index da4180570793e5..f8679f7e4b477a 100644
--- a/api/extensions/ext_redis.py
+++ b/api/extensions/ext_redis.py
@@ -54,7 +54,7 @@ def init_app(app: DifyApp):
redis_params: dict[str, Any] = {
"username": dify_config.REDIS_USERNAME,
- "password": dify_config.REDIS_PASSWORD,
+ "password": dify_config.REDIS_PASSWORD or None, # Temporary fix for empty password
"db": dify_config.REDIS_DB,
"encoding": "utf-8",
"encoding_errors": "strict",
diff --git a/api/extensions/storage/opendal_storage.py b/api/extensions/storage/opendal_storage.py
index b78fc94dae7843..98f646f4f9f92a 100644
--- a/api/extensions/storage/opendal_storage.py
+++ b/api/extensions/storage/opendal_storage.py
@@ -34,7 +34,7 @@ def __init__(self, scheme: str, **kwargs):
root = kwargs.get("root", "storage")
Path(root).mkdir(parents=True, exist_ok=True)
- self.op = opendal.Operator(scheme=scheme, **kwargs)
+ self.op = opendal.Operator(scheme=scheme, **kwargs) # type: ignore
logger.debug(f"opendal operator created with scheme {scheme}")
retry_layer = opendal.layers.RetryLayer(max_times=3, factor=2.0, jitter=True)
self.op = self.op.layer(retry_layer)
diff --git a/api/factories/agent_factory.py b/api/factories/agent_factory.py
new file mode 100644
index 00000000000000..4b2d2cc769db6a
--- /dev/null
+++ b/api/factories/agent_factory.py
@@ -0,0 +1,15 @@
+from core.agent.strategy.plugin import PluginAgentStrategy
+from core.plugin.manager.agent import PluginAgentManager
+
+
+def get_plugin_agent_strategy(
+ tenant_id: str, agent_strategy_provider_name: str, agent_strategy_name: str
+) -> PluginAgentStrategy:
+ # TODO: use contexts to cache the agent provider
+ manager = PluginAgentManager()
+ agent_provider = manager.fetch_agent_strategy_provider(tenant_id, agent_strategy_provider_name)
+ for agent_strategy in agent_provider.declaration.strategies:
+ if agent_strategy.identity.name == agent_strategy_name:
+ return PluginAgentStrategy(tenant_id, agent_strategy)
+
+ raise ValueError(f"Agent strategy {agent_strategy_name} not found")
diff --git a/api/factories/file_factory.py b/api/factories/file_factory.py
index c6dc748e93bd03..24d3c8b90669da 100644
--- a/api/factories/file_factory.py
+++ b/api/factories/file_factory.py
@@ -302,3 +302,7 @@ def _get_file_type_by_mimetype(mime_type: str) -> FileType | None:
else:
file_type = FileType.CUSTOM
return file_type
+
+
+def get_file_type_by_mime_type(mime_type: str) -> FileType:
+ return _get_file_type_by_mimetype(mime_type) or FileType.CUSTOM
diff --git a/api/fields/app_fields.py b/api/fields/app_fields.py
index 73800eab853cd3..eaf684f16ad832 100644
--- a/api/fields/app_fields.py
+++ b/api/fields/app_fields.py
@@ -149,6 +149,12 @@
"updated_at": TimestampField,
}
+deleted_tool_fields = {
+ "type": fields.String,
+ "tool_name": fields.String,
+ "provider_id": fields.String,
+}
+
app_detail_fields_with_site = {
"id": fields.String,
"name": fields.String,
@@ -169,9 +175,10 @@
"created_at": TimestampField,
"updated_by": fields.String,
"updated_at": TimestampField,
- "deleted_tools": fields.List(fields.String),
+ "deleted_tools": fields.List(fields.Nested(deleted_tool_fields)),
}
+
app_site_fields = {
"app_id": fields.String,
"access_token": fields.String(attribute="code"),
@@ -191,6 +198,8 @@
"use_icon_as_answer_icon": fields.Boolean,
}
+leaked_dependency_fields = {"type": fields.String, "value": fields.Raw, "current_identifier": fields.String}
+
app_import_fields = {
"id": fields.String,
"status": fields.String,
@@ -199,3 +208,7 @@
"imported_dsl_version": fields.String,
"error": fields.String,
}
+
+app_import_check_dependencies_fields = {
+ "leaked_dependencies": fields.List(fields.Nested(leaked_dependency_fields)),
+}
diff --git a/api/fields/conversation_fields.py b/api/fields/conversation_fields.py
index c54554a6de8405..80d1f0baf5f897 100644
--- a/api/fields/conversation_fields.py
+++ b/api/fields/conversation_fields.py
@@ -95,10 +95,6 @@ def format(self, value):
"agent_mode": fields.Raw,
}
-simple_configs_fields = {
- "prompt_template": fields.String,
-}
-
simple_model_config_fields = {
"model": fields.Raw(attribute="model_dict"),
"pre_prompt": fields.String,
@@ -212,14 +208,3 @@ def format(self, value):
"has_more": fields.Boolean,
"data": fields.List(fields.Nested(simple_conversation_fields)),
}
-
-conversation_with_model_config_fields = {
- **simple_conversation_fields,
- "model_config": fields.Raw,
-}
-
-conversation_with_model_config_infinite_scroll_pagination_fields = {
- "limit": fields.Integer,
- "has_more": fields.Boolean,
- "data": fields.List(fields.Nested(conversation_with_model_config_fields)),
-}
diff --git a/api/fields/hit_testing_fields.py b/api/fields/hit_testing_fields.py
index b9f7e78c170529..d9758474f7afb0 100644
--- a/api/fields/hit_testing_fields.py
+++ b/api/fields/hit_testing_fields.py
@@ -14,6 +14,7 @@
"position": fields.Integer,
"document_id": fields.String,
"content": fields.String,
+ "sign_content": fields.String,
"answer": fields.String,
"word_count": fields.Integer,
"tokens": fields.Integer,
diff --git a/api/fields/message_fields.py b/api/fields/message_fields.py
index 0571faab08c134..76e61f07079a5c 100644
--- a/api/fields/message_fields.py
+++ b/api/fields/message_fields.py
@@ -7,27 +7,6 @@
feedback_fields = {"rating": fields.String}
-retriever_resource_fields = {
- "id": fields.String,
- "message_id": fields.String,
- "position": fields.Integer,
- "dataset_id": fields.String,
- "dataset_name": fields.String,
- "document_id": fields.String,
- "document_name": fields.String,
- "data_source_type": fields.String,
- "segment_id": fields.String,
- "score": fields.Float,
- "hit_count": fields.Integer,
- "word_count": fields.Integer,
- "segment_position": fields.Integer,
- "index_node_hash": fields.String,
- "content": fields.String,
- "created_at": TimestampField,
-}
-
-feedback_fields = {"rating": fields.String}
-
agent_thought_fields = {
"id": fields.String,
"chain_id": fields.String,
diff --git a/api/fields/segment_fields.py b/api/fields/segment_fields.py
index 52f89859c931b7..aaac31cf40109e 100644
--- a/api/fields/segment_fields.py
+++ b/api/fields/segment_fields.py
@@ -18,6 +18,7 @@
"position": fields.Integer,
"document_id": fields.String,
"content": fields.String,
+ "sign_content": fields.String,
"answer": fields.String,
"word_count": fields.Integer,
"tokens": fields.Integer,
diff --git a/api/libs/helper.py b/api/libs/helper.py
index 4f14f010f4b98b..f0325734d83639 100644
--- a/api/libs/helper.py
+++ b/api/libs/helper.py
@@ -9,7 +9,7 @@
from collections.abc import Generator, Mapping
from datetime import datetime
from hashlib import sha256
-from typing import Any, Optional, Union, cast
+from typing import TYPE_CHECKING, Any, Optional, Union, cast
from zoneinfo import available_timezones
from flask import Response, stream_with_context
@@ -19,7 +19,9 @@
from core.app.features.rate_limiting.rate_limit import RateLimitGenerator
from core.file import helpers as file_helpers
from extensions.ext_redis import redis_client
-from models.account import Account
+
+if TYPE_CHECKING:
+ from models.account import Account
def run(script):
@@ -192,9 +194,7 @@ def generate_text_hash(text: str) -> str:
return sha256(hash_text.encode()).hexdigest()
-def compact_generate_response(
- response: Union[Mapping[str, Any], RateLimitGenerator, Generator[str, None, None]],
-) -> Response:
+def compact_generate_response(response: Union[Mapping, Generator, RateLimitGenerator]) -> Response:
if isinstance(response, dict):
return Response(response=json.dumps(response), status=200, mimetype="application/json")
else:
@@ -210,7 +210,7 @@ class TokenManager:
def generate_token(
cls,
token_type: str,
- account: Optional[Account] = None,
+ account: Optional["Account"] = None,
email: Optional[str] = None,
additional_data: Optional[dict] = None,
) -> str:
diff --git a/api/libs/login.py b/api/libs/login.py
index 5395534a6df93a..b128c53c62114f 100644
--- a/api/libs/login.py
+++ b/api/libs/login.py
@@ -10,6 +10,7 @@
from configs import dify_config
from extensions.ext_database import db
from models.account import Account, Tenant, TenantAccountJoin
+from models.model import EndUser
#: A proxy for the current user. If no user is logged in, this will be an
#: anonymous user
@@ -96,11 +97,11 @@ def decorated_view(*args, **kwargs):
return decorated_view
-def _get_user():
+def _get_user() -> EndUser | Account | None:
if has_request_context():
if "_login_user" not in g:
current_app.login_manager._load_user() # type: ignore
- return g._login_user
+ return g._login_user # type: ignore
return None
diff --git a/api/migrations/env.py b/api/migrations/env.py
index ad3a122c04bc2d..a5d815dcfd44bc 100644
--- a/api/migrations/env.py
+++ b/api/migrations/env.py
@@ -31,19 +31,16 @@ def get_engine_url():
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
config.set_main_option('sqlalchemy.url', get_engine_url())
-target_db = current_app.extensions['migrate'].db
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
+from models.base import Base
def get_metadata():
- if hasattr(target_db, 'metadatas'):
- return target_db.metadatas[None]
- return target_db.metadata
-
+ return Base.metadata
def include_object(object, name, type_, reflected, compare_to):
if type_ == "foreign_key_constraint":
diff --git a/api/migrations/versions/2024_09_29_0835-ddcc8bbef391_increase_max_length_of_builtin_tool_provider.py b/api/migrations/versions/2024_09_29_0835-ddcc8bbef391_increase_max_length_of_builtin_tool_provider.py
new file mode 100644
index 00000000000000..71006679e1ac29
--- /dev/null
+++ b/api/migrations/versions/2024_09_29_0835-ddcc8bbef391_increase_max_length_of_builtin_tool_provider.py
@@ -0,0 +1,39 @@
+"""increase max length of builtin tool provider
+
+Revision ID: ddcc8bbef391
+Revises: d57ba9ebb251
+Create Date: 2024-09-29 08:35:58.062698
+
+"""
+from alembic import op
+import models as models
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'ddcc8bbef391'
+down_revision = 'a91b476a53de' # HEAD OF PLUGIN BRANCH
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('tool_builtin_providers', schema=None) as batch_op:
+ batch_op.alter_column('provider',
+ existing_type=sa.VARCHAR(length=40),
+ type_=sa.String(length=256),
+ existing_nullable=False)
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('tool_builtin_providers', schema=None) as batch_op:
+ batch_op.alter_column('provider',
+ existing_type=sa.String(length=256),
+ type_=sa.VARCHAR(length=40),
+ existing_nullable=False)
+
+ # ### end Alembic commands ###
diff --git a/api/migrations/versions/2024_10_28_0720-08ec4f75af5e_add_tenant_plugin_permisisons.py b/api/migrations/versions/2024_10_28_0720-08ec4f75af5e_add_tenant_plugin_permisisons.py
new file mode 100644
index 00000000000000..51a0b1b211766b
--- /dev/null
+++ b/api/migrations/versions/2024_10_28_0720-08ec4f75af5e_add_tenant_plugin_permisisons.py
@@ -0,0 +1,37 @@
+"""add_tenant_plugin_permisisons
+
+Revision ID: 08ec4f75af5e
+Revises: ddcc8bbef391
+Create Date: 2024-10-28 07:20:39.711124
+
+"""
+from alembic import op
+import models as models
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '08ec4f75af5e'
+down_revision = 'ddcc8bbef391'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('account_plugin_permissions',
+ sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False),
+ sa.Column('tenant_id', models.types.StringUUID(), nullable=False),
+ sa.Column('install_permission', sa.String(length=16), server_default='everyone', nullable=False),
+ sa.Column('debug_permission', sa.String(length=16), server_default='noone', nullable=False),
+ sa.PrimaryKeyConstraint('id', name='account_plugin_permission_pkey'),
+ sa.UniqueConstraint('tenant_id', name='unique_tenant_plugin')
+ )
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('account_plugin_permissions')
+ # ### end Alembic commands ###
diff --git a/api/models/__init__.py b/api/models/__init__.py
index b0b9880ca42a9d..1237c55a306b93 100644
--- a/api/models/__init__.py
+++ b/api/models/__init__.py
@@ -73,7 +73,6 @@
from .tools import (
ApiToolProvider,
BuiltinToolProvider,
- PublishedAppTool,
ToolConversationVariables,
ToolFile,
ToolLabelBinding,
@@ -150,7 +149,6 @@
"ProviderOrder",
"ProviderQuotaType",
"ProviderType",
- "PublishedAppTool",
"RecommendedApp",
"SavedMessage",
"Site",
diff --git a/api/models/account.py b/api/models/account.py
index 35a28df7505943..bac1ec1c2e3e05 100644
--- a/api/models/account.py
+++ b/api/models/account.py
@@ -5,6 +5,8 @@
from sqlalchemy import func
from sqlalchemy.orm import Mapped, mapped_column
+from models.base import Base
+
from .engine import db
from .types import StringUUID
@@ -17,7 +19,7 @@ class AccountStatus(enum.StrEnum):
CLOSED = "closed"
-class Account(UserMixin, db.Model): # type: ignore[name-defined]
+class Account(UserMixin, Base):
__tablename__ = "accounts"
__table_args__ = (db.PrimaryKeyConstraint("id", name="account_pkey"), db.Index("account_email_idx", "email"))
@@ -54,8 +56,8 @@ def current_tenant(self, value: "Tenant"):
if ta:
tenant.current_role = ta.role
else:
- # FIXME: fix the type error later, because the type is important maybe cause some bugs
tenant = None # type: ignore
+
self._current_tenant = tenant
@property
@@ -78,7 +80,7 @@ def current_tenant_id(self, value: str):
tenant.current_role = ta.role
else:
tenant = None
- except:
+ except Exception:
tenant = None
self._current_tenant = tenant
@@ -102,6 +104,7 @@ def get_by_openid(cls, provider: str, open_id: str):
return db.session.query(Account).filter(Account.id == account_integrate.account_id).one_or_none()
return None
+ # check current_user.current_tenant.current_role in ['admin', 'owner']
@property
def is_admin_or_owner(self):
return TenantAccountRole.is_privileged_role(self._current_tenant.current_role)
@@ -137,6 +140,8 @@ class TenantAccountRole(enum.StrEnum):
@staticmethod
def is_valid_role(role: str) -> bool:
+ if not role:
+ return False
return role in {
TenantAccountRole.OWNER,
TenantAccountRole.ADMIN,
@@ -147,14 +152,20 @@ def is_valid_role(role: str) -> bool:
@staticmethod
def is_privileged_role(role: str) -> bool:
+ if not role:
+ return False
return role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN}
@staticmethod
def is_admin_role(role: str) -> bool:
+ if not role:
+ return False
return role == TenantAccountRole.ADMIN
@staticmethod
def is_non_owner_role(role: str) -> bool:
+ if not role:
+ return False
return role in {
TenantAccountRole.ADMIN,
TenantAccountRole.EDITOR,
@@ -164,10 +175,14 @@ def is_non_owner_role(role: str) -> bool:
@staticmethod
def is_editing_role(role: str) -> bool:
+ if not role:
+ return False
return role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR}
@staticmethod
def is_dataset_edit_role(role: str) -> bool:
+ if not role:
+ return False
return role in {
TenantAccountRole.OWNER,
TenantAccountRole.ADMIN,
@@ -264,4 +279,29 @@ class InvitationCode(db.Model): # type: ignore[name-defined]
used_by_tenant_id = db.Column(StringUUID)
used_by_account_id = db.Column(StringUUID)
deprecated_at = db.Column(db.DateTime)
- created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
+ created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
+
+
+class TenantPluginPermission(Base):
+ class InstallPermission(enum.StrEnum):
+ EVERYONE = "everyone"
+ ADMINS = "admins"
+ NOBODY = "noone"
+
+ class DebugPermission(enum.StrEnum):
+ EVERYONE = "everyone"
+ ADMINS = "admins"
+ NOBODY = "noone"
+
+ __tablename__ = "account_plugin_permissions"
+ __table_args__ = (
+ db.PrimaryKeyConstraint("id", name="account_plugin_permission_pkey"),
+ db.UniqueConstraint("tenant_id", name="unique_tenant_plugin"),
+ )
+
+ id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
+ tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
+ install_permission: Mapped[InstallPermission] = mapped_column(
+ db.String(16), nullable=False, server_default="everyone"
+ )
+ debug_permission: Mapped[DebugPermission] = mapped_column(db.String(16), nullable=False, server_default="noone")
diff --git a/api/models/base.py b/api/models/base.py
new file mode 100644
index 00000000000000..da9509301afae4
--- /dev/null
+++ b/api/models/base.py
@@ -0,0 +1,5 @@
+from sqlalchemy.orm import declarative_base
+
+from models.engine import metadata
+
+Base = declarative_base(metadata=metadata)
diff --git a/api/models/dataset.py b/api/models/dataset.py
index 1cf3dc42fe8235..2e7fd6b41f62ec 100644
--- a/api/models/dataset.py
+++ b/api/models/dataset.py
@@ -584,6 +584,10 @@ def child_chunks(self):
else:
return []
+ @property
+ def sign_content(self):
+ return self.get_sign_content()
+
def get_sign_content(self):
signed_urls = []
text = self.content
diff --git a/api/models/model.py b/api/models/model.py
index 2780b79c98e2b6..7591e664648082 100644
--- a/api/models/model.py
+++ b/api/models/model.py
@@ -3,20 +3,31 @@
import uuid
from collections.abc import Mapping
from datetime import datetime
-from enum import Enum, StrEnum
-from typing import TYPE_CHECKING, Any, Literal, Optional, cast
+from enum import Enum
+from typing import TYPE_CHECKING, Optional
+
+from core.plugin.entities.plugin import GenericProviderID
+from core.tools.entities.tool_entities import ToolProviderType
+from services.plugin.plugin_service import PluginService
+
+if TYPE_CHECKING:
+ from models.workflow import Workflow
+
+from enum import StrEnum
+from typing import TYPE_CHECKING, Any, Literal, cast
import sqlalchemy as sa
from flask import request
from flask_login import UserMixin # type: ignore
-from sqlalchemy import Float, func, text
-from sqlalchemy.orm import Mapped, mapped_column
+from sqlalchemy import Float, Index, PrimaryKeyConstraint, func, text
+from sqlalchemy.orm import Mapped, Session, mapped_column
from configs import dify_config
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 libs.helper import generate_string
+from models.base import Base
from models.enums import CreatedByRole
from models.workflow import WorkflowRunStatus
@@ -28,7 +39,7 @@
from .workflow import Workflow
-class DifySetup(db.Model): # type: ignore[name-defined]
+class DifySetup(Base):
__tablename__ = "dify_setups"
__table_args__ = (db.PrimaryKeyConstraint("version", name="dify_setup_pkey"),)
@@ -63,7 +74,7 @@ class IconType(Enum):
EMOJI = "emoji"
-class App(db.Model): # type: ignore[name-defined]
+class App(Base):
__tablename__ = "apps"
__table_args__ = (db.PrimaryKeyConstraint("id", name="app_pkey"), db.Index("app_tenant_id_idx", "tenant_id"))
@@ -141,7 +152,8 @@ def is_agent(self) -> bool:
return False
if not app_model_config.agent_mode:
return False
- if self.app_model_config.agent_mode_dict.get("enabled", False) and self.app_model_config.agent_mode_dict.get(
+
+ if app_model_config.agent_mode_dict.get("enabled", False) and app_model_config.agent_mode_dict.get(
"strategy", ""
) in {"function_call", "react"}:
self.mode = AppMode.AGENT_CHAT.value
@@ -158,47 +170,113 @@ def mode_compatible_with_agent(self) -> str:
@property
def deleted_tools(self) -> list:
+ from core.tools.tool_manager import ToolManager
+
# get agent mode tools
app_model_config = self.app_model_config
if not app_model_config:
return []
+
if not app_model_config.agent_mode:
return []
+
agent_mode = app_model_config.agent_mode_dict
tools = agent_mode.get("tools", [])
- provider_ids = []
+ api_provider_ids: list[str] = []
+ builtin_provider_ids: list[GenericProviderID] = []
for tool in tools:
keys = list(tool.keys())
if len(keys) >= 4:
provider_type = tool.get("provider_type", "")
provider_id = tool.get("provider_id", "")
- if provider_type == "api":
- # check if provider id is a uuid string, if not, skip
+ if provider_type == ToolProviderType.API.value:
try:
uuid.UUID(provider_id)
except Exception:
continue
- provider_ids.append(provider_id)
+ api_provider_ids.append(provider_id)
+ if provider_type == ToolProviderType.BUILT_IN.value:
+ try:
+ # check if it's hardcoded
+ try:
+ ToolManager.get_hardcoded_provider(provider_id)
+ is_hardcoded = True
+ except Exception:
+ is_hardcoded = False
+
+ provider_id = GenericProviderID(provider_id, is_hardcoded)
+ except Exception:
+ continue
+
+ builtin_provider_ids.append(provider_id)
- if not provider_ids:
+ if not api_provider_ids and not builtin_provider_ids:
return []
- api_providers = db.session.execute(
- text("SELECT id FROM tool_api_providers WHERE id IN :provider_ids"), {"provider_ids": tuple(provider_ids)}
- ).fetchall()
+ with Session(db.engine) as session:
+ if api_provider_ids:
+ existing_api_providers = [
+ api_provider.id
+ for api_provider in session.execute(
+ text("SELECT id FROM tool_api_providers WHERE id IN :provider_ids"),
+ {"provider_ids": tuple(api_provider_ids)},
+ ).fetchall()
+ ]
+ else:
+ existing_api_providers = []
+
+ if builtin_provider_ids:
+ # get the non-hardcoded builtin providers
+ non_hardcoded_builtin_providers = [
+ provider_id for provider_id in builtin_provider_ids if not provider_id.is_hardcoded
+ ]
+ if non_hardcoded_builtin_providers:
+ existence = list(PluginService.check_tools_existence(self.tenant_id, non_hardcoded_builtin_providers))
+ else:
+ existence = []
+ # add the hardcoded builtin providers
+ existence.extend([True] * (len(builtin_provider_ids) - len(non_hardcoded_builtin_providers)))
+ builtin_provider_ids = non_hardcoded_builtin_providers + [
+ provider_id for provider_id in builtin_provider_ids if provider_id.is_hardcoded
+ ]
+ else:
+ existence = []
+
+ existing_builtin_providers = {
+ provider_id.provider_name: existence[i] for i, provider_id in enumerate(builtin_provider_ids)
+ }
deleted_tools = []
- current_api_provider_ids = [str(api_provider.id) for api_provider in api_providers]
for tool in tools:
keys = list(tool.keys())
if len(keys) >= 4:
provider_type = tool.get("provider_type", "")
provider_id = tool.get("provider_id", "")
- if provider_type == "api" and provider_id not in current_api_provider_ids:
- deleted_tools.append(tool["tool_name"])
+
+ if provider_type == ToolProviderType.API.value:
+ if provider_id not in existing_api_providers:
+ deleted_tools.append(
+ {
+ "type": ToolProviderType.API.value,
+ "tool_name": tool["tool_name"],
+ "provider_id": provider_id,
+ }
+ )
+
+ if provider_type == ToolProviderType.BUILT_IN.value:
+ generic_provider_id = GenericProviderID(provider_id)
+
+ if not existing_builtin_providers[generic_provider_id.provider_name]:
+ deleted_tools.append(
+ {
+ "type": ToolProviderType.BUILT_IN.value,
+ "tool_name": tool["tool_name"],
+ "provider_id": provider_id, # use the original one
+ }
+ )
return deleted_tools
@@ -219,7 +297,7 @@ def tags(self):
return tags or []
-class AppModelConfig(db.Model): # type: ignore[name-defined]
+class AppModelConfig(Base):
__tablename__ = "app_model_configs"
__table_args__ = (db.PrimaryKeyConstraint("id", name="app_model_config_pkey"), db.Index("app_app_id_idx", "app_id"))
@@ -292,6 +370,9 @@ def annotation_reply_dict(self) -> dict:
)
if annotation_setting:
collection_binding_detail = annotation_setting.collection_binding_detail
+ if not collection_binding_detail:
+ raise ValueError("Collection binding detail not found")
+
return {
"id": annotation_setting.id,
"enabled": True,
@@ -322,7 +403,7 @@ def external_data_tools_list(self) -> list[dict]:
return json.loads(self.external_data_tools) if self.external_data_tools else []
@property
- def user_input_form_list(self) -> list[dict]:
+ def user_input_form_list(self):
return json.loads(self.user_input_form) if self.user_input_form else []
@property
@@ -466,7 +547,7 @@ def copy(self):
return new_app_model_config
-class RecommendedApp(db.Model): # type: ignore[name-defined]
+class RecommendedApp(Base):
__tablename__ = "recommended_apps"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="recommended_app_pkey"),
@@ -494,7 +575,7 @@ def app(self):
return app
-class InstalledApp(db.Model): # type: ignore[name-defined]
+class InstalledApp(Base):
__tablename__ = "installed_apps"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="installed_app_pkey"),
@@ -523,7 +604,7 @@ def tenant(self):
return tenant
-class Conversation(db.Model): # type: ignore[name-defined]
+class Conversation(Base):
__tablename__ = "conversations"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="conversation_pkey"),
@@ -758,16 +839,16 @@ def in_debug_mode(self):
return self.override_model_configs is not None
-class Message(db.Model): # type: ignore[name-defined]
+class Message(Base):
__tablename__ = "messages"
__table_args__ = (
- db.PrimaryKeyConstraint("id", name="message_pkey"),
- db.Index("message_app_id_idx", "app_id", "created_at"),
- db.Index("message_conversation_id_idx", "conversation_id"),
- db.Index("message_end_user_idx", "app_id", "from_source", "from_end_user_id"),
- db.Index("message_account_idx", "app_id", "from_source", "from_account_id"),
- db.Index("message_workflow_run_id_idx", "conversation_id", "workflow_run_id"),
- db.Index("message_created_at_idx", "created_at"),
+ PrimaryKeyConstraint("id", name="message_pkey"),
+ Index("message_app_id_idx", "app_id", "created_at"),
+ Index("message_conversation_id_idx", "conversation_id"),
+ Index("message_end_user_idx", "app_id", "from_source", "from_end_user_id"),
+ Index("message_account_idx", "app_id", "from_source", "from_account_id"),
+ Index("message_workflow_run_id_idx", "conversation_id", "workflow_run_id"),
+ Index("message_created_at_idx", "created_at"),
)
id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
@@ -1109,7 +1190,7 @@ def from_dict(cls, data: dict):
)
-class MessageFeedback(db.Model): # type: ignore[name-defined]
+class MessageFeedback(Base):
__tablename__ = "message_feedbacks"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="message_feedback_pkey"),
@@ -1136,7 +1217,7 @@ def from_account(self):
return account
-class MessageFile(db.Model): # type: ignore[name-defined]
+class MessageFile(Base):
__tablename__ = "message_files"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="message_file_pkey"),
@@ -1177,7 +1258,7 @@ def __init__(
created_at: Mapped[datetime] = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class MessageAnnotation(db.Model): # type: ignore[name-defined]
+class MessageAnnotation(Base):
__tablename__ = "message_annotations"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="message_annotation_pkey"),
@@ -1246,7 +1327,7 @@ def annotation_create_account(self):
return account
-class AppAnnotationSetting(db.Model): # type: ignore[name-defined]
+class AppAnnotationSetting(Base):
__tablename__ = "app_annotation_settings"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="app_annotation_settings_pkey"),
@@ -1294,7 +1375,7 @@ def collection_binding_detail(self):
return collection_binding_detail
-class OperationLog(db.Model): # type: ignore[name-defined]
+class OperationLog(Base):
__tablename__ = "operation_logs"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="operation_log_pkey"),
@@ -1311,7 +1392,7 @@ class OperationLog(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class EndUser(UserMixin, db.Model): # type: ignore[name-defined]
+class EndUser(Base, UserMixin):
__tablename__ = "end_users"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="end_user_pkey"),
@@ -1331,7 +1412,7 @@ class EndUser(UserMixin, db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class Site(db.Model): # type: ignore[name-defined]
+class Site(Base):
__tablename__ = "sites"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="site_pkey"),
@@ -1388,7 +1469,7 @@ def app_base_url(self):
return dify_config.APP_WEB_URL or request.url_root.rstrip("/")
-class ApiToken(db.Model): # type: ignore[name-defined]
+class ApiToken(Base):
__tablename__ = "api_tokens"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="api_token_pkey"),
@@ -1414,7 +1495,7 @@ def generate_api_key(prefix, n):
return result
-class UploadFile(db.Model): # type: ignore[name-defined]
+class UploadFile(Base):
__tablename__ = "upload_files"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="upload_file_pkey"),
@@ -1476,7 +1557,7 @@ def __init__(
self.source_url = source_url
-class ApiRequest(db.Model): # type: ignore[name-defined]
+class ApiRequest(Base):
__tablename__ = "api_requests"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="api_request_pkey"),
@@ -1493,7 +1574,7 @@ class ApiRequest(db.Model): # type: ignore[name-defined]
created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class MessageChain(db.Model): # type: ignore[name-defined]
+class MessageChain(Base):
__tablename__ = "message_chains"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="message_chain_pkey"),
@@ -1508,7 +1589,7 @@ class MessageChain(db.Model): # type: ignore[name-defined]
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.current_timestamp())
-class MessageAgentThought(db.Model): # type: ignore[name-defined]
+class MessageAgentThought(Base):
__tablename__ = "message_agent_thoughts"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="message_agent_thought_pkey"),
@@ -1563,7 +1644,7 @@ def tool_labels(self) -> dict:
return cast(dict, json.loads(self.tool_labels_str))
else:
return {}
- except Exception as e:
+ except Exception:
return {}
@property
@@ -1573,7 +1654,7 @@ def tool_meta(self) -> dict:
return cast(dict, json.loads(self.tool_meta_str))
else:
return {}
- except Exception as e:
+ except Exception:
return {}
@property
@@ -1594,11 +1675,11 @@ def tool_inputs_dict(self) -> dict:
return result
else:
return {tool: {} for tool in tools}
- except Exception as e:
+ except Exception:
return {}
@property
- def tool_outputs_dict(self) -> dict:
+ def tool_outputs_dict(self):
tools = self.tools
try:
if self.observation:
@@ -1615,14 +1696,14 @@ def tool_outputs_dict(self) -> dict:
return result
else:
return {tool: {} for tool in tools}
- except Exception as e:
+ except Exception:
if self.observation:
return dict.fromkeys(tools, self.observation)
else:
return {}
-class DatasetRetrieverResource(db.Model): # type: ignore[name-defined]
+class DatasetRetrieverResource(Base):
__tablename__ = "dataset_retriever_resources"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="dataset_retriever_resource_pkey"),
@@ -1649,7 +1730,7 @@ class DatasetRetrieverResource(db.Model): # type: ignore[name-defined]
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.current_timestamp())
-class Tag(db.Model): # type: ignore[name-defined]
+class Tag(Base):
__tablename__ = "tags"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tag_pkey"),
@@ -1667,7 +1748,7 @@ class Tag(db.Model): # type: ignore[name-defined]
created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class TagBinding(db.Model): # type: ignore[name-defined]
+class TagBinding(Base):
__tablename__ = "tag_bindings"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tag_binding_pkey"),
@@ -1683,7 +1764,7 @@ class TagBinding(db.Model): # type: ignore[name-defined]
created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class TraceAppConfig(db.Model): # type: ignore[name-defined]
+class TraceAppConfig(Base):
__tablename__ = "trace_app_config"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tracing_app_config_pkey"),
diff --git a/api/models/provider.py b/api/models/provider.py
index abe673975c1ccc..567400702d9822 100644
--- a/api/models/provider.py
+++ b/api/models/provider.py
@@ -2,6 +2,8 @@
from sqlalchemy import func
+from models.base import Base
+
from .engine import db
from .types import StringUUID
@@ -36,7 +38,7 @@ def value_of(value):
raise ValueError(f"No matching enum found for value '{value}'")
-class Provider(db.Model): # type: ignore[name-defined]
+class Provider(Base):
"""
Provider model representing the API providers and their configurations.
"""
@@ -89,7 +91,7 @@ def is_enabled(self):
return self.is_valid and self.token_is_set
-class ProviderModel(db.Model): # type: ignore[name-defined]
+class ProviderModel(Base):
"""
Provider model representing the API provider_models and their configurations.
"""
@@ -114,7 +116,7 @@ class ProviderModel(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class TenantDefaultModel(db.Model): # type: ignore[name-defined]
+class TenantDefaultModel(Base):
__tablename__ = "tenant_default_models"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tenant_default_model_pkey"),
@@ -130,7 +132,7 @@ class TenantDefaultModel(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class TenantPreferredModelProvider(db.Model): # type: ignore[name-defined]
+class TenantPreferredModelProvider(Base):
__tablename__ = "tenant_preferred_model_providers"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tenant_preferred_model_provider_pkey"),
@@ -145,7 +147,7 @@ class TenantPreferredModelProvider(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class ProviderOrder(db.Model): # type: ignore[name-defined]
+class ProviderOrder(Base):
__tablename__ = "provider_orders"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="provider_order_pkey"),
@@ -170,7 +172,7 @@ class ProviderOrder(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class ProviderModelSetting(db.Model): # type: ignore[name-defined]
+class ProviderModelSetting(Base):
"""
Provider model settings for record the model enabled status and load balancing status.
"""
@@ -192,7 +194,7 @@ class ProviderModelSetting(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class LoadBalancingModelConfig(db.Model): # type: ignore[name-defined]
+class LoadBalancingModelConfig(Base):
"""
Configurations for load balancing models.
"""
diff --git a/api/models/source.py b/api/models/source.py
index 881cfaac7d3998..b9d7d913468856 100644
--- a/api/models/source.py
+++ b/api/models/source.py
@@ -3,6 +3,8 @@
from sqlalchemy import func
from sqlalchemy.dialects.postgresql import JSONB
+from models.base import Base
+
from .engine import db
from .types import StringUUID
@@ -25,7 +27,7 @@ class DataSourceOauthBinding(db.Model): # type: ignore[name-defined]
disabled = db.Column(db.Boolean, nullable=True, server_default=db.text("false"))
-class DataSourceApiKeyAuthBinding(db.Model): # type: ignore[name-defined]
+class DataSourceApiKeyAuthBinding(Base):
__tablename__ = "data_source_api_key_auth_bindings"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="data_source_api_key_auth_binding_pkey"),
diff --git a/api/models/task.py b/api/models/task.py
index 0db1c632299fcb..d853c1dd9a0e83 100644
--- a/api/models/task.py
+++ b/api/models/task.py
@@ -2,10 +2,12 @@
from celery import states # type: ignore
+from models.base import Base
+
from .engine import db
-class CeleryTask(db.Model): # type: ignore[name-defined]
+class CeleryTask(Base):
"""Task result/status."""
__tablename__ = "celery_taskmeta"
@@ -29,7 +31,7 @@ class CeleryTask(db.Model): # type: ignore[name-defined]
queue = db.Column(db.String(155), nullable=True)
-class CeleryTaskSet(db.Model): # type: ignore[name-defined]
+class CeleryTaskSet(Base):
"""TaskSet result."""
__tablename__ = "celery_tasksetmeta"
diff --git a/api/models/tools.py b/api/models/tools.py
index 13a112ee83b513..b1e321ed857fb6 100644
--- a/api/models/tools.py
+++ b/api/models/tools.py
@@ -1,20 +1,23 @@
import json
-from typing import Any, Optional
+from datetime import datetime
+from typing import Any, Optional, cast
import sqlalchemy as sa
+from deprecated import deprecated
from sqlalchemy import ForeignKey, func
from sqlalchemy.orm import Mapped, mapped_column
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_bundle import ApiToolBundle
from core.tools.entities.tool_entities import ApiProviderSchemaType, WorkflowToolParameterConfiguration
+from models.base import Base
from .engine import db
from .model import Account, App, Tenant
from .types import StringUUID
-class BuiltinToolProvider(db.Model): # type: ignore[name-defined]
+class BuiltinToolProvider(Base):
"""
This table stores the tool provider information for built-in tools for each tenant.
"""
@@ -27,66 +30,28 @@ class BuiltinToolProvider(db.Model): # type: ignore[name-defined]
)
# id of the tool provider
- id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
+ id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
# id of the tenant
- tenant_id = db.Column(StringUUID, nullable=True)
+ tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=True)
# who created this tool provider
- user_id = db.Column(StringUUID, nullable=False)
+ user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# name of the tool provider
- provider = db.Column(db.String(40), nullable=False)
+ provider: Mapped[str] = mapped_column(db.String(256), nullable=False)
# credential of the tool provider
- encrypted_credentials = db.Column(db.Text, nullable=True)
- created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
- updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-
- @property
- def credentials(self) -> dict:
- return dict(json.loads(self.encrypted_credentials))
-
-
-class PublishedAppTool(db.Model): # type: ignore[name-defined]
- """
- The table stores the apps published as a tool for each person.
- """
-
- __tablename__ = "tool_published_apps"
- __table_args__ = (
- db.PrimaryKeyConstraint("id", name="published_app_tool_pkey"),
- db.UniqueConstraint("app_id", "user_id", name="unique_published_app_tool"),
+ encrypted_credentials: Mapped[str] = mapped_column(db.Text, nullable=True)
+ created_at: Mapped[datetime] = mapped_column(
+ db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
+ )
+ updated_at: Mapped[datetime] = mapped_column(
+ db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
)
-
- # id of the tool provider
- id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
- # id of the app
- app_id = db.Column(StringUUID, ForeignKey("apps.id"), nullable=False)
- # who published this tool
- user_id = db.Column(StringUUID, nullable=False)
- # description of the tool, stored in i18n format, for human
- description = db.Column(db.Text, nullable=False)
- # llm_description of the tool, for LLM
- llm_description = db.Column(db.Text, nullable=False)
- # query description, query will be seem as a parameter of the tool,
- # to describe this parameter to llm, we need this field
- query_description = db.Column(db.Text, nullable=False)
- # query name, the name of the query parameter
- query_name = db.Column(db.String(40), nullable=False)
- # name of the tool provider
- tool_name = db.Column(db.String(40), nullable=False)
- # author
- author = db.Column(db.String(40), nullable=False)
- created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
- updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-
- @property
- def description_i18n(self) -> I18nObject:
- return I18nObject(**json.loads(self.description))
@property
- def app(self):
- return db.session.query(App).filter(App.id == self.app_id).first()
+ def credentials(self) -> dict:
+ return cast(dict, json.loads(self.encrypted_credentials))
-class ApiToolProvider(db.Model): # type: ignore[name-defined]
+class ApiToolProvider(Base):
"""
The table stores the api providers.
"""
@@ -120,8 +85,8 @@ class ApiToolProvider(db.Model): # type: ignore[name-defined]
# custom_disclaimer
custom_disclaimer: Mapped[str] = mapped_column(sa.TEXT, default="")
- created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
- updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
+ created_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp())
+ updated_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp())
@property
def schema_type(self) -> ApiProviderSchemaType:
@@ -144,7 +109,7 @@ def tenant(self) -> Tenant | None:
return db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()
-class ToolLabelBinding(db.Model): # type: ignore[name-defined]
+class ToolLabelBinding(Base):
"""
The table stores the labels for tools.
"""
@@ -155,16 +120,16 @@ class ToolLabelBinding(db.Model): # type: ignore[name-defined]
db.UniqueConstraint("tool_id", "label_name", name="unique_tool_label_bind"),
)
- id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
+ id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
# tool id
- tool_id = db.Column(db.String(64), nullable=False)
+ tool_id: Mapped[str] = mapped_column(db.String(64), nullable=False)
# tool type
- tool_type = db.Column(db.String(40), nullable=False)
+ tool_type: Mapped[str] = mapped_column(db.String(40), nullable=False)
# label name
- label_name = db.Column(db.String(40), nullable=False)
+ label_name: Mapped[str] = mapped_column(db.String(40), nullable=False)
-class WorkflowToolProvider(db.Model): # type: ignore[name-defined]
+class WorkflowToolProvider(Base):
"""
The table stores the workflow providers.
"""
@@ -176,30 +141,38 @@ class WorkflowToolProvider(db.Model): # type: ignore[name-defined]
db.UniqueConstraint("tenant_id", "app_id", name="unique_workflow_tool_provider_app_id"),
)
- id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
+ id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
# name of the workflow provider
- name = db.Column(db.String(40), nullable=False)
+ name: Mapped[str] = mapped_column(db.String(40), nullable=False)
# label of the workflow provider
- label = db.Column(db.String(255), nullable=False, server_default="")
+ label: Mapped[str] = mapped_column(db.String(255), nullable=False, server_default="")
# icon
- icon = db.Column(db.String(255), nullable=False)
+ icon: Mapped[str] = mapped_column(db.String(255), nullable=False)
# app id of the workflow provider
- app_id = db.Column(StringUUID, nullable=False)
+ app_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# version of the workflow provider
- version = db.Column(db.String(255), nullable=False, server_default="")
+ version: Mapped[str] = mapped_column(db.String(255), nullable=False, server_default="")
# who created this tool
- user_id = db.Column(StringUUID, nullable=False)
+ user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# tenant id
- tenant_id = db.Column(StringUUID, nullable=False)
+ tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# description of the provider
- description = db.Column(db.Text, nullable=False)
+ description: Mapped[str] = mapped_column(db.Text, nullable=False)
# parameter configuration
- parameter_configuration = db.Column(db.Text, nullable=False, server_default="[]")
+ parameter_configuration: Mapped[str] = mapped_column(db.Text, nullable=False, server_default="[]")
# privacy policy
- privacy_policy = db.Column(db.String(255), nullable=True, server_default="")
+ privacy_policy: Mapped[str] = mapped_column(db.String(255), nullable=True, server_default="")
- created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
- updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
+ created_at: Mapped[datetime] = mapped_column(
+ db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
+ )
+ updated_at: Mapped[datetime] = mapped_column(
+ db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
+ )
+
+ @property
+ def schema_type(self) -> ApiProviderSchemaType:
+ return ApiProviderSchemaType.value_of(self.schema_type_str)
@property
def user(self) -> Account | None:
@@ -218,7 +191,7 @@ def app(self) -> App | None:
return db.session.query(App).filter(App.id == self.app_id).first()
-class ToolModelInvoke(db.Model): # type: ignore[name-defined]
+class ToolModelInvoke(Base):
"""
store the invoke logs from tool invoke
"""
@@ -255,7 +228,8 @@ class ToolModelInvoke(db.Model): # type: ignore[name-defined]
updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
-class ToolConversationVariables(db.Model): # type: ignore[name-defined]
+@deprecated
+class ToolConversationVariables(Base):
"""
store the conversation variables from tool invoke
"""
@@ -286,13 +260,70 @@ def variables(self) -> Any:
return json.loads(self.variables_str)
-class ToolFile(db.Model): # type: ignore[name-defined]
+class ToolFile(Base):
+ """
+ store the file created by agent
+ """
+
__tablename__ = "tool_files"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="tool_file_pkey"),
db.Index("tool_file_conversation_id_idx", "conversation_id"),
)
+ id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
+ # conversation user id
+ user_id: Mapped[str] = mapped_column(StringUUID)
+ # tenant id
+ tenant_id: Mapped[str] = mapped_column(StringUUID)
+ # conversation id
+ conversation_id: Mapped[str] = mapped_column(StringUUID, nullable=True)
+ # file key
+ file_key: Mapped[str] = mapped_column(db.String(255), nullable=False)
+ # mime type
+ mimetype: Mapped[str] = mapped_column(db.String(255), nullable=False)
+ # original url
+ original_url: Mapped[str] = mapped_column(db.String(2048), nullable=True)
+ # name
+ name: Mapped[str] = mapped_column(default="")
+ # size
+ size: Mapped[int] = mapped_column(default=-1)
+
+
+@deprecated
+class DeprecatedPublishedAppTool(Base):
+ """
+ The table stores the apps published as a tool for each person.
+ """
+
+ __tablename__ = "tool_published_apps"
+ __table_args__ = (
+ db.PrimaryKeyConstraint("id", name="published_app_tool_pkey"),
+ db.UniqueConstraint("app_id", "user_id", name="unique_published_app_tool"),
+ )
+
+ # id of the app
+ app_id = db.Column(StringUUID, ForeignKey("apps.id"), nullable=False)
+ # who published this tool
+ description = db.Column(db.Text, nullable=False)
+ # llm_description of the tool, for LLM
+ llm_description = db.Column(db.Text, nullable=False)
+ # query description, query will be seem as a parameter of the tool,
+ # to describe this parameter to llm, we need this field
+ query_description = db.Column(db.Text, nullable=False)
+ # query name, the name of the query parameter
+ query_name = db.Column(db.String(40), nullable=False)
+ # name of the tool provider
+ tool_name = db.Column(db.String(40), nullable=False)
+ # author
+ author = db.Column(db.String(40), nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
+ updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
+
+ @property
+ def description_i18n(self) -> I18nObject:
+ return I18nObject(**json.loads(self.description))
+
id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
user_id: Mapped[str] = db.Column(StringUUID, nullable=False)
tenant_id: Mapped[str] = db.Column(StringUUID, nullable=False)
diff --git a/api/models/web.py b/api/models/web.py
index 864428fe0931b6..fe2f0c47f8c9a6 100644
--- a/api/models/web.py
+++ b/api/models/web.py
@@ -1,12 +1,14 @@
from sqlalchemy import func
from sqlalchemy.orm import Mapped, mapped_column
+from models.base import Base
+
from .engine import db
from .model import Message
from .types import StringUUID
-class SavedMessage(db.Model): # type: ignore[name-defined]
+class SavedMessage(Base):
__tablename__ = "saved_messages"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="saved_message_pkey"),
@@ -25,7 +27,7 @@ def message(self):
return db.session.query(Message).filter(Message.id == self.message_id).first()
-class PinnedConversation(db.Model): # type: ignore[name-defined]
+class PinnedConversation(Base):
__tablename__ = "pinned_conversations"
__table_args__ = (
db.PrimaryKeyConstraint("id", name="pinned_conversation_pkey"),
diff --git a/api/models/workflow.py b/api/models/workflow.py
index e1776ce887340b..5af2590e50c1d3 100644
--- a/api/models/workflow.py
+++ b/api/models/workflow.py
@@ -1,11 +1,16 @@
import json
from collections.abc import Mapping, Sequence
from datetime import UTC, datetime
-from enum import Enum, StrEnum
+from enum import Enum
from typing import TYPE_CHECKING, Any, Optional, Union
+if TYPE_CHECKING:
+ from models.model import AppMode
+from enum import StrEnum
+from typing import TYPE_CHECKING
+
import sqlalchemy as sa
-from sqlalchemy import func
+from sqlalchemy import Index, PrimaryKeyConstraint, func
from sqlalchemy.orm import Mapped, mapped_column
import contexts
@@ -14,6 +19,7 @@
from core.variables import SecretVariable, Variable
from factories import variable_factory
from libs import helper
+from models.base import Base
from models.enums import CreatedByRole
from .account import Account
@@ -21,7 +27,7 @@
from .types import StringUUID
if TYPE_CHECKING:
- from models.model import AppMode, Message
+ from models.model import AppMode
class WorkflowType(Enum):
@@ -59,7 +65,7 @@ def from_app_mode(cls, app_mode: Union[str, "AppMode"]) -> "WorkflowType":
return cls.WORKFLOW if app_mode == AppMode.WORKFLOW else cls.CHAT
-class Workflow(db.Model): # type: ignore[name-defined]
+class Workflow(Base):
"""
Workflow, for `Workflow App` and `Chat App workflow mode`.
@@ -249,11 +255,13 @@ def environment_variables(self) -> Sequence[Variable]:
]
# decrypt secret variables value
- decrypt_func = (
- lambda var: var.model_copy(update={"value": encrypter.decrypt_token(tenant_id=tenant_id, token=var.value)})
- if isinstance(var, SecretVariable)
- else var
- )
+ def decrypt_func(var):
+ return (
+ var.model_copy(update={"value": encrypter.decrypt_token(tenant_id=tenant_id, token=var.value)})
+ if isinstance(var, SecretVariable)
+ else var
+ )
+
results = list(map(decrypt_func, results))
return results
@@ -277,11 +285,13 @@ def environment_variables(self, value: Sequence[Variable]):
value[i] = origin_variables_dictionary[variable.id].model_copy(update={"name": variable.name})
# encrypt secret variables value
- encrypt_func = (
- lambda var: var.model_copy(update={"value": encrypter.encrypt_token(tenant_id=tenant_id, token=var.value)})
- if isinstance(var, SecretVariable)
- else var
- )
+ def encrypt_func(var):
+ return (
+ var.model_copy(update={"value": encrypter.encrypt_token(tenant_id=tenant_id, token=var.value)})
+ if isinstance(var, SecretVariable)
+ else var
+ )
+
encrypted_vars = list(map(encrypt_func, value))
environment_variables_json = json.dumps(
{var.name: var.model_dump() for var in encrypted_vars},
@@ -347,7 +357,7 @@ def value_of(cls, value: str) -> "WorkflowRunStatus":
raise ValueError(f"invalid workflow run status value {value}")
-class WorkflowRun(db.Model): # type: ignore[name-defined]
+class WorkflowRun(Base):
"""
Workflow Run
@@ -439,7 +449,7 @@ def outputs_dict(self) -> Mapping[str, Any]:
return json.loads(self.outputs) if self.outputs else {}
@property
- def message(self) -> Optional["Message"]:
+ def message(self):
from models.model import Message
return (
@@ -549,7 +559,7 @@ def value_of(cls, value: str) -> "WorkflowNodeExecutionStatus":
raise ValueError(f"invalid workflow node execution status value {value}")
-class WorkflowNodeExecution(db.Model): # type: ignore[name-defined]
+class WorkflowNodeExecution(Base):
"""
Workflow Node Execution
@@ -715,7 +725,7 @@ def value_of(cls, value: str) -> "WorkflowAppLogCreatedFrom":
raise ValueError(f"invalid workflow app log created from value {value}")
-class WorkflowAppLog(db.Model): # type: ignore[name-defined]
+class WorkflowAppLog(Base):
"""
Workflow App execution log, excluding workflow debugging records.
@@ -777,15 +787,20 @@ def created_by_end_user(self):
return db.session.get(EndUser, self.created_by) if created_by_role == CreatedByRole.END_USER else None
-class ConversationVariable(db.Model): # type: ignore[name-defined]
+class ConversationVariable(Base):
__tablename__ = "workflow_conversation_variables"
+ __table_args__ = (
+ PrimaryKeyConstraint("id", "conversation_id", name="workflow_conversation_variables_pkey"),
+ Index("workflow__conversation_variables_app_id_idx", "app_id"),
+ Index("workflow__conversation_variables_created_at_idx", "created_at"),
+ )
- id: Mapped[str] = db.Column(StringUUID, primary_key=True)
- conversation_id: Mapped[str] = db.Column(StringUUID, nullable=False, primary_key=True)
- app_id: Mapped[str] = db.Column(StringUUID, nullable=False, index=True)
- data = db.Column(db.Text, nullable=False)
- created_at = db.Column(db.DateTime, nullable=False, index=True, server_default=func.current_timestamp())
- updated_at = db.Column(
+ id: Mapped[str] = mapped_column(StringUUID, primary_key=True)
+ conversation_id: Mapped[str] = mapped_column(StringUUID, nullable=False, primary_key=True)
+ app_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
+ data = mapped_column(db.Text, nullable=False)
+ created_at = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp())
+ updated_at = mapped_column(
db.DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp()
)
diff --git a/api/poetry.lock b/api/poetry.lock
index 1fdc292b92e132..52e3a646731a64 100644
--- a/api/poetry.lock
+++ b/api/poetry.lock
@@ -15,102 +15,107 @@ files = [
[[package]]
name = "aiohappyeyeballs"
-version = "2.4.4"
+version = "2.4.6"
description = "Happy Eyeballs for asyncio"
optional = false
-python-versions = ">=3.8"
-groups = ["main", "storage", "tools", "vdb"]
+python-versions = ">=3.9"
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"},
- {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"},
+ {file = "aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1"},
+ {file = "aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"},
]
[[package]]
name = "aiohttp"
-version = "3.11.11"
+version = "3.11.12"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.9"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"},
- {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"},
- {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"},
- {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"},
- {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"},
- {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"},
- {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"},
- {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"},
- {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"},
- {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"},
- {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"},
- {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"},
- {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"},
- {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"},
- {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"},
- {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"},
- {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"},
- {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"},
- {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"},
- {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"},
- {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"},
- {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"},
- {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"},
- {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"},
- {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"},
- {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"},
- {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"},
- {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"},
- {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"},
- {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"},
- {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"},
- {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"},
- {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"},
- {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"},
- {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"},
- {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"},
- {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"},
- {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"},
- {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"},
- {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"},
- {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"},
- {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"},
- {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"},
- {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"},
- {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"},
- {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"},
- {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"},
- {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"},
- {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"},
- {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"},
- {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"},
- {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"},
- {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"},
- {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"},
- {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"},
- {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"},
- {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"},
- {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"},
- {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"},
- {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"},
- {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"},
- {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"},
- {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"},
- {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"},
- {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"},
- {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"},
- {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"},
- {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"},
- {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"},
- {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"},
- {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"},
- {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"},
- {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"},
- {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"},
- {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"},
- {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"},
+ {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"},
+ {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"},
+ {file = "aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957"},
+ {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42"},
+ {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55"},
+ {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb"},
+ {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae"},
+ {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf"},
+ {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff"},
+ {file = "aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d"},
+ {file = "aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5"},
+ {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb"},
+ {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9"},
+ {file = "aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933"},
+ {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1"},
+ {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94"},
+ {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6"},
+ {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5"},
+ {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804"},
+ {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b"},
+ {file = "aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16"},
+ {file = "aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6"},
+ {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250"},
+ {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1"},
+ {file = "aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c"},
+ {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df"},
+ {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259"},
+ {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d"},
+ {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e"},
+ {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef"},
+ {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9"},
+ {file = "aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a"},
+ {file = "aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802"},
+ {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9"},
+ {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c"},
+ {file = "aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0"},
+ {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2"},
+ {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1"},
+ {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7"},
+ {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e"},
+ {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a"},
+ {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce"},
+ {file = "aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f"},
+ {file = "aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"},
+ {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b"},
+ {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78"},
+ {file = "aiohttp-3.11.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73"},
+ {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460"},
+ {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a"},
+ {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6"},
+ {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5"},
+ {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4"},
+ {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8"},
+ {file = "aiohttp-3.11.12-cp39-cp39-win32.whl", hash = "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462"},
+ {file = "aiohttp-3.11.12-cp39-cp39-win_amd64.whl", hash = "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798"},
+ {file = "aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0"},
]
[package.dependencies]
@@ -125,22 +130,6 @@ yarl = ">=1.17.0,<2.0"
[package.extras]
speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
-[[package]]
-name = "aiohttp-retry"
-version = "2.9.1"
-description = "Simple retry client for aiohttp"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"},
- {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"},
-]
-
-[package.dependencies]
-aiohttp = "*"
-
[[package]]
name = "aiomysql"
version = "0.2.0"
@@ -167,7 +156,7 @@ version = "1.3.2"
description = "aiosignal: a list of registered asynchronous callbacks"
optional = false
python-versions = ">=3.9"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"},
@@ -179,15 +168,15 @@ frozenlist = ">=1.1.0"
[[package]]
name = "alembic"
-version = "1.14.0"
+version = "1.14.1"
description = "A database migration tool for SQLAlchemy."
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "alembic-1.14.0-py3-none-any.whl", hash = "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25"},
- {file = "alembic-1.14.0.tar.gz", hash = "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b"},
+ {file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"},
+ {file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"},
]
[package.dependencies]
@@ -196,7 +185,7 @@ SQLAlchemy = ">=1.3.0"
typing-extensions = ">=4"
[package.extras]
-tz = ["backports.zoneinfo"]
+tz = ["backports.zoneinfo", "tzdata"]
[[package]]
name = "alibabacloud-credentials"
@@ -466,15 +455,15 @@ vine = ">=5.0.0,<6.0.0"
[[package]]
name = "aniso8601"
-version = "9.0.1"
+version = "10.0.0"
description = "A library for parsing ISO 8601 strings."
optional = false
python-versions = "*"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
- {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
+ {file = "aniso8601-10.0.0-py2.py3-none-any.whl", hash = "sha256:3c943422efaa0229ebd2b0d7d223effb5e7c89e24d2267ebe76c61a2d8e290cb"},
+ {file = "aniso8601-10.0.0.tar.gz", hash = "sha256:ff1d0fc2346688c62c0151547136ac30e322896ed8af316ef7602c47da9426cf"},
]
[package.extras]
@@ -493,32 +482,6 @@ files = [
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
-[[package]]
-name = "anthropic"
-version = "0.23.1"
-description = "The official Python library for the anthropic API"
-optional = false
-python-versions = ">=3.7"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "anthropic-0.23.1-py3-none-any.whl", hash = "sha256:6dc5779dae83a5834864f4a4af0166c972b70f4cb8fd2765e1558282cc6d6242"},
- {file = "anthropic-0.23.1.tar.gz", hash = "sha256:9325103702cbc96bb09d1b58c36bde75c726f6a01029fb4d85f41ebba07e9066"},
-]
-
-[package.dependencies]
-anyio = ">=3.5.0,<5"
-distro = ">=1.7.0,<2"
-httpx = ">=0.23.0,<1"
-pydantic = ">=1.9.0,<3"
-sniffio = "*"
-tokenizers = ">=0.13.0"
-typing-extensions = ">=4.7,<5"
-
-[package.extras]
-bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"]
-vertex = ["google-auth (>=2,<3)"]
-
[[package]]
name = "anyio"
version = "4.8.0"
@@ -542,23 +505,6 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)",
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
trio = ["trio (>=0.26.1)"]
-[[package]]
-name = "arxiv"
-version = "2.1.0"
-description = "Python wrapper for the arXiv API: https://arxiv.org/help/api/"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "arxiv-2.1.0-py3-none-any.whl", hash = "sha256:d634a0a59c9f05baf524eaa65563bb0a4532d2b4727a1162a1a9ba7e1e6e48cc"},
- {file = "arxiv-2.1.0.tar.gz", hash = "sha256:eb4b1d5ab9dfd66027c344bb324c20be21d56fe15f6ce216ed5b209df747dea8"},
-]
-
-[package.dependencies]
-feedparser = "6.0.10"
-requests = "2.31.0"
-
[[package]]
name = "asgiref"
version = "3.8.1"
@@ -590,24 +536,24 @@ files = [
[[package]]
name = "attrs"
-version = "23.2.0"
+version = "25.1.0"
description = "Classes Without Boilerplate"
optional = false
-python-versions = ">=3.7"
-groups = ["main", "lint", "storage", "tools", "vdb"]
+python-versions = ">=3.8"
+groups = ["main", "lint", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
- {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
+ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"},
+ {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"},
]
[package.extras]
-cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[tests]", "pre-commit"]
-docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
-tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
-tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
+benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
+tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
[[package]]
name = "authlib"
@@ -625,79 +571,6 @@ files = [
[package.dependencies]
cryptography = "*"
-[[package]]
-name = "azure-ai-inference"
-version = "1.0.0b8"
-description = "Microsoft Azure AI Inference Client Library for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure_ai_inference-1.0.0b8-py3-none-any.whl", hash = "sha256:9bfcfe6ef5b1699fed6c70058027c253bcbc88f4730e7409fbfc675636ec05e4"},
- {file = "azure_ai_inference-1.0.0b8.tar.gz", hash = "sha256:b7bcaaac5f53f2be06804ac6c755be9583ac6ba99df533a3970da081838b4cc1"},
-]
-
-[package.dependencies]
-azure-core = ">=1.30.0"
-isodate = ">=0.6.1"
-typing-extensions = ">=4.6.0"
-
-[package.extras]
-opentelemetry = ["azure-core-tracing-opentelemetry"]
-prompts = ["pyyaml"]
-
-[[package]]
-name = "azure-ai-ml"
-version = "1.20.0"
-description = "Microsoft Azure Machine Learning Client Library for Python"
-optional = false
-python-versions = ">=3.7"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure-ai-ml-1.20.0.tar.gz", hash = "sha256:6432a0da1b7250cb0db5a1c33202e0419935e19ea32d4c2b3220705f8f1d4101"},
- {file = "azure_ai_ml-1.20.0-py3-none-any.whl", hash = "sha256:c7eb3c5ccf82a6ee94403c3e5060763decd38cf03ff2620a4a6577526e605104"},
-]
-
-[package.dependencies]
-azure-common = ">=1.1"
-azure-core = ">=1.23.0"
-azure-mgmt-core = ">=1.3.0"
-azure-storage-blob = ">=12.10.0"
-azure-storage-file-datalake = ">=12.2.0"
-azure-storage-file-share = "*"
-colorama = "*"
-isodate = "*"
-jsonschema = ">=4.0.0"
-marshmallow = ">=3.5"
-msrest = ">=0.6.18"
-opencensus-ext-azure = "*"
-opencensus-ext-logging = "*"
-pydash = ">=6.0.0"
-pyjwt = "*"
-pyyaml = ">=5.1.0"
-strictyaml = "*"
-tqdm = "*"
-typing-extensions = "*"
-
-[package.extras]
-designer = ["mldesigner"]
-mount = ["azureml-dataprep-rslex (>=2.22.0)"]
-
-[[package]]
-name = "azure-common"
-version = "1.1.28"
-description = "Microsoft Azure Client Library for Python (Common)"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3"},
- {file = "azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"},
-]
-
[[package]]
name = "azure-core"
version = "1.32.0"
@@ -738,29 +611,13 @@ cryptography = ">=2.5"
msal = ">=1.24.0"
msal-extensions = ">=0.3.0"
-[[package]]
-name = "azure-mgmt-core"
-version = "1.5.0"
-description = "Microsoft Azure Management Core Library for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure_mgmt_core-1.5.0-py3-none-any.whl", hash = "sha256:18aaa5a723ee8ae05bf1bfc9f6d0ffb996631c7ea3c922cc86f522973ce07b5f"},
- {file = "azure_mgmt_core-1.5.0.tar.gz", hash = "sha256:380ae3dfa3639f4a5c246a7db7ed2d08374e88230fd0da3eb899f7c11e5c441a"},
-]
-
-[package.dependencies]
-azure-core = ">=1.31.0"
-
[[package]]
name = "azure-storage-blob"
version = "12.13.0"
description = "Microsoft Azure Blob Storage Client Library for Python"
optional = false
python-versions = ">=3.6"
-groups = ["main", "storage"]
+groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "azure-storage-blob-12.13.0.zip", hash = "sha256:53f0d4cd32970ac9ff9b9753f83dd2fb3f9ac30e1d01e71638c436c509bfd884"},
@@ -772,46 +629,6 @@ azure-core = ">=1.23.1,<2.0.0"
cryptography = ">=2.1.4"
msrest = ">=0.6.21"
-[[package]]
-name = "azure-storage-file-datalake"
-version = "12.8.0"
-description = "Microsoft Azure File DataLake Storage Client Library for Python"
-optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure-storage-file-datalake-12.8.0.zip", hash = "sha256:12e6306e5efb5ca28e0ccd9fa79a2c61acd589866d6109fe5601b18509da92f4"},
- {file = "azure_storage_file_datalake-12.8.0-py3-none-any.whl", hash = "sha256:b6cf5733fe794bf3c866efbe3ce1941409e35b6b125028ac558b436bf90f2de7"},
-]
-
-[package.dependencies]
-azure-core = ">=1.23.1,<2.0.0"
-azure-storage-blob = ">=12.13.0,<13.0.0"
-msrest = ">=0.6.21"
-
-[[package]]
-name = "azure-storage-file-share"
-version = "12.20.0"
-description = "Microsoft Azure Azure File Share Storage Client Library for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "azure_storage_file_share-12.20.0-py3-none-any.whl", hash = "sha256:fd5c4f09d7784d68b8ed3de473b7525904f1c4b115f9cd200c838b0ee720cb5f"},
- {file = "azure_storage_file_share-12.20.0.tar.gz", hash = "sha256:f120fc67bae0a84c1b54d06faa70df351be14d1395b9a085350e833f7d347a65"},
-]
-
-[package.dependencies]
-azure-core = ">=1.30.0"
-cryptography = ">=2.1.4"
-isodate = ">=0.6.1"
-typing-extensions = ">=4.6.0"
-
-[package.extras]
-aio = ["azure-core[aio] (>=1.30.0)"]
-
[[package]]
name = "backoff"
version = "2.2.1"
@@ -827,15 +644,15 @@ files = [
[[package]]
name = "bce-python-sdk"
-version = "0.9.25"
+version = "0.9.29"
description = "BCE SDK for python"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,<4,>=2.7"
groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "bce_python_sdk-0.9.25-py3-none-any.whl", hash = "sha256:cd1ab4c887e163adba6bfb3cd40465a365e5f4255705a015b0cdbe768e649877"},
- {file = "bce_python_sdk-0.9.25.tar.gz", hash = "sha256:93a0623fbb1bf3a58b4f2d7bdbd799a3b342a538f0c72950c77168e431470e86"},
+ {file = "bce_python_sdk-0.9.29-py3-none-any.whl", hash = "sha256:6518dc0ada422acd1841eeabcb7f89cfc07e3bb1a4be3c75945cab953907b555"},
+ {file = "bce_python_sdk-0.9.29.tar.gz", hash = "sha256:326fbd50d57bf6d2fc21d58f589b069e0e84fc0a8733be9575c109293ab08cc4"},
]
[package.dependencies]
@@ -889,7 +706,7 @@ version = "4.12.2"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.6.0"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
@@ -922,7 +739,7 @@ version = "1.9.0"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
@@ -952,15 +769,15 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.36.12"
+version = "1.36.21"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "botocore-1.36.12-py3-none-any.whl", hash = "sha256:5ae1ed362c8ed908a6ced8cdd12b21e2196c100bc79f9e95c9c1fc7f9ea74f5a"},
- {file = "botocore-1.36.12.tar.gz", hash = "sha256:86ed88beb4f244c96529435c868d3940073c2774116f0023fb7691f6e7053bd9"},
+ {file = "botocore-1.36.21-py3-none-any.whl", hash = "sha256:24a7052e792639dc2726001bd474cd0aaa959c1e18ddd92c17f3adc6efa1b132"},
+ {file = "botocore-1.36.21.tar.gz", hash = "sha256:da746240e2ad64fd4997f7f3664a0a8e303d18075fc1d473727cb6375080ea16"},
]
[package.dependencies]
@@ -1273,15 +1090,15 @@ zstd = ["zstandard (==0.22.0)"]
[[package]]
name = "certifi"
-version = "2024.12.14"
+version = "2025.1.31"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["main", "storage", "tools", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
- {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
+ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
+ {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
]
[[package]]
@@ -1290,7 +1107,7 @@ version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -1360,7 +1177,7 @@ files = [
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
-markers = {main = "python_version == \"3.11\" or python_version >= \"3.12\"", storage = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"", tools = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation == \"PyPy\"", vdb = "python_version == \"3.11\" or python_version >= \"3.12\""}
+markers = {main = "python_version == \"3.11\" or python_version >= \"3.12\"", storage = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"", vdb = "python_version == \"3.11\" or python_version >= \"3.12\""}
[package.dependencies]
pycparser = "*"
@@ -1676,7 +1493,7 @@ version = "0.7.19"
description = "ClickHouse Database Core Driver for Python, Pandas, and Superset"
optional = false
python-versions = "~=3.8"
-groups = ["tools", "vdb"]
+groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "clickhouse-connect-0.7.19.tar.gz", hash = "sha256:ce8f21f035781c5ef6ff57dc162e8150779c009b59f14030ba61f8c9c10c06d0"},
@@ -1762,19 +1579,6 @@ pandas = ["pandas"]
sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"]
tzlocal = ["tzlocal (>=4.0)"]
-[[package]]
-name = "cloudpickle"
-version = "2.2.1"
-description = "Extended pickling support for Python objects"
-optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"},
- {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"},
-]
-
[[package]]
name = "cloudscraper"
version = "1.2.71"
@@ -1793,28 +1597,6 @@ pyparsing = ">=2.4.7"
requests = ">=2.9.2"
requests-toolbelt = ">=0.9.1"
-[[package]]
-name = "cohere"
-version = "5.2.6"
-description = ""
-optional = false
-python-versions = "<4.0,>=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "cohere-5.2.6-py3-none-any.whl", hash = "sha256:256b4ed00f47eb315401d7f28834655714f098382908e7d0ad5c98225aa6a57d"},
- {file = "cohere-5.2.6.tar.gz", hash = "sha256:15d13682706fbafc8cf700e195f628389a643eb7ebd6d7c5e9d6e1ebd3f942fb"},
-]
-
-[package.dependencies]
-fastavro = ">=1.9.4,<2.0.0"
-httpx = ">=0.21.2"
-pydantic = ">=1.9.2"
-requests = ">=2.0.0,<3.0.0"
-tokenizers = ">=0.15.2,<0.16.0"
-types-requests = ">=2.0.0,<3.0.0"
-typing_extensions = ">=4.0.0"
-
[[package]]
name = "colorama"
version = "0.4.6"
@@ -1826,7 +1608,7 @@ files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
-markers = {main = "python_version == \"3.11\" or python_version >= \"3.12\"", dev = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or sys_platform == \"win32\")", lint = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\"", tools = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\"", vdb = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\")"}
+markers = {main = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or sys_platform == \"win32\")", dev = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or sys_platform == \"win32\")", lint = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\"", tools = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\"", vdb = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\")"}
[[package]]
name = "coloredlogs"
@@ -1847,81 +1629,6 @@ humanfriendly = ">=9.1"
[package.extras]
cron = ["capturer (>=2.4)"]
-[[package]]
-name = "contourpy"
-version = "1.3.1"
-description = "Python library for calculating contours of 2D quadrilateral grids"
-optional = false
-python-versions = ">=3.10"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"},
- {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"},
- {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"},
- {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"},
- {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"},
- {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"},
- {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"},
- {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"},
- {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"},
- {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"},
- {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"},
- {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"},
- {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"},
- {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"},
- {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"},
- {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"},
- {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"},
- {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"},
- {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"},
- {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"},
- {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"},
- {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"},
- {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"},
- {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"},
- {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"},
- {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"},
- {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"},
- {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"},
- {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"},
- {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"},
- {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"},
- {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"},
- {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"},
- {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"},
- {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"},
- {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"},
- {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"},
- {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"},
- {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"},
- {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"},
- {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"},
- {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"},
- {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"},
- {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"},
- {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"},
- {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"},
- {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"},
- {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"},
- {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"},
- {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"},
- {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"},
- {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"},
- {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"},
- {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"},
-]
-
-[package.dependencies]
-numpy = ">=1.23"
-
-[package.extras]
-bokeh = ["bokeh", "selenium"]
-docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
-mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"]
-test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
-test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
-
[[package]]
name = "cos-python-sdk-v5"
version = "1.9.30"
@@ -1943,44 +1650,44 @@ xmltodict = "*"
[[package]]
name = "couchbase"
-version = "4.3.4"
+version = "4.3.5"
description = "Python Client for Couchbase"
optional = false
python-versions = ">=3.7"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "couchbase-4.3.4-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:395e7b05495132a071dce5cdd84a3ec6e803205875f8ee22e85a89a16bb1b5f4"},
- {file = "couchbase-4.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:263a18307d1f1a141b93ae370b19843b1160dd702559152aea19dd08768f59f5"},
- {file = "couchbase-4.3.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:16751d4f3b0fe49666515ebed7967e8f38ec3862b329f773f88252acfd7c2b1f"},
- {file = "couchbase-4.3.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4dcc7eb9f57825c0097785d1c042e146908d2883f5e733d4ca07ac548bb532a2"},
- {file = "couchbase-4.3.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42f16ca2ec636db9ecacd3b97db85c923be8374eaae2fe097124d8eb92b4293f"},
- {file = "couchbase-4.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:8eecc9cdead68efe4119ebe41b065dadf83bc1653ec56470800c5093e378cacc"},
- {file = "couchbase-4.3.4-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1a6d6c4542e4ffe223960553e057bc175cfcee3fe274f63551e9d90d7c2435c5"},
- {file = "couchbase-4.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae44db4ce78b691028075fc54beec2dc1a59829f53a2b282f9a8b3ea6b71ad22"},
- {file = "couchbase-4.3.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a175f1e447b9aeb7ab5aab66350261be28ad7d9a07fff9c7fe48c55828133ec3"},
- {file = "couchbase-4.3.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb0170d5e7766593d47292c14a782e201f0167175f0e60cd7ba3b9acd75e349"},
- {file = "couchbase-4.3.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3f7d9e0492727b8560d36f5cb45c2a6ec9507dd2120ddd6313fd21e04cfc2ab9"},
- {file = "couchbase-4.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:f32e9d87e5157b86af5de85200cab433690789551d2bda1b8e7a25bf2680d511"},
- {file = "couchbase-4.3.4-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:395afab875aa3d293429cebc080cc12ac6e32c665275740d5a8445c688ad84ce"},
- {file = "couchbase-4.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:852ff1e36668a9b0e0e4dc015df06e3a663bd5e0301a52c25b724969073a1a11"},
- {file = "couchbase-4.3.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79ab95992829de574e23588ce35fc14ab6f8a5fd378c046522a678b7583a9b29"},
- {file = "couchbase-4.3.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:88827951b4132b89b6f37f1f2706b1e2f04870825c420e931c3caa770fc4a4e8"},
- {file = "couchbase-4.3.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d8f88c731d0d28132a992978aae5e1140a71276cc528ecc2ed408b2e386d1183"},
- {file = "couchbase-4.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:fb137358e249c752dbecb44393769696c07fd069eb976b2a9890ddd457d35fcb"},
- {file = "couchbase-4.3.4-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:d3f84932dd2d26a06048fe0858be21f5c6907a304ce59d673d56691e6eda7626"},
- {file = "couchbase-4.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f0975f9efeba9c425c2c73e5c3b6f3b4041cb61e1c5c0240c581454b0fc222fe"},
- {file = "couchbase-4.3.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:43609d64306ac8be7c396c0395a140c8f6b5bbab889e4b943f1b0dd500e34568"},
- {file = "couchbase-4.3.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8a549126875e38a79f7f7d97094a482b3fd446c20266ddb5c274d6398be8477"},
- {file = "couchbase-4.3.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c1adc3c7cf411f1c61e2f9b4454719d25f7229594280d7dedc7a8c9c2da8189f"},
- {file = "couchbase-4.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:9b34b9599b29c2366e2943309c45c0666956e458848eb9b88a43a765afc8728c"},
- {file = "couchbase-4.3.4-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f9b9b5523fbc89189119eceea170c329cf02115e1eba59818faefb594b729520"},
- {file = "couchbase-4.3.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e2cb01a0b567694a12abdb01f73392cf64cbc881e496e70b32f05f36ac50ca0f"},
- {file = "couchbase-4.3.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31eb077c2cd9694b933a8a18836c117f6682a220b33a767b3379934b540e6e1c"},
- {file = "couchbase-4.3.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ad97d9467485667f8ba2b644c5823bb53fb1799dca5a29b671258d6af719ca0"},
- {file = "couchbase-4.3.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:51f50dd684e9894d5c8059ee6da5e9bc6e1a47ab3be103a3b299e7d01de02bab"},
- {file = "couchbase-4.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:1059b4358d1f1b69812f114d0c5a547f830ab9fb24bcd5076a05ceb4788adee1"},
- {file = "couchbase-4.3.4.tar.gz", hash = "sha256:f195958606cf3a255fd96646ca3dd7e2ddcecf3664b3883826c7b89ef680088e"},
+ {file = "couchbase-4.3.5-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:7b354e59ebd3da994b54fa48859116e59d72394307f52783b83cb76d125414d5"},
+ {file = "couchbase-4.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3a701989e3539faf8b50278337a1df88c6713b1da2d4eb7c1161c0c73c618a3a"},
+ {file = "couchbase-4.3.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80144ead758494e353d79ac939cdfd8d567f08d5c49c6c3633b4d30f5cb1bb34"},
+ {file = "couchbase-4.3.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2981c90cb222809513333d7edcfdff4cda16eb7144cd100775552900109d22d9"},
+ {file = "couchbase-4.3.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2ca0bf5d0a4a70747c5bb8284efb9e7631dfbfd671b2c1847715cc338c4cf17b"},
+ {file = "couchbase-4.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:e21fe0d38828db02271ece96359cc6b4f2bbc17b3a7f617c799987c6fdea6920"},
+ {file = "couchbase-4.3.5-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:21cb325b4a5b017581ee038dc2362865bcd59b3aa65685432a362f9f8e8f3276"},
+ {file = "couchbase-4.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5f063f79af8fe2425025ac9675e95bc914329db72f40adfd609451dcaf177920"},
+ {file = "couchbase-4.3.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c28c05189c138408693d926211d80e0cb82fae02ac5e4619cb37efeb26349c1c"},
+ {file = "couchbase-4.3.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:caf8f30cd1d587829685cffe575ec2b0f750a10611fe9f14e615070c1756079b"},
+ {file = "couchbase-4.3.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0aaef43bac983332e8bd8484a286a9576a878a95b4e5bfc6bf9c4aa8b3ff71b4"},
+ {file = "couchbase-4.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:07deb48cd32e726088d2b2b93bb52fdca0b26e3e36b7d8b3728ea993a90b4327"},
+ {file = "couchbase-4.3.5-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:5c9f5754d7391ad3c53adb46a8b401bfd4e0db38aba707a5ed6daad295091afd"},
+ {file = "couchbase-4.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:92cea5d666d484c3801c2186ba2e0dd9df1bc654296c5862c94bf7a56e7e1e34"},
+ {file = "couchbase-4.3.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f471b746cf13e9789de13fccd1ba4fbd3ae95e72710c3b07eb98cf8d72a5053b"},
+ {file = "couchbase-4.3.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80de6c8ddf39c4ee380d093fcf83a6300e837db8a6f60a8ddb7df6fcbb662595"},
+ {file = "couchbase-4.3.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9ce0c9e073a616cc6353ea93e9bac0fe6f79ff6df03fccf0c846f19b5ddf957b"},
+ {file = "couchbase-4.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:b78f98001f40450ef1bbaf7d73fa28f0734e9bd72b38b96c18eb2090a09094fb"},
+ {file = "couchbase-4.3.5-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:4f1462a0a353a13203f21313b17b30a5e13a2f86205b5437b4f5bd86878ad2aa"},
+ {file = "couchbase-4.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d13924326834f81eae2ecb820119bc80460d6b556eb9cad63c5214ddd12e408e"},
+ {file = "couchbase-4.3.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12ffcdbf11ab959c69f6e96f9e2aa9259c84ef9771cd3f10fa3930dade2fb2f"},
+ {file = "couchbase-4.3.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9deee11a39765455a326fd66d570163543cdaf65dc95e0e44c257dd14febdd3e"},
+ {file = "couchbase-4.3.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:693675658668e83685f6e3d8a969ffb0447a48e74efe6fa77dadca281fa72b8d"},
+ {file = "couchbase-4.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:3b6aaeb3aea49c2b8f891e3dbaf8e5d73d049badee784bef14c6f24098528db9"},
+ {file = "couchbase-4.3.5-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f25329baa41b013802b8fe8f978cf5603891afc8024dc0cda4ca3e52ec26bfe"},
+ {file = "couchbase-4.3.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8129d25c1bbcb78c32fc73e2480cb2beaf50dd5938fb8fa671ab63f89482528f"},
+ {file = "couchbase-4.3.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26e9447e9dfc6e33d78a72f306830261147b0e62aebf5789f4b4b4ba9b86b3ef"},
+ {file = "couchbase-4.3.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c01c5e835bb397525686d6c8e90ecf62a4510b71488e2a407c8bd76573f89ba"},
+ {file = "couchbase-4.3.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57fec6e1ef686804336664ca679b7550c6aa4fab14609a265151afd31e2b5707"},
+ {file = "couchbase-4.3.5-cp39-cp39-win_amd64.whl", hash = "sha256:679c92733d4ff421f72a54481608e7b005b87e93eae95af184e8212d88dca262"},
+ {file = "couchbase-4.3.5.tar.gz", hash = "sha256:2e2f239b2959ece983195e3ee17a9142bc75b23b0f6733031198648a5f1107ed"},
]
[[package]]
@@ -2071,42 +1778,44 @@ files = [
[[package]]
name = "cryptography"
-version = "44.0.0"
+version = "44.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = "!=3.9.0,!=3.9.1,>=3.7"
groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
- {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
- {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
- {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
- {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
- {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"},
- {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
- {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
- {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
- {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
- {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
- {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"},
- {file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"},
- {file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"},
+ {file = "cryptography-44.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd"},
+ {file = "cryptography-44.0.1-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0"},
+ {file = "cryptography-44.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf"},
+ {file = "cryptography-44.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864"},
+ {file = "cryptography-44.0.1-cp37-abi3-win32.whl", hash = "sha256:24979e9f2040c953a94bf3c6782e67795a4c260734e5264dceea65c8f4bae64a"},
+ {file = "cryptography-44.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:fd0ee90072861e276b0ff08bd627abec29e32a53b2be44e41dbcdf87cbee2b00"},
+ {file = "cryptography-44.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a2d8a7045e1ab9b9f803f0d9531ead85f90c5f2859e653b61497228b18452008"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62"},
+ {file = "cryptography-44.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41"},
+ {file = "cryptography-44.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b"},
+ {file = "cryptography-44.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7"},
+ {file = "cryptography-44.0.1-cp39-abi3-win32.whl", hash = "sha256:9b336599e2cb77b1008cb2ac264b290803ec5e8e89d618a5e978ff5eb6f715d9"},
+ {file = "cryptography-44.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:e403f7f766ded778ecdb790da786b418a9f2394f36e8cc8b796cc056ab05f44f"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1f9a92144fa0c877117e9748c74501bea842f93d21ee00b0cf922846d9d0b183"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:610a83540765a8d8ce0f351ce42e26e53e1f774a6efb71eb1b41eb01d01c3d12"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5fed5cd6102bb4eb843e3315d2bf25fede494509bddadb81e03a859c1bc17b83"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:f4daefc971c2d1f82f03097dc6f216744a6cd2ac0f04c68fb935ea2ba2a0d420"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94f99f2b943b354a5b6307d7e8d19f5c423a794462bde2bf310c770ba052b1c4"},
+ {file = "cryptography-44.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d9c5b9f698a83c8bd71e0f4d3f9f839ef244798e5ffe96febfa9714717db7af7"},
+ {file = "cryptography-44.0.1.tar.gz", hash = "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14"},
]
[package.dependencies]
@@ -2119,82 +1828,9 @@ nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
sdist = ["build (>=1.0.0)"]
ssh = ["bcrypt (>=3.1.5)"]
-test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
+test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
test-randomorder = ["pytest-randomly"]
-[[package]]
-name = "cssselect"
-version = "1.2.0"
-description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"},
- {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"},
-]
-
-[[package]]
-name = "cycler"
-version = "0.12.1"
-description = "Composable style cycles"
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
- {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
-]
-
-[package.extras]
-docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
-tests = ["pytest", "pytest-cov", "pytest-xdist"]
-
-[[package]]
-name = "dashscope"
-version = "1.17.1"
-description = "dashscope client sdk library"
-optional = false
-python-versions = ">=3.8.0"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "dashscope-1.17.1-py3-none-any.whl", hash = "sha256:1e07e7ff4544684797f86ede646766b5ab8f5bd6eb43d2d01f0f757a2941efe1"},
-]
-
-[package.dependencies]
-aiohttp = "*"
-requests = "*"
-tiktoken = {version = "*", optional = true, markers = "extra == \"tokenizer\""}
-
-[package.extras]
-tokenizer = ["tiktoken"]
-
-[[package]]
-name = "dataclass-wizard"
-version = "0.34.0"
-description = "Lightning-fast JSON wizardry for Python dataclasses — effortless serialization right out of the box!"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "dataclass-wizard-0.34.0.tar.gz", hash = "sha256:f917db2220e395806a852f7c57e9011dd783b7fe3eee763bb56ae2d48968ab03"},
- {file = "dataclass_wizard-0.34.0-py2.py3-none-any.whl", hash = "sha256:9c184edd3526c3523fec2de5b6d6cdfcdc97ed7b2c5ba8bc574284b793704f01"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=4.9.0", markers = "python_version <= \"3.12\""}
-
-[package.extras]
-dev = ["Sphinx (==7.4.7)", "Sphinx (==8.1.3)", "bump2version (==1.0.1)", "coverage (>=6.2)", "dacite (==1.8.1)", "dataclass-factory (==2.16)", "dataclass-wizard[toml]", "dataclasses-json (==0.6.7)", "flake8 (>=3)", "jsons (==1.6.3)", "mashumaro (==3.15)", "matplotlib", "pip (>=21.3.1)", "pydantic (==2.10.3)", "pytest (==8.3.4)", "pytest-benchmark[histogram]", "pytest-cov (==6.0.0)", "pytest-mock (>=3.6.1)", "python-dotenv (>=1,<2)", "pytimeparse (==1.1.8)", "sphinx-autodoc-typehints (==2.5.0)", "sphinx-copybutton (==0.5.2)", "sphinx-issues (==5.0.0)", "tomli (>=2,<3)", "tomli (>=2,<3)", "tomli-w (>=1,<2)", "tox (==4.23.2)", "twine (==6.0.1)", "typing-extensions (>=4.9.0)", "watchdog[watchmedo] (==6.0.0)", "wheel (==0.45.1)"]
-dotenv = ["python-dotenv (>=1,<2)"]
-timedelta = ["pytimeparse (>=1.1.7)"]
-toml = ["tomli (>=2,<3)", "tomli (>=2,<3)", "tomli-w (>=1,<2)"]
-yaml = ["PyYAML (>=6,<7)"]
-
[[package]]
name = "dataclasses-json"
version = "0.6.7"
@@ -2212,25 +1848,6 @@ files = [
marshmallow = ">=3.18.0,<4.0.0"
typing-inspect = ">=0.4.0,<1"
-[[package]]
-name = "db-dtypes"
-version = "1.3.1"
-description = "Pandas Data Types for SQL systems (BigQuery, Spanner)"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "db_dtypes-1.3.1-py2.py3-none-any.whl", hash = "sha256:fbc9d1740d94aaf2b5ae24601cfc875a69b4635bb9d049e3c3036e9f10203af8"},
- {file = "db_dtypes-1.3.1.tar.gz", hash = "sha256:a058f05dab100891f3e76a7a3db9ad0f107f18dd3d1bdd13680749a2f07eae77"},
-]
-
-[package.dependencies]
-numpy = ">=1.16.6"
-packaging = ">=17.0"
-pandas = ">=0.24.2"
-pyarrow = ">=3.0.0"
-
[[package]]
name = "decorator"
version = "5.1.1"
@@ -2259,22 +1876,22 @@ files = [
[[package]]
name = "deprecated"
-version = "1.2.15"
+version = "1.2.18"
description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"},
- {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"},
+ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
+ {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
]
[package.dependencies]
wrapt = ">=1.10,<2"
[package.extras]
-dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
[[package]]
name = "deprecation"
@@ -2292,23 +1909,6 @@ files = [
[package.dependencies]
packaging = "*"
-[[package]]
-name = "dill"
-version = "0.3.9"
-description = "serialize all of Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"},
- {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"},
-]
-
-[package.extras]
-graph = ["objgraph (>=1.7.2)"]
-profile = ["gprof2dot (>=2022.7.29)"]
-
[[package]]
name = "distro"
version = "1.9.0"
@@ -2322,30 +1922,6 @@ files = [
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
]
-[[package]]
-name = "docker"
-version = "7.1.0"
-description = "A Python library for the Docker Engine API."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"},
- {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"},
-]
-
-[package.dependencies]
-pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""}
-requests = ">=2.26.0"
-urllib3 = ">=1.26.0"
-
-[package.extras]
-dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"]
-docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"]
-ssh = ["paramiko (>=2.4.3)"]
-websockets = ["websocket-client (>=1.3.0)"]
-
[[package]]
name = "docstring-parser"
version = "0.16"
@@ -2380,96 +1956,12 @@ ply = ">=3.11,<4.0"
typing_extensions = ">=4.0,<5.0"
[[package]]
-name = "duckdb"
-version = "1.1.3"
-description = "DuckDB in-process database"
+name = "durationpy"
+version = "0.9"
+description = "Module for converting between datetime.timedelta and Go's Duration strings."
optional = false
-python-versions = ">=3.7.0"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:1c0226dc43e2ee4cc3a5a4672fddb2d76fd2cf2694443f395c02dd1bea0b7fce"},
- {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7c71169fa804c0b65e49afe423ddc2dc83e198640e3b041028da8110f7cd16f7"},
- {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:872d38b65b66e3219d2400c732585c5b4d11b13d7a36cd97908d7981526e9898"},
- {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25fb02629418c0d4d94a2bc1776edaa33f6f6ccaa00bd84eb96ecb97ae4b50e9"},
- {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3f5cd604e7c39527e6060f430769b72234345baaa0987f9500988b2814f5e4"},
- {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08935700e49c187fe0e9b2b86b5aad8a2ccd661069053e38bfaed3b9ff795efd"},
- {file = "duckdb-1.1.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9b47036945e1db32d70e414a10b1593aec641bd4c5e2056873d971cc21e978b"},
- {file = "duckdb-1.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:35c420f58abc79a68a286a20fd6265636175fadeca1ce964fc8ef159f3acc289"},
- {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:4f0e2e5a6f5a53b79aee20856c027046fba1d73ada6178ed8467f53c3877d5e0"},
- {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:911d58c22645bfca4a5a049ff53a0afd1537bc18fedb13bc440b2e5af3c46148"},
- {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c443d3d502335e69fc1e35295fcfd1108f72cb984af54c536adfd7875e79cee5"},
- {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a55169d2d2e2e88077d91d4875104b58de45eff6a17a59c7dc41562c73df4be"},
- {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d0767ada9f06faa5afcf63eb7ba1befaccfbcfdac5ff86f0168c673dd1f47aa"},
- {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51c6d79e05b4a0933672b1cacd6338f882158f45ef9903aef350c4427d9fc898"},
- {file = "duckdb-1.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:183ac743f21c6a4d6adfd02b69013d5fd78e5e2cd2b4db023bc8a95457d4bc5d"},
- {file = "duckdb-1.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:a30dd599b8090ea6eafdfb5a9f1b872d78bac318b6914ada2d35c7974d643640"},
- {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a433ae9e72c5f397c44abdaa3c781d94f94f4065bcbf99ecd39433058c64cb38"},
- {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:d08308e0a46c748d9c30f1d67ee1143e9c5ea3fbcccc27a47e115b19e7e78aa9"},
- {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:5d57776539211e79b11e94f2f6d63de77885f23f14982e0fac066f2885fcf3ff"},
- {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e59087dbbb63705f2483544e01cccf07d5b35afa58be8931b224f3221361d537"},
- {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ebf5f60ddbd65c13e77cddb85fe4af671d31b851f125a4d002a313696af43f1"},
- {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4ef7ba97a65bd39d66f2a7080e6fb60e7c3e41d4c1e19245f90f53b98e3ac32"},
- {file = "duckdb-1.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f58db1b65593ff796c8ea6e63e2e144c944dd3d51c8d8e40dffa7f41693d35d3"},
- {file = "duckdb-1.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:e86006958e84c5c02f08f9b96f4bc26990514eab329b1b4f71049b3727ce5989"},
- {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:0897f83c09356206ce462f62157ce064961a5348e31ccb2a557a7531d814e70e"},
- {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_universal2.whl", hash = "sha256:cddc6c1a3b91dcc5f32493231b3ba98f51e6d3a44fe02839556db2b928087378"},
- {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:1d9ab6143e73bcf17d62566e368c23f28aa544feddfd2d8eb50ef21034286f24"},
- {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f073d15d11a328f2e6d5964a704517e818e930800b7f3fa83adea47f23720d3"},
- {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5724fd8a49e24d730be34846b814b98ba7c304ca904fbdc98b47fa95c0b0cee"},
- {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51e7dbd968b393343b226ab3f3a7b5a68dee6d3fe59be9d802383bf916775cb8"},
- {file = "duckdb-1.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00cca22df96aa3473fe4584f84888e2cf1c516e8c2dd837210daec44eadba586"},
- {file = "duckdb-1.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:77f26884c7b807c7edd07f95cf0b00e6d47f0de4a534ac1706a58f8bc70d0d31"},
- {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4748635875fc3c19a7320a6ae7410f9295557450c0ebab6d6712de12640929a"},
- {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74e121ab65dbec5290f33ca92301e3a4e81797966c8d9feef6efdf05fc6dafd"},
- {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c619e4849837c8c83666f2cd5c6c031300cd2601e9564b47aa5de458ff6e69d"},
- {file = "duckdb-1.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0ba6baa0af33ded836b388b09433a69b8bec00263247f6bf0a05c65c897108d3"},
- {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:ecb1dc9062c1cc4d2d88a5e5cd8cc72af7818ab5a3c0f796ef0ffd60cfd3efb4"},
- {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:5ace6e4b1873afdd38bd6cc8fcf90310fb2d454f29c39a61d0c0cf1a24ad6c8d"},
- {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:a1fa0c502f257fa9caca60b8b1478ec0f3295f34bb2efdc10776fc731b8a6c5f"},
- {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6411e21a2128d478efbd023f2bdff12464d146f92bc3e9c49247240448ace5a6"},
- {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5336939d83837af52731e02b6a78a446794078590aa71fd400eb17f083dda3e"},
- {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f549af9f7416573ee48db1cf8c9d27aeed245cb015f4b4f975289418c6cf7320"},
- {file = "duckdb-1.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:2141c6b28162199999075d6031b5d63efeb97c1e68fb3d797279d31c65676269"},
- {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:09c68522c30fc38fc972b8a75e9201616b96ae6da3444585f14cf0d116008c95"},
- {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:8ee97ec337794c162c0638dda3b4a30a483d0587deda22d45e1909036ff0b739"},
- {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:a1f83c7217c188b7ab42e6a0963f42070d9aed114f6200e3c923c8899c090f16"},
- {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aa3abec8e8995a03ff1a904b0e66282d19919f562dd0a1de02f23169eeec461"},
- {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80158f4c7c7ada46245837d5b6869a336bbaa28436fbb0537663fa324a2750cd"},
- {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:647f17bd126170d96a38a9a6f25fca47ebb0261e5e44881e3782989033c94686"},
- {file = "duckdb-1.1.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:252d9b17d354beb9057098d4e5d5698e091a4f4a0d38157daeea5fc0ec161670"},
- {file = "duckdb-1.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:eeacb598120040e9591f5a4edecad7080853aa8ac27e62d280f151f8c862afa3"},
- {file = "duckdb-1.1.3.tar.gz", hash = "sha256:68c3a46ab08836fe041d15dcbf838f74a990d551db47cb24ab1c4576fc19351c"},
-]
-
-[[package]]
-name = "duckduckgo-search"
-version = "6.3.7"
-description = "Search for words, documents, images, news, maps and text translation using the DuckDuckGo.com search engine."
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "duckduckgo_search-6.3.7-py3-none-any.whl", hash = "sha256:6a831a27977751e8928222f04c99a5d069ff80e2a7c78b699c9b9ac6cb48c41b"},
- {file = "duckduckgo_search-6.3.7.tar.gz", hash = "sha256:53d84966429a6377647e2a1ea7224b657575c7a4d506729bdb837e4ee12915ed"},
-]
-
-[package.dependencies]
-click = ">=8.1.7"
-primp = ">=0.8.1"
-
-[package.extras]
-dev = ["mypy (>=1.11.1)", "pytest (>=8.3.1)", "pytest-asyncio (>=0.23.8)", "ruff (>=0.6.1)"]
-lxml = ["lxml (>=5.2.2)"]
-
-[[package]]
-name = "durationpy"
-version = "0.9"
-description = "Module for converting between datetime.timedelta and Go's Duration strings."
-optional = false
-python-versions = "*"
-groups = ["vdb"]
+python-versions = "*"
+groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"},
@@ -2520,15 +2012,15 @@ vectorstore-mmr = ["numpy (>=1)", "simsimd (>=3)"]
[[package]]
name = "emoji"
-version = "2.14.0"
+version = "2.14.1"
description = "Emoji for Python"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "emoji-2.14.0-py3-none-any.whl", hash = "sha256:fcc936bf374b1aec67dda5303ae99710ba88cc9cdce2d1a71c5f2204e6d78799"},
- {file = "emoji-2.14.0.tar.gz", hash = "sha256:f68ac28915a2221667cddb3e6c589303c3c6954c6c5af6fefaec7f9bdf72fdca"},
+ {file = "emoji-2.14.1-py3-none-any.whl", hash = "sha256:35a8a486c1460addb1499e3bf7929d3889b2e2841a57401903699fef595e942b"},
+ {file = "emoji-2.14.1.tar.gz", hash = "sha256:f8c50043d79a2c1410ebfae833ae1868d5941a67a6cd4d18377e2eb0bd79346b"},
]
[package.extras]
@@ -2595,147 +2087,44 @@ files = [
python-dateutil = ">=2.4"
typing-extensions = "*"
-[[package]]
-name = "fal-client"
-version = "0.5.6"
-description = "Python client for fal.ai"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-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.6"
+version = "0.115.8"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"},
- {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"},
+ {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"},
+ {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"},
]
[package.dependencies]
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-starlette = ">=0.40.0,<0.42.0"
+starlette = ">=0.40.0,<0.46.0"
typing-extensions = ">=4.8.0"
[package.extras]
-all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
-standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
-
-[[package]]
-name = "fastavro"
-version = "1.10.0"
-description = "Fast read/write of AVRO files"
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "fastavro-1.10.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1a9fe0672d2caf0fe54e3be659b13de3cad25a267f2073d6f4b9f8862acc31eb"},
- {file = "fastavro-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86dd0410770e0c99363788f0584523709d85e57bb457372ec5c285a482c17fe6"},
- {file = "fastavro-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:190e80dc7d77d03a6a8597a026146b32a0bbe45e3487ab4904dc8c1bebecb26d"},
- {file = "fastavro-1.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bf570d63be9155c3fdc415f60a49c171548334b70fff0679a184b69c29b6bc61"},
- {file = "fastavro-1.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e07abb6798e95dccecaec316265e35a018b523d1f3944ad396d0a93cb95e0a08"},
- {file = "fastavro-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:37203097ed11d0b8fd3c004904748777d730cafd26e278167ea602eebdef8eb2"},
- {file = "fastavro-1.10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d183c075f527ab695a27ae75f210d4a86bce660cda2f85ae84d5606efc15ef50"},
- {file = "fastavro-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7a95a2c0639bffd7c079b59e9a796bfc3a9acd78acff7088f7c54ade24e4a77"},
- {file = "fastavro-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a678153b5da1b024a32ec3f611b2e7afd24deac588cb51dd1b0019935191a6d"},
- {file = "fastavro-1.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a597a5cfea4dddcf8b49eaf8c2b5ffee7fda15b578849185bc690ec0cd0d8f"},
- {file = "fastavro-1.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fd689724760b17f69565d8a4e7785ed79becd451d1c99263c40cb2d6491f1d4"},
- {file = "fastavro-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f949d463f9ac4221128a51e4e34e2562f401e5925adcadfd28637a73df6c2d8"},
- {file = "fastavro-1.10.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cfe57cb0d72f304bd0dcc5a3208ca6a7363a9ae76f3073307d095c9d053b29d4"},
- {file = "fastavro-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74e517440c824cb65fb29d3e3903a9406f4d7c75490cef47e55c4c82cdc66270"},
- {file = "fastavro-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:203c17d44cadde76e8eecb30f2d1b4f33eb478877552d71f049265dc6f2ecd10"},
- {file = "fastavro-1.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6575be7f2b5f94023b5a4e766b0251924945ad55e9a96672dc523656d17fe251"},
- {file = "fastavro-1.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe471deb675ed2f01ee2aac958fbf8ebb13ea00fa4ce7f87e57710a0bc592208"},
- {file = "fastavro-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:567ff515f2a5d26d9674b31c95477f3e6022ec206124c62169bc2ffaf0889089"},
- {file = "fastavro-1.10.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82263af0adfddb39c85f9517d736e1e940fe506dfcc35bc9ab9f85e0fa9236d8"},
- {file = "fastavro-1.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566c193109ff0ff84f1072a165b7106c4f96050078a4e6ac7391f81ca1ef3efa"},
- {file = "fastavro-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e400d2e55d068404d9fea7c5021f8b999c6f9d9afa1d1f3652ec92c105ffcbdd"},
- {file = "fastavro-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b8227497f71565270f9249fc9af32a93644ca683a0167cfe66d203845c3a038"},
- {file = "fastavro-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e62d04c65461b30ac6d314e4197ad666371e97ae8cb2c16f971d802f6c7f514"},
- {file = "fastavro-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:86baf8c9740ab570d0d4d18517da71626fe9be4d1142bea684db52bd5adb078f"},
- {file = "fastavro-1.10.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5bccbb6f8e9e5b834cca964f0e6ebc27ebe65319d3940b0b397751a470f45612"},
- {file = "fastavro-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0132f6b0b53f61a0a508a577f64beb5de1a5e068a9b4c0e1df6e3b66568eec4"},
- {file = "fastavro-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca37a363b711202c6071a6d4787e68e15fa3ab108261058c4aae853c582339af"},
- {file = "fastavro-1.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cf38cecdd67ca9bd92e6e9ba34a30db6343e7a3bedf171753ee78f8bd9f8a670"},
- {file = "fastavro-1.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f4dd10e0ed42982122d20cdf1a88aa50ee09e5a9cd9b39abdffb1aa4f5b76435"},
- {file = "fastavro-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:aaef147dc14dd2d7823246178fd06fc5e477460e070dc6d9e07dd8193a6bc93c"},
- {file = "fastavro-1.10.0.tar.gz", hash = "sha256:47bf41ac6d52cdfe4a3da88c75a802321321b37b663a900d12765101a5d6886f"},
-]
-
-[package.extras]
-codecs = ["cramjam", "lz4", "zstandard"]
-lz4 = ["lz4"]
-snappy = ["cramjam"]
-zstandard = ["zstandard"]
-
-[[package]]
-name = "feedfinder2"
-version = "0.0.4"
-description = "Find the feed URLs for a website."
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "feedfinder2-0.0.4.tar.gz", hash = "sha256:3701ee01a6c85f8b865a049c30ba0b4608858c803fe8e30d1d289fdbe89d0efe"},
-]
-
-[package.dependencies]
-beautifulsoup4 = "*"
-requests = "*"
-six = "*"
-
-[[package]]
-name = "feedparser"
-version = "6.0.10"
-description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds"
-optional = false
-python-versions = ">=3.6"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "feedparser-6.0.10-py3-none-any.whl", hash = "sha256:79c257d526d13b944e965f6095700587f27388e50ea16fd245babe4dfae7024f"},
- {file = "feedparser-6.0.10.tar.gz", hash = "sha256:27da485f4637ce7163cdeab13a80312b93b7d0c1b775bef4a47629a3110bca51"},
-]
-
-[package.dependencies]
-sgmllib3k = "*"
+all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
+standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "filelock"
-version = "3.16.1"
+version = "3.17.0"
description = "A platform independent file lock."
optional = false
-python-versions = ">=3.8"
-groups = ["main", "tools", "vdb"]
+python-versions = ">=3.9"
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
- {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
+ {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"},
+ {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"},
]
[package.extras]
-docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
+docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
typing = ["typing-extensions (>=4.12.2)"]
[[package]]
@@ -2751,48 +2140,13 @@ files = [
{file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"},
]
-[[package]]
-name = "fire"
-version = "0.7.0"
-description = "A library for automatically generating command line interfaces."
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "fire-0.7.0.tar.gz", hash = "sha256:961550f07936eaf65ad1dc8360f2b2bf8408fad46abbfa4d2a3794f8d2a95cdf"},
-]
-
-[package.dependencies]
-termcolor = "*"
-
-[[package]]
-name = "flasgger"
-version = "0.9.7.1"
-description = "Extract swagger specs from your flask project"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "flasgger-0.9.7.1.tar.gz", hash = "sha256:ca098e10bfbb12f047acc6299cc70a33851943a746e550d86e65e60d4df245fb"},
-]
-
-[package.dependencies]
-Flask = ">=0.10"
-jsonschema = ">=3.0.1"
-mistune = "*"
-packaging = "*"
-PyYAML = ">=3.0"
-six = ">=1.10.0"
-
[[package]]
name = "flask"
version = "3.1.0"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"},
@@ -2905,26 +2259,6 @@ six = ">=1.3.0"
[package.extras]
docs = ["sphinx"]
-[[package]]
-name = "flask-sock"
-version = "0.7.0"
-description = "WebSocket support for Flask"
-optional = false
-python-versions = ">=3.6"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "flask-sock-0.7.0.tar.gz", hash = "sha256:e023b578284195a443b8d8bdb4469e6a6acf694b89aeb51315b1a34fcf427b7d"},
- {file = "flask_sock-0.7.0-py3-none-any.whl", hash = "sha256:caac4d679392aaf010d02fabcf73d52019f5bdaf1c9c131ec5a428cb3491204a"},
-]
-
-[package.dependencies]
-flask = ">=2"
-simple-websocket = ">=0.5.1"
-
-[package.extras]
-docs = ["sphinx"]
-
[[package]]
name = "flask-sqlalchemy"
version = "3.1.1"
@@ -2944,155 +2278,15 @@ sqlalchemy = ">=2.0.16"
[[package]]
name = "flatbuffers"
-version = "24.12.23"
+version = "25.2.10"
description = "The FlatBuffers serialization format for Python"
optional = false
python-versions = "*"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "flatbuffers-24.12.23-py2.py3-none-any.whl", hash = "sha256:c418e0d48890f4142b92fd3e343e73a48f194e1f80075ddcc5793779b3585444"},
- {file = "flatbuffers-24.12.23.tar.gz", hash = "sha256:2910b0bc6ae9b6db78dd2b18d0b7a0709ba240fb5585f286a3a2b30785c22dac"},
-]
-
-[[package]]
-name = "fontmeta"
-version = "1.6.1"
-description = "An Utility to get ttf/otf font metadata"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "fontmeta-1.6.1.tar.gz", hash = "sha256:837e5bc4da879394b41bda1428a8a480eb7c4e993799a93cfb582bab771a9c24"},
-]
-
-[package.dependencies]
-fonttools = "*"
-
-[[package]]
-name = "fonttools"
-version = "4.55.3"
-description = "Tools to manipulate font files"
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0"},
- {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f"},
- {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841"},
- {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674"},
- {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276"},
- {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5"},
- {file = "fonttools-4.55.3-cp310-cp310-win32.whl", hash = "sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261"},
- {file = "fonttools-4.55.3-cp310-cp310-win_amd64.whl", hash = "sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5"},
- {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e"},
- {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b"},
- {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90"},
- {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0"},
- {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b"},
- {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765"},
- {file = "fonttools-4.55.3-cp311-cp311-win32.whl", hash = "sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f"},
- {file = "fonttools-4.55.3-cp311-cp311-win_amd64.whl", hash = "sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72"},
- {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35"},
- {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c"},
- {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7"},
- {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314"},
- {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427"},
- {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a"},
- {file = "fonttools-4.55.3-cp312-cp312-win32.whl", hash = "sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07"},
- {file = "fonttools-4.55.3-cp312-cp312-win_amd64.whl", hash = "sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54"},
- {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29"},
- {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4"},
- {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca"},
- {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b"},
- {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048"},
- {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe"},
- {file = "fonttools-4.55.3-cp313-cp313-win32.whl", hash = "sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628"},
- {file = "fonttools-4.55.3-cp313-cp313-win_amd64.whl", hash = "sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b"},
- {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3"},
- {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d"},
- {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa"},
- {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e"},
- {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de"},
- {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926"},
- {file = "fonttools-4.55.3-cp38-cp38-win32.whl", hash = "sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b"},
- {file = "fonttools-4.55.3-cp38-cp38-win_amd64.whl", hash = "sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56"},
- {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af"},
- {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831"},
- {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02"},
- {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4"},
- {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd"},
- {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32"},
- {file = "fonttools-4.55.3-cp39-cp39-win32.whl", hash = "sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851"},
- {file = "fonttools-4.55.3-cp39-cp39-win_amd64.whl", hash = "sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d"},
- {file = "fonttools-4.55.3-py3-none-any.whl", hash = "sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977"},
- {file = "fonttools-4.55.3.tar.gz", hash = "sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45"},
-]
-
-[package.extras]
-all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
-graphite = ["lz4 (>=1.7.4.2)"]
-interpolatable = ["munkres", "pycairo", "scipy"]
-lxml = ["lxml (>=4.0)"]
-pathops = ["skia-pathops (>=0.5.0)"]
-plot = ["matplotlib"]
-repacker = ["uharfbuzz (>=0.23.0)"]
-symfont = ["sympy"]
-type1 = ["xattr"]
-ufo = ["fs (>=2.2.0,<3)"]
-unicode = ["unicodedata2 (>=15.1.0)"]
-woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
-
-[[package]]
-name = "frozendict"
-version = "2.4.6"
-description = "A simple immutable dictionary"
-optional = false
-python-versions = ">=3.6"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "frozendict-2.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a05c0a50cab96b4bb0ea25aa752efbfceed5ccb24c007612bc63e51299336f"},
- {file = "frozendict-2.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5b94d5b07c00986f9e37a38dd83c13f5fe3bf3f1ccc8e88edea8fe15d6cd88c"},
- {file = "frozendict-2.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c789fd70879ccb6289a603cdebdc4953e7e5dea047d30c1b180529b28257b5"},
- {file = "frozendict-2.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da6a10164c8a50b34b9ab508a9420df38f4edf286b9ca7b7df8a91767baecb34"},
- {file = "frozendict-2.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9a8a43036754a941601635ea9c788ebd7a7efbed2becba01b54a887b41b175b9"},
- {file = "frozendict-2.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9905dcf7aa659e6a11b8051114c9fa76dfde3a6e50e6dc129d5aece75b449a2"},
- {file = "frozendict-2.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:323f1b674a2cc18f86ab81698e22aba8145d7a755e0ac2cccf142ee2db58620d"},
- {file = "frozendict-2.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:eabd21d8e5db0c58b60d26b4bb9839cac13132e88277e1376970172a85ee04b3"},
- {file = "frozendict-2.4.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:eddabeb769fab1e122d3a6872982c78179b5bcc909fdc769f3cf1964f55a6d20"},
- {file = "frozendict-2.4.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:377a65be0a700188fc21e669c07de60f4f6d35fae8071c292b7df04776a1c27b"},
- {file = "frozendict-2.4.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce1e9217b85eec6ba9560d520d5089c82dbb15f977906eb345d81459723dd7e3"},
- {file = "frozendict-2.4.6-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:7291abacf51798d5ffe632771a69c14fb423ab98d63c4ccd1aa382619afe2f89"},
- {file = "frozendict-2.4.6-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:e72fb86e48811957d66ffb3e95580af7b1af1e6fbd760ad63d7bd79b2c9a07f8"},
- {file = "frozendict-2.4.6-cp36-cp36m-win_amd64.whl", hash = "sha256:622301b1c29c4f9bba633667d592a3a2b093cb408ba3ce578b8901ace3931ef3"},
- {file = "frozendict-2.4.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a4e3737cb99ed03200cd303bdcd5514c9f34b29ee48f405c1184141bd68611c9"},
- {file = "frozendict-2.4.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49ffaf09241bc1417daa19362a2241a4aa435f758fd4375c39ce9790443a39cd"},
- {file = "frozendict-2.4.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d69418479bfb834ba75b0e764f058af46ceee3d655deb6a0dd0c0c1a5e82f09"},
- {file = "frozendict-2.4.6-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c131f10c4d3906866454c4e89b87a7e0027d533cce8f4652aa5255112c4d6677"},
- {file = "frozendict-2.4.6-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:fc67cbb3c96af7a798fab53d52589752c1673027e516b702ab355510ddf6bdff"},
- {file = "frozendict-2.4.6-cp37-cp37m-win_amd64.whl", hash = "sha256:7730f8ebe791d147a1586cbf6a42629351d4597773317002181b66a2da0d509e"},
- {file = "frozendict-2.4.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:807862e14b0e9665042458fde692c4431d660c4219b9bb240817f5b918182222"},
- {file = "frozendict-2.4.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9647c74efe3d845faa666d4853cfeabbaee403b53270cabfc635b321f770e6b8"},
- {file = "frozendict-2.4.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:665fad3f0f815aa41294e561d98dbedba4b483b3968e7e8cab7d728d64b96e33"},
- {file = "frozendict-2.4.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f42e6b75254ea2afe428ad6d095b62f95a7ae6d4f8272f0bd44a25dddd20f67"},
- {file = "frozendict-2.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:02331541611f3897f260900a1815b63389654951126e6e65545e529b63c08361"},
- {file = "frozendict-2.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:18d50a2598350b89189da9150058191f55057581e40533e470db46c942373acf"},
- {file = "frozendict-2.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:1b4a3f8f6dd51bee74a50995c39b5a606b612847862203dd5483b9cd91b0d36a"},
- {file = "frozendict-2.4.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a76cee5c4be2a5d1ff063188232fffcce05dde6fd5edd6afe7b75b247526490e"},
- {file = "frozendict-2.4.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba5ef7328706db857a2bdb2c2a17b4cd37c32a19c017cff1bb7eeebc86b0f411"},
- {file = "frozendict-2.4.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:669237c571856be575eca28a69e92a3d18f8490511eff184937283dc6093bd67"},
- {file = "frozendict-2.4.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aaa11e7c472150efe65adbcd6c17ac0f586896096ab3963775e1c5c58ac0098"},
- {file = "frozendict-2.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b8f2829048f29fe115da4a60409be2130e69402e29029339663fac39c90e6e2b"},
- {file = "frozendict-2.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:94321e646cc39bebc66954a31edd1847d3a2a3483cf52ff051cd0996e7db07db"},
- {file = "frozendict-2.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:74b6b26c15dddfefddeb89813e455b00ebf78d0a3662b89506b4d55c6445a9f4"},
- {file = "frozendict-2.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:7088102345d1606450bd1801a61139bbaa2cb0d805b9b692f8d81918ea835da6"},
- {file = "frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea"},
- {file = "frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9"},
- {file = "frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757"},
- {file = "frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e"},
+ {file = "flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051"},
+ {file = "flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e"},
]
[[package]]
@@ -3101,7 +2295,7 @@ version = "1.5.0"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false
python-versions = ">=3.8"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"},
@@ -3200,15 +2394,15 @@ files = [
[[package]]
name = "fsspec"
-version = "2024.12.0"
+version = "2025.2.0"
description = "File-system specification"
optional = false
python-versions = ">=3.8"
groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2"},
- {file = "fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f"},
+ {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"},
+ {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"},
]
[package.extras]
@@ -3235,7 +2429,7 @@ sftp = ["paramiko"]
smb = ["smbprotocol"]
ssh = ["paramiko"]
test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
-test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
+test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
tqdm = ["tqdm"]
@@ -3394,25 +2588,6 @@ files = [
[package.dependencies]
beautifulsoup4 = "*"
-[[package]]
-name = "google-ai-generativelanguage"
-version = "0.6.9"
-description = "Google Ai Generativelanguage API client library"
-optional = false
-python-versions = ">=3.7"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "google_ai_generativelanguage-0.6.9-py3-none-any.whl", hash = "sha256:50360cd80015d1a8cc70952e98560f32fa06ddee2e8e9f4b4b98e431dc561e0b"},
- {file = "google_ai_generativelanguage-0.6.9.tar.gz", hash = "sha256:899f1d3a06efa9739f1cd9d2788070178db33c89d4a76f2e8f4da76f649155fa"},
-]
-
-[package.dependencies]
-google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
-google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
-proto-plus = ">=1.22.3,<2.0.0dev"
-protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
-
[[package]]
name = "google-api-core"
version = "2.18.0"
@@ -3553,15 +2728,15 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"]
[[package]]
name = "google-cloud-bigquery"
-version = "3.27.0"
+version = "3.29.0"
description = "Google BigQuery API client library"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "google_cloud_bigquery-3.27.0-py2.py3-none-any.whl", hash = "sha256:b53b0431e5ba362976a4cd8acce72194b4116cdf8115030c7b339b884603fcc3"},
- {file = "google_cloud_bigquery-3.27.0.tar.gz", hash = "sha256:379c524054d7b090fa56d0c22662cc6e6458a6229b6754c0e7177e3a73421d2c"},
+ {file = "google_cloud_bigquery-3.29.0-py2.py3-none-any.whl", hash = "sha256:5453a4eabe50118254eda9778f3d7dad413490de5f7046b5e66c98f5a1580308"},
+ {file = "google_cloud_bigquery-3.29.0.tar.gz", hash = "sha256:fafc2b455ffce3bcc6ce0e884184ef50b6a11350a83b91e327fadda4d5566e72"},
]
[package.dependencies]
@@ -3574,10 +2749,10 @@ python-dateutil = ">=2.7.3,<3.0dev"
requests = ">=2.21.0,<3.0.0dev"
[package.extras]
-all = ["Shapely (>=1.8.4,<3.0.0dev)", "bigquery-magics (>=0.1.0)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "importlib-metadata (>=1.0.0)", "ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "proto-plus (>=1.22.3,<2.0.0dev)", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"]
+all = ["google-cloud-bigquery[bigquery-v2,bqstorage,geopandas,ipython,ipywidgets,opentelemetry,pandas,tqdm]"]
bigquery-v2 = ["proto-plus (>=1.22.3,<2.0.0dev)", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev)"]
bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"]
-geopandas = ["Shapely (>=1.8.4,<3.0.0dev)", "geopandas (>=0.9.0,<1.0dev)"]
+geopandas = ["Shapely (>=1.8.4,<3.0.0dev)", "geopandas (>=0.9.0,<2.0dev)"]
ipython = ["bigquery-magics (>=0.1.0)"]
ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"]
opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"]
@@ -3689,48 +2864,6 @@ files = [
[package.extras]
testing = ["pytest"]
-[[package]]
-name = "google-generativeai"
-version = "0.8.1"
-description = "Google Generative AI High level API client library and tools."
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "google_generativeai-0.8.1-py3-none-any.whl", hash = "sha256:b031877f24d51af0945207657c085896a0a886eceec7a1cb7029327b0aa6e2f6"},
-]
-
-[package.dependencies]
-google-ai-generativelanguage = "0.6.9"
-google-api-core = "*"
-google-api-python-client = "*"
-google-auth = ">=2.15.0"
-protobuf = "*"
-pydantic = "*"
-tqdm = "*"
-typing-extensions = "*"
-
-[package.extras]
-dev = ["Pillow", "absl-py", "black", "ipython", "nose2", "pandas", "pytype", "pyyaml"]
-
-[[package]]
-name = "google-pasta"
-version = "0.2.0"
-description = "pasta is an AST-based Python refactoring library"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e"},
- {file = "google_pasta-0.2.0-py2-none-any.whl", hash = "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954"},
- {file = "google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed"},
-]
-
-[package.dependencies]
-six = "*"
-
[[package]]
name = "google-resumable-media"
version = "2.7.2"
@@ -3773,15 +2906,15 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"]
[[package]]
name = "gotrue"
-version = "2.11.1"
+version = "2.11.3"
description = "Python Client Library for Supabase Auth"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "gotrue-2.11.1-py3-none-any.whl", hash = "sha256:1b2d915bdc65fd0ad608532759ce9c72fa2e910145c1e6901f2188519e7bcd2d"},
- {file = "gotrue-2.11.1.tar.gz", hash = "sha256:5594ceee60bd873e5f4fdd028b08dece3906f6013b6ed08e7786b71c0092fed0"},
+ {file = "gotrue-2.11.3-py3-none-any.whl", hash = "sha256:8ad90771ff6b8ede180cf6242c5b0246b9288ad58b57ce0387ef94166e84284b"},
+ {file = "gotrue-2.11.3.tar.gz", hash = "sha256:14b03eb856b94a96fab73c8d41970ad645a74326ee4da95e66395e6b2c208ff7"},
]
[package.dependencies]
@@ -3794,7 +2927,7 @@ version = "3.1.1"
description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev", "tools", "vdb"]
+groups = ["main", "dev", "vdb"]
files = [
{file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"},
{file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"},
@@ -3870,7 +3003,7 @@ files = [
{file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"},
{file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"},
]
-markers = {main = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_python_implementation == \"CPython\")", dev = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")", tools = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")", vdb = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
+markers = {main = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_python_implementation == \"CPython\")", dev = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")", vdb = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
[package.extras]
docs = ["Sphinx", "furo"]
@@ -4074,7 +3207,7 @@ version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
python-versions = ">=3.7"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
@@ -4083,20 +3216,20 @@ files = [
[[package]]
name = "h2"
-version = "4.1.0"
-description = "HTTP/2 State-Machine based protocol implementation"
+version = "4.2.0"
+description = "Pure-Python HTTP/2 protocol implementation"
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.9"
groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"},
- {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"},
+ {file = "h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0"},
+ {file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"},
]
[package.dependencies]
-hpack = ">=4.0,<5"
-hyperframe = ">=6.0,<7"
+hpack = ">=4.1,<5"
+hyperframe = ">=6.1,<7"
[[package]]
name = "hiredis"
@@ -4220,15 +3353,15 @@ files = [
[[package]]
name = "hpack"
-version = "4.0.0"
-description = "Pure-Python HPACK header compression"
+version = "4.1.0"
+description = "Pure-Python HPACK header encoding"
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.9"
groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
- {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"},
+ {file = "hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"},
+ {file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"},
]
[[package]]
@@ -4237,7 +3370,7 @@ version = "1.1"
description = "HTML parser based on the WHATWG HTML specification"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"},
@@ -4379,35 +3512,22 @@ 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"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-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"
+version = "0.28.1"
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
optional = false
-python-versions = ">=3.7.0"
+python-versions = ">=3.8.0"
groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "huggingface_hub-0.16.4-py3-none-any.whl", hash = "sha256:0d3df29932f334fead024afc7cb4cc5149d955238b8b5e42dcf9740d6995a349"},
- {file = "huggingface_hub-0.16.4.tar.gz", hash = "sha256:608c7d4f3d368b326d1747f91523dbd1f692871e8e2e7a4750314a2dd8b63e14"},
+ {file = "huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7"},
+ {file = "huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae"},
]
[package.dependencies]
filelock = "*"
-fsspec = "*"
+fsspec = ">=2023.5.0"
packaging = ">=20.9"
pyyaml = ">=5.1"
requests = "*"
@@ -4415,16 +3535,18 @@ tqdm = ">=4.42.1"
typing-extensions = ">=3.7.4.3"
[package.extras]
-all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "black (>=23.1,<24.0)", "gradio", "jedi", "mypy (==0.982)", "numpy", "pydantic", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-vcr", "pytest-xdist", "ruff (>=0.0.241)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "urllib3 (<2.0)"]
+all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
cli = ["InquirerPy (==0.3.4)"]
-dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "black (>=23.1,<24.0)", "gradio", "jedi", "mypy (==0.982)", "numpy", "pydantic", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-vcr", "pytest-xdist", "ruff (>=0.0.241)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "urllib3 (<2.0)"]
+dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
-inference = ["aiohttp", "pydantic"]
-quality = ["black (>=23.1,<24.0)", "mypy (==0.982)", "ruff (>=0.0.241)"]
+hf-transfer = ["hf-transfer (>=0.1.4)"]
+inference = ["aiohttp"]
+quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.9.0)"]
tensorflow = ["graphviz", "pydot", "tensorflow"]
-testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "numpy", "pydantic", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
-torch = ["torch"]
-typing = ["pydantic", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3"]
+tensorflow-testing = ["keras (<3.0)", "tensorflow"]
+testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
+torch = ["safetensors[torch]", "torch"]
+typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
[[package]]
name = "humanfriendly"
@@ -4444,15 +3566,15 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve
[[package]]
name = "hyperframe"
-version = "6.0.1"
-description = "HTTP/2 framing layer for Python"
+version = "6.1.0"
+description = "Pure-Python HTTP/2 framing"
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.9"
groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
- {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
+ {file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"},
+ {file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"},
]
[[package]]
@@ -4473,24 +3595,28 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]]
name = "importlib-metadata"
-version = "6.11.0"
+version = "8.5.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "importlib_metadata-6.11.0-py3-none-any.whl", hash = "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b"},
- {file = "importlib_metadata-6.11.0.tar.gz", hash = "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443"},
+ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"},
+ {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"},
]
[package.dependencies]
-zipp = ">=0.5"
+zipp = ">=3.20"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["pytest-mypy"]
[[package]]
name = "importlib-resources"
@@ -4532,7 +3658,7 @@ version = "0.7.2"
description = "An ISO 8601 date/time/duration parser and formatter"
optional = false
python-versions = ">=3.7"
-groups = ["main", "storage"]
+groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15"},
@@ -4545,7 +3671,7 @@ version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
@@ -4564,25 +3690,13 @@ files = [
{file = "jieba-0.42.1.tar.gz", hash = "sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2"},
]
-[[package]]
-name = "jieba3k"
-version = "0.35.1"
-description = "Chinese Words Segementation Utilities"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "jieba3k-0.35.1.zip", hash = "sha256:980a4f2636b778d312518066be90c7697d410dd5a472385f5afced71a2db1c10"},
-]
-
[[package]]
name = "jinja2"
version = "3.1.5"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
@@ -4708,38 +3822,6 @@ files = [
{file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"},
]
-[[package]]
-name = "jsonlines"
-version = "4.0.0"
-description = "Library with helpers for the jsonlines file format"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "jsonlines-4.0.0-py3-none-any.whl", hash = "sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55"},
- {file = "jsonlines-4.0.0.tar.gz", hash = "sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74"},
-]
-
-[package.dependencies]
-attrs = ">=19.2.0"
-
-[[package]]
-name = "jsonpath-ng"
-version = "1.6.1"
-description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming."
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "jsonpath-ng-1.6.1.tar.gz", hash = "sha256:086c37ba4917304850bd837aeab806670224d3f038fe2833ff593a672ef0a5fa"},
- {file = "jsonpath_ng-1.6.1-py3-none-any.whl", hash = "sha256:8f22cd8273d7772eea9aaa84d922e0841aa36fdb8a2c6b7f6c3791a16a9bc0be"},
-]
-
-[package.dependencies]
-ply = "*"
-
[[package]]
name = "jsonpath-python"
version = "1.0.6"
@@ -4759,7 +3841,7 @@ version = "4.23.0"
description = "An implementation of JSON Schema validation for Python"
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"},
@@ -4782,7 +3864,7 @@ version = "2024.10.1"
description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry"
optional = false
python-versions = ">=3.9"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"},
@@ -4798,7 +3880,7 @@ version = "0.2.1"
description = "Static image export for web-based visualization libraries with zero dependencies"
optional = false
python-versions = "*"
-groups = ["indirect", "tools"]
+groups = ["indirect"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "kaleido-0.2.1-py2.py3-none-macosx_10_11_x86_64.whl", hash = "sha256:ca6f73e7ff00aaebf2843f73f1d3bacde1930ef5041093fe76b83a15785049a7"},
@@ -4809,97 +3891,6 @@ files = [
{file = "kaleido-0.2.1-py2.py3-none-win_amd64.whl", hash = "sha256:4670985f28913c2d063c5734d125ecc28e40810141bdb0a46f15b76c1d45f23c"},
]
-[[package]]
-name = "kiwisolver"
-version = "1.4.8"
-description = "A fast implementation of the Cassowary constraint solver"
-optional = false
-python-versions = ">=3.10"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"},
- {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"},
- {file = "kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d"},
- {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d"},
- {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c"},
- {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3"},
- {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed"},
- {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f"},
- {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff"},
- {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d"},
- {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c"},
- {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605"},
- {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e"},
- {file = "kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751"},
- {file = "kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271"},
- {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"},
- {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"},
- {file = "kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7"},
- {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03"},
- {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954"},
- {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79"},
- {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6"},
- {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0"},
- {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab"},
- {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc"},
- {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25"},
- {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc"},
- {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67"},
- {file = "kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34"},
- {file = "kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2"},
- {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502"},
- {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31"},
- {file = "kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb"},
- {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f"},
- {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc"},
- {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a"},
- {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a"},
- {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a"},
- {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3"},
- {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b"},
- {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4"},
- {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d"},
- {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8"},
- {file = "kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50"},
- {file = "kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476"},
- {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09"},
- {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1"},
- {file = "kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c"},
- {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b"},
- {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47"},
- {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16"},
- {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc"},
- {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246"},
- {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794"},
- {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b"},
- {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3"},
- {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957"},
- {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb"},
- {file = "kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2"},
- {file = "kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90"},
- {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b"},
- {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b"},
- {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"},
-]
-
[[package]]
name = "kombu"
version = "5.4.2"
@@ -4937,15 +3928,15 @@ zookeeper = ["kazoo (>=2.8.0)"]
[[package]]
name = "kubernetes"
-version = "31.0.0"
+version = "32.0.0"
description = "Kubernetes python client"
optional = false
python-versions = ">=3.6"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "kubernetes-31.0.0-py2.py3-none-any.whl", hash = "sha256:bf141e2d380c8520eada8b351f4e319ffee9636328c137aa432bc486ca1200e1"},
- {file = "kubernetes-31.0.0.tar.gz", hash = "sha256:28945de906c8c259c1ebe62703b56a03b714049372196f854105afe4e6d014c0"},
+ {file = "kubernetes-32.0.0-py2.py3-none-any.whl", hash = "sha256:60fd8c29e8e43d9c553ca4811895a687426717deba9c0a66fb2dcc3f5ef96692"},
+ {file = "kubernetes-32.0.0.tar.gz", hash = "sha256:319fa840345a482001ac5d6062222daeb66ec4d1bcb3087402aed685adf0aecb"},
]
[package.dependencies]
@@ -5137,291 +4128,266 @@ rapidfuzz = ">=3.9.0,<4.0.0"
[[package]]
name = "litellm"
-version = "1.51.3"
+version = "1.61.6"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "litellm-1.51.3-py3-none-any.whl", hash = "sha256:440d3c7cc5ab8eeb12cee8f4d806bff05b7db834ebc11117d7fa070a1142ced5"},
- {file = "litellm-1.51.3.tar.gz", hash = "sha256:31eff9fcbf7b058bac0fd7432c4ea0487e8555f12446a1f30e5862e33716f44d"},
+ {file = "litellm-1.61.6-py3-none-any.whl", hash = "sha256:eef4c4a84a2c93de4c6d5a05a785f9b0cc61f63bafb3b3dc83d977db649e1b13"},
+ {file = "litellm-1.61.6.tar.gz", hash = "sha256:2c613823f86ce2aa7956e2458857ab6aa62258dc7da9816bfdac90735be270be"},
]
[package.dependencies]
aiohttp = "*"
click = "*"
+httpx = ">=0.23.0"
importlib-metadata = ">=6.8.0"
jinja2 = ">=3.1.2,<4.0.0"
jsonschema = ">=4.22.0,<5.0.0"
-openai = ">=1.52.0"
+openai = ">=1.61.0"
pydantic = ">=2.0.0,<3.0.0"
python-dotenv = ">=0.2.0"
-requests = ">=2.31.0,<3.0.0"
tiktoken = ">=0.7.0"
tokenizers = "*"
[package.extras]
extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"]
-proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"]
+proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=43.0.1,<44.0.0)", "fastapi (>=0.115.5,<0.116.0)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.29.0,<0.30.0)", "uvloop (>=0.21.0,<0.22.0)"]
[[package]]
name = "llvmlite"
-version = "0.43.0"
+version = "0.44.0"
description = "lightweight wrapper around basic LLVM functionality"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"},
- {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"},
- {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d434ec7e2ce3cc8f452d1cd9a28591745de022f931d67be688a737320dfcead"},
- {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6912a87782acdff6eb8bf01675ed01d60ca1f2551f8176a300a886f09e836a6a"},
- {file = "llvmlite-0.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:14f0e4bf2fd2d9a75a3534111e8ebeb08eda2f33e9bdd6dfa13282afacdde0ed"},
- {file = "llvmlite-0.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8d0618cb9bfe40ac38a9633f2493d4d4e9fcc2f438d39a4e854f39cc0f5f98"},
- {file = "llvmlite-0.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0a9a1a39d4bf3517f2af9d23d479b4175ead205c592ceeb8b89af48a327ea57"},
- {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1da416ab53e4f7f3bc8d4eeba36d801cc1894b9fbfbf2022b29b6bad34a7df2"},
- {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977525a1e5f4059316b183fb4fd34fa858c9eade31f165427a3977c95e3ee749"},
- {file = "llvmlite-0.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5bd550001d26450bd90777736c69d68c487d17bf371438f975229b2b8241a91"},
- {file = "llvmlite-0.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f99b600aa7f65235a5a05d0b9a9f31150c390f31261f2a0ba678e26823ec38f7"},
- {file = "llvmlite-0.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:35d80d61d0cda2d767f72de99450766250560399edc309da16937b93d3b676e7"},
- {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eccce86bba940bae0d8d48ed925f21dbb813519169246e2ab292b5092aba121f"},
- {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6509e1507ca0760787a199d19439cc887bfd82226f5af746d6977bd9f66844"},
- {file = "llvmlite-0.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a2872ee80dcf6b5dbdc838763d26554c2a18aa833d31a2635bff16aafefb9c9"},
- {file = "llvmlite-0.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cd2a7376f7b3367019b664c21f0c61766219faa3b03731113ead75107f3b66c"},
- {file = "llvmlite-0.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18e9953c748b105668487b7c81a3e97b046d8abf95c4ddc0cd3c94f4e4651ae8"},
- {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74937acd22dc11b33946b67dca7680e6d103d6e90eeaaaf932603bec6fe7b03a"},
- {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9efc739cc6ed760f795806f67889923f7274276f0eb45092a1473e40d9b867"},
- {file = "llvmlite-0.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:47e147cdda9037f94b399bf03bfd8a6b6b1f2f90be94a454e3386f006455a9b4"},
- {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"},
-]
-
-[[package]]
-name = "loguru"
-version = "0.7.3"
-description = "Python logging made (stupidly) simple"
-optional = false
-python-versions = "<4.0,>=3.5"
+ {file = "llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614"},
+ {file = "llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791"},
+ {file = "llvmlite-0.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7202b678cdf904823c764ee0fe2dfe38a76981f4c1e51715b4cb5abb6cf1d9e8"},
+ {file = "llvmlite-0.44.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40526fb5e313d7b96bda4cbb2c85cd5374e04d80732dd36a282d72a560bb6408"},
+ {file = "llvmlite-0.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:41e3839150db4330e1b2716c0be3b5c4672525b4c9005e17c7597f835f351ce2"},
+ {file = "llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3"},
+ {file = "llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427"},
+ {file = "llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1"},
+ {file = "llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610"},
+ {file = "llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955"},
+ {file = "llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad"},
+ {file = "llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db"},
+ {file = "llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9"},
+ {file = "llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d"},
+ {file = "llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1"},
+ {file = "llvmlite-0.44.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:319bddd44e5f71ae2689859b7203080716448a3cd1128fb144fe5c055219d516"},
+ {file = "llvmlite-0.44.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c58867118bad04a0bb22a2e0068c693719658105e40009ffe95c7000fcde88e"},
+ {file = "llvmlite-0.44.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46224058b13c96af1365290bdfebe9a6264ae62fb79b2b55693deed11657a8bf"},
+ {file = "llvmlite-0.44.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0097052c32bf721a4efc03bd109d335dfa57d9bffb3d4c24cc680711b8b4fc"},
+ {file = "llvmlite-0.44.0-cp313-cp313-win_amd64.whl", hash = "sha256:2fb7c4f2fb86cbae6dca3db9ab203eeea0e22d73b99bc2341cdf9de93612e930"},
+ {file = "llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4"},
+]
+
+[[package]]
+name = "lxml"
+version = "5.3.1"
+description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
+optional = false
+python-versions = ">=3.6"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"},
- {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"},
-]
-
-[package.dependencies]
-colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
-win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
-
-[package.extras]
-dev = ["Sphinx (==8.1.3)", "build (==1.2.2)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.5.0)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.13.0)", "mypy (==v1.4.1)", "myst-parser (==4.0.0)", "pre-commit (==4.0.1)", "pytest (==6.1.2)", "pytest (==8.3.2)", "pytest-cov (==2.12.1)", "pytest-cov (==5.0.0)", "pytest-cov (==6.0.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.1.0)", "sphinx-rtd-theme (==3.0.2)", "tox (==3.27.1)", "tox (==4.23.2)", "twine (==6.0.1)"]
-
-[[package]]
-name = "lxml"
-version = "5.3.0"
-description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
-optional = false
-python-versions = ">=3.6"
-groups = ["main", "tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"},
- {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"},
- {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"},
- {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"},
- {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"},
- {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"},
- {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"},
- {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"},
- {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"},
- {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"},
- {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"},
- {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"},
- {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"},
- {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"},
- {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"},
- {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"},
- {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"},
- {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"},
- {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"},
- {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"},
- {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"},
- {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"},
- {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"},
- {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"},
- {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"},
- {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"},
- {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"},
- {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"},
- {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"},
- {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"},
- {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"},
- {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"},
- {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"},
- {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"},
- {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"},
- {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"},
- {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"},
- {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"},
- {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"},
- {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"},
- {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"},
- {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"},
- {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"},
- {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"},
- {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"},
- {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"},
- {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"},
- {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"},
- {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"},
- {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"},
- {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"},
- {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"},
- {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"},
- {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"},
- {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"},
- {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"},
- {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"},
- {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"},
- {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"},
- {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"},
- {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"},
- {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"},
- {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"},
- {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"},
- {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"},
- {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"},
- {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"},
- {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"},
- {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"},
- {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"},
- {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"},
- {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"},
- {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"},
- {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"},
- {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"},
- {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"},
- {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"},
- {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"},
+ {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b"},
+ {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79"},
+ {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2"},
+ {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51"},
+ {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406"},
+ {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5"},
+ {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0"},
+ {file = "lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23"},
+ {file = "lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c"},
+ {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f"},
+ {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629"},
+ {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33"},
+ {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295"},
+ {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c"},
+ {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c"},
+ {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff"},
+ {file = "lxml-5.3.1-cp311-cp311-win32.whl", hash = "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2"},
+ {file = "lxml-5.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6"},
+ {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c"},
+ {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468"},
+ {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367"},
+ {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd"},
+ {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c"},
+ {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f"},
+ {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645"},
+ {file = "lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5"},
+ {file = "lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf"},
+ {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e"},
+ {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8"},
+ {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9"},
+ {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c"},
+ {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b"},
+ {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5"},
+ {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252"},
+ {file = "lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78"},
+ {file = "lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332"},
+ {file = "lxml-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81"},
+ {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a"},
+ {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439"},
+ {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac"},
+ {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d"},
+ {file = "lxml-5.3.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a"},
+ {file = "lxml-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576"},
+ {file = "lxml-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9"},
+ {file = "lxml-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32"},
+ {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61"},
+ {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19"},
+ {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307"},
+ {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70"},
+ {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9"},
+ {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53"},
+ {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce"},
+ {file = "lxml-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407"},
+ {file = "lxml-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5"},
+ {file = "lxml-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e"},
+ {file = "lxml-5.3.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098"},
+ {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5"},
+ {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7"},
+ {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf"},
+ {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2"},
+ {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb"},
+ {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73"},
+ {file = "lxml-5.3.1-cp38-cp38-win32.whl", hash = "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc"},
+ {file = "lxml-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7"},
+ {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f"},
+ {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b"},
+ {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be"},
+ {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e"},
+ {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675"},
+ {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2"},
+ {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af"},
+ {file = "lxml-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0"},
+ {file = "lxml-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877"},
+ {file = "lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98"},
+ {file = "lxml-5.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac"},
+ {file = "lxml-5.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe"},
+ {file = "lxml-5.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2"},
+ {file = "lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8"},
]
[package.extras]
cssselect = ["cssselect (>=0.7)"]
-html-clean = ["lxml-html-clean"]
+html-clean = ["lxml_html_clean"]
html5 = ["html5lib"]
htmlsoup = ["BeautifulSoup4"]
-source = ["Cython (>=3.0.11)"]
+source = ["Cython (>=3.0.11,<3.1.0)"]
[[package]]
name = "lz4"
-version = "4.3.3"
+version = "4.4.3"
description = "LZ4 Bindings for Python"
optional = false
-python-versions = ">=3.8"
-groups = ["tools", "vdb"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "lz4-4.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b891880c187e96339474af2a3b2bfb11a8e4732ff5034be919aa9029484cd201"},
- {file = "lz4-4.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:222a7e35137d7539c9c33bb53fcbb26510c5748779364014235afc62b0ec797f"},
- {file = "lz4-4.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f76176492ff082657ada0d0f10c794b6da5800249ef1692b35cf49b1e93e8ef7"},
- {file = "lz4-4.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d18718f9d78182c6b60f568c9a9cec8a7204d7cb6fad4e511a2ef279e4cb05"},
- {file = "lz4-4.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdc60e21ec70266947a48839b437d46025076eb4b12c76bd47f8e5eb8a75dcc"},
- {file = "lz4-4.3.3-cp310-cp310-win32.whl", hash = "sha256:c81703b12475da73a5d66618856d04b1307e43428a7e59d98cfe5a5d608a74c6"},
- {file = "lz4-4.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:43cf03059c0f941b772c8aeb42a0813d68d7081c009542301637e5782f8a33e2"},
- {file = "lz4-4.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30e8c20b8857adef7be045c65f47ab1e2c4fabba86a9fa9a997d7674a31ea6b6"},
- {file = "lz4-4.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7b1839f795315e480fb87d9bc60b186a98e3e5d17203c6e757611ef7dcef61"},
- {file = "lz4-4.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edfd858985c23523f4e5a7526ca6ee65ff930207a7ec8a8f57a01eae506aaee7"},
- {file = "lz4-4.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e9c410b11a31dbdc94c05ac3c480cb4b222460faf9231f12538d0074e56c563"},
- {file = "lz4-4.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2507ee9c99dbddd191c86f0e0c8b724c76d26b0602db9ea23232304382e1f21"},
- {file = "lz4-4.3.3-cp311-cp311-win32.whl", hash = "sha256:f180904f33bdd1e92967923a43c22899e303906d19b2cf8bb547db6653ea6e7d"},
- {file = "lz4-4.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:b14d948e6dce389f9a7afc666d60dd1e35fa2138a8ec5306d30cd2e30d36b40c"},
- {file = "lz4-4.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e36cd7b9d4d920d3bfc2369840da506fa68258f7bb176b8743189793c055e43d"},
- {file = "lz4-4.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31ea4be9d0059c00b2572d700bf2c1bc82f241f2c3282034a759c9a4d6ca4dc2"},
- {file = "lz4-4.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33c9a6fd20767ccaf70649982f8f3eeb0884035c150c0b818ea660152cf3c809"},
- {file = "lz4-4.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca8fccc15e3add173da91be8f34121578dc777711ffd98d399be35487c934bf"},
- {file = "lz4-4.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d84b479ddf39fe3ea05387f10b779155fc0990125f4fb35d636114e1c63a2e"},
- {file = "lz4-4.3.3-cp312-cp312-win32.whl", hash = "sha256:337cb94488a1b060ef1685187d6ad4ba8bc61d26d631d7ba909ee984ea736be1"},
- {file = "lz4-4.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:5d35533bf2cee56f38ced91f766cd0038b6abf46f438a80d50c52750088be93f"},
- {file = "lz4-4.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:363ab65bf31338eb364062a15f302fc0fab0a49426051429866d71c793c23394"},
- {file = "lz4-4.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a136e44a16fc98b1abc404fbabf7f1fada2bdab6a7e970974fb81cf55b636d0"},
- {file = "lz4-4.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abc197e4aca8b63f5ae200af03eb95fb4b5055a8f990079b5bdf042f568469dd"},
- {file = "lz4-4.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56f4fe9c6327adb97406f27a66420b22ce02d71a5c365c48d6b656b4aaeb7775"},
- {file = "lz4-4.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0e822cd7644995d9ba248cb4b67859701748a93e2ab7fc9bc18c599a52e4604"},
- {file = "lz4-4.3.3-cp38-cp38-win32.whl", hash = "sha256:24b3206de56b7a537eda3a8123c644a2b7bf111f0af53bc14bed90ce5562d1aa"},
- {file = "lz4-4.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:b47839b53956e2737229d70714f1d75f33e8ac26e52c267f0197b3189ca6de24"},
- {file = "lz4-4.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6756212507405f270b66b3ff7f564618de0606395c0fe10a7ae2ffcbbe0b1fba"},
- {file = "lz4-4.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee9ff50557a942d187ec85462bb0960207e7ec5b19b3b48949263993771c6205"},
- {file = "lz4-4.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b901c7784caac9a1ded4555258207d9e9697e746cc8532129f150ffe1f6ba0d"},
- {file = "lz4-4.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d9ec061b9eca86e4dcc003d93334b95d53909afd5a32c6e4f222157b50c071"},
- {file = "lz4-4.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4c7bf687303ca47d69f9f0133274958fd672efaa33fb5bcde467862d6c621f0"},
- {file = "lz4-4.3.3-cp39-cp39-win32.whl", hash = "sha256:054b4631a355606e99a42396f5db4d22046a3397ffc3269a348ec41eaebd69d2"},
- {file = "lz4-4.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:eac9af361e0d98335a02ff12fb56caeb7ea1196cf1a49dbf6f17828a131da807"},
- {file = "lz4-4.3.3.tar.gz", hash = "sha256:01fe674ef2889dbb9899d8a67361e0c4a2c833af5aeb37dd505727cf5d2a131e"},
-]
-
-[package.extras]
-docs = ["sphinx (>=1.6.0)", "sphinx-bootstrap-theme"]
+python-versions = ">=3.9"
+groups = ["vdb"]
+markers = "python_version == \"3.11\" or python_version >= \"3.12\""
+files = [
+ {file = "lz4-4.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ebf23ffd36b32b980f720a81990fcfdeadacafe7498fbeff7a8e058259d4e58"},
+ {file = "lz4-4.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8fe3caea61427057a9e3697c69b2403510fdccfca4483520d02b98ffae74531e"},
+ {file = "lz4-4.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c7fbe46f6e2e9dfb5377ee690fb8987e8e8363f435886ab91012b88f08a26"},
+ {file = "lz4-4.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a46f48740584eab3194fbee91c61f7fa396dbb1c5e7aa76ca08165d4e63fb40f"},
+ {file = "lz4-4.4.3-cp310-cp310-win32.whl", hash = "sha256:434a1d1547a0547164866f1ccc31bbda235ac5b9087f24a84956756b52371f40"},
+ {file = "lz4-4.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:0aea6f283abd6acb1883b70d7a117b913e20c770845559f9421394bc9c522b24"},
+ {file = "lz4-4.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1b98f0a4137d01b84c680813eef6198e1e00f1f28bc20ce7b5c436459a0d146"},
+ {file = "lz4-4.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20e385cb8bd8321593788f11101d8c89a823a56191978e427e3c5141e129f14b"},
+ {file = "lz4-4.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9e32989df06c57f10aa09ad9b30e8a25baf1aefe850e13b0ea5de600477d6a"},
+ {file = "lz4-4.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3d2d5df5476b065aae9d1ad551fdc7b17c151b84e8edd9212108946b2337c66"},
+ {file = "lz4-4.4.3-cp311-cp311-win32.whl", hash = "sha256:e365850166729fa82be618f476966161d5c47ea081eafc4febfc542bc85bac5d"},
+ {file = "lz4-4.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:7f5c05bd4b0909b682608c453acc31f1a9170d55f56d27cd701213e0683fc66a"},
+ {file = "lz4-4.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:43461e439ef71d49bb0ee3a1719494cd952a58d205496698e0cde866f22006bc"},
+ {file = "lz4-4.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ae50a175fb7b900f7aa42575f4fe99c32ca0ff57e5a8c1fd25e1243e67409db"},
+ {file = "lz4-4.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38df5929ffefa9dda120ba1790a2e94fda81916c5aaa1ee652f4b1e515ebb9ed"},
+ {file = "lz4-4.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b45914f25d916324531d0259072b402c5f99b67c6e9ac8cbc3d49935aeb1d97"},
+ {file = "lz4-4.4.3-cp312-cp312-win32.whl", hash = "sha256:848c5b040d2cfe35097b1d65d1095d83a3f86374ce879e189533f61405d8763b"},
+ {file = "lz4-4.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:b1d179bdefd9ddb8d11d7de7825e73fb957511b722a8cb484e417885c210e68c"},
+ {file = "lz4-4.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:174b7ce5456671c73b81bb115defac8a584363d8b38a48ed3ad976e08eea27cd"},
+ {file = "lz4-4.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab26b4af13308b8296688b03d74c3b0c8e8ed1f6b2d1454ef97bdb589db409db"},
+ {file = "lz4-4.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61e08d84e3bf8ca9f43dc6b33f8cd7ba19f49864e2c91eb2160f83b6f9a268fa"},
+ {file = "lz4-4.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71ebdaadf546d6d393b9a21796172723724b737e84f68f36caf367d1c87a86a1"},
+ {file = "lz4-4.4.3-cp313-cp313-win32.whl", hash = "sha256:1f25e1b571a8be2c3d60d46679ef2471ae565f7ba9ba8382596695413523b188"},
+ {file = "lz4-4.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:da091dd8c96dbda124d766231f38619afd5c544051fb4424d2566c905957d342"},
+ {file = "lz4-4.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:699d26ac579eb42c71d131f9fb7b6e1c495a14e257264206a3c3bfcc146ed9bb"},
+ {file = "lz4-4.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c4be1e5d9c8ad61345730c41c9ef21bdbb022cced4df70431110888d3ad5c0fb"},
+ {file = "lz4-4.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86400c8b60c7707665e63934a82ae6792e7102c17a72e9b361a7f40d3c6049"},
+ {file = "lz4-4.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe6080299a25fd7cbb1957c921cca6a884acbfcd44cc23de48079389d322e326"},
+ {file = "lz4-4.4.3-cp39-cp39-win32.whl", hash = "sha256:447993c4dda0b6b0e1bd862752c855df8745f2910bea5015344f83ff3e99f305"},
+ {file = "lz4-4.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:3f21e503c18157512d2e34ae4c301e44a826c7b87e1d8998981367e3c9fe0932"},
+ {file = "lz4-4.4.3.tar.gz", hash = "sha256:91ed5b71f9179bf3dbfe85d92b52d4b53de2e559aa4daa3b7de18e0dd24ad77d"},
+]
+
+[package.extras]
+docs = ["sphinx (>=1.6.0)", "sphinx_bootstrap_theme"]
flake8 = ["flake8"]
tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"]
@@ -5446,15 +4412,15 @@ urllib3 = ">=1.23"
[[package]]
name = "mako"
-version = "1.3.8"
+version = "1.3.9"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627"},
- {file = "mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8"},
+ {file = "Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1"},
+ {file = "mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac"},
]
[package.dependencies]
@@ -5514,7 +4480,7 @@ version = "3.0.2"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
@@ -5582,15 +4548,15 @@ files = [
[[package]]
name = "marshmallow"
-version = "3.24.1"
+version = "3.26.1"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "marshmallow-3.24.1-py3-none-any.whl", hash = "sha256:ddb5c9987017d37be351c184e4e867e7bf55f7331f4da730dedad6b7af662cdd"},
- {file = "marshmallow-3.24.1.tar.gz", hash = "sha256:efdcb656ac8788f0e3d1d938f8dc0f237bf1a99aff8f6dfbffa594981641cea0"},
+ {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
+ {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
]
[package.dependencies]
@@ -5598,59 +4564,9 @@ packaging = ">=17.0"
[package.extras]
dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"]
-docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)"]
+docs = ["autodocsumm (==0.2.14)", "furo (==2024.8.6)", "sphinx (==8.1.3)", "sphinx-copybutton (==0.5.2)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.9.1)"]
tests = ["pytest", "simplejson"]
-[[package]]
-name = "matplotlib"
-version = "3.8.4"
-description = "Python plotting package"
-optional = false
-python-versions = ">=3.9"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"},
- {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"},
- {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"},
- {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"},
- {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"},
- {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"},
- {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"},
- {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"},
- {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"},
- {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"},
- {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"},
- {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"},
- {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"},
- {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"},
- {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"},
- {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"},
- {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"},
- {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"},
- {file = "matplotlib-3.8.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6"},
- {file = "matplotlib-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67"},
- {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc"},
- {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9"},
- {file = "matplotlib-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54"},
- {file = "matplotlib-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0"},
- {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"},
- {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"},
- {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"},
- {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"},
-]
-
-[package.dependencies]
-contourpy = ">=1.0.1"
-cycler = ">=0.10"
-fonttools = ">=4.22.0"
-kiwisolver = ">=1.3.1"
-numpy = ">=1.21"
-packaging = ">=20.0"
-pillow = ">=8"
-pyparsing = ">=2.3.1"
-python-dateutil = ">=2.7"
-
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -5682,151 +4598,105 @@ files = [
[package.dependencies]
tqdm = "*"
-[[package]]
-name = "mistune"
-version = "3.1.0"
-description = "A sane and fast Markdown parser with useful plugins and renderers"
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1"},
- {file = "mistune-3.1.0.tar.gz", hash = "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667"},
-]
-
[[package]]
name = "mmh3"
-version = "5.0.1"
+version = "5.1.0"
description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f0a4b4bf05778ed77d820d6e7d0e9bd6beb0c01af10e1ce9233f5d2f814fcafa"},
- {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac7a391039aeab95810c2d020b69a94eb6b4b37d4e2374831e92db3a0cdf71c6"},
- {file = "mmh3-5.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3a2583b5521ca49756d8d8bceba80627a9cc295f255dcab4e3df7ccc2f09679a"},
- {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:081a8423fe53c1ac94f87165f3e4c500125d343410c1a0c5f1703e898a3ef038"},
- {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8b4d72713799755dc8954a7d36d5c20a6c8de7b233c82404d122c7c7c1707cc"},
- {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:389a6fd51efc76d3182d36ec306448559c1244f11227d2bb771bdd0e6cc91321"},
- {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39f4128edaa074bff721b1d31a72508cba4d2887ee7867f22082e1fe9d4edea0"},
- {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5d23a94d91aabba3386b3769048d5f4210fdfef80393fece2f34ba5a7b466c"},
- {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:16347d038361f8b8f24fd2b7ef378c9b68ddee9f7706e46269b6e0d322814713"},
- {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e299408565af7d61f2d20a5ffdd77cf2ed902460fe4e6726839d59ba4b72316"},
- {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42050af21ddfc5445ee5a66e73a8fc758c71790305e3ee9e4a85a8e69e810f94"},
- {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2ae9b1f5ef27ec54659920f0404b7ceb39966e28867c461bfe83a05e8d18ddb0"},
- {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50c2495a02045f3047d71d4ae9cdd7a15efc0bcbb7ff17a18346834a8e2d1d19"},
- {file = "mmh3-5.0.1-cp310-cp310-win32.whl", hash = "sha256:c028fa77cddf351ca13b4a56d43c1775652cde0764cadb39120b68f02a23ecf6"},
- {file = "mmh3-5.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c5e741e421ec14400c4aae30890515c201f518403bdef29ae1e00d375bb4bbb5"},
- {file = "mmh3-5.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:b17156d56fabc73dbf41bca677ceb6faed435cc8544f6566d72ea77d8a17e9d0"},
- {file = "mmh3-5.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a6d5a9b1b923f1643559ba1fc0bf7a5076c90cbb558878d3bf3641ce458f25d"},
- {file = "mmh3-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3349b968be555f7334bbcce839da98f50e1e80b1c615d8e2aa847ea4a964a012"},
- {file = "mmh3-5.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1bd3c94b110e55db02ab9b605029f48a2f7f677c6e58c09d44e42402d438b7e1"},
- {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ba84d48608f79adbb10bb09986b6dc33eeda5c2d1bd75d00820081b73bde9"},
- {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0217987a8b8525c8d9170f66d036dec4ab45cfbd53d47e8d76125791ceb155e"},
- {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2797063a34e78d1b61639a98b0edec1c856fa86ab80c7ec859f1796d10ba429"},
- {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bba16340adcbd47853a2fbe5afdb397549e8f2e79324ff1dced69a3f8afe7c3"},
- {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:282797957c9f60b51b9d768a602c25f579420cc9af46feb77d457a27823d270a"},
- {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e4fb670c29e63f954f9e7a2cdcd57b36a854c2538f579ef62681ccbaa1de2b69"},
- {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ee7d85438dc6aff328e19ab052086a3c29e8a9b632998a49e5c4b0034e9e8d6"},
- {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7fb5db231f3092444bc13901e6a8d299667126b00636ffbad4a7b45e1051e2f"},
- {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c100dd441703da5ec136b1d9003ed4a041d8a1136234c9acd887499796df6ad8"},
- {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71f3b765138260fd7a7a2dba0ea5727dabcd18c1f80323c9cfef97a7e86e01d0"},
- {file = "mmh3-5.0.1-cp311-cp311-win32.whl", hash = "sha256:9a76518336247fd17689ce3ae5b16883fd86a490947d46a0193d47fb913e26e3"},
- {file = "mmh3-5.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:336bc4df2e44271f1c302d289cc3d78bd52d3eed8d306c7e4bff8361a12bf148"},
- {file = "mmh3-5.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:af6522722fbbc5999aa66f7244d0986767a46f1fb05accc5200f75b72428a508"},
- {file = "mmh3-5.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2730bb263ed9c388e8860438b057a53e3cc701134a6ea140f90443c4c11aa40"},
- {file = "mmh3-5.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6246927bc293f6d56724536400b85fb85f5be26101fa77d5f97dd5e2a4c69bf2"},
- {file = "mmh3-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fbca322519a6e6e25b6abf43e940e1667cf8ea12510e07fb4919b48a0cd1c411"},
- {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae8c19903ed8a1724ad9e67e86f15d198a7a1271a4f9be83d47e38f312ed672"},
- {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09fd6cc72c07c0c07c3357714234b646d78052487c4a3bd5f7f6e08408cff60"},
- {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ff8551fee7ae3b11c5d986b6347ade0dccaadd4670ffdb2b944dee120ffcc84"},
- {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e39694c73a5a20c8bf36dfd8676ed351e5234d55751ba4f7562d85449b21ef3f"},
- {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eba6001989a92f72a89c7cf382fda831678bd780707a66b4f8ca90239fdf2123"},
- {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0771f90c9911811cc606a5c7b7b58f33501c9ee896ed68a6ac22c7d55878ecc0"},
- {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:09b31ed0c0c0920363e96641fac4efde65b1ab62b8df86293142f35a254e72b4"},
- {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5cf4a8deda0235312db12075331cb417c4ba163770edfe789bde71d08a24b692"},
- {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41f7090a95185ef20ac018581a99337f0cbc84a2135171ee3290a9c0d9519585"},
- {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b97b5b368fb7ff22194ec5854f5b12d8de9ab67a0f304728c7f16e5d12135b76"},
- {file = "mmh3-5.0.1-cp312-cp312-win32.whl", hash = "sha256:842516acf04da546f94fad52db125ee619ccbdcada179da51c326a22c4578cb9"},
- {file = "mmh3-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:d963be0dbfd9fca209c17172f6110787ebf78934af25e3694fe2ba40e55c1e2b"},
- {file = "mmh3-5.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:a5da292ceeed8ce8e32b68847261a462d30fd7b478c3f55daae841404f433c15"},
- {file = "mmh3-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:673e3f1c8d4231d6fb0271484ee34cb7146a6499fc0df80788adb56fd76842da"},
- {file = "mmh3-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f795a306bd16a52ad578b663462cc8e95500b3925d64118ae63453485d67282b"},
- {file = "mmh3-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5ed57a5e28e502a1d60436cc25c76c3a5ba57545f250f2969af231dc1221e0a5"},
- {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:632c28e7612e909dbb6cbe2fe496201ada4695b7715584005689c5dc038e59ad"},
- {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53fd6bd525a5985e391c43384672d9d6b317fcb36726447347c7fc75bfed34ec"},
- {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dceacf6b0b961a0e499836af3aa62d60633265607aef551b2a3e3c48cdaa5edd"},
- {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0738d478fdfb5d920f6aff5452c78f2c35b0eff72caa2a97dfe38e82f93da2"},
- {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e70285e7391ab88b872e5bef632bad16b9d99a6d3ca0590656a4753d55988af"},
- {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27e5fc6360aa6b828546a4318da1a7da6bf6e5474ccb053c3a6aa8ef19ff97bd"},
- {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7989530c3c1e2c17bf5a0ec2bba09fd19819078ba90beedabb1c3885f5040b0d"},
- {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cdad7bee649950da7ecd3cbbbd12fb81f1161072ecbdb5acfa0018338c5cb9cf"},
- {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e143b8f184c1bb58cecd85ab4a4fd6dc65a2d71aee74157392c3fddac2a4a331"},
- {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5eb12e886f3646dd636f16b76eb23fc0c27e8ff3c1ae73d4391e50ef60b40f6"},
- {file = "mmh3-5.0.1-cp313-cp313-win32.whl", hash = "sha256:16e6dddfa98e1c2d021268e72c78951234186deb4df6630e984ac82df63d0a5d"},
- {file = "mmh3-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d3ffb792d70b8c4a2382af3598dad6ae0c5bd9cee5b7ffcc99aa2f5fd2c1bf70"},
- {file = "mmh3-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:122fa9ec148383f9124292962bda745f192b47bfd470b2af5fe7bb3982b17896"},
- {file = "mmh3-5.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b12bad8c75e6ff5d67319794fb6a5e8c713826c818d47f850ad08b4aa06960c6"},
- {file = "mmh3-5.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e5bbb066538c1048d542246fc347bb7994bdda29a3aea61c22f9f8b57111ce69"},
- {file = "mmh3-5.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:eee6134273f64e2a106827cc8fd77e70cc7239a285006fc6ab4977d59b015af2"},
- {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d04d9aa19d48e4c7bbec9cabc2c4dccc6ff3b2402f856d5bf0de03e10f167b5b"},
- {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79f37da1eed034d06567a69a7988456345c7f29e49192831c3975b464493b16e"},
- {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:242f77666743337aa828a2bf2da71b6ba79623ee7f93edb11e009f69237c8561"},
- {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffd943fff690463945f6441a2465555b3146deaadf6a5e88f2590d14c655d71b"},
- {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565b15f8d7df43acb791ff5a360795c20bfa68bca8b352509e0fbabd06cc48cd"},
- {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc6aafb867c2030df98ac7760ff76b500359252867985f357bd387739f3d5287"},
- {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:32898170644d45aa27c974ab0d067809c066205110f5c6d09f47d9ece6978bfe"},
- {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:42865567838d2193eb64e0ef571f678bf361a254fcdef0c5c8e73243217829bd"},
- {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5ff5c1f301c4a8b6916498969c0fcc7e3dbc56b4bfce5cfe3fe31f3f4609e5ae"},
- {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:be74c2dda8a6f44a504450aa2c3507f8067a159201586fc01dd41ab80efc350f"},
- {file = "mmh3-5.0.1-cp38-cp38-win32.whl", hash = "sha256:5610a842621ff76c04b20b29cf5f809b131f241a19d4937971ba77dc99a7f330"},
- {file = "mmh3-5.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:de15739ac50776fe8aa1ef13f1be46a6ee1fbd45f6d0651084097eb2be0a5aa4"},
- {file = "mmh3-5.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:48e84cf3cc7e8c41bc07de72299a73b92d9e3cde51d97851420055b1484995f7"},
- {file = "mmh3-5.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd9dc28c2d168c49928195c2e29b96f9582a5d07bd690a28aede4cc07b0e696"},
- {file = "mmh3-5.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2771a1c56a3d4bdad990309cff5d0a8051f29c8ec752d001f97d6392194ae880"},
- {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5ff2a8322ba40951a84411550352fba1073ce1c1d1213bb7530f09aed7f8caf"},
- {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a16bd3ec90682c9e0a343e6bd4c778c09947c8c5395cdb9e5d9b82b2559efbca"},
- {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d45733a78d68b5b05ff4a823aea51fa664df1d3bf4929b152ff4fd6dea2dd69b"},
- {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:904285e83cedebc8873b0838ed54c20f7344120be26e2ca5a907ab007a18a7a0"},
- {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac4aeb1784e43df728034d0ed72e4b2648db1a69fef48fa58e810e13230ae5ff"},
- {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cb3d4f751a0b8b4c8d06ef1c085216c8fddcc8b8c8d72445976b5167a40c6d1e"},
- {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8021851935600e60c42122ed1176399d7692df338d606195cd599d228a04c1c6"},
- {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6182d5924a5efc451900f864cbb021d7e8ad5d524816ca17304a0f663bc09bb5"},
- {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:5f30b834552a4f79c92e3d266336fb87fd92ce1d36dc6813d3e151035890abbd"},
- {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cd4383f35e915e06d077df27e04ffd3be7513ec6a9de2d31f430393f67e192a7"},
- {file = "mmh3-5.0.1-cp39-cp39-win32.whl", hash = "sha256:1455fb6b42665a97db8fc66e89a861e52b567bce27ed054c47877183f86ea6e3"},
- {file = "mmh3-5.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:9e26a0f4eb9855a143f5938a53592fa14c2d3b25801c2106886ab6c173982780"},
- {file = "mmh3-5.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:0d0a35a69abdad7549c4030a714bb4ad07902edb3bbe61e1bbc403ded5d678be"},
- {file = "mmh3-5.0.1.tar.gz", hash = "sha256:7dab080061aeb31a6069a181f27c473a1f67933854e36a3464931f2716508896"},
-]
-
-[package.extras]
-benchmark = ["pymmh3 (==0.0.5)", "pyperf (==2.7.0)", "xxhash (==3.5.0)"]
-docs = ["myst-parser (==4.0.0)", "shibuya (==2024.8.30)", "sphinx (==8.0.2)", "sphinx-copybutton (==0.5.2)"]
-lint = ["black (==24.8.0)", "clang-format (==18.1.8)", "isort (==5.13.2)", "pylint (==3.2.7)"]
-plot = ["matplotlib (==3.9.2)", "pandas (==2.2.2)"]
-test = ["pytest (==8.3.3)", "pytest-sugar (==1.0.0)"]
-type = ["mypy (==1.11.2)"]
-
-[[package]]
-name = "mock"
-version = "4.0.3"
-description = "Rolling backport of unittest.mock for all Pythons"
-optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"},
- {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"},
-]
-
-[package.extras]
-build = ["blurb", "twine", "wheel"]
-docs = ["sphinx"]
-test = ["pytest (<5.4)", "pytest-cov"]
+ {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eaf4ac5c6ee18ca9232238364d7f2a213278ae5ca97897cafaa123fcc7bb8bec"},
+ {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48f9aa8ccb9ad1d577a16104834ac44ff640d8de8c0caed09a2300df7ce8460a"},
+ {file = "mmh3-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4ba8cac21e1f2d4e436ce03a82a7f87cda80378691f760e9ea55045ec480a3d"},
+ {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d69281c281cb01994f054d862a6bb02a2e7acfe64917795c58934b0872b9ece4"},
+ {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d05ed3962312fbda2a1589b97359d2467f677166952f6bd410d8c916a55febf"},
+ {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78ae6a03f4cff4aa92ddd690611168856f8c33a141bd3e5a1e0a85521dc21ea0"},
+ {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95f983535b39795d9fb7336438faae117424c6798f763d67c6624f6caf2c4c01"},
+ {file = "mmh3-5.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d46fdd80d4c7ecadd9faa6181e92ccc6fe91c50991c9af0e371fdf8b8a7a6150"},
+ {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16e976af7365ea3b5c425124b2a7f0147eed97fdbb36d99857f173c8d8e096"},
+ {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6fa97f7d1e1f74ad1565127229d510f3fd65d931fdedd707c1e15100bc9e5ebb"},
+ {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4052fa4a8561bd62648e9eb993c8f3af3bdedadf3d9687aa4770d10e3709a80c"},
+ {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3f0e8ae9f961037f812afe3cce7da57abf734285961fffbeff9a4c011b737732"},
+ {file = "mmh3-5.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99297f207db967814f1f02135bb7fe7628b9eacb046134a34e1015b26b06edce"},
+ {file = "mmh3-5.1.0-cp310-cp310-win32.whl", hash = "sha256:2e6c8dc3631a5e22007fbdb55e993b2dbce7985c14b25b572dd78403c2e79182"},
+ {file = "mmh3-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:e4e8c7ad5a4dddcfde35fd28ef96744c1ee0f9d9570108aa5f7e77cf9cfdf0bf"},
+ {file = "mmh3-5.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:45da549269883208912868a07d0364e1418d8292c4259ca11699ba1b2475bd26"},
+ {file = "mmh3-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b529dcda3f951ff363a51d5866bc6d63cf57f1e73e8961f864ae5010647079d"},
+ {file = "mmh3-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db1079b3ace965e562cdfc95847312f9273eb2ad3ebea983435c8423e06acd7"},
+ {file = "mmh3-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:22d31e3a0ff89b8eb3b826d6fc8e19532998b2aa6b9143698043a1268da413e1"},
+ {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2139bfbd354cd6cb0afed51c4b504f29bcd687a3b1460b7e89498329cc28a894"},
+ {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c8105c6a435bc2cd6ea2ef59558ab1a2976fd4a4437026f562856d08996673a"},
+ {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57730067174a7f36fcd6ce012fe359bd5510fdaa5fe067bc94ed03e65dafb769"},
+ {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde80eb196d7fdc765a318604ded74a4378f02c5b46c17aa48a27d742edaded2"},
+ {file = "mmh3-5.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9c8eddcb441abddeb419c16c56fd74b3e2df9e57f7aa2903221996718435c7a"},
+ {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:99e07e4acafbccc7a28c076a847fb060ffc1406036bc2005acb1b2af620e53c3"},
+ {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e25ba5b530e9a7d65f41a08d48f4b3fedc1e89c26486361166a5544aa4cad33"},
+ {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bb9bf7475b4d99156ce2f0cf277c061a17560c8c10199c910a680869a278ddc7"},
+ {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a1b0878dd281ea3003368ab53ff6f568e175f1b39f281df1da319e58a19c23a"},
+ {file = "mmh3-5.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:25f565093ac8b8aefe0f61f8f95c9a9d11dd69e6a9e9832ff0d293511bc36258"},
+ {file = "mmh3-5.1.0-cp311-cp311-win32.whl", hash = "sha256:1e3554d8792387eac73c99c6eaea0b3f884e7130eb67986e11c403e4f9b6d372"},
+ {file = "mmh3-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8ad777a48197882492af50bf3098085424993ce850bdda406a358b6ab74be759"},
+ {file = "mmh3-5.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f29dc4efd99bdd29fe85ed6c81915b17b2ef2cf853abf7213a48ac6fb3eaabe1"},
+ {file = "mmh3-5.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45712987367cb9235026e3cbf4334670522a97751abfd00b5bc8bfa022c3311d"},
+ {file = "mmh3-5.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b1020735eb35086ab24affbea59bb9082f7f6a0ad517cb89f0fc14f16cea4dae"},
+ {file = "mmh3-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:babf2a78ce5513d120c358722a2e3aa7762d6071cd10cede026f8b32452be322"},
+ {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4f47f58cd5cbef968c84a7c1ddc192fef0a36b48b0b8a3cb67354531aa33b00"},
+ {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2044a601c113c981f2c1e14fa33adc9b826c9017034fe193e9eb49a6882dbb06"},
+ {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94d999c9f2eb2da44d7c2826d3fbffdbbbbcde8488d353fee7c848ecc42b968"},
+ {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a015dcb24fa0c7a78f88e9419ac74f5001c1ed6a92e70fd1803f74afb26a4c83"},
+ {file = "mmh3-5.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457da019c491a2d20e2022c7d4ce723675e4c081d9efc3b4d8b9f28a5ea789bd"},
+ {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71408579a570193a4ac9c77344d68ddefa440b00468a0b566dcc2ba282a9c559"},
+ {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8b3a04bc214a6e16c81f02f855e285c6df274a2084787eeafaa45f2fbdef1b63"},
+ {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:832dae26a35514f6d3c1e267fa48e8de3c7b978afdafa0529c808ad72e13ada3"},
+ {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf658a61fc92ef8a48945ebb1076ef4ad74269e353fffcb642dfa0890b13673b"},
+ {file = "mmh3-5.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3313577453582b03383731b66447cdcdd28a68f78df28f10d275d7d19010c1df"},
+ {file = "mmh3-5.1.0-cp312-cp312-win32.whl", hash = "sha256:1d6508504c531ab86c4424b5a5ff07c1132d063863339cf92f6657ff7a580f76"},
+ {file = "mmh3-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:aa75981fcdf3f21759d94f2c81b6a6e04a49dfbcdad88b152ba49b8e20544776"},
+ {file = "mmh3-5.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4c1a76808dfea47f7407a0b07aaff9087447ef6280716fd0783409b3088bb3c"},
+ {file = "mmh3-5.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a523899ca29cfb8a5239618474a435f3d892b22004b91779fcb83504c0d5b8c"},
+ {file = "mmh3-5.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:17cef2c3a6ca2391ca7171a35ed574b5dab8398163129a3e3a4c05ab85a4ff40"},
+ {file = "mmh3-5.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52e12895b30110f3d89dae59a888683cc886ed0472dd2eca77497edef6161997"},
+ {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d6719045cda75c3f40397fc24ab67b18e0cb8f69d3429ab4c39763c4c608dd"},
+ {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d19fa07d303a91f8858982c37e6939834cb11893cb3ff20e6ee6fa2a7563826a"},
+ {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31b47a620d622fbde8ca1ca0435c5d25de0ac57ab507209245e918128e38e676"},
+ {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f810647c22c179b6821079f7aa306d51953ac893587ee09cf1afb35adf87cb"},
+ {file = "mmh3-5.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6128b610b577eed1e89ac7177ab0c33d06ade2aba93f5c89306032306b5f1c6"},
+ {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1e550a45d2ff87a1c11b42015107f1778c93f4c6f8e731bf1b8fa770321b8cc4"},
+ {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:785ae09276342f79fd8092633e2d52c0f7c44d56e8cfda8274ccc9b76612dba2"},
+ {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0f4be3703a867ef976434afd3661a33884abe73ceb4ee436cac49d3b4c2aaa7b"},
+ {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e513983830c4ff1f205ab97152a0050cf7164f1b4783d702256d39c637b9d107"},
+ {file = "mmh3-5.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9135c300535c828c0bae311b659f33a31c941572eae278568d1a953c4a57b59"},
+ {file = "mmh3-5.1.0-cp313-cp313-win32.whl", hash = "sha256:c65dbd12885a5598b70140d24de5839551af5a99b29f9804bb2484b29ef07692"},
+ {file = "mmh3-5.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:10db7765201fc65003fa998faa067417ef6283eb5f9bba8f323c48fd9c33e91f"},
+ {file = "mmh3-5.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:b22fe2e54be81f6c07dcb36b96fa250fb72effe08aa52fbb83eade6e1e2d5fd7"},
+ {file = "mmh3-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:166b67749a1d8c93b06f5e90576f1ba838a65c8e79f28ffd9dfafba7c7d0a084"},
+ {file = "mmh3-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adba83c7ba5cc8ea201ee1e235f8413a68e7f7b8a657d582cc6c6c9d73f2830e"},
+ {file = "mmh3-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a61f434736106804eb0b1612d503c4e6eb22ba31b16e6a2f987473de4226fa55"},
+ {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba9ce59816b30866093f048b3312c2204ff59806d3a02adee71ff7bd22b87554"},
+ {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd51597bef1e503363b05cb579db09269e6e6c39d419486626b255048daf545b"},
+ {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d51a1ed642d3fb37b8f4cab966811c52eb246c3e1740985f701ef5ad4cdd2145"},
+ {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:709bfe81c53bf8a3609efcbd65c72305ade60944f66138f697eefc1a86b6e356"},
+ {file = "mmh3-5.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e01a9b0092b6f82e861137c8e9bb9899375125b24012eb5219e61708be320032"},
+ {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:27e46a2c13c9a805e03c9ec7de0ca8e096794688ab2125bdce4229daf60c4a56"},
+ {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5766299c1d26f6bfd0a638e070bd17dbd98d4ccb067d64db3745bf178e700ef0"},
+ {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7785205e3e4443fdcbb73766798c7647f94c2f538b90f666688f3e757546069e"},
+ {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8e574fbd39afb433b3ab95683b1b4bf18313dc46456fc9daaddc2693c19ca565"},
+ {file = "mmh3-5.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1b6727a5a20e32cbf605743749f3862abe5f5e097cbf2afc7be5aafd32a549ae"},
+ {file = "mmh3-5.1.0-cp39-cp39-win32.whl", hash = "sha256:d6eaa711d4b9220fe5252032a44bf68e5dcfb7b21745a96efc9e769b0dd57ec2"},
+ {file = "mmh3-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:49d444913f6c02980e5241a53fe9af2338f2043d6ce5b6f5ea7d302c52c604ac"},
+ {file = "mmh3-5.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:0daaeaedd78773b70378f2413c7d6b10239a75d955d30d54f460fb25d599942d"},
+ {file = "mmh3-5.1.0.tar.gz", hash = "sha256:136e1e670500f177f49ec106a4ebf0adf20d18d96990cc36ea492c651d2b406c"},
+]
+
+[package.extras]
+benchmark = ["pymmh3 (==0.0.5)", "pyperf (==2.8.1)", "xxhash (==3.5.0)"]
+docs = ["myst-parser (==4.0.0)", "shibuya (==2024.12.21)", "sphinx (==8.1.3)", "sphinx-copybutton (==0.5.2)"]
+lint = ["black (==24.10.0)", "clang-format (==19.1.7)", "isort (==5.13.2)", "pylint (==3.3.3)"]
+plot = ["matplotlib (==3.10.0)", "pandas (==2.2.3)"]
+test = ["pytest (==8.3.4)", "pytest-sugar (==1.0.0)"]
+type = ["mypy (==1.14.1)"]
[[package]]
name = "monotonic"
@@ -5841,25 +4711,6 @@ files = [
{file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"},
]
-[[package]]
-name = "mplfonts"
-version = "0.0.10"
-description = "Fonts manager for matplotlib"
-optional = false
-python-versions = ">=3.9"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "mplfonts-0.0.10-py3-none-any.whl", hash = "sha256:835e35ada4a6ef85ce29ea81dd589a98b92af5b5c8e8b9f4e2d79dfea9c2ba40"},
- {file = "mplfonts-0.0.10.tar.gz", hash = "sha256:5da8d1afd53b8d38a1053d61a7ebd936de08b8480fba17f9b655beb270af8089"},
-]
-
-[package.dependencies]
-fire = ">=0.4.0"
-fontmeta = ">=1.6.1"
-matplotlib = ">=3.4"
-numpy = ">=1.26.0"
-
[[package]]
name = "mpmath"
version = "1.3.0"
@@ -5923,7 +4774,7 @@ version = "0.7.1"
description = "AutoRest swagger generator Python client runtime."
optional = false
python-versions = ">=3.6"
-groups = ["main", "storage"]
+groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32"},
@@ -5946,7 +4797,7 @@ version = "6.1.0"
description = "multidict implementation"
optional = false
python-versions = ">=3.8"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
@@ -6043,49 +4894,6 @@ files = [
{file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"},
]
-[[package]]
-name = "multiprocess"
-version = "0.70.17"
-description = "better multiprocessing and multithreading in Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "multiprocess-0.70.17-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ddb24e5bcdb64e90ec5543a1f05a39463068b6d3b804aa3f2a4e16ec28562d6"},
- {file = "multiprocess-0.70.17-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d729f55198a3579f6879766a6d9b72b42d4b320c0dcb7844afb774d75b573c62"},
- {file = "multiprocess-0.70.17-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2c82d0375baed8d8dd0d8c38eb87c5ae9c471f8e384ad203a36f095ee860f67"},
- {file = "multiprocess-0.70.17-pp38-pypy38_pp73-macosx_10_9_arm64.whl", hash = "sha256:a22a6b1a482b80eab53078418bb0f7025e4f7d93cc8e1f36481477a023884861"},
- {file = "multiprocess-0.70.17-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:349525099a0c9ac5936f0488b5ee73199098dac3ac899d81d326d238f9fd3ccd"},
- {file = "multiprocess-0.70.17-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:27b8409c02b5dd89d336107c101dfbd1530a2cd4fd425fc27dcb7adb6e0b47bf"},
- {file = "multiprocess-0.70.17-pp39-pypy39_pp73-macosx_10_13_arm64.whl", hash = "sha256:2ea0939b0f4760a16a548942c65c76ff5afd81fbf1083c56ae75e21faf92e426"},
- {file = "multiprocess-0.70.17-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:2b12e081df87ab755190e227341b2c3b17ee6587e9c82fecddcbe6aa812cd7f7"},
- {file = "multiprocess-0.70.17-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a0f01cd9d079af7a8296f521dc03859d1a414d14c1e2b6e676ef789333421c95"},
- {file = "multiprocess-0.70.17-py310-none-any.whl", hash = "sha256:38357ca266b51a2e22841b755d9a91e4bb7b937979a54d411677111716c32744"},
- {file = "multiprocess-0.70.17-py311-none-any.whl", hash = "sha256:2884701445d0177aec5bd5f6ee0df296773e4fb65b11903b94c613fb46cfb7d1"},
- {file = "multiprocess-0.70.17-py312-none-any.whl", hash = "sha256:2818af14c52446b9617d1b0755fa70ca2f77c28b25ed97bdaa2c69a22c47b46c"},
- {file = "multiprocess-0.70.17-py313-none-any.whl", hash = "sha256:20c28ca19079a6c879258103a6d60b94d4ffe2d9da07dda93fb1c8bc6243f522"},
- {file = "multiprocess-0.70.17-py38-none-any.whl", hash = "sha256:1d52f068357acd1e5bbc670b273ef8f81d57863235d9fbf9314751886e141968"},
- {file = "multiprocess-0.70.17-py39-none-any.whl", hash = "sha256:c3feb874ba574fbccfb335980020c1ac631fbf2a3f7bee4e2042ede62558a021"},
- {file = "multiprocess-0.70.17.tar.gz", hash = "sha256:4ae2f11a3416809ebc9a48abfc8b14ecce0652a0944731a1493a3c1ba44ff57a"},
-]
-
-[package.dependencies]
-dill = ">=0.3.9"
-
-[[package]]
-name = "multitasking"
-version = "0.0.11"
-description = "Non-blocking Python methods using decorators"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "multitasking-0.0.11-py3-none-any.whl", hash = "sha256:1e5b37a5f8fc1e6cfaafd1a82b6b1cc6d2ed20037d3b89c25a84f499bd7b3dd4"},
- {file = "multitasking-0.0.11.tar.gz", hash = "sha256:4d6bc3cc65f9b2dca72fb5a787850a88dae8f620c2b36ae9b55248e51bcd6026"},
-]
-
[[package]]
name = "mypy"
version = "1.13.0"
@@ -6153,19 +4961,6 @@ files = [
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
-[[package]]
-name = "ndjson"
-version = "0.3.1"
-description = "JsonDecoder for ndjson"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "ndjson-0.3.1-py2.py3-none-any.whl", hash = "sha256:839c22275e6baa3040077b83c005ac24199b94973309a8a1809be962c753a410"},
- {file = "ndjson-0.3.1.tar.gz", hash = "sha256:bf9746cb6bb1cb53d172cda7f154c07c786d665ff28341e4e689b796b229e5d6"},
-]
-
[[package]]
name = "nest-asyncio"
version = "1.6.0"
@@ -6179,34 +4974,6 @@ files = [
{file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
]
-[[package]]
-name = "newspaper3k"
-version = "0.2.8"
-description = "Simplified python article discovery & extraction."
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "newspaper3k-0.2.8-py3-none-any.whl", hash = "sha256:44a864222633d3081113d1030615991c3dbba87239f6bbf59d91240f71a22e3e"},
- {file = "newspaper3k-0.2.8.tar.gz", hash = "sha256:9f1bd3e1fb48f400c715abf875cc7b0a67b7ddcd87f50c9aeeb8fcbbbd9004fb"},
-]
-
-[package.dependencies]
-beautifulsoup4 = ">=4.4.1"
-cssselect = ">=0.9.2"
-feedfinder2 = ">=0.0.4"
-feedparser = ">=5.2.1"
-jieba3k = ">=0.35.1"
-lxml = ">=3.6.0"
-nltk = ">=3.2.1"
-Pillow = ">=3.3.0"
-python-dateutil = ">=2.5.3"
-PyYAML = ">=3.11"
-requests = ">=2.10.0"
-tinysegmenter = "0.3"
-tldextract = ">=2.0.1"
-
[[package]]
name = "nltk"
version = "3.9.1"
@@ -6234,134 +5001,91 @@ plot = ["matplotlib"]
tgrep = ["pyparsing"]
twitter = ["twython"]
-[[package]]
-name = "nomic"
-version = "3.1.3"
-description = "The official Nomic python client."
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "nomic-3.1.3.tar.gz", hash = "sha256:b06744b79fbe47451874ca7b272cafa1bb272cfb82acc79c64abfc943a98e035"},
-]
-
-[package.dependencies]
-click = "*"
-jsonlines = "*"
-loguru = "*"
-numpy = "*"
-pandas = "*"
-pillow = "*"
-pyarrow = "*"
-pydantic = "*"
-pyjwt = "*"
-requests = "*"
-rich = "*"
-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 (<=1.1.377)", "pytest", "pytorch-lightning", "twine"]
-local = ["gpt4all (>=2.5.0,<3)"]
-
-[[package]]
-name = "novita-client"
-version = "0.5.7"
-description = "novita SDK for Python"
-optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "novita_client-0.5.7-py3-none-any.whl", hash = "sha256:844a4c09c98328c8d4f72e1d3f63f76285c2963dcc37ccb2de41cbfdbe7fa51d"},
- {file = "novita_client-0.5.7.tar.gz", hash = "sha256:65baf748757aafd8ab080a64f9ab069a40c0810fc1fa9be9c26596988a0aa4b4"},
-]
-
-[package.dependencies]
-dataclass-wizard = ">=0.22.2"
-pillow = ">=10.2.0"
-requests = ">=2.27.1"
-
[[package]]
name = "numba"
-version = "0.60.0"
+version = "0.61.0"
description = "compiling Python code using LLVM"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"},
- {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"},
- {file = "numba-0.60.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1527dc578b95c7c4ff248792ec33d097ba6bef9eda466c948b68dfc995c25781"},
- {file = "numba-0.60.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe0b28abb8d70f8160798f4de9d486143200f34458d34c4a214114e445d7124e"},
- {file = "numba-0.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:19407ced081d7e2e4b8d8c36aa57b7452e0283871c296e12d798852bc7d7f198"},
- {file = "numba-0.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a17b70fc9e380ee29c42717e8cc0bfaa5556c416d94f9aa96ba13acb41bdece8"},
- {file = "numba-0.60.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb02b344a2a80efa6f677aa5c40cd5dd452e1b35f8d1c2af0dfd9ada9978e4b"},
- {file = "numba-0.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f4fde652ea604ea3c86508a3fb31556a6157b2c76c8b51b1d45eb40c8598703"},
- {file = "numba-0.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4142d7ac0210cc86432b818338a2bc368dc773a2f5cf1e32ff7c5b378bd63ee8"},
- {file = "numba-0.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cac02c041e9b5bc8cf8f2034ff6f0dbafccd1ae9590dc146b3a02a45e53af4e2"},
- {file = "numba-0.60.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7da4098db31182fc5ffe4bc42c6f24cd7d1cb8a14b59fd755bfee32e34b8404"},
- {file = "numba-0.60.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38d6ea4c1f56417076ecf8fc327c831ae793282e0ff51080c5094cb726507b1c"},
- {file = "numba-0.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62908d29fb6a3229c242e981ca27e32a6e606cc253fc9e8faeb0e48760de241e"},
- {file = "numba-0.60.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ebaa91538e996f708f1ab30ef4d3ddc344b64b5227b67a57aa74f401bb68b9d"},
- {file = "numba-0.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:f75262e8fe7fa96db1dca93d53a194a38c46da28b112b8a4aca168f0df860347"},
- {file = "numba-0.60.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01ef4cd7d83abe087d644eaa3d95831b777aa21d441a23703d649e06b8e06b74"},
- {file = "numba-0.60.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:819a3dfd4630d95fd574036f99e47212a1af41cbcb019bf8afac63ff56834449"},
- {file = "numba-0.60.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b983bd6ad82fe868493012487f34eae8bf7dd94654951404114f23c3466d34b"},
- {file = "numba-0.60.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c151748cd269ddeab66334bd754817ffc0cabd9433acb0f551697e5151917d25"},
- {file = "numba-0.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:3031547a015710140e8c87226b4cfe927cac199835e5bf7d4fe5cb64e814e3ab"},
- {file = "numba-0.60.0.tar.gz", hash = "sha256:5df6158e5584eece5fc83294b949fd30b9f1125df7708862205217e068aabf16"},
+ {file = "numba-0.61.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9cab9783a700fa428b1a54d65295122bc03b3de1d01fb819a6b9dbbddfdb8c43"},
+ {file = "numba-0.61.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:46c5ae094fb3706f5adf9021bfb7fc11e44818d61afee695cdee4eadfed45e98"},
+ {file = "numba-0.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fb74e81aa78a2303e30593d8331327dfc0d2522b5db05ac967556a26db3ef87"},
+ {file = "numba-0.61.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0ebbd4827091384ab8c4615ba1b3ca8bc639a3a000157d9c37ba85d34cd0da1b"},
+ {file = "numba-0.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:43aa4d7d10c542d3c78106b8481e0cbaaec788c39ee8e3d7901682748ffdf0b4"},
+ {file = "numba-0.61.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:bf64c2d0f3d161af603de3825172fb83c2600bcb1d53ae8ea568d4c53ba6ac08"},
+ {file = "numba-0.61.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de5aa7904741425f28e1028b85850b31f0a245e9eb4f7c38507fb893283a066c"},
+ {file = "numba-0.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21c2fe25019267a608e2710a6a947f557486b4b0478b02e45a81cf606a05a7d4"},
+ {file = "numba-0.61.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:74250b26ed6a1428763e774dc5b2d4e70d93f73795635b5412b8346a4d054574"},
+ {file = "numba-0.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:b72bbc8708e98b3741ad0c63f9929c47b623cc4ee86e17030a4f3e301e8401ac"},
+ {file = "numba-0.61.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:152146ecdbb8d8176f294e9f755411e6f270103a11c3ff50cecc413f794e52c8"},
+ {file = "numba-0.61.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5cafa6095716fcb081618c28a8d27bf7c001e09696f595b41836dec114be2905"},
+ {file = "numba-0.61.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ffe9fe373ed30638d6e20a0269f817b2c75d447141f55a675bfcf2d1fe2e87fb"},
+ {file = "numba-0.61.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9f25f7fef0206d55c1cfb796ad833cbbc044e2884751e56e798351280038484c"},
+ {file = "numba-0.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:550d389573bc3b895e1ccb18289feea11d937011de4d278b09dc7ed585d1cdcb"},
+ {file = "numba-0.61.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:b96fafbdcf6f69b69855273e988696aae4974115a815f6818fef4af7afa1f6b8"},
+ {file = "numba-0.61.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f6c452dca1de8e60e593f7066df052dd8da09b243566ecd26d2b796e5d3087d"},
+ {file = "numba-0.61.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44240e694d4aa321430c97b21453e46014fe6c7b8b7d932afa7f6a88cc5d7e5e"},
+ {file = "numba-0.61.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:764f0e47004f126f58c3b28e0a02374c420a9d15157b90806d68590f5c20cc89"},
+ {file = "numba-0.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:074cd38c5b1f9c65a4319d1f3928165f48975ef0537ad43385b2bd908e6e2e35"},
+ {file = "numba-0.61.0.tar.gz", hash = "sha256:888d2e89b8160899e19591467e8fdd4970e07606e1fbc248f239c89818d5f925"},
]
[package.dependencies]
-llvmlite = "==0.43.*"
-numpy = ">=1.22,<2.1"
+llvmlite = "==0.44.*"
+numpy = ">=1.24,<2.2"
[[package]]
name = "numexpr"
-version = "2.9.0"
+version = "2.10.2"
description = "Fast numerical expression evaluator for NumPy"
optional = false
python-versions = ">=3.9"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "numexpr-2.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c52b4ac54514f5d4d8ead66768810cd5f77aa198e6064213d9b5c7b2e1c97c35"},
- {file = "numexpr-2.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50f57bc333f285e8c46b1ce61c6e94ec9bb74e4ea0d674d1c6c6f4a286f64fe4"},
- {file = "numexpr-2.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:943ba141f3884ffafa3fa1a3ebf3cdda9e9688a67a3c91986e6eae13dc073d43"},
- {file = "numexpr-2.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee48acd6339748a65c0e32403b802ebfadd9cb0e3b602ba5889896238eafdd61"},
- {file = "numexpr-2.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:972e29b5cecc21466c5b177e38568372ab66aab1f053ae04690a49cea09e747d"},
- {file = "numexpr-2.9.0-cp310-cp310-win32.whl", hash = "sha256:520e55d75bd99c76e376b6326e35ecf44c5ce2635a5caed72799a3885fc49173"},
- {file = "numexpr-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:5615497c3f34b637fda9b571f7774b6a82f2367cc1364b7a4573068dd1aabcaa"},
- {file = "numexpr-2.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bffcbc55dea5a5f5255e2586da08f00929998820e6592ee717273a08ad021eb3"},
- {file = "numexpr-2.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:374dc6ca54b2af813cb15c2b34e85092dfeac1f73d51ec358dd81876bd9adcec"},
- {file = "numexpr-2.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:549afc1622296cca3478a132c6e0fb5e55a19e08d32bc0d5a415434824a9c157"},
- {file = "numexpr-2.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c618a5895e34db0a364dcdb9960084c080f93f9d377c45b1ca9c394c24b4e77"},
- {file = "numexpr-2.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:37a7dd36fd79a2b69c3fd2bc2b51ac8270bebc69cc96e6d78f1148e147fcbfa8"},
- {file = "numexpr-2.9.0-cp311-cp311-win32.whl", hash = "sha256:00dab81d49239ea5423861ad627097b44d10d802df5f883d1b00f742139c3349"},
- {file = "numexpr-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:0e2574cafb18373774f351cac45ed23b5b360d9ecd1dbf3c12dac6d6eefefc87"},
- {file = "numexpr-2.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9761195526a228e05eba400b8c484c94bbabfea853b9ea35ab8fa1bf415331b1"},
- {file = "numexpr-2.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0f619e91034b346ea85a4e1856ff06011dcb7dce10a60eda75e74db90120f880"},
- {file = "numexpr-2.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2749bce1c48706d58894992634a43b8458c4ba9411191471c4565fa41e9979ec"},
- {file = "numexpr-2.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c31f621a625c7be602f92b027d90f2d3d60dcbc19b106e77fb04a4362152af"},
- {file = "numexpr-2.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b937861d13de67d440d54c85a835faed7572be5a6fd10d4f3bd4e66e157f"},
- {file = "numexpr-2.9.0-cp312-cp312-win32.whl", hash = "sha256:aa6298fb46bd7ec69911b5b80927a00663d066e719b29f48eb952d559bdd8371"},
- {file = "numexpr-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:8efd879839572bde5a38a1aa3ac23fd4dd9b956fb969bc5e43d1c403419e1e8c"},
- {file = "numexpr-2.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b04f12a6130094a251e3a8fff40130589c1c83be6d4eb223873bea14d8c8b630"},
- {file = "numexpr-2.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:977537f2a1cc843f888fb5f0507626f956ada674e4b3847168214a3f3c7446fa"},
- {file = "numexpr-2.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eae6c0c2d5682c02e8ac9c4287c2232c2443c9148b239df22500eaa3c5d73b7"},
- {file = "numexpr-2.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fae6828042b70c2f52a132bfcb9139da704274ed11b982fbf537f91c075d2ef"},
- {file = "numexpr-2.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c77392aea53f0700d60eb270ad63174b4ff10b04f8de92861101ca2129fee51"},
- {file = "numexpr-2.9.0-cp39-cp39-win32.whl", hash = "sha256:3b03a6cf37a72f5b52f2b962d7ac7f565bea8eaba83c3c4e5fcf8fbb6a938153"},
- {file = "numexpr-2.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:d655b6eacc4e81006b662cba014e4615a9ddd96881b8b4db4ad0d7f6d38069af"},
- {file = "numexpr-2.9.0.tar.gz", hash = "sha256:f21d12f6c432ce349089eb95342babf6629aebb3fddf187a4492d3aadaadaaf0"},
-]
-
-[package.dependencies]
-numpy = ">=1.13.3"
+ {file = "numexpr-2.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5b0e82d2109c1d9e63fcd5ea177d80a11b881157ab61178ddbdebd4c561ea46"},
+ {file = "numexpr-2.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc2b8035a0c2cdc352e58c3875cb668836018065cbf5752cb531015d9a568d8"},
+ {file = "numexpr-2.10.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0db5ff5183935d1612653559c319922143e8fa3019007696571b13135f216458"},
+ {file = "numexpr-2.10.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f59655458056fdb3a621b1bb8e071581ccf7e823916c7568bb7c9a3e393025"},
+ {file = "numexpr-2.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ce8cccf944339051e44a49a124a06287fe3066d0acbff33d1aa5aee10a96abb7"},
+ {file = "numexpr-2.10.2-cp310-cp310-win32.whl", hash = "sha256:ba85371c9a8d03e115f4dfb6d25dfbce05387002b9bc85016af939a1da9624f0"},
+ {file = "numexpr-2.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:deb64235af9eeba59fcefa67e82fa80cfc0662e1b0aa373b7118a28da124d51d"},
+ {file = "numexpr-2.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b360eb8d392483410fe6a3d5a7144afa298c9a0aa3e9fe193e89590b47dd477"},
+ {file = "numexpr-2.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9a42f5c24880350d88933c4efee91b857c378aaea7e8b86221fff569069841e"},
+ {file = "numexpr-2.10.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fcb11988b57cc25b028a36d285287d706d1f536ebf2662ea30bd990e0de8b9"},
+ {file = "numexpr-2.10.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4213a92efa9770bc28e3792134e27c7e5c7e97068bdfb8ba395baebbd12f991b"},
+ {file = "numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e"},
+ {file = "numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5"},
+ {file = "numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0"},
+ {file = "numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be"},
+ {file = "numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42"},
+ {file = "numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470"},
+ {file = "numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6"},
+ {file = "numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db"},
+ {file = "numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e"},
+ {file = "numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead"},
+ {file = "numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d"},
+ {file = "numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f"},
+ {file = "numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e"},
+ {file = "numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527"},
+ {file = "numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19"},
+ {file = "numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01"},
+ {file = "numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd"},
+ {file = "numexpr-2.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0495f8111c3633e265248709b8b3b521bbfa646ba384909edd10e2b9a588a83a"},
+ {file = "numexpr-2.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2aa05ac71bee3b1253e73173c4d7fa96a09a18970c0226f1c2c07a71ffe988dc"},
+ {file = "numexpr-2.10.2-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3a23c3002ab330056fbdd2785871937a6f2f2fa85d06c8d0ff74ea8418119d1"},
+ {file = "numexpr-2.10.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a018a7d81326f4c73d8b5aee61794d7d8514512f43957c0db61eb2a8a86848c7"},
+ {file = "numexpr-2.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:037859b17a0abe2b489d4c2cfdadd2bf458ec80dd83f338ea5544c7987e06b85"},
+ {file = "numexpr-2.10.2-cp39-cp39-win32.whl", hash = "sha256:eb278ccda6f893a312aa0452701bb17d098b7b14eb7c9381517d509cce0a39a3"},
+ {file = "numexpr-2.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:734b64c6d6a597601ce9d0ef7b666e678ec015b446f1d1412c23903c021436c3"},
+ {file = "numexpr-2.10.2.tar.gz", hash = "sha256:b0aff6b48ebc99d2f54f27b5f73a58cb92fde650aeff1b397c71c8788b4fff1a"},
+]
+
+[package.dependencies]
+numpy = ">=1.23.0"
[[package]]
name = "numpy"
@@ -6369,7 +5093,7 @@ version = "1.26.4"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
-groups = ["main", "indirect", "tools", "vdb"]
+groups = ["main", "indirect", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
@@ -6416,7 +5140,7 @@ version = "3.2.2"
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
optional = false
python-versions = ">=3.6"
-groups = ["main", "storage", "vdb"]
+groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"},
@@ -6522,15 +5246,15 @@ sympy = "*"
[[package]]
name = "openai"
-version = "1.61.0"
+version = "1.61.1"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "openai-1.61.0-py3-none-any.whl", hash = "sha256:e8c512c0743accbdbe77f3429a1490d862f8352045de8dc81969301eb4a4f666"},
- {file = "openai-1.61.0.tar.gz", hash = "sha256:216f325a24ed8578e929b0f1b3fb2052165f3b04b0461818adaa51aa29c71f8a"},
+ {file = "openai-1.61.1-py3-none-any.whl", hash = "sha256:72b0826240ce26026ac2cd17951691f046e5be82ad122d20a8e1b30ca18bd11e"},
+ {file = "openai-1.61.1.tar.gz", hash = "sha256:ce1851507218209961f89f3520e06726c0aa7d0512386f0f977e3ac3e4f2472e"},
]
[package.dependencies]
@@ -6547,93 +5271,30 @@ typing-extensions = ">=4.11,<5"
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
realtime = ["websockets (>=13,<15)"]
-[[package]]
-name = "opencensus"
-version = "0.11.4"
-description = "A stats collection and distributed tracing framework"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "opencensus-0.11.4-py2.py3-none-any.whl", hash = "sha256:a18487ce68bc19900336e0ff4655c5a116daf10c1b3685ece8d971bddad6a864"},
- {file = "opencensus-0.11.4.tar.gz", hash = "sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2"},
-]
-
-[package.dependencies]
-google-api-core = {version = ">=1.0.0,<3.0.0", markers = "python_version >= \"3.6\""}
-opencensus-context = ">=0.1.3"
-six = ">=1.16,<2.0"
-
-[[package]]
-name = "opencensus-context"
-version = "0.1.3"
-description = "OpenCensus Runtime Context"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "opencensus-context-0.1.3.tar.gz", hash = "sha256:a03108c3c10d8c80bb5ddf5c8a1f033161fa61972a9917f9b9b3a18517f0088c"},
- {file = "opencensus_context-0.1.3-py2.py3-none-any.whl", hash = "sha256:073bb0590007af276853009fac7e4bab1d523c3f03baf4cb4511ca38967c6039"},
-]
-
-[[package]]
-name = "opencensus-ext-azure"
-version = "1.1.14"
-description = "OpenCensus Azure Monitor Exporter"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "opencensus-ext-azure-1.1.14.tar.gz", hash = "sha256:c9c6ebad542aeb61813322e627d5889a563e7b8c4e024bf58469d06db73ab148"},
- {file = "opencensus_ext_azure-1.1.14-py2.py3-none-any.whl", hash = "sha256:a1f6870d6e4e312832e6ebd95df28ed499ac637c36cbd77665fe06e24ddeb2f1"},
-]
-
-[package.dependencies]
-azure-core = ">=1.12.0,<2.0.0"
-azure-identity = ">=1.5.0,<2.0.0"
-opencensus = ">=0.11.4,<1.0.0"
-psutil = ">=5.6.3"
-requests = ">=2.19.0"
-
-[[package]]
-name = "opencensus-ext-logging"
-version = "0.1.1"
-description = "OpenCensus logging Integration"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "opencensus-ext-logging-0.1.1.tar.gz", hash = "sha256:c203b70f034151dada529f543af330ba17aaffec27d8a5267d03c713eb1de334"},
- {file = "opencensus_ext_logging-0.1.1-py2.py3-none-any.whl", hash = "sha256:cfdaf5da5d8b195ff3d1af87a4066a6621a28046173f6be4b0b6caec4a3ca89f"},
-]
-
-[package.dependencies]
-opencensus = ">=0.8.0,<1.0.0"
-
[[package]]
name = "opendal"
-version = "0.45.13"
+version = "0.45.15"
description = "Apache OpenDAL™ Python Binding"
optional = false
python-versions = ">=3.10"
groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opendal-0.45.13-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3afe389215249b1d067cace6b8d1259ab1a2a74bc963d1c7e47dac5e85c8ffc5"},
- {file = "opendal-0.45.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0062482d348617abdc89515fa9cea5c17ae8ac28694b8b5a704530eb91c90e"},
- {file = "opendal-0.45.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5cb06d73cc93a13e1a4faa2f369ffe64726f459e53358058720d67efec6dd5fd"},
- {file = "opendal-0.45.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122942d8185b441774d566c6970b0012ffde9370282c4384c84e6eaa793d4891"},
- {file = "opendal-0.45.13-cp310-cp310-win_amd64.whl", hash = "sha256:e451e1ae63343d07fa57225417e898639240083d2a53ecd7dbafa72254f058bd"},
- {file = "opendal-0.45.13-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4b8faf7f780849c6bd777528080864c6c5e46e61488fafb8d2dcb0f9e4e88845"},
- {file = "opendal-0.45.13-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1987cc0ac3e05ebcd963b431a0c05607d9a9d7ed204ba8053258f812434b262"},
- {file = "opendal-0.45.13-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fb89b30ee3cb8fd432ada45be0ae9dad9d7483e1e1db22deb3074ef61a194ca"},
- {file = "opendal-0.45.13-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e99a147de8a0a41285fb240e0280fea6a7436fe3f7341815157bce69104d6"},
- {file = "opendal-0.45.13-cp311-abi3-win_amd64.whl", hash = "sha256:ab187174dede49a7e9821a4d4792676a62c887279465d33624009e720a086667"},
- {file = "opendal-0.45.13.tar.gz", hash = "sha256:ed818dd564beeace57a040f65415525838ad78c20bdffdbe0ba54281e7f17064"},
+ {file = "opendal-0.45.15-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c8bfb16edce3d43d2caf0391af547c581ffdea9d7c145c054bbaba60d064dab4"},
+ {file = "opendal-0.45.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:adf989577cf127e13f86d0179fb26e673e3036eae0406002c7724dab2c0e4c5e"},
+ {file = "opendal-0.45.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f1b60722a266bbad4336492b37b9825d20c838c584897600f520ef6956959ae"},
+ {file = "opendal-0.45.15-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8dba4c824fb5b291fb5ebd7d2f31c703cc9d9a89dc10b9acc3c7e3908e625165"},
+ {file = "opendal-0.45.15-cp310-cp310-win_amd64.whl", hash = "sha256:68f63a87f24dd210af37180de873329bf46d5b9548420405f282410ff960f06a"},
+ {file = "opendal-0.45.15-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a7ee0f817bd0f7187dea13d95baf6fb54c51dd676593ccb7b3d0f71270170195"},
+ {file = "opendal-0.45.15-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f496be9c020557a9651138f141d8e9e87ce4551a7be881b08d449af559737810"},
+ {file = "opendal-0.45.15-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df87172a8181805b058c0c2d2468441e9f046ecc3d73d202032536b4f8b883ae"},
+ {file = "opendal-0.45.15-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c2ba47b733907babf66118e7579c06c561900780fbdb9c5709397ca7e06ac17"},
+ {file = "opendal-0.45.15-cp311-abi3-win_amd64.whl", hash = "sha256:476b5505f08d346a22a3f603be33499503ff52fab8f43d731c6ec31f6974ad51"},
+ {file = "opendal-0.45.15-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:921a5f917ac44f5965c3fd3d56ab6d2c2df9243cb507780f5f06e6a0f40fdce9"},
+ {file = "opendal-0.45.15-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bac5c3843903c54b929de561821055fab806d49eb055770ba68ef5b88616fae"},
+ {file = "opendal-0.45.15-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fabd759dfafdc04fdefaccd5764d4eb689059b491a6428a8f2528d2fcad8205"},
+ {file = "opendal-0.45.15-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e7ea3955bbd4f3ad93cf270d9ef0cce19d5b0d79d9b37e5fd39ab1cf68ce8b64"},
+ {file = "opendal-0.45.15.tar.gz", hash = "sha256:ce7ac10d359a029efb4def31816f665dd02200ec3b076c50c67eabaf67f80117"},
]
[package.extras]
@@ -6686,15 +5347,15 @@ kerberos = ["requests-kerberos"]
[[package]]
name = "opentelemetry-api"
-version = "1.29.0"
+version = "1.30.0"
description = "OpenTelemetry Python API"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_api-1.29.0-py3-none-any.whl", hash = "sha256:5fcd94c4141cc49c736271f3e1efb777bebe9cc535759c54c936cca4f1b312b8"},
- {file = "opentelemetry_api-1.29.0.tar.gz", hash = "sha256:d04a6cf78aad09614f52964ecb38021e248f5714dc32c2e0d8fd99517b4d69cf"},
+ {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"},
+ {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"},
]
[package.dependencies]
@@ -6727,65 +5388,65 @@ test = ["pytest-grpc"]
[[package]]
name = "opentelemetry-instrumentation"
-version = "0.50b0"
+version = "0.51b0"
description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_instrumentation-0.50b0-py3-none-any.whl", hash = "sha256:b8f9fc8812de36e1c6dffa5bfc6224df258841fb387b6dfe5df15099daa10630"},
- {file = "opentelemetry_instrumentation-0.50b0.tar.gz", hash = "sha256:7d98af72de8dec5323e5202e46122e5f908592b22c6d24733aad619f07d82979"},
+ {file = "opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf"},
+ {file = "opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa"},
]
[package.dependencies]
opentelemetry-api = ">=1.4,<2.0"
-opentelemetry-semantic-conventions = "0.50b0"
+opentelemetry-semantic-conventions = "0.51b0"
packaging = ">=18.0"
wrapt = ">=1.0.0,<2.0.0"
[[package]]
name = "opentelemetry-instrumentation-asgi"
-version = "0.50b0"
+version = "0.51b0"
description = "ASGI instrumentation for OpenTelemetry"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_instrumentation_asgi-0.50b0-py3-none-any.whl", hash = "sha256:2ba1297f746e55dec5a17fe825689da0613662fb25c004c3965a6c54b1d5be22"},
- {file = "opentelemetry_instrumentation_asgi-0.50b0.tar.gz", hash = "sha256:3ca4cb5616ae6a3e8ce86e7d5c360a8d8cc8ed722cf3dc8a5e44300774e87d49"},
+ {file = "opentelemetry_instrumentation_asgi-0.51b0-py3-none-any.whl", hash = "sha256:e8072993db47303b633c6ec1bc74726ba4d32bd0c46c28dfadf99f79521a324c"},
+ {file = "opentelemetry_instrumentation_asgi-0.51b0.tar.gz", hash = "sha256:b3fe97c00f0bfa934371a69674981d76591c68d937b6422a5716ca21081b4148"},
]
[package.dependencies]
asgiref = ">=3.0,<4.0"
opentelemetry-api = ">=1.12,<2.0"
-opentelemetry-instrumentation = "0.50b0"
-opentelemetry-semantic-conventions = "0.50b0"
-opentelemetry-util-http = "0.50b0"
+opentelemetry-instrumentation = "0.51b0"
+opentelemetry-semantic-conventions = "0.51b0"
+opentelemetry-util-http = "0.51b0"
[package.extras]
instruments = ["asgiref (>=3.0,<4.0)"]
[[package]]
name = "opentelemetry-instrumentation-fastapi"
-version = "0.50b0"
+version = "0.51b0"
description = "OpenTelemetry FastAPI Instrumentation"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_instrumentation_fastapi-0.50b0-py3-none-any.whl", hash = "sha256:8f03b738495e4705fbae51a2826389c7369629dace89d0f291c06ffefdff5e52"},
- {file = "opentelemetry_instrumentation_fastapi-0.50b0.tar.gz", hash = "sha256:16b9181682136da210295def2bb304a32fb9bdee9a935cdc9da43567f7c1149e"},
+ {file = "opentelemetry_instrumentation_fastapi-0.51b0-py3-none-any.whl", hash = "sha256:10513bbc11a1188adb9c1d2c520695f7a8f2b5f4de14e8162098035901cd6493"},
+ {file = "opentelemetry_instrumentation_fastapi-0.51b0.tar.gz", hash = "sha256:1624e70f2f4d12ceb792d8a0c331244cd6723190ccee01336273b4559bc13abc"},
]
[package.dependencies]
opentelemetry-api = ">=1.12,<2.0"
-opentelemetry-instrumentation = "0.50b0"
-opentelemetry-instrumentation-asgi = "0.50b0"
-opentelemetry-semantic-conventions = "0.50b0"
-opentelemetry-util-http = "0.50b0"
+opentelemetry-instrumentation = "0.51b0"
+opentelemetry-instrumentation-asgi = "0.51b0"
+opentelemetry-semantic-conventions = "0.51b0"
+opentelemetry-util-http = "0.51b0"
[package.extras]
instruments = ["fastapi (>=0.58,<1.0)"]
@@ -6808,63 +5469,63 @@ protobuf = ">=3.19,<5.0"
[[package]]
name = "opentelemetry-sdk"
-version = "1.29.0"
+version = "1.30.0"
description = "OpenTelemetry Python SDK"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_sdk-1.29.0-py3-none-any.whl", hash = "sha256:173be3b5d3f8f7d671f20ea37056710217959e774e2749d984355d1f9391a30a"},
- {file = "opentelemetry_sdk-1.29.0.tar.gz", hash = "sha256:b0787ce6aade6ab84315302e72bd7a7f2f014b0fb1b7c3295b88afe014ed0643"},
+ {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"},
+ {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"},
]
[package.dependencies]
-opentelemetry-api = "1.29.0"
-opentelemetry-semantic-conventions = "0.50b0"
+opentelemetry-api = "1.30.0"
+opentelemetry-semantic-conventions = "0.51b0"
typing-extensions = ">=3.7.4"
[[package]]
name = "opentelemetry-semantic-conventions"
-version = "0.50b0"
+version = "0.51b0"
description = "OpenTelemetry Semantic Conventions"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_semantic_conventions-0.50b0-py3-none-any.whl", hash = "sha256:e87efba8fdb67fb38113efea6a349531e75ed7ffc01562f65b802fcecb5e115e"},
- {file = "opentelemetry_semantic_conventions-0.50b0.tar.gz", hash = "sha256:02dc6dbcb62f082de9b877ff19a3f1ffaa3c306300fa53bfac761c4567c83d38"},
+ {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"},
+ {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"},
]
[package.dependencies]
deprecated = ">=1.2.6"
-opentelemetry-api = "1.29.0"
+opentelemetry-api = "1.30.0"
[[package]]
name = "opentelemetry-util-http"
-version = "0.50b0"
+version = "0.51b0"
description = "Web util for OpenTelemetry"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opentelemetry_util_http-0.50b0-py3-none-any.whl", hash = "sha256:21f8aedac861ffa3b850f8c0a6c373026189eb8630ac6e14a2bf8c55695cc090"},
- {file = "opentelemetry_util_http-0.50b0.tar.gz", hash = "sha256:dc4606027e1bc02aabb9533cc330dd43f874fca492e4175c31d7154f341754af"},
+ {file = "opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20"},
+ {file = "opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9"},
]
[[package]]
name = "opik"
-version = "1.3.4"
+version = "1.3.5"
description = "Comet tool for logging and evaluating LLM traces"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "opik-1.3.4-py3-none-any.whl", hash = "sha256:c5e10a9f1fb18188471cce2ae8b841e8b187d04ee3b1aed01c643102bae588fb"},
- {file = "opik-1.3.4.tar.gz", hash = "sha256:6013d3af4aea61f38b9e7121aa5d8cf4305a5ed3807b3f43d9ab91602b2a5785"},
+ {file = "opik-1.3.5-py3-none-any.whl", hash = "sha256:c6a195b33851959b8e96ac78fe211b6157288eddc03fa8bfbd1ef53424b702dc"},
+ {file = "opik-1.3.5.tar.gz", hash = "sha256:943e4b636b70e5781f7a6f40b33fadda0935b57ecad0997f195ce909956b68d7"},
]
[package.dependencies]
@@ -6928,87 +5589,91 @@ cryptography = ">=3.2.1"
[[package]]
name = "orjson"
-version = "3.10.13"
+version = "3.10.15"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
optional = false
python-versions = ">=3.8"
groups = ["main", "vdb"]
files = [
- {file = "orjson-3.10.13-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1232c5e873a4d1638ef957c5564b4b0d6f2a6ab9e207a9b3de9de05a09d1d920"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d26a0eca3035619fa366cbaf49af704c7cb1d4a0e6c79eced9f6a3f2437964b6"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d4b6acd7c9c829895e50d385a357d4b8c3fafc19c5989da2bae11783b0fd4977"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1884e53c6818686891cc6fc5a3a2540f2f35e8c76eac8dc3b40480fb59660b00"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a428afb5720f12892f64920acd2eeb4d996595bf168a26dd9190115dbf1130d"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba5b13b8739ce5b630c65cb1c85aedbd257bcc2b9c256b06ab2605209af75a2e"},
- {file = "orjson-3.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cab83e67f6aabda1b45882254b2598b48b80ecc112968fc6483fa6dae609e9f0"},
- {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:62c3cc00c7e776c71c6b7b9c48c5d2701d4c04e7d1d7cdee3572998ee6dc57cc"},
- {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:dc03db4922e75bbc870b03fc49734cefbd50fe975e0878327d200022210b82d8"},
- {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:22f1c9a30b43d14a041a6ea190d9eca8a6b80c4beb0e8b67602c82d30d6eec3e"},
- {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b42f56821c29e697c68d7d421410d7c1d8f064ae288b525af6a50cf99a4b1200"},
- {file = "orjson-3.10.13-cp310-cp310-win32.whl", hash = "sha256:0dbf3b97e52e093d7c3e93eb5eb5b31dc7535b33c2ad56872c83f0160f943487"},
- {file = "orjson-3.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:46c249b4e934453be4ff2e518cd1adcd90467da7391c7a79eaf2fbb79c51e8c7"},
- {file = "orjson-3.10.13-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a36c0d48d2f084c800763473020a12976996f1109e2fcb66cfea442fdf88047f"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0065896f85d9497990731dfd4a9991a45b0a524baec42ef0a63c34630ee26fd6"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92b4ec30d6025a9dcdfe0df77063cbce238c08d0404471ed7a79f309364a3d19"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a94542d12271c30044dadad1125ee060e7a2048b6c7034e432e116077e1d13d2"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3723e137772639af8adb68230f2aa4bcb27c48b3335b1b1e2d49328fed5e244c"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f00c7fb18843bad2ac42dc1ce6dd214a083c53f1e324a0fd1c8137c6436269b"},
- {file = "orjson-3.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0e2759d3172300b2f892dee85500b22fca5ac49e0c42cfff101aaf9c12ac9617"},
- {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ee948c6c01f6b337589c88f8e0bb11e78d32a15848b8b53d3f3b6fea48842c12"},
- {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa6fe68f0981fba0d4bf9cdc666d297a7cdba0f1b380dcd075a9a3dd5649a69e"},
- {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbcd7aad6bcff258f6896abfbc177d54d9b18149c4c561114f47ebfe74ae6bfd"},
- {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2149e2fcd084c3fd584881c7f9d7f9e5ad1e2e006609d8b80649655e0d52cd02"},
- {file = "orjson-3.10.13-cp311-cp311-win32.whl", hash = "sha256:89367767ed27b33c25c026696507c76e3d01958406f51d3a2239fe9e91959df2"},
- {file = "orjson-3.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:dca1d20f1af0daff511f6e26a27354a424f0b5cf00e04280279316df0f604a6f"},
- {file = "orjson-3.10.13-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a3614b00621c77f3f6487792238f9ed1dd8a42f2ec0e6540ee34c2d4e6db813a"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c976bad3996aa027cd3aef78aa57873f3c959b6c38719de9724b71bdc7bd14b"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f74d878d1efb97a930b8a9f9898890067707d683eb5c7e20730030ecb3fb930"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33ef84f7e9513fb13b3999c2a64b9ca9c8143f3da9722fbf9c9ce51ce0d8076e"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd2bcde107221bb9c2fa0c4aaba735a537225104173d7e19cf73f70b3126c993"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:064b9dbb0217fd64a8d016a8929f2fae6f3312d55ab3036b00b1d17399ab2f3e"},
- {file = "orjson-3.10.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0044b0b8c85a565e7c3ce0a72acc5d35cda60793edf871ed94711e712cb637d"},
- {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7184f608ad563032e398f311910bc536e62b9fbdca2041be889afcbc39500de8"},
- {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d36f689e7e1b9b6fb39dbdebc16a6f07cbe994d3644fb1c22953020fc575935f"},
- {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54433e421618cd5873e51c0e9d0b9fb35f7bf76eb31c8eab20b3595bb713cd3d"},
- {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e1ba0c5857dd743438acecc1cd0e1adf83f0a81fee558e32b2b36f89e40cee8b"},
- {file = "orjson-3.10.13-cp312-cp312-win32.whl", hash = "sha256:a42b9fe4b0114b51eb5cdf9887d8c94447bc59df6dbb9c5884434eab947888d8"},
- {file = "orjson-3.10.13-cp312-cp312-win_amd64.whl", hash = "sha256:3a7df63076435f39ec024bdfeb4c9767ebe7b49abc4949068d61cf4857fa6d6c"},
- {file = "orjson-3.10.13-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2cdaf8b028a976ebab837a2c27b82810f7fc76ed9fb243755ba650cc83d07730"},
- {file = "orjson-3.10.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48a946796e390cbb803e069472de37f192b7a80f4ac82e16d6eb9909d9e39d56"},
- {file = "orjson-3.10.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d64f1db5ecbc21eb83097e5236d6ab7e86092c1cd4c216c02533332951afc"},
- {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:711878da48f89df194edd2ba603ad42e7afed74abcd2bac164685e7ec15f96de"},
- {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cf16f06cb77ce8baf844bc222dbcb03838f61d0abda2c3341400c2b7604e436e"},
- {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8257c3fb8dd7b0b446b5e87bf85a28e4071ac50f8c04b6ce2d38cb4abd7dff57"},
- {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9c3a87abe6f849a4a7ac8a8a1dede6320a4303d5304006b90da7a3cd2b70d2c"},
- {file = "orjson-3.10.13-cp313-cp313-win32.whl", hash = "sha256:527afb6ddb0fa3fe02f5d9fba4920d9d95da58917826a9be93e0242da8abe94a"},
- {file = "orjson-3.10.13-cp313-cp313-win_amd64.whl", hash = "sha256:b5f7c298d4b935b222f52d6c7f2ba5eafb59d690d9a3840b7b5c5cda97f6ec5c"},
- {file = "orjson-3.10.13-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e49333d1038bc03a25fdfe11c86360df9b890354bfe04215f1f54d030f33c342"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:003721c72930dbb973f25c5d8e68d0f023d6ed138b14830cc94e57c6805a2eab"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63664bf12addb318dc8f032160e0f5dc17eb8471c93601e8f5e0d07f95003784"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6066729cf9552d70de297b56556d14b4f49c8f638803ee3c90fd212fa43cc6af"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a1152e2761025c5d13b5e1908d4b1c57f3797ba662e485ae6f26e4e0c466388"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b21d91c5c5ef8a201036d207b1adf3aa596b930b6ca3c71484dd11386cf6c3"},
- {file = "orjson-3.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b12a63f48bb53dba8453d36ca2661f2330126d54e26c1661e550b32864b28ce3"},
- {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a5a7624ab4d121c7e035708c8dd1f99c15ff155b69a1c0affc4d9d8b551281ba"},
- {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0fee076134398d4e6cb827002468679ad402b22269510cf228301b787fdff5ae"},
- {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ae537fcf330b3947e82c6ae4271e092e6cf16b9bc2cef68b14ffd0df1fa8832a"},
- {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f81b26c03f5fb5f0d0ee48d83cea4d7bc5e67e420d209cc1a990f5d1c62f9be0"},
- {file = "orjson-3.10.13-cp38-cp38-win32.whl", hash = "sha256:0bc858086088b39dc622bc8219e73d3f246fb2bce70a6104abd04b3a080a66a8"},
- {file = "orjson-3.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:3ca6f17467ebbd763f8862f1d89384a5051b461bb0e41074f583a0ebd7120e8e"},
- {file = "orjson-3.10.13-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a11532cbfc2f5752c37e84863ef8435b68b0e6d459b329933294f65fa4bda1a"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c96d2fb80467d1d0dfc4d037b4e1c0f84f1fe6229aa7fea3f070083acef7f3d7"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dda4ba4d3e6f6c53b6b9c35266788053b61656a716a7fef5c884629c2a52e7aa"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f998bbf300690be881772ee9c5281eb9c0044e295bcd4722504f5b5c6092ff"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1cc42ed75b585c0c4dc5eb53a90a34ccb493c09a10750d1a1f9b9eff2bd12"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b0f29d485411e3c13d79604b740b14e4e5fb58811743f6f4f9693ee6480a8f"},
- {file = "orjson-3.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:233aae4474078d82f425134bb6a10fb2b3fc5a1a1b3420c6463ddd1b6a97eda8"},
- {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e384e330a67cf52b3597ee2646de63407da6f8fc9e9beec3eaaaef5514c7a1c9"},
- {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4222881d0aab76224d7b003a8e5fdae4082e32c86768e0e8652de8afd6c4e2c1"},
- {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e400436950ba42110a20c50c80dff4946c8e3ec09abc1c9cf5473467e83fd1c5"},
- {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f47c9e7d224b86ffb086059cdcf634f4b3f32480f9838864aa09022fe2617ce2"},
- {file = "orjson-3.10.13-cp39-cp39-win32.whl", hash = "sha256:a9ecea472f3eb653e1c0a3d68085f031f18fc501ea392b98dcca3e87c24f9ebe"},
- {file = "orjson-3.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:5385935a73adce85cc7faac9d396683fd813566d3857fa95a0b521ef84a5b588"},
- {file = "orjson-3.10.13.tar.gz", hash = "sha256:eb9bfb14ab8f68d9d9492d4817ae497788a15fd7da72e14dfabc289c3bb088ec"},
+ {file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf"},
+ {file = "orjson-3.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061"},
+ {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3"},
+ {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d"},
+ {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182"},
+ {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e"},
+ {file = "orjson-3.10.15-cp310-cp310-win32.whl", hash = "sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab"},
+ {file = "orjson-3.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806"},
+ {file = "orjson-3.10.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13"},
+ {file = "orjson-3.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5"},
+ {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b"},
+ {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399"},
+ {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388"},
+ {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c"},
+ {file = "orjson-3.10.15-cp311-cp311-win32.whl", hash = "sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e"},
+ {file = "orjson-3.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e"},
+ {file = "orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41"},
+ {file = "orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514"},
+ {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17"},
+ {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b"},
+ {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7"},
+ {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a"},
+ {file = "orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665"},
+ {file = "orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa"},
+ {file = "orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e"},
+ {file = "orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7"},
+ {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8"},
+ {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca"},
+ {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561"},
+ {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825"},
+ {file = "orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890"},
+ {file = "orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf"},
+ {file = "orjson-3.10.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e8afd6200e12771467a1a44e5ad780614b86abb4b11862ec54861a82d677746"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9a18c500f19273e9e104cca8c1f0b40a6470bcccfc33afcc088045d0bf5ea6"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb00b7bfbdf5d34a13180e4805d76b4567025da19a197645ca746fc2fb536586"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33aedc3d903378e257047fee506f11e0833146ca3e57a1a1fb0ddb789876c1e1"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0099ae6aed5eb1fc84c9eb72b95505a3df4267e6962eb93cdd5af03be71c98"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c864a80a2d467d7786274fce0e4f93ef2a7ca4ff31f7fc5634225aaa4e9e98c"},
+ {file = "orjson-3.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c25774c9e88a3e0013d7d1a6c8056926b607a61edd423b50eb5c88fd7f2823ae"},
+ {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e78c211d0074e783d824ce7bb85bf459f93a233eb67a5b5003498232ddfb0e8a"},
+ {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:43e17289ffdbbac8f39243916c893d2ae41a2ea1a9cbb060a56a4d75286351ae"},
+ {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:781d54657063f361e89714293c095f506c533582ee40a426cb6489c48a637b81"},
+ {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6875210307d36c94873f553786a808af2788e362bd0cf4c8e66d976791e7b528"},
+ {file = "orjson-3.10.15-cp38-cp38-win32.whl", hash = "sha256:305b38b2b8f8083cc3d618927d7f424349afce5975b316d33075ef0f73576b60"},
+ {file = "orjson-3.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:5dd9ef1639878cc3efffed349543cbf9372bdbd79f478615a1c633fe4e4180d1"},
+ {file = "orjson-3.10.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8"},
+ {file = "orjson-3.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3"},
+ {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480"},
+ {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829"},
+ {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a"},
+ {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428"},
+ {file = "orjson-3.10.15-cp39-cp39-win32.whl", hash = "sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507"},
+ {file = "orjson-3.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd"},
+ {file = "orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e"},
]
markers = {main = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"", vdb = "python_version == \"3.11\" or python_version >= \"3.12\""}
@@ -7051,7 +5716,7 @@ version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev", "storage", "tools", "vdb"]
+groups = ["main", "dev", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
@@ -7064,7 +5729,7 @@ version = "2.2.3"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.9"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"},
@@ -7171,37 +5836,6 @@ files = [
numpy = ">=1.23.5"
types-pytz = ">=2022.1.1"
-[[package]]
-name = "pathos"
-version = "0.3.3"
-description = "parallel graph management and execution in heterogeneous computing"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pathos-0.3.3-py3-none-any.whl", hash = "sha256:e04616c6448608ad1f809360be22e3f2078d949a36a81e6991da6c2dd1f82513"},
- {file = "pathos-0.3.3.tar.gz", hash = "sha256:dcb2a5f321aa34ca541c1c1861011ea49df357bb908379c21dd5741f666e0a58"},
-]
-
-[package.dependencies]
-dill = ">=0.3.9"
-multiprocess = ">=0.70.17"
-pox = ">=0.3.5"
-ppft = ">=1.7.6.9"
-
-[[package]]
-name = "peewee"
-version = "3.17.8"
-description = "a little orm"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "peewee-3.17.8.tar.gz", hash = "sha256:ce1d05db3438830b989a1b9d0d0aa4e7f6134d5f6fd57686eeaa26a3e6485a8c"},
-]
-
[[package]]
name = "pgvecto-rs"
version = "0.2.2"
@@ -7247,7 +5881,7 @@ version = "11.1.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.9"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"},
@@ -7331,41 +5965,6 @@ tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "ole
typing = ["typing-extensions"]
xmp = ["defusedxml"]
-[[package]]
-name = "platformdirs"
-version = "4.3.6"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
- {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
-]
-
-[package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.11.2)"]
-
-[[package]]
-name = "plotly"
-version = "5.24.1"
-description = "An open-source, interactive data visualization library for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "plotly-5.24.1-py3-none-any.whl", hash = "sha256:f67073a1e637eb0dc3e46324d9d51e2fe76e9727c892dde64ddf1e1b51f29089"},
- {file = "plotly-5.24.1.tar.gz", hash = "sha256:dbc8ac8339d248a4bcc36e08a5659bacfe1b079390b8953533f4eb22169b4bae"},
-]
-
-[package.dependencies]
-packaging = "*"
-tenacity = ">=6.2.0"
-
[[package]]
name = "pluggy"
version = "1.5.0"
@@ -7389,7 +5988,7 @@ version = "3.11"
description = "Python Lex & Yacc"
optional = false
python-versions = "*"
-groups = ["lint", "tools"]
+groups = ["lint"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
@@ -7437,15 +6036,15 @@ pydantic = ">=1.9,<3.0"
[[package]]
name = "posthog"
-version = "3.7.5"
+version = "3.13.0"
description = "Integrate PostHog into any python application."
optional = false
python-versions = "*"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "posthog-3.7.5-py2.py3-none-any.whl", hash = "sha256:022132c17069dde03c5c5904e2ae1b9bd68d5059cbc5a8dffc5c1537a1b71cb5"},
- {file = "posthog-3.7.5.tar.gz", hash = "sha256:8ba40ab623da35db72715fc87fe7dccb7fc272ced92581fe31db2d4dbe7ad761"},
+ {file = "posthog-3.13.0-py2.py3-none-any.whl", hash = "sha256:0afd0132055a3da9c6b0ecf763e7f2ce2b66659ef16169883394d0835c30d501"},
+ {file = "posthog-3.13.0.tar.gz", hash = "sha256:54e9de232459846b1686a0cfb58acb02b7ccda379d837e1eb1c3af62c3775915"},
]
[package.dependencies]
@@ -7456,73 +6055,22 @@ requests = ">=2.7,<3.0"
six = ">=1.5"
[package.extras]
-dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"]
+dev = ["black", "django-stubs", "flake8", "flake8-print", "isort", "lxml", "mypy", "mypy-baseline", "pre-commit", "pydantic", "types-mock", "types-python-dateutil", "types-requests", "types-setuptools", "types-six"]
+langchain = ["langchain (>=0.2.0)"]
sentry = ["django", "sentry-sdk"]
-test = ["coverage", "django", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"]
-
-[[package]]
-name = "pox"
-version = "0.3.5"
-description = "utilities for filesystem exploration and automated builds"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pox-0.3.5-py3-none-any.whl", hash = "sha256:9e82bcc9e578b43e80a99cad80f0d8f44f4d424f0ee4ee8d4db27260a6aa365a"},
- {file = "pox-0.3.5.tar.gz", hash = "sha256:8120ee4c94e950e6e0483e050a4f0e56076e590ba0a9add19524c254bd23c2d1"},
-]
-
-[[package]]
-name = "ppft"
-version = "1.7.6.9"
-description = "distributed and parallel Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "ppft-1.7.6.9-py3-none-any.whl", hash = "sha256:dab36548db5ca3055067fbe6b1a17db5fee29f3c366c579a9a27cebb52ed96f0"},
- {file = "ppft-1.7.6.9.tar.gz", hash = "sha256:73161c67474ea9d81d04bcdad166d399cff3f084d5d2dc21ebdd46c075bbc265"},
-]
-
-[package.extras]
-dill = ["dill (>=0.3.9)"]
-
-[[package]]
-name = "primp"
-version = "0.10.0"
-description = "HTTP client that can impersonate web browsers, mimicking their headers and `TLS/JA3/JA4/HTTP2` fingerprints"
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "primp-0.10.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7a91a089bf2962b5b56c8d83d09535eb81cf55b53c09d83208b9e5a715cf2c17"},
- {file = "primp-0.10.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:0128453cce81552f7aa6ac2bf9b8741b7816cdb2d10536e62c77daaf6483b9af"},
- {file = "primp-0.10.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a959e9a83cff0ae7a85a02cc183e4db636f69ff41dddb7c4e32f997924923417"},
- {file = "primp-0.10.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8e711cfa019fa9bdc0cba4d5d596f319c884a4329e505bd73e92eee0b024061a"},
- {file = "primp-0.10.0-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:b859336d9a35669b68a29c5d8f050e0dca380452dabf6c9667bb8599f010d164"},
- {file = "primp-0.10.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dc875cc9a733fe3e6344a37f2b5888e0a9605bb37807fc3009f3b03786408f34"},
- {file = "primp-0.10.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a27c5d997c37bf8237963c11e376eaa66e7eccee39164e3e259a1c3767c304d6"},
- {file = "primp-0.10.0-cp38-abi3-win_amd64.whl", hash = "sha256:7fe94c3164c2efffff08f7f54c018ac445112961b3ce4f4f499315ba0a9d1ef3"},
- {file = "primp-0.10.0.tar.gz", hash = "sha256:93142590a5a1958240ee5b74faaf2f55185ed499ccaabc622d71cb0cc8a47a0b"},
-]
-
-[package.extras]
-dev = ["certifi", "pytest (>=8.1.1)"]
+test = ["anthropic", "coverage", "django", "flake8", "freezegun (==1.5.1)", "langchain-anthropic (>=0.2.0)", "langchain-community (>=0.2.0)", "langchain-openai (>=0.2.0)", "langgraph", "mock (>=2.0.0)", "openai", "pydantic", "pylint", "pytest", "pytest-asyncio", "pytest-timeout"]
[[package]]
name = "prompt-toolkit"
-version = "3.0.48"
+version = "3.0.50"
description = "Library for building powerful interactive command lines in Python"
optional = false
-python-versions = ">=3.7.0"
+python-versions = ">=3.8.0"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"},
- {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"},
+ {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"},
+ {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"},
]
[package.dependencies]
@@ -7534,7 +6082,7 @@ version = "0.2.1"
description = "Accelerated property cache"
optional = false
python-versions = ">=3.9"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"},
@@ -7623,15 +6171,15 @@ files = [
[[package]]
name = "proto-plus"
-version = "1.25.0"
-description = "Beautiful, Pythonic protocol buffers."
+version = "1.26.0"
+description = "Beautiful, Pythonic protocol buffers"
optional = false
python-versions = ">=3.7"
groups = ["main", "storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"},
- {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"},
+ {file = "proto_plus-1.26.0-py3-none-any.whl", hash = "sha256:bf2dfaa3da281fc3187d12d224c707cb57214fb2c22ba854eb0c105a3fb2d4d7"},
+ {file = "proto_plus-1.26.0.tar.gz", hash = "sha256:6e93d5f5ca267b54300880fff156b6a3386b3fa3f43b1da62e680fc0c586ef22"},
]
[package.dependencies]
@@ -7642,56 +6190,49 @@ testing = ["google-api-core (>=1.31.5)"]
[[package]]
name = "protobuf"
-version = "4.25.5"
+version = "4.25.6"
description = ""
optional = false
python-versions = ">=3.8"
groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"},
- {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"},
- {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"},
- {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"},
- {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"},
- {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"},
- {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"},
- {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"},
- {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"},
- {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"},
- {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"},
+ {file = "protobuf-4.25.6-cp310-abi3-win32.whl", hash = "sha256:61df6b5786e2b49fc0055f636c1e8f0aff263808bb724b95b164685ac1bcc13a"},
+ {file = "protobuf-4.25.6-cp310-abi3-win_amd64.whl", hash = "sha256:b8f837bfb77513fe0e2f263250f423217a173b6d85135be4d81e96a4653bcd3c"},
+ {file = "protobuf-4.25.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:6d4381f2417606d7e01750e2729fe6fbcda3f9883aa0c32b51d23012bded6c91"},
+ {file = "protobuf-4.25.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:5dd800da412ba7f6f26d2c08868a5023ce624e1fdb28bccca2dc957191e81fb5"},
+ {file = "protobuf-4.25.6-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:4434ff8bb5576f9e0c78f47c41cdf3a152c0b44de475784cd3fd170aef16205a"},
+ {file = "protobuf-4.25.6-cp38-cp38-win32.whl", hash = "sha256:8bad0f9e8f83c1fbfcc34e573352b17dfce7d0519512df8519994168dc015d7d"},
+ {file = "protobuf-4.25.6-cp38-cp38-win_amd64.whl", hash = "sha256:b6905b68cde3b8243a198268bb46fbec42b3455c88b6b02fb2529d2c306d18fc"},
+ {file = "protobuf-4.25.6-cp39-cp39-win32.whl", hash = "sha256:3f3b0b39db04b509859361ac9bca65a265fe9342e6b9406eda58029f5b1d10b2"},
+ {file = "protobuf-4.25.6-cp39-cp39-win_amd64.whl", hash = "sha256:6ef2045f89d4ad8d95fd43cd84621487832a61d15b49500e4c1350e8a0ef96be"},
+ {file = "protobuf-4.25.6-py3-none-any.whl", hash = "sha256:07972021c8e30b870cfc0863409d033af940213e0e7f64e27fe017b929d2c9f7"},
+ {file = "protobuf-4.25.6.tar.gz", hash = "sha256:f8cfbae7c5afd0d0eaccbe73267339bff605a2315860bb1ba08eb66670a9a91f"},
]
[[package]]
name = "psutil"
-version = "6.1.1"
-description = "Cross-platform lib for process and system monitoring in Python."
+version = "7.0.0"
+description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+python-versions = ">=3.6"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"},
- {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"},
- {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"},
- {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"},
- {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"},
- {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"},
- {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"},
- {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"},
- {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"},
- {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"},
- {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"},
- {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"},
- {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"},
- {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"},
- {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"},
- {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"},
- {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"},
+ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
+ {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
+ {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"},
+ {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"},
+ {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"},
+ {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"},
+ {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"},
+ {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"},
+ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"},
+ {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"},
]
[package.extras]
-dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
+dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
test = ["pytest", "pytest-xdist", "setuptools"]
[[package]]
@@ -7712,7 +6253,7 @@ version = "2.9.10"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"},
@@ -7810,62 +6351,6 @@ files = [
{file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"},
]
-[[package]]
-name = "pyarrow"
-version = "18.1.0"
-description = "Python library for Apache Arrow"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c"},
- {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4"},
- {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b"},
- {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71"},
- {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470"},
- {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56"},
- {file = "pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812"},
- {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854"},
- {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c"},
- {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21"},
- {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6"},
- {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe"},
- {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0"},
- {file = "pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a"},
- {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d"},
- {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee"},
- {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992"},
- {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54"},
- {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33"},
- {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30"},
- {file = "pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99"},
- {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b"},
- {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2"},
- {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191"},
- {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa"},
- {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c"},
- {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c"},
- {file = "pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181"},
- {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc"},
- {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386"},
- {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324"},
- {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8"},
- {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9"},
- {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba"},
- {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e"},
- {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76"},
- {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9"},
- {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754"},
- {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e"},
- {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7"},
- {file = "pyarrow-18.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052"},
- {file = "pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73"},
-]
-
-[package.extras]
-test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"]
-
[[package]]
name = "pyasn1"
version = "0.6.1"
@@ -7901,12 +6386,12 @@ version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
-markers = {main = "python_version == \"3.11\" or python_version >= \"3.12\"", storage = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"", tools = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation == \"PyPy\"", vdb = "python_version == \"3.11\" or python_version >= \"3.12\""}
+markers = {main = "python_version == \"3.11\" or python_version >= \"3.12\"", storage = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"", vdb = "python_version == \"3.11\" or python_version >= \"3.12\""}
[[package]]
name = "pycryptodome"
@@ -8122,38 +6607,6 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0
toml = ["tomli (>=2.0.1)"]
yaml = ["pyyaml (>=6.0.1)"]
-[[package]]
-name = "pydash"
-version = "8.0.4"
-description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pydash-8.0.4-py3-none-any.whl", hash = "sha256:59d0c9ca0d22b4f8bcfab01bfe2e89b49f4c9e9fa75961caf156094670260999"},
- {file = "pydash-8.0.4.tar.gz", hash = "sha256:a33fb17b4b06c617da5c57c711605d2dc8723311ee5388c8371f87cd44bf4112"},
-]
-
-[package.dependencies]
-typing-extensions = ">3.10,<4.6.0 || >4.6.0"
-
-[package.extras]
-dev = ["build", "coverage", "furo", "invoke", "mypy", "pytest", "pytest-cov", "pytest-mypy-testing", "ruff", "sphinx", "sphinx-autodoc-typehints", "tox", "twine", "wheel"]
-
-[[package]]
-name = "pydub"
-version = "0.25.1"
-description = "Manipulate audio with an simple and easy high level interface"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6"},
- {file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
-]
-
[[package]]
name = "pygments"
version = "2.19.1"
@@ -8176,7 +6629,7 @@ version = "2.8.0"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.7"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
@@ -8194,15 +6647,15 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pymilvus"
-version = "2.5.3"
+version = "2.5.4"
description = "Python Sdk for Milvus"
optional = false
python-versions = ">=3.8"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "pymilvus-2.5.3-py3-none-any.whl", hash = "sha256:64ca63594284586937274800be27a402f3be2d078130bf81d94ab8d7798ac9c8"},
- {file = "pymilvus-2.5.3.tar.gz", hash = "sha256:68bc3797b7a14c494caf116cee888894ffd6eba7b96a3ac841be85d60694cc5d"},
+ {file = "pymilvus-2.5.4-py3-none-any.whl", hash = "sha256:3f7ddaeae0c8f63554b8e316b73f265d022e05a457d47c366ce47293434a3aea"},
+ {file = "pymilvus-2.5.4.tar.gz", hash = "sha256:611732428ff669d57ded3d1f823bdeb10febf233d0251cce8498b287e5a10ce8"},
]
[package.dependencies]
@@ -8243,7 +6696,7 @@ version = "1.1.1"
description = "Pure Python MySQL Driver"
optional = false
python-versions = ">=3.7"
-groups = ["tools", "vdb"]
+groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c"},
@@ -8295,15 +6748,15 @@ test = ["pretend", "pytest (>=3.0.1)", "pytest-rerunfailures"]
[[package]]
name = "pypandoc"
-version = "1.14"
+version = "1.15"
description = "Thin wrapper for pandoc."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "pypandoc-1.14-py3-none-any.whl", hash = "sha256:1315c7ad7fac7236dacf69a05b521ed2c3f1d0177f70e9b92bfffce6c023df22"},
- {file = "pypandoc-1.14.tar.gz", hash = "sha256:6b4c45f5f1b9fb5bb562079164806bdbbc3e837b5402bcf3f1139edc5730a197"},
+ {file = "pypandoc-1.15-py3-none-any.whl", hash = "sha256:4ededcc76c8770f27aaca6dff47724578428eca84212a31479403a9731fc2b16"},
+ {file = "pypandoc-1.15.tar.gz", hash = "sha256:ea25beebe712ae41d63f7410c08741a3cab0e420f6703f95bc9b3a749192ce13"},
]
[[package]]
@@ -8324,15 +6777,15 @@ diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pypdf"
-version = "5.1.0"
+version = "5.3.0"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "pypdf-5.1.0-py3-none-any.whl", hash = "sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc"},
- {file = "pypdf-5.1.0.tar.gz", hash = "sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740"},
+ {file = "pypdf-5.3.0-py3-none-any.whl", hash = "sha256:d7b6db242f5f8fdb4990ae11815c394b8e1b955feda0befcce862efd8559c181"},
+ {file = "pypdf-5.3.0.tar.gz", hash = "sha256:08393660dfea25b27ec6fe863fb2f2248e6270da5103fae49e9dea8178741951"},
]
[package.extras]
@@ -8379,19 +6832,6 @@ files = [
{file = "PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378"},
]
-[[package]]
-name = "pypng"
-version = "0.20220715.0"
-description = "Pure Python library for saving and loading PNG images"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"},
- {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"},
-]
-
[[package]]
name = "pyproject-hooks"
version = "1.2.0"
@@ -8623,7 +7063,7 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-groups = ["main", "dev", "storage", "tools", "vdb"]
+groups = ["main", "dev", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
@@ -8668,19 +7108,19 @@ cli = ["click (>=5.0)"]
[[package]]
name = "python-iso639"
-version = "2024.10.22"
+version = "2025.2.8"
description = "ISO 639 language codes, names, and other associated information"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "python_iso639-2024.10.22-py3-none-any.whl", hash = "sha256:02d3ce2e01c6896b30b9cbbd3e1c8ee0d7221250b5d63ea9803e0d2a81fd1047"},
- {file = "python_iso639-2024.10.22.tar.gz", hash = "sha256:750f21b6a0bc6baa24253a3d8aae92b582bf93aa40988361cd96852c2c6d9a52"},
+ {file = "python_iso639-2025.2.8-py3-none-any.whl", hash = "sha256:fc072f1f2007eae4a877778a73d7653c51020973e719b502e0d31e95a92c99d0"},
+ {file = "python_iso639-2025.2.8.tar.gz", hash = "sha256:94f27c0286fc81ff0e033d7b63d63a5967eef584b97f13289366bf178953f5d7"},
]
[package.extras]
-dev = ["black (==24.10.0)", "build (==1.2.1)", "flake8 (==7.1.1)", "pytest (==8.3.3)", "requests (==2.32.3)", "twine (==5.1.1)"]
+dev = ["black (==25.1.0)", "build (==1.2.2)", "flake8 (==7.1.1)", "mypy (==1.15.0)", "pytest (==8.3.4)", "requests (==2.32.3)", "twine (==6.1.0)"]
[[package]]
name = "python-magic"
@@ -8697,21 +7137,21 @@ files = [
[[package]]
name = "python-oxmsg"
-version = "0.0.1"
+version = "0.0.2"
description = "Extract attachments from Outlook .msg files."
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "python_oxmsg-0.0.1-py3-none-any.whl", hash = "sha256:8ea7d5dda1bc161a413213da9e18ed152927c1fda2feaf5d1f02192d8ad45eea"},
- {file = "python_oxmsg-0.0.1.tar.gz", hash = "sha256:b65c1f93d688b85a9410afa824192a1ddc39da359b04a0bd2cbd3874e84d4994"},
+ {file = "python_oxmsg-0.0.2-py3-none-any.whl", hash = "sha256:22be29b14c46016bcd05e34abddfd8e05ee82082f53b82753d115da3fc7d0355"},
+ {file = "python_oxmsg-0.0.2.tar.gz", hash = "sha256:a6aff4deb1b5975d44d49dab1d9384089ffeec819e19c6940bc7ffbc84775fad"},
]
[package.dependencies]
click = "*"
olefile = "*"
-typing-extensions = ">=4.9.0"
+typing_extensions = ">=4.9.0"
[[package]]
name = "python-pptx"
@@ -8734,15 +7174,15 @@ XlsxWriter = ">=0.5.7"
[[package]]
name = "pytz"
-version = "2024.2"
+version = "2025.1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
- {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
+ {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"},
+ {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"},
]
[[package]]
@@ -8752,6 +7192,7 @@ description = "Python for Window Extensions"
optional = false
python-versions = "*"
groups = ["main", "vdb"]
+markers = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\""
files = [
{file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"},
{file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"},
@@ -8772,7 +7213,6 @@ files = [
{file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"},
{file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"},
]
-markers = {main = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_system == \"Windows\" or sys_platform == \"win32\")", vdb = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_system == \"Windows\""}
[[package]]
name = "pyxlsb"
@@ -8793,7 +7233,7 @@ version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
@@ -8879,31 +7319,6 @@ urllib3 = ">=1.26.14,<3"
[package.extras]
fastembed = ["fastembed (==0.1.1)"]
-[[package]]
-name = "qrcode"
-version = "7.4.2"
-description = "QR Code image generator"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"},
- {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-pypng = "*"
-typing-extensions = "*"
-
-[package.extras]
-all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"]
-dev = ["pytest", "pytest-cov", "tox"]
-maintainer = ["zest.releaser[recommended]"]
-pil = ["pillow (>=9.1.0)"]
-test = ["coverage", "pytest"]
-
[[package]]
name = "rank-bm25"
version = "0.2.2"
@@ -8925,101 +7340,101 @@ dev = ["pytest"]
[[package]]
name = "rapidfuzz"
-version = "3.11.0"
+version = "3.12.1"
description = "rapid fuzzy string matching"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb8a54543d16ab1b69e2c5ed96cabbff16db044a50eddfc028000138ca9ddf33"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:231c8b2efbd7f8d2ecd1ae900363ba168b8870644bb8f2b5aa96e4a7573bde19"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e7f442fb9cca81e9df32333fb075ef729052bcabe05b0afc0441f462299114"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:906f1f2a1b91c06599b3dd1be207449c5d4fc7bd1e1fa2f6aef161ea6223f165"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed59044aea9eb6c663112170f2399b040d5d7b162828b141f2673e822093fa8"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cb1965a28b0fa64abdee130c788a0bc0bb3cf9ef7e3a70bf055c086c14a3d7e"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b488b244931d0291412917e6e46ee9f6a14376625e150056fe7c4426ef28225"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ba13557fec9d5ffc0a22826754a7457cc77f1b25145be10b7bb1d143ce84c6"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3871fa7dfcef00bad3c7e8ae8d8fd58089bad6fb21f608d2bf42832267ca9663"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b2669eafee38c5884a6e7cc9769d25c19428549dcdf57de8541cf9e82822e7db"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ffa1bb0e26297b0f22881b219ffc82a33a3c84ce6174a9d69406239b14575bd5"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:45b15b8a118856ac9caac6877f70f38b8a0d310475d50bc814698659eabc1cdb"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-win32.whl", hash = "sha256:22033677982b9c4c49676f215b794b0404073f8974f98739cb7234e4a9ade9ad"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:be15496e7244361ff0efcd86e52559bacda9cd975eccf19426a0025f9547c792"},
- {file = "rapidfuzz-3.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:714a7ba31ba46b64d30fccfe95f8013ea41a2e6237ba11a805a27cdd3bce2573"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8724a978f8af7059c5323d523870bf272a097478e1471295511cf58b2642ff83"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b63cb1f2eb371ef20fb155e95efd96e060147bdd4ab9fc400c97325dfee9fe1"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82497f244aac10b20710448645f347d862364cc4f7d8b9ba14bd66b5ce4dec18"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:339607394941801e6e3f6c1ecd413a36e18454e7136ed1161388de674f47f9d9"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84819390a36d6166cec706b9d8f0941f115f700b7faecab5a7e22fc367408bc3"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eea8d9e20632d68f653455265b18c35f90965e26f30d4d92f831899d6682149b"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b659e1e2ea2784a9a397075a7fc395bfa4fe66424042161c4bcaf6e4f637b38"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1315cd2a351144572e31fe3df68340d4b83ddec0af8b2e207cd32930c6acd037"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a7743cca45b4684c54407e8638f6d07b910d8d811347b9d42ff21262c7c23245"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5bb636b0150daa6d3331b738f7c0f8b25eadc47f04a40e5c23c4bfb4c4e20ae3"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:42f4dd264ada7a9aa0805ea0da776dc063533917773cf2df5217f14eb4429eae"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51f24cb39e64256221e6952f22545b8ce21cacd59c0d3e367225da8fc4b868d8"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-win32.whl", hash = "sha256:aaf391fb6715866bc14681c76dc0308f46877f7c06f61d62cc993b79fc3c4a2a"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ebadd5b8624d8ad503e505a99b8eb26fe3ea9f8e9c2234e805a27b269e585842"},
- {file = "rapidfuzz-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:d895998fec712544c13cfe833890e0226585cf0391dd3948412441d5d68a2b8c"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f382fec4a7891d66fb7163c90754454030bb9200a13f82ee7860b6359f3f2fa8"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dfaefe08af2a928e72344c800dcbaf6508e86a4ed481e28355e8d4b6a6a5230e"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92ebb7c12f682b5906ed98429f48a3dd80dd0f9721de30c97a01473d1a346576"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1b3ebc62d4bcdfdeba110944a25ab40916d5383c5e57e7c4a8dc0b6c17211a"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c6d7fea39cb33e71de86397d38bf7ff1a6273e40367f31d05761662ffda49e4"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99aebef8268f2bc0b445b5640fd3312e080bd17efd3fbae4486b20ac00466308"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4469307f464ae3089acf3210b8fc279110d26d10f79e576f385a98f4429f7d97"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb97c53112b593f89a90b4f6218635a9d1eea1d7f9521a3b7d24864228bbc0aa"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef8937dae823b889c0273dfa0f0f6c46a3658ac0d851349c464d1b00e7ff4252"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d95f9e9f3777b96241d8a00d6377cc9c716981d828b5091082d0fe3a2924b43e"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b1d67d67f89e4e013a5295e7523bc34a7a96f2dba5dd812c7c8cb65d113cbf28"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d994cf27e2f874069884d9bddf0864f9b90ad201fcc9cb2f5b82bacc17c8d5f2"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-win32.whl", hash = "sha256:ba26d87fe7fcb56c4a53b549a9e0e9143f6b0df56d35fe6ad800c902447acd5b"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f7efdd7b7adb32102c2fa481ad6f11923e2deb191f651274be559d56fc913b"},
- {file = "rapidfuzz-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:ed78c8e94f57b44292c1a0350f580e18d3a3c5c0800e253f1583580c1b417ad2"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e60814edd0c9b511b5f377d48b9782b88cfe8be07a98f99973669299c8bb318a"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f28952da055dbfe75828891cd3c9abf0984edc8640573c18b48c14c68ca5e06"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e8f93bc736020351a6f8e71666e1f486bb8bd5ce8112c443a30c77bfde0eb68"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76a4a11ba8f678c9e5876a7d465ab86def047a4fcc043617578368755d63a1bc"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc0e0d41ad8a056a9886bac91ff9d9978e54a244deb61c2972cc76b66752de9c"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8ea35f2419c7d56b3e75fbde2698766daedb374f20eea28ac9b1f668ef4f74"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd340bbd025302276b5aa221dccfe43040c7babfc32f107c36ad783f2ffd8775"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:494eef2c68305ab75139034ea25328a04a548d297712d9cf887bf27c158c388b"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a167344c1d6db06915fb0225592afdc24d8bafaaf02de07d4788ddd37f4bc2f"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c7af25bda96ac799378ac8aba54a8ece732835c7b74cfc201b688a87ed11152"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d2a0f7e17f33e7890257367a1662b05fecaf56625f7dbb6446227aaa2b86448b"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d0d26c7172bdb64f86ee0765c5b26ea1dc45c52389175888ec073b9b28f4305"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-win32.whl", hash = "sha256:6ad02bab756751c90fa27f3069d7b12146613061341459abf55f8190d899649f"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:b1472986fd9c5d318399a01a0881f4a0bf4950264131bb8e2deba9df6d8c362b"},
- {file = "rapidfuzz-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c408f09649cbff8da76f8d3ad878b64ba7f7abdad1471efb293d2c075e80c822"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1bac4873f6186f5233b0084b266bfb459e997f4c21fc9f029918f44a9eccd304"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f9f12c2d0aa52b86206d2059916153876a9b1cf9dfb3cf2f344913167f1c3d4"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd501de6f7a8f83557d20613b58734d1cb5f0be78d794cde64fe43cfc63f5f2"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4416ca69af933d4a8ad30910149d3db6d084781d5c5fdedb713205389f535385"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0821b9bdf18c5b7d51722b906b233a39b17f602501a966cfbd9b285f8ab83cd"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0edecc3f90c2653298d380f6ea73b536944b767520c2179ec5d40b9145e47aa"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4513dd01cee11e354c31b75f652d4d466c9440b6859f84e600bdebfccb17735a"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d9727b85511b912571a76ce53c7640ba2c44c364e71cef6d7359b5412739c570"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ab9eab33ee3213f7751dc07a1a61b8d9a3d748ca4458fffddd9defa6f0493c16"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6b01c1ddbb054283797967ddc5433d5c108d680e8fa2684cf368be05407b07e4"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3857e335f97058c4b46fa39ca831290b70de554a5c5af0323d2f163b19c5f2a6"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d98a46cf07c0c875d27e8a7ed50f304d83063e49b9ab63f21c19c154b4c0d08d"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-win32.whl", hash = "sha256:c36539ed2c0173b053dafb221458812e178cfa3224ade0960599bec194637048"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec8d7d8567e14af34a7911c98f5ac74a3d4a743cd848643341fc92b12b3784ff"},
- {file = "rapidfuzz-3.11.0-cp39-cp39-win_arm64.whl", hash = "sha256:62171b270ecc4071be1c1f99960317db261d4c8c83c169e7f8ad119211fe7397"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f06e3c4c0a8badfc4910b9fd15beb1ad8f3b8fafa8ea82c023e5e607b66a78e4"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fe7aaf5a54821d340d21412f7f6e6272a9b17a0cbafc1d68f77f2fc11009dcd5"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25398d9ac7294e99876a3027ffc52c6bebeb2d702b1895af6ae9c541ee676702"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a52eea839e4bdc72c5e60a444d26004da00bb5bc6301e99b3dde18212e41465"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c87319b0ab9d269ab84f6453601fd49b35d9e4a601bbaef43743f26fabf496c"},
- {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3048c6ed29d693fba7d2a7caf165f5e0bb2b9743a0989012a98a47b975355cca"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b04f29735bad9f06bb731c214f27253bd8bedb248ef9b8a1b4c5bde65b838454"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7864e80a0d4e23eb6194254a81ee1216abdc53f9dc85b7f4d56668eced022eb8"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3794df87313dfb56fafd679b962e0613c88a293fd9bd5dd5c2793d66bf06a101"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d71da0012face6f45432a11bc59af19e62fac5a41f8ce489e80c0add8153c3d1"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff38378346b7018f42cbc1f6d1d3778e36e16d8595f79a312b31e7c25c50bd08"},
- {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6668321f90aa02a5a789d4e16058f2e4f2692c5230252425c3532a8a62bc3424"},
- {file = "rapidfuzz-3.11.0.tar.gz", hash = "sha256:a53ca4d3f52f00b393fab9b5913c5bafb9afc27d030c8a1db1283da6917a860f"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbb7ea2fd786e6d66f225ef6eef1728832314f47e82fee877cb2a793ebda9579"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ae41361de05762c1eaa3955e5355de7c4c6f30d1ef1ea23d29bf738a35809ab"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc3c39e0317e7f68ba01bac056e210dd13c7a0abf823e7b6a5fe7e451ddfc496"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69f2520296f1ae1165b724a3aad28c56fd0ac7dd2e4cff101a5d986e840f02d4"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34dcbf5a7daecebc242f72e2500665f0bde9dd11b779246c6d64d106a7d57c99"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:773ab37fccf6e0513891f8eb4393961ddd1053c6eb7e62eaa876e94668fc6d31"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ecf0e6de84c0bc2c0f48bc03ba23cef2c5f1245db7b26bc860c11c6fd7a097c"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4dc2ebad4adb29d84a661f6a42494df48ad2b72993ff43fad2b9794804f91e45"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8389d98b9f54cb4f8a95f1fa34bf0ceee639e919807bb931ca479c7a5f2930bf"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:165bcdecbfed9978962da1d3ec9c191b2ff9f1ccc2668fbaf0613a975b9aa326"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:129d536740ab0048c1a06ccff73c683f282a2347c68069affae8dbc423a37c50"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b67e390261ffe98ec86c771b89425a78b60ccb610c3b5874660216fcdbded4b"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-win32.whl", hash = "sha256:a66520180d3426b9dc2f8d312f38e19bc1fc5601f374bae5c916f53fa3534a7d"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:82260b20bc7a76556cecb0c063c87dad19246a570425d38f8107b8404ca3ac97"},
+ {file = "rapidfuzz-3.12.1-cp310-cp310-win_arm64.whl", hash = "sha256:3a860d103bbb25c69c2e995fdf4fac8cb9f77fb69ec0a00469d7fd87ff148f46"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d9afad7b16d01c9e8929b6a205a18163c7e61b6cd9bcf9c81be77d5afc1067a"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb424ae7240f2d2f7d8dda66a61ebf603f74d92f109452c63b0dbf400204a437"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42149e6d13bd6d06437d2a954dae2184dadbbdec0fdb82dafe92860d99f80519"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:760ac95d788f2964b73da01e0bdffbe1bf2ad8273d0437565ce9092ae6ad1fbc"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf27e8e4bf7bf9d92ef04f3d2b769e91c3f30ba99208c29f5b41e77271a2614"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00ceb8ff3c44ab0d6014106c71709c85dee9feedd6890eff77c814aa3798952b"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b61c558574fbc093d85940c3264c08c2b857b8916f8e8f222e7b86b0bb7d12"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:346a2d8f17224e99f9ef988606c83d809d5917d17ad00207237e0965e54f9730"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d60d1db1b7e470e71ae096b6456e20ec56b52bde6198e2dbbc5e6769fa6797dc"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2477da227e266f9c712f11393182c69a99d3c8007ea27f68c5afc3faf401cc43"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8499c7d963ddea8adb6cffac2861ee39a1053e22ca8a5ee9de1197f8dc0275a5"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12802e5c4d8ae104fb6efeeb436098325ce0dca33b461c46e8df015c84fbef26"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-win32.whl", hash = "sha256:e1061311d07e7cdcffa92c9b50c2ab4192907e70ca01b2e8e1c0b6b4495faa37"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6e4ed63e204daa863a802eec09feea5448617981ba5d150f843ad8e3ae071a4"},
+ {file = "rapidfuzz-3.12.1-cp311-cp311-win_arm64.whl", hash = "sha256:920733a28c3af47870835d59ca9879579f66238f10de91d2b4b3f809d1ebfc5b"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f6235b57ae3faa3f85cb3f90c9fee49b21bd671b76e90fc99e8ca2bdf0b5e4a3"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af4585e5812632c357fee5ab781c29f00cd06bea58f8882ff244cc4906ba6c9e"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5942dc4460e5030c5f9e1d4c9383de2f3564a2503fe25e13e89021bcbfea2f44"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b31ab59e1a0df5afc21f3109b6cfd77b34040dbf54f1bad3989f885cfae1e60"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97c885a7a480b21164f57a706418c9bbc9a496ec6da087e554424358cadde445"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d844c0587d969ce36fbf4b7cbf0860380ffeafc9ac5e17a7cbe8abf528d07bb"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93c95dce8917bf428064c64024de43ffd34ec5949dd4425780c72bd41f9d969"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:834f6113d538af358f39296604a1953e55f8eeffc20cb4caf82250edbb8bf679"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a940aa71a7f37d7f0daac186066bf6668d4d3b7e7ef464cb50bc7ba89eae1f51"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ec9eaf73501c9a7de2c6938cb3050392e2ee0c5ca3921482acf01476b85a7226"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c5ec360694ac14bfaeb6aea95737cf1a6cf805b5fe8ea7fd28814706c7fa838"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b5e176524653ac46f1802bdd273a4b44a5f8d0054ed5013a8e8a4b72f254599"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-win32.whl", hash = "sha256:6f463c6f1c42ec90e45d12a6379e18eddd5cdf74138804d8215619b6f4d31cea"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:b894fa2b30cd6498a29e5c470cb01c6ea898540b7e048a0342775a5000531334"},
+ {file = "rapidfuzz-3.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:43bb17056c5d1332f517b888c4e57846c4b5f936ed304917eeb5c9ac85d940d4"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:97f824c15bc6933a31d6e3cbfa90188ba0e5043cf2b6dd342c2b90ee8b3fd47c"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a973b3f5cabf931029a3ae4a0f72e3222e53d412ea85fc37ddc49e1774f00fbf"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7880e012228722dec1be02b9ef3898ed023388b8a24d6fa8213d7581932510"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c78582f50e75e6c2bc38c791ed291cb89cf26a3148c47860c1a04d6e5379c8e"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d7d9e6a04d8344b0198c96394c28874086888d0a2b2f605f30d1b27b9377b7d"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5620001fd4d6644a2f56880388179cc8f3767670f0670160fcb97c3b46c828af"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0666ab4c52e500af7ba5cc17389f5d15c0cdad06412c80312088519fdc25686d"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27b4d440fa50b50c515a91a01ee17e8ede719dca06eef4c0cccf1a111a4cfad3"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83dccfd5a754f2a0e8555b23dde31f0f7920601bfa807aa76829391ea81e7c67"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b572b634740e047c53743ed27a1bb3b4f93cf4abbac258cd7af377b2c4a9ba5b"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7fa7b81fb52902d5f78dac42b3d6c835a6633b01ddf9b202a3ca8443be4b2d6a"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1d4fbff980cb6baef4ee675963c081f7b5d6580a105d6a4962b20f1f880e1fb"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-win32.whl", hash = "sha256:3fe8da12ea77271097b303fa7624cfaf5afd90261002314e3b0047d36f4afd8d"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:6f7e92fc7d2a7f02e1e01fe4f539324dfab80f27cb70a30dd63a95445566946b"},
+ {file = "rapidfuzz-3.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:e31be53d7f4905a6a038296d8b773a79da9ee9f0cd19af9490c5c5a22e37d2e5"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bef5c91d5db776523530073cda5b2a276283258d2f86764be4a008c83caf7acd"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:841e0c2a5fbe8fc8b9b1a56e924c871899932c0ece7fbd970aa1c32bfd12d4bf"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:046fc67f3885d94693a2151dd913aaf08b10931639cbb953dfeef3151cb1027c"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4d2d39b2e76c17f92edd6d384dc21fa020871c73251cdfa017149358937a41d"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5857dda85165b986c26a474b22907db6b93932c99397c818bcdec96340a76d5"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c26cd1b9969ea70dbf0dbda3d2b54ab4b2e683d0fd0f17282169a19563efeb1"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf56ea4edd69005786e6c80a9049d95003aeb5798803e7a2906194e7a3cb6472"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbe7580b5fb2db8ebd53819171ff671124237a55ada3f64d20fc9a149d133960"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:018506a53c3b20dcbda8c93d4484b9eb1764c93d5ea16be103cf6b0d8b11d860"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:325c9c71b737fcd32e2a4e634c430c07dd3d374cfe134eded3fe46e4c6f9bf5d"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:930756639643e3aa02d3136b6fec74e5b9370a24f8796e1065cd8a857a6a6c50"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0acbd27543b158cb915fde03877383816a9e83257832818f1e803bac9b394900"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-win32.whl", hash = "sha256:80ff9283c54d7d29b2d954181e137deee89bec62f4a54675d8b6dbb6b15d3e03"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:fd37e53f0ed239d0cec27b250cec958982a8ba252ce64aa5e6052de3a82fa8db"},
+ {file = "rapidfuzz-3.12.1-cp39-cp39-win_arm64.whl", hash = "sha256:4a4422e4f73a579755ab60abccb3ff148b5c224b3c7454a13ca217dfbad54da6"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b7cba636c32a6fc3a402d1cb2c70c6c9f8e6319380aaf15559db09d868a23e56"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b79286738a43e8df8420c4b30a92712dec6247430b130f8e015c3a78b6d61ac2"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dc1937198e7ff67e217e60bfa339f05da268d91bb15fec710452d11fe2fdf60"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b85817a57cf8db32dd5d2d66ccfba656d299b09eaf86234295f89f91be1a0db2"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04283c6f3e79f13a784f844cd5b1df4f518ad0f70c789aea733d106c26e1b4fb"},
+ {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a718f740553aad5f4daef790191511da9c6eae893ee1fc2677627e4b624ae2db"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cbdf145c7e4ebf2e81c794ed7a582c4acad19e886d5ad6676086369bd6760753"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0d03ad14a26a477be221fddc002954ae68a9e2402b9d85433f2d0a6af01aa2bb"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1187aeae9c89e838d2a0a2b954b4052e4897e5f62e5794ef42527bf039d469e"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd47dfb1bca9673a48b923b3d988b7668ee8efd0562027f58b0f2b7abf27144c"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187cdb402e223264eebed2fe671e367e636a499a7a9c82090b8d4b75aa416c2a"},
+ {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6899b41bf6c30282179f77096c1939f1454836440a8ab05b48ebf7026a3b590"},
+ {file = "rapidfuzz-3.12.1.tar.gz", hash = "sha256:6a98bbca18b4a37adddf2d8201856441c26e9c981d8895491b5bc857b5f780eb"},
]
[package.extras]
@@ -9051,22 +7466,22 @@ test = ["coveralls", "pycodestyle", "pyflakes", "pylint", "pytest", "pytest-benc
[[package]]
name = "realtime"
-version = "2.1.0"
+version = "2.3.0"
description = ""
optional = false
python-versions = "<4.0,>=3.9"
groups = ["storage"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "realtime-2.1.0-py3-none-any.whl", hash = "sha256:e2d4f28bb2a08c1cf80e40fbf31e6116544ad29d67dd4093093e511ad738708c"},
- {file = "realtime-2.1.0.tar.gz", hash = "sha256:ca3ae6be47667a3cf3a307fec982ec1bf60313c38a8e29f016ab0380b76d7adb"},
+ {file = "realtime-2.3.0-py3-none-any.whl", hash = "sha256:6c241681d0517a3bc5e0132842bffd8b592286131b01a68b41cf7e0be94828fc"},
+ {file = "realtime-2.3.0.tar.gz", hash = "sha256:4071b095d7f750fcd68ec322e05045fce067b5cd5309a7ca809fcc87e50f56a1"},
]
[package.dependencies]
aiohttp = ">=3.11.11,<4.0.0"
python-dateutil = ">=2.8.1,<3.0.0"
typing-extensions = ">=4.12.2,<5.0.0"
-websockets = ">=11,<14"
+websockets = ">=11,<15"
[[package]]
name = "redis"
@@ -9091,20 +7506,21 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]]
name = "referencing"
-version = "0.35.1"
+version = "0.36.2"
description = "JSON Referencing + Python"
optional = false
-python-versions = ">=3.8"
-groups = ["main", "tools"]
+python-versions = ">=3.9"
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"},
- {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"},
+ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"},
+ {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"},
]
[package.dependencies]
attrs = ">=22.2.0"
rpds-py = ">=0.7.0"
+typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""}
[[package]]
name = "regex"
@@ -9211,28 +7627,6 @@ files = [
{file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"},
]
-[[package]]
-name = "replicate"
-version = "0.22.0"
-description = "Python client for Replicate"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "replicate-0.22.0-py3-none-any.whl", hash = "sha256:a11e20e9589981a96bee6f3817494b5cc29735a108c71aff4515a81863ad9996"},
- {file = "replicate-0.22.0.tar.gz", hash = "sha256:cab48c15ede619d5aa7d023a241626d504c70ea2b7db5792ebfb5ae9fa373cbc"},
-]
-
-[package.dependencies]
-httpx = ">=0.21.0,<1"
-packaging = "*"
-pydantic = ">1"
-typing-extensions = ">=4.5.0"
-
-[package.extras]
-dev = ["pylint", "pyright", "pytest", "pytest-asyncio", "pytest-recording", "respx", "ruff (>=0.1.3)"]
-
[[package]]
name = "requests"
version = "2.31.0"
@@ -9256,29 +7650,13 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-[[package]]
-name = "requests-file"
-version = "2.1.0"
-description = "File transport adapter for Requests"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c"},
- {file = "requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658"},
-]
-
-[package.dependencies]
-requests = ">=1.0.0"
-
[[package]]
name = "requests-oauthlib"
version = "2.0.0"
description = "OAuthlib authentication support for Requests."
optional = false
python-versions = ">=3.4"
-groups = ["main", "storage", "vdb"]
+groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"},
@@ -9367,7 +7745,7 @@ version = "0.22.3"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.9"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"},
@@ -9493,31 +7871,31 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
-version = "0.9.2"
+version = "0.9.6"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["lint"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"},
- {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"},
- {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"},
- {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"},
- {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"},
- {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"},
- {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"},
- {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"},
- {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"},
- {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"},
- {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"},
- {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"},
+ {file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
+ {file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
+ {file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
+ {file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
+ {file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
+ {file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
+ {file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
]
[[package]]
@@ -9673,201 +8051,6 @@ tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"]
testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"]
torch = ["safetensors[numpy]", "torch (>=1.10)"]
-[[package]]
-name = "sagemaker"
-version = "2.231.0"
-description = "Open source library for training and deploying models on Amazon SageMaker."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "sagemaker-2.231.0-py3-none-any.whl", hash = "sha256:5b6d84484a58c6ac8b22af42c6c5e0ea3c5f42d719345fe6aafba42f93635000"},
- {file = "sagemaker-2.231.0.tar.gz", hash = "sha256:d49ee9c35725832dd9810708938af723201b831e82924a3a6ac1c4260a3d8239"},
-]
-
-[package.dependencies]
-attrs = ">=23.1.0,<24"
-boto3 = ">=1.34.142,<2.0"
-cloudpickle = "2.2.1"
-docker = "*"
-google-pasta = "*"
-importlib-metadata = ">=1.4.0,<7.0"
-jsonschema = "*"
-numpy = ">=1.9.0,<2.0"
-packaging = ">=20.0"
-pandas = "*"
-pathos = "*"
-platformdirs = "*"
-protobuf = ">=3.12,<5.0"
-psutil = "*"
-pyyaml = ">=6.0,<7.0"
-requests = "*"
-sagemaker-core = ">=1.0.0,<2.0.0"
-schema = "*"
-smdebug-rulesconfig = "1.0.1"
-tblib = ">=1.7.0,<4"
-tqdm = "*"
-urllib3 = ">=1.26.8,<3.0.0"
-
-[package.extras]
-all = ["accelerate (>=0.24.1,<=0.27.0)", "docker (>=5.0.2,<8.0.0)", "fastapi (>=0.111.0)", "nest-asyncio", "pyspark (==3.3.1)", "pyyaml (>=5.4.1,<7)", "sagemaker-feature-store-pyspark-3-3", "sagemaker-schema-inference-artifacts (>=0.0.5)", "scipy (==1.10.1)", "urllib3 (>=1.26.8,<3.0.0)", "uvicorn (>=0.30.1)"]
-feature-processor = ["pyspark (==3.3.1)", "sagemaker-feature-store-pyspark-3-3"]
-huggingface = ["accelerate (>=0.24.1,<=0.27.0)", "fastapi (>=0.111.0)", "nest-asyncio", "sagemaker-schema-inference-artifacts (>=0.0.5)", "uvicorn (>=0.30.1)"]
-local = ["docker (>=5.0.2,<8.0.0)", "pyyaml (>=5.4.1,<7)", "urllib3 (>=1.26.8,<3.0.0)"]
-scipy = ["scipy (==1.10.1)"]
-test = ["accelerate (>=0.24.1,<=0.27.0)", "apache-airflow (==2.9.3)", "apache-airflow-providers-amazon (==7.2.1)", "attrs (>=23.1.0,<24)", "awslogs (==0.14.0)", "black (==24.3.0)", "build[virtualenv] (==1.2.1)", "cloudpickle (==2.2.1)", "contextlib2 (==21.6.0)", "coverage (>=5.2,<6.2)", "docker (>=5.0.2,<8.0.0)", "fabric (==2.6.0)", "fastapi (>=0.111.0)", "flake8 (==4.0.1)", "huggingface-hub (>=0.23.4)", "jinja2 (==3.1.4)", "mlflow (>=2.12.2,<2.13)", "mock (==4.0.3)", "nbformat (>=5.9,<6)", "nest-asyncio", "numpy (>=1.24.0)", "onnx (>=1.15.0)", "pandas (>=1.3.5,<1.5)", "pillow (>=10.0.1,<=11)", "pyspark (==3.3.1)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "pytest-rerunfailures (==10.2)", "pytest-timeout (==2.1.0)", "pytest-xdist (==2.4.0)", "pyvis (==0.2.1)", "pyyaml (==6.0)", "pyyaml (>=5.4.1,<7)", "requests (==2.32.2)", "sagemaker-experiments (==0.1.35)", "sagemaker-feature-store-pyspark-3-3", "sagemaker-schema-inference-artifacts (>=0.0.5)", "schema (==0.7.5)", "scikit-learn (==1.3.0)", "scipy (==1.10.1)", "stopit (==1.1.2)", "tensorflow (>=2.1,<=2.16)", "tox (==3.24.5)", "tritonclient[http] (<2.37.0)", "urllib3 (>=1.26.8,<3.0.0)", "uvicorn (>=0.30.1)", "xgboost (>=1.6.2,<=1.7.6)"]
-
-[[package]]
-name = "sagemaker-core"
-version = "1.0.16"
-description = "An python package for sagemaker core functionalities"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "sagemaker_core-1.0.16-py3-none-any.whl", hash = "sha256:603f70552c63d7a798b76749cad00a06af4b7362604a0f965d04b1c97f7a7128"},
- {file = "sagemaker_core-1.0.16.tar.gz", hash = "sha256:a5e7325bb2d5ad84e9a34fa81ea9a6d36a3b6aa0f02bf9c356a7973476951def"},
-]
-
-[package.dependencies]
-boto3 = ">=1.34.0,<2.0.0"
-importlib-metadata = ">=1.4.0,<7.0"
-jsonschema = "<5.0.0"
-mock = ">4.0,<5.0"
-platformdirs = ">=4.0.0,<5.0.0"
-pydantic = ">=2.0.0,<3.0.0"
-PyYAML = ">=6.0,<7.0"
-rich = ">=13.0.0,<14.0.0"
-
-[package.extras]
-codegen = ["black (>=24.3.0,<25.0.0)", "pandas (>=2.0.0,<3.0.0)", "pylint (>=3.0.0,<4.0.0)", "pytest (>=8.0.0,<9.0.0)"]
-
-[[package]]
-name = "schema"
-version = "0.7.7"
-description = "Simple data validation library"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "schema-0.7.7-py2.py3-none-any.whl", hash = "sha256:5d976a5b50f36e74e2157b47097b60002bd4d42e65425fcc9c9befadb4255dde"},
- {file = "schema-0.7.7.tar.gz", hash = "sha256:7da553abd2958a19dc2547c388cde53398b39196175a9be59ea1caf5ab0a1807"},
-]
-
-[[package]]
-name = "scikit-learn"
-version = "1.5.2"
-description = "A set of python modules for machine learning and data mining"
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "scikit_learn-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6"},
- {file = "scikit_learn-1.5.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0"},
- {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540"},
- {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8"},
- {file = "scikit_learn-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113"},
- {file = "scikit_learn-1.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03b6158efa3faaf1feea3faa884c840ebd61b6484167c711548fce208ea09445"},
- {file = "scikit_learn-1.5.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1ff45e26928d3b4eb767a8f14a9a6efbf1cbff7c05d1fb0f95f211a89fd4f5de"},
- {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f763897fe92d0e903aa4847b0aec0e68cadfff77e8a0687cabd946c89d17e675"},
- {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8b0ccd4a902836493e026c03256e8b206656f91fbcc4fde28c57a5b752561f1"},
- {file = "scikit_learn-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:6c16d84a0d45e4894832b3c4d0bf73050939e21b99b01b6fd59cbb0cf39163b6"},
- {file = "scikit_learn-1.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a"},
- {file = "scikit_learn-1.5.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1"},
- {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"},
- {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca64b3089a6d9b9363cd3546f8978229dcbb737aceb2c12144ee3f70f95684b7"},
- {file = "scikit_learn-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:3bed4909ba187aca80580fe2ef370d9180dcf18e621a27c4cf2ef10d279a7efe"},
- {file = "scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d"},
-]
-
-[package.dependencies]
-joblib = ">=1.2.0"
-numpy = ">=1.19.5"
-scipy = ">=1.6.0"
-threadpoolctl = ">=3.1.0"
-
-[package.extras]
-benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"]
-build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"]
-docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.16.0)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)"]
-examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"]
-install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"]
-maintenance = ["conda-lock (==2.5.6)"]
-tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.2.1)", "scikit-image (>=0.17.2)"]
-
-[[package]]
-name = "scipy"
-version = "1.15.0"
-description = "Fundamental algorithms for scientific computing in Python"
-optional = false
-python-versions = ">=3.10"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "scipy-1.15.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:aeac60d3562a7bf2f35549bdfdb6b1751c50590f55ce7322b4b2fc821dc27fca"},
- {file = "scipy-1.15.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5abbdc6ede5c5fed7910cf406a948e2c0869231c0db091593a6b2fa78be77e5d"},
- {file = "scipy-1.15.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:eb1533c59f0ec6c55871206f15a5c72d1fae7ad3c0a8ca33ca88f7c309bbbf8c"},
- {file = "scipy-1.15.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:de112c2dae53107cfeaf65101419662ac0a54e9a088c17958b51c95dac5de56d"},
- {file = "scipy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2240e1fd0782e62e1aacdc7234212ee271d810f67e9cd3b8d521003a82603ef8"},
- {file = "scipy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35aef233b098e4de88b1eac29f0df378278e7e250a915766786b773309137c4"},
- {file = "scipy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b29e4fc02e155a5fd1165f1e6a73edfdd110470736b0f48bcbe48083f0eee37"},
- {file = "scipy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:0e5b34f8894f9904cc578008d1a9467829c1817e9f9cb45e6d6eeb61d2ab7731"},
- {file = "scipy-1.15.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:46e91b5b16909ff79224b56e19cbad65ca500b3afda69225820aa3afbf9ec020"},
- {file = "scipy-1.15.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:82bff2eb01ccf7cea8b6ee5274c2dbeadfdac97919da308ee6d8e5bcbe846443"},
- {file = "scipy-1.15.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:9c8254fe21dd2c6c8f7757035ec0c31daecf3bb3cffd93bc1ca661b731d28136"},
- {file = "scipy-1.15.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:c9624eeae79b18cab1a31944b5ef87aa14b125d6ab69b71db22f0dbd962caf1e"},
- {file = "scipy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d13bbc0658c11f3d19df4138336e4bce2c4fbd78c2755be4bf7b8e235481557f"},
- {file = "scipy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdca4c7bb8dc41307e5f39e9e5d19c707d8e20a29845e7533b3bb20a9d4ccba0"},
- {file = "scipy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f376d7c767731477bac25a85d0118efdc94a572c6b60decb1ee48bf2391a73b"},
- {file = "scipy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:61513b989ee8d5218fbeb178b2d51534ecaddba050db949ae99eeb3d12f6825d"},
- {file = "scipy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5beb0a2200372b7416ec73fdae94fe81a6e85e44eb49c35a11ac356d2b8eccc6"},
- {file = "scipy-1.15.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fde0f3104dfa1dfbc1f230f65506532d0558d43188789eaf68f97e106249a913"},
- {file = "scipy-1.15.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:35c68f7044b4e7ad73a3e68e513dda946989e523df9b062bd3cf401a1a882192"},
- {file = "scipy-1.15.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:52475011be29dfcbecc3dfe3060e471ac5155d72e9233e8d5616b84e2b542054"},
- {file = "scipy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5972e3f96f7dda4fd3bb85906a17338e65eaddfe47f750e240f22b331c08858e"},
- {file = "scipy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe00169cf875bed0b3c40e4da45b57037dc21d7c7bf0c85ed75f210c281488f1"},
- {file = "scipy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:161f80a98047c219c257bf5ce1777c574bde36b9d962a46b20d0d7e531f86863"},
- {file = "scipy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:327163ad73e54541a675240708244644294cb0a65cca420c9c79baeb9648e479"},
- {file = "scipy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0fcb16eb04d84670722ce8d93b05257df471704c913cb0ff9dc5a1c31d1e9422"},
- {file = "scipy-1.15.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:767e8cf6562931f8312f4faa7ddea412cb783d8df49e62c44d00d89f41f9bbe8"},
- {file = "scipy-1.15.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:37ce9394cdcd7c5f437583fc6ef91bd290014993900643fdfc7af9b052d1613b"},
- {file = "scipy-1.15.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6d26f17c64abd6c6c2dfb39920f61518cc9e213d034b45b2380e32ba78fde4c0"},
- {file = "scipy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e2448acd79c6374583581a1ded32ac71a00c2b9c62dfa87a40e1dd2520be111"},
- {file = "scipy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36be480e512d38db67f377add5b759fb117edd987f4791cdf58e59b26962bee4"},
- {file = "scipy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ccb6248a9987193fe74363a2d73b93bc2c546e0728bd786050b7aef6e17db03c"},
- {file = "scipy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:952d2e9eaa787f0a9e95b6e85da3654791b57a156c3e6609e65cc5176ccfe6f2"},
- {file = "scipy-1.15.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b1432102254b6dc7766d081fa92df87832ac25ff0b3d3a940f37276e63eb74ff"},
- {file = "scipy-1.15.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:4e08c6a36f46abaedf765dd2dfcd3698fa4bd7e311a9abb2d80e33d9b2d72c34"},
- {file = "scipy-1.15.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ec915cd26d76f6fc7ae8522f74f5b2accf39546f341c771bb2297f3871934a52"},
- {file = "scipy-1.15.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:351899dd2a801edd3691622172bc8ea01064b1cada794f8641b89a7dc5418db6"},
- {file = "scipy-1.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9baff912ea4f78a543d183ed6f5b3bea9784509b948227daaf6f10727a0e2e5"},
- {file = "scipy-1.15.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cd9d9198a7fd9a77f0eb5105ea9734df26f41faeb2a88a0e62e5245506f7b6df"},
- {file = "scipy-1.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:129f899ed275c0515d553b8d31696924e2ca87d1972421e46c376b9eb87de3d2"},
- {file = "scipy-1.15.0.tar.gz", hash = "sha256:300742e2cc94e36a2880ebe464a1c8b4352a7b0f3e36ec3d2ac006cdbe0219ac"},
-]
-
-[package.dependencies]
-numpy = ">=1.23.5,<2.5"
-
-[package.extras]
-dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
-doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
-test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
-
[[package]]
name = "sentry-sdk"
version = "1.44.1"
@@ -9922,15 +8105,15 @@ tornado = ["tornado (>=5)"]
[[package]]
name = "setuptools"
-version = "75.7.0"
+version = "75.8.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.9"
groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "setuptools-75.7.0-py3-none-any.whl", hash = "sha256:84fb203f278ebcf5cd08f97d3fb96d3fbed4b629d500b29ad60d11e00769b183"},
- {file = "setuptools-75.7.0.tar.gz", hash = "sha256:886ff7b16cd342f1d1defc16fc98c9ce3fde69e087a4e1983d7ab634e5f41f4f"},
+ {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"},
+ {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"},
]
[package.extras]
@@ -9938,73 +8121,61 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"]
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "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.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.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.14.*)", "pytest-mypy"]
-
-[[package]]
-name = "sgmllib3k"
-version = "1.0.0"
-description = "Py3k port of sgmllib."
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"},
-]
+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.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.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.14.*)", "pytest-mypy"]
[[package]]
name = "shapely"
-version = "2.0.6"
+version = "2.0.7"
description = "Manipulation and analysis of geometric objects"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "shapely-2.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a34e068da2d321e926b5073539fd2a1d4429a2c656bd63f0bd4c8f5b236d0b"},
- {file = "shapely-2.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c84c3f53144febf6af909d6b581bc05e8785d57e27f35ebaa5c1ab9baba13b"},
- {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad2fae12dca8d2b727fa12b007e46fbc522148a584f5d6546c539f3464dccde"},
- {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3304883bd82d44be1b27a9d17f1167fda8c7f5a02a897958d86c59ec69b705e"},
- {file = "shapely-2.0.6-cp310-cp310-win32.whl", hash = "sha256:3ec3a0eab496b5e04633a39fa3d5eb5454628228201fb24903d38174ee34565e"},
- {file = "shapely-2.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:28f87cdf5308a514763a5c38de295544cb27429cfa655d50ed8431a4796090c4"},
- {file = "shapely-2.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5aeb0f51a9db176da9a30cb2f4329b6fbd1e26d359012bb0ac3d3c7781667a9e"},
- {file = "shapely-2.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a7a78b0d51257a367ee115f4d41ca4d46edbd0dd280f697a8092dd3989867b2"},
- {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32c23d2f43d54029f986479f7c1f6e09c6b3a19353a3833c2ffb226fb63a855"},
- {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dc9fb0eb56498912025f5eb352b5126f04801ed0e8bdbd867d21bdbfd7cbd0"},
- {file = "shapely-2.0.6-cp311-cp311-win32.whl", hash = "sha256:d93b7e0e71c9f095e09454bf18dad5ea716fb6ced5df3cb044564a00723f339d"},
- {file = "shapely-2.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:c02eb6bf4cfb9fe6568502e85bb2647921ee49171bcd2d4116c7b3109724ef9b"},
- {file = "shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0"},
- {file = "shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3"},
- {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8"},
- {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726"},
- {file = "shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f"},
- {file = "shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48"},
- {file = "shapely-2.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:42805ef90783ce689a4dde2b6b2f261e2c52609226a0438d882e3ced40bb3013"},
- {file = "shapely-2.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d2cb146191a47bd0cee8ff5f90b47547b82b6345c0d02dd8b25b88b68af62d7"},
- {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3fdef0a1794a8fe70dc1f514440aa34426cc0ae98d9a1027fb299d45741c381"},
- {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c665a0301c645615a107ff7f52adafa2153beab51daf34587170d85e8ba6805"},
- {file = "shapely-2.0.6-cp313-cp313-win32.whl", hash = "sha256:0334bd51828f68cd54b87d80b3e7cee93f249d82ae55a0faf3ea21c9be7b323a"},
- {file = "shapely-2.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:d37d070da9e0e0f0a530a621e17c0b8c3c9d04105655132a87cfff8bd77cc4c2"},
- {file = "shapely-2.0.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fa7468e4f5b92049c0f36d63c3e309f85f2775752e076378e36c6387245c5462"},
- {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed5867e598a9e8ac3291da6cc9baa62ca25706eea186117034e8ec0ea4355653"},
- {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81d9dfe155f371f78c8d895a7b7f323bb241fb148d848a2bf2244f79213123fe"},
- {file = "shapely-2.0.6-cp37-cp37m-win32.whl", hash = "sha256:fbb7bf02a7542dba55129062570211cfb0defa05386409b3e306c39612e7fbcc"},
- {file = "shapely-2.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:837d395fac58aa01aa544495b97940995211e3e25f9aaf87bc3ba5b3a8cd1ac7"},
- {file = "shapely-2.0.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c6d88ade96bf02f6bfd667ddd3626913098e243e419a0325ebef2bbd481d1eb6"},
- {file = "shapely-2.0.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b3b818c4407eaa0b4cb376fd2305e20ff6df757bf1356651589eadc14aab41b"},
- {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbc783529a21f2bd50c79cef90761f72d41c45622b3e57acf78d984c50a5d13"},
- {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2423f6c0903ebe5df6d32e0066b3d94029aab18425ad4b07bf98c3972a6e25a1"},
- {file = "shapely-2.0.6-cp38-cp38-win32.whl", hash = "sha256:2de00c3bfa80d6750832bde1d9487e302a6dd21d90cb2f210515cefdb616e5f5"},
- {file = "shapely-2.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:3a82d58a1134d5e975f19268710e53bddd9c473743356c90d97ce04b73e101ee"},
- {file = "shapely-2.0.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:392f66f458a0a2c706254f473290418236e52aa4c9b476a072539d63a2460595"},
- {file = "shapely-2.0.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eba5bae271d523c938274c61658ebc34de6c4b33fdf43ef7e938b5776388c1be"},
- {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060566bc4888b0c8ed14b5d57df8a0ead5c28f9b69fb6bed4476df31c51b0af"},
- {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b02154b3e9d076a29a8513dffcb80f047a5ea63c897c0cd3d3679f29363cf7e5"},
- {file = "shapely-2.0.6-cp39-cp39-win32.whl", hash = "sha256:44246d30124a4f1a638a7d5419149959532b99dfa25b54393512e6acc9c211ac"},
- {file = "shapely-2.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:2b542d7f1dbb89192d3512c52b679c822ba916f93479fa5d4fc2fe4fa0b3c9e8"},
- {file = "shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6"},
+ {file = "shapely-2.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:33fb10e50b16113714ae40adccf7670379e9ccf5b7a41d0002046ba2b8f0f691"},
+ {file = "shapely-2.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f44eda8bd7a4bccb0f281264b34bf3518d8c4c9a8ffe69a1a05dabf6e8461147"},
+ {file = "shapely-2.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf6c50cd879831955ac47af9c907ce0310245f9d162e298703f82e1785e38c98"},
+ {file = "shapely-2.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04a65d882456e13c8b417562c36324c0cd1e5915f3c18ad516bb32ee3f5fc895"},
+ {file = "shapely-2.0.7-cp310-cp310-win32.whl", hash = "sha256:7e97104d28e60b69f9b6a957c4d3a2a893b27525bc1fc96b47b3ccef46726bf2"},
+ {file = "shapely-2.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:35524cc8d40ee4752520819f9894b9f28ba339a42d4922e92c99b148bed3be39"},
+ {file = "shapely-2.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5cf23400cb25deccf48c56a7cdda8197ae66c0e9097fcdd122ac2007e320bc34"},
+ {file = "shapely-2.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8f1da01c04527f7da59ee3755d8ee112cd8967c15fab9e43bba936b81e2a013"},
+ {file = "shapely-2.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f623b64bb219d62014781120f47499a7adc30cf7787e24b659e56651ceebcb0"},
+ {file = "shapely-2.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6d95703efaa64aaabf278ced641b888fc23d9c6dd71f8215091afd8a26a66e3"},
+ {file = "shapely-2.0.7-cp311-cp311-win32.whl", hash = "sha256:2f6e4759cf680a0f00a54234902415f2fa5fe02f6b05546c662654001f0793a2"},
+ {file = "shapely-2.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:b52f3ab845d32dfd20afba86675c91919a622f4627182daec64974db9b0b4608"},
+ {file = "shapely-2.0.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4c2b9859424facbafa54f4a19b625a752ff958ab49e01bc695f254f7db1835fa"},
+ {file = "shapely-2.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5aed1c6764f51011d69a679fdf6b57e691371ae49ebe28c3edb5486537ffbd51"},
+ {file = "shapely-2.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73c9ae8cf443187d784d57202199bf9fd2d4bb7d5521fe8926ba40db1bc33e8e"},
+ {file = "shapely-2.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9469f49ff873ef566864cb3516091881f217b5d231c8164f7883990eec88b73"},
+ {file = "shapely-2.0.7-cp312-cp312-win32.whl", hash = "sha256:6bca5095e86be9d4ef3cb52d56bdd66df63ff111d580855cb8546f06c3c907cd"},
+ {file = "shapely-2.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:f86e2c0259fe598c4532acfcf638c1f520fa77c1275912bbc958faecbf00b108"},
+ {file = "shapely-2.0.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a0c09e3e02f948631c7763b4fd3dd175bc45303a0ae04b000856dedebefe13cb"},
+ {file = "shapely-2.0.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06ff6020949b44baa8fc2e5e57e0f3d09486cd5c33b47d669f847c54136e7027"},
+ {file = "shapely-2.0.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6dbf096f961ca6bec5640e22e65ccdec11e676344e8157fe7d636e7904fd36"},
+ {file = "shapely-2.0.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adeddfb1e22c20548e840403e5e0b3d9dc3daf66f05fa59f1fcf5b5f664f0e98"},
+ {file = "shapely-2.0.7-cp313-cp313-win32.whl", hash = "sha256:a7f04691ce1c7ed974c2f8b34a1fe4c3c5dfe33128eae886aa32d730f1ec1913"},
+ {file = "shapely-2.0.7-cp313-cp313-win_amd64.whl", hash = "sha256:aaaf5f7e6cc234c1793f2a2760da464b604584fb58c6b6d7d94144fd2692d67e"},
+ {file = "shapely-2.0.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19cbc8808efe87a71150e785b71d8a0e614751464e21fb679d97e274eca7bd43"},
+ {file = "shapely-2.0.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc19b78cc966db195024d8011649b4e22812f805dd49264323980715ab80accc"},
+ {file = "shapely-2.0.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd37d65519b3f8ed8976fa4302a2827cbb96e0a461a2e504db583b08a22f0b98"},
+ {file = "shapely-2.0.7-cp37-cp37m-win32.whl", hash = "sha256:25085a30a2462cee4e850a6e3fb37431cbbe4ad51cbcc163af0cea1eaa9eb96d"},
+ {file = "shapely-2.0.7-cp37-cp37m-win_amd64.whl", hash = "sha256:1a2e03277128e62f9a49a58eb7eb813fa9b343925fca5e7d631d50f4c0e8e0b8"},
+ {file = "shapely-2.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e1c4f1071fe9c09af077a69b6c75f17feb473caeea0c3579b3e94834efcbdc36"},
+ {file = "shapely-2.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3697bd078b4459f5a1781015854ef5ea5d824dbf95282d0b60bfad6ff83ec8dc"},
+ {file = "shapely-2.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e9fed9a7d6451979d914cb6ebbb218b4b4e77c0d50da23e23d8327948662611"},
+ {file = "shapely-2.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2934834c7f417aeb7cba3b0d9b4441a76ebcecf9ea6e80b455c33c7c62d96a24"},
+ {file = "shapely-2.0.7-cp38-cp38-win32.whl", hash = "sha256:2e4a1749ad64bc6e7668c8f2f9479029f079991f4ae3cb9e6b25440e35a4b532"},
+ {file = "shapely-2.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8ae5cb6b645ac3fba34ad84b32fbdccb2ab321facb461954925bde807a0d3b74"},
+ {file = "shapely-2.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4abeb44b3b946236e4e1a1b3d2a0987fb4d8a63bfb3fdefb8a19d142b72001e5"},
+ {file = "shapely-2.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd0e75d9124b73e06a42bf1615ad3d7d805f66871aa94538c3a9b7871d620013"},
+ {file = "shapely-2.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7977d8a39c4cf0e06247cd2dca695ad4e020b81981d4c82152c996346cf1094b"},
+ {file = "shapely-2.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0145387565fcf8f7c028b073c802956431308da933ef41d08b1693de49990d27"},
+ {file = "shapely-2.0.7-cp39-cp39-win32.whl", hash = "sha256:98697c842d5c221408ba8aa573d4f49caef4831e9bc6b6e785ce38aca42d1999"},
+ {file = "shapely-2.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:a3fb7fbae257e1b042f440289ee7235d03f433ea880e73e687f108d044b24db5"},
+ {file = "shapely-2.0.7.tar.gz", hash = "sha256:28fe2997aab9a9dc026dc6a355d04e85841546b2a5d232ed953e3321ab958ee5"},
]
[package.dependencies]
@@ -10027,52 +8198,19 @@ files = [
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
]
-[[package]]
-name = "simple-websocket"
-version = "1.1.0"
-description = "Simple WebSocket server and client for Python"
-optional = false
-python-versions = ">=3.6"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"},
- {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"},
-]
-
-[package.dependencies]
-wsproto = "*"
-
-[package.extras]
-dev = ["flake8", "pytest", "pytest-cov", "tox"]
-docs = ["sphinx"]
-
[[package]]
name = "six"
version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-groups = ["main", "dev", "storage", "tools", "vdb"]
+groups = ["main", "dev", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
]
-[[package]]
-name = "smdebug-rulesconfig"
-version = "1.0.1"
-description = "SMDebug RulesConfig"
-optional = false
-python-versions = ">=2.7"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "smdebug_rulesconfig-1.0.1-py2.py3-none-any.whl", hash = "sha256:104da3e6931ecf879dfc687ca4bbb3bee5ea2bc27f4478e9dbb3ee3655f1ae61"},
- {file = "smdebug_rulesconfig-1.0.1.tar.gz", hash = "sha256:7a19e6eb2e6bcfefbc07e4a86ef7a88f32495001a038bf28c7d8e77ab793fcd6"},
-]
-
[[package]]
name = "sniffio"
version = "1.3.1"
@@ -10105,7 +8243,7 @@ version = "2.6"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"},
@@ -10118,7 +8256,7 @@ version = "2.0.35"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev", "tools", "vdb"]
+groups = ["main", "dev", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b"},
@@ -10201,23 +8339,6 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"]
-[[package]]
-name = "sqlparse"
-version = "0.5.3"
-description = "A non-validating SQL parser."
-optional = false
-python-versions = ">=3.8"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"},
- {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"},
-]
-
-[package.extras]
-dev = ["build", "hatch"]
-doc = ["sphinx"]
-
[[package]]
name = "starlette"
version = "0.41.0"
@@ -10255,22 +8376,6 @@ httpx = {version = ">=0.26,<0.28", extras = ["http2"]}
python-dateutil = ">=2.8.2,<3.0.0"
typing-extensions = ">=4.2.0,<5.0.0"
-[[package]]
-name = "strictyaml"
-version = "1.7.3"
-description = "Strict, typed YAML parser"
-optional = false
-python-versions = ">=3.7.0"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "strictyaml-1.7.3-py3-none-any.whl", hash = "sha256:fb5c8a4edb43bebb765959e420f9b3978d7f1af88c80606c03fb420888f5d1c7"},
- {file = "strictyaml-1.7.3.tar.gz", hash = "sha256:22f854a5fcab42b5ddba8030a0e4be51ca89af0267961c8d6cfa86395586c407"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.6.0"
-
[[package]]
name = "supabase"
version = "2.8.1"
@@ -10328,35 +8433,6 @@ mpmath = ">=1.1.0,<1.4"
[package.extras]
dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"]
-[[package]]
-name = "tabulate"
-version = "0.9.0"
-description = "Pretty-print tabular data"
-optional = false
-python-versions = ">=3.7"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"},
- {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"},
-]
-
-[package.extras]
-widechars = ["wcwidth"]
-
-[[package]]
-name = "tblib"
-version = "3.0.0"
-description = "Traceback serialization library."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129"},
- {file = "tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6"},
-]
-
[[package]]
name = "tcvectordb"
version = "1.3.2"
@@ -10380,7 +8456,7 @@ version = "9.0.0"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"},
@@ -10391,67 +8467,6 @@ files = [
doc = ["reno", "sphinx"]
test = ["pytest", "tornado (>=4.5)", "typeguard"]
-[[package]]
-name = "tencentcloud-sdk-python-common"
-version = "3.0.1298"
-description = "Tencent Cloud Common SDK for Python"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tencentcloud-sdk-python-common-3.0.1298.tar.gz", hash = "sha256:0f0f182410c1ceda5764ff8bcbef27aa6139caf1c5f5985d94ec731a41c8a59f"},
- {file = "tencentcloud_sdk_python_common-3.0.1298-py2.py3-none-any.whl", hash = "sha256:c80929a0ff57ebee4ceec749dc82d5f2d1105b888e55175a7e9c722afc3a5d7a"},
-]
-
-[package.dependencies]
-requests = ">=2.16.0"
-
-[[package]]
-name = "tencentcloud-sdk-python-hunyuan"
-version = "3.0.1298"
-description = "Tencent Cloud Hunyuan SDK for Python"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tencentcloud-sdk-python-hunyuan-3.0.1298.tar.gz", hash = "sha256:c3d86a577de02046d25682a3804955453555fa641082bb8765238460bded3f03"},
- {file = "tencentcloud_sdk_python_hunyuan-3.0.1298-py2.py3-none-any.whl", hash = "sha256:f01e33318b6a4152ac88c500fda77f2cda1864eeca000cdd29c41e4f92f8de65"},
-]
-
-[package.dependencies]
-tencentcloud-sdk-python-common = "3.0.1298"
-
-[[package]]
-name = "termcolor"
-version = "2.5.0"
-description = "ANSI color formatting for output in terminal"
-optional = false
-python-versions = ">=3.9"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"},
- {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"},
-]
-
-[package.extras]
-tests = ["pytest", "pytest-cov"]
-
-[[package]]
-name = "threadpoolctl"
-version = "3.5.0"
-description = "threadpoolctl"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"},
- {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"},
-]
-
[[package]]
name = "tidb-vector"
version = "0.0.9"
@@ -10520,41 +8535,6 @@ requests = ">=2.26.0"
[package.extras]
blobfile = ["blobfile (>=2)"]
-[[package]]
-name = "tinysegmenter"
-version = "0.3"
-description = "Very compact Japanese tokenizer"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tinysegmenter-0.3.tar.gz", hash = "sha256:ed1f6d2e806a4758a73be589754384cbadadc7e1a414c81a166fc9adf2d40c6d"},
-]
-
-[[package]]
-name = "tldextract"
-version = "5.1.3"
-description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well."
-optional = false
-python-versions = ">=3.9"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "tldextract-5.1.3-py3-none-any.whl", hash = "sha256:78de310cc2ca018692de5ddf320f9d6bd7c5cf857d0fd4f2175f0cdf4440ea75"},
- {file = "tldextract-5.1.3.tar.gz", hash = "sha256:d43c7284c23f5dc8a42fd0fee2abede2ff74cc622674e4cb07f514ab3330c338"},
-]
-
-[package.dependencies]
-filelock = ">=3.0.8"
-idna = "*"
-requests = ">=2.1.0"
-requests-file = ">=1.4"
-
-[package.extras]
-release = ["build", "twine"]
-testing = ["mypy", "pytest", "pytest-gitignore", "pytest-mock", "responses", "ruff", "syrupy", "tox", "tox-uv", "types-filelock", "types-requests"]
-
[[package]]
name = "tokenizers"
version = "0.15.2"
@@ -10809,25 +8789,6 @@ torchhub = ["filelock", "huggingface-hub (>=0.16.4,<1.0)", "importlib-metadata",
video = ["av (==9.2.0)", "decord (==0.6.0)"]
vision = ["Pillow (<10.0.0)"]
-[[package]]
-name = "twilio"
-version = "9.0.5"
-description = "Twilio API client and TwiML generator"
-optional = false
-python-versions = ">=3.7.0"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "twilio-9.0.5-py2.py3-none-any.whl", hash = "sha256:5e09e910b9368f50f23cb3c3dd5ba77164d80a81e9d97db955cbac322deb2a4e"},
- {file = "twilio-9.0.5.tar.gz", hash = "sha256:e9b5727943584d25d618fe502f0100fc5283215f31c863f80b5c64581b4702b0"},
-]
-
-[package.dependencies]
-aiohttp = ">=3.8.4"
-aiohttp-retry = ">=2.8.3"
-PyJWT = ">=2.0.0,<3.0.0"
-requests = ">=2.0.0"
-
[[package]]
name = "typer"
version = "0.15.1"
@@ -10849,15 +8810,15 @@ typing-extensions = ">=3.7.4.3"
[[package]]
name = "types-beautifulsoup4"
-version = "4.12.0.20241020"
+version = "4.12.0.20250204"
description = "Typing stubs for beautifulsoup4"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "types-beautifulsoup4-4.12.0.20241020.tar.gz", hash = "sha256:158370d08d0cd448bd11b132a50ff5279237a5d4b5837beba074de152a513059"},
- {file = "types_beautifulsoup4-4.12.0.20241020-py3-none-any.whl", hash = "sha256:c95e66ce15a4f5f0835f7fbc5cd886321ae8294f977c495424eaf4225307fd30"},
+ {file = "types_beautifulsoup4-4.12.0.20250204-py3-none-any.whl", hash = "sha256:57ce9e75717b63c390fd789c787d267a67eb01fa6d800a03b9bdde2e877ed1eb"},
+ {file = "types_beautifulsoup4-4.12.0.20250204.tar.gz", hash = "sha256:f083d8edcbd01279f8c3995b56cfff2d01f1bb894c3b502ba118d36fbbc495bf"},
]
[package.dependencies]
@@ -10924,15 +8885,15 @@ files = [
[[package]]
name = "types-protobuf"
-version = "5.29.1.20241207"
+version = "5.29.1.20250208"
description = "Typing stubs for protobuf"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "types_protobuf-5.29.1.20241207-py3-none-any.whl", hash = "sha256:92893c42083e9b718c678badc0af7a9a1307b92afe1599e5cba5f3d35b668b2f"},
- {file = "types_protobuf-5.29.1.20241207.tar.gz", hash = "sha256:2ebcadb8ab3ef2e3e2f067e0882906d64ba0dc65fc5b0fd7a8b692315b4a0be9"},
+ {file = "types_protobuf-5.29.1.20250208-py3-none-any.whl", hash = "sha256:c5f8bfb4afdc1b5cbca1848f2c8b361a2090add7401f410b22b599ef647bf483"},
+ {file = "types_protobuf-5.29.1.20250208.tar.gz", hash = "sha256:c1acd6a59ab554dbe09b5d1fa7dd701e2fcfb2212937a3af1c03b736060b792a"},
]
[[package]]
@@ -11019,7 +8980,7 @@ version = "2.32.0.20241016"
description = "Typing stubs for requests"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev"]
+groups = ["dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"},
@@ -11064,7 +9025,7 @@ version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev", "lint", "storage", "tools", "vdb"]
+groups = ["main", "dev", "lint", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
@@ -11090,15 +9051,15 @@ typing-extensions = ">=3.7.4"
[[package]]
name = "tzdata"
-version = "2024.2"
+version = "2025.1"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
- {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
+ {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"},
+ {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"},
]
[[package]]
@@ -11192,15 +9153,15 @@ files = [
[[package]]
name = "unstructured"
-version = "0.16.12"
+version = "0.16.20"
description = "A library that prepares raw documents for downstream ML tasks."
optional = false
-python-versions = "<3.13,>=3.9.0"
+python-versions = ">=3.9.0"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "unstructured-0.16.12-py3-none-any.whl", hash = "sha256:bcac29ac1b38fba4228c5a1a7721d1aa7c48220f7c1dd43b563645c56e978c49"},
- {file = "unstructured-0.16.12.tar.gz", hash = "sha256:c3133731c6edb9c2f474e62cb2b560cd0a8d578c4532ec14d8c0941e401770b0"},
+ {file = "unstructured-0.16.20-py3-none-any.whl", hash = "sha256:9749f4310dde0973f4732ee68f9e98d4a673bb06e455e98bc9522027a99c77cd"},
+ {file = "unstructured-0.16.20.tar.gz", hash = "sha256:95e8b604fae908cfd53b5bf05c4683e0041aa6c914627bb0226edb4530bbfa44"},
]
[package.dependencies]
@@ -11214,7 +9175,6 @@ html5lib = "*"
langdetect = "*"
lxml = "*"
markdown = {version = "*", optional = true, markers = "extra == \"md\""}
-ndjson = "*"
nltk = "*"
numpy = "<2"
psutil = "*"
@@ -11232,19 +9192,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.1)", "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.6)", "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.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"]
+image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (>=0.8.6)", "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.6)", "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.1)", "unstructured.pytesseract (>=0.3.12)"]
+pdf = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (>=0.8.6)", "unstructured.pytesseract (>=0.3.12)"]
ppt = ["python-pptx (>=1.0.1)"]
pptx = ["python-pptx (>=1.0.1)"]
rst = ["pypandoc"]
@@ -11432,69 +9392,6 @@ files = [
{file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"},
]
-[[package]]
-name = "vanna"
-version = "0.7.5"
-description = "Generate SQL queries from natural language"
-optional = false
-python-versions = ">=3.9"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "vanna-0.7.5-py3-none-any.whl", hash = "sha256:07458c7befa49de517a8760c2d80a13147278b484c515d49a906acc88edcb835"},
- {file = "vanna-0.7.5.tar.gz", hash = "sha256:2fdffc58832898e4fc8e93c45b173424db59a22773b22ca348640161d391eacf"},
-]
-
-[package.dependencies]
-clickhouse_connect = {version = "*", optional = true, markers = "extra == \"clickhouse\""}
-db-dtypes = {version = "*", optional = true, markers = "extra == \"postgres\""}
-duckdb = {version = "*", optional = true, markers = "extra == \"duckdb\""}
-flasgger = "*"
-flask = "*"
-flask-sock = "*"
-kaleido = "*"
-pandas = "*"
-plotly = "*"
-psycopg2-binary = {version = "*", optional = true, markers = "extra == \"postgres\""}
-PyMySQL = {version = "*", optional = true, markers = "extra == \"mysql\""}
-requests = "*"
-sqlalchemy = "*"
-sqlparse = "*"
-tabulate = "*"
-
-[package.extras]
-all = ["PyMySQL", "anthropic", "azure-common", "azure-identity", "azure-search-documents", "boto", "boto3", "botocore", "chromadb", "db-dtypes", "duckdb", "faiss-cpu", "fastembed", "google-cloud-aiplatform", "google-cloud-bigquery", "google-generativeai", "httpx", "langchain_core", "langchain_postgres", "marqo", "mistralai (>=1.0.0)", "ollama", "openai", "opensearch-dsl", "opensearch-py", "pinecone-client", "psycopg2-binary", "pymilvus[model]", "qdrant-client", "qianfan", "snowflake-connector-python", "transformers", "weaviate-client", "xinference-client", "zhipuai"]
-anthropic = ["anthropic"]
-azuresearch = ["azure-common", "azure-identity", "azure-search-documents", "fastembed"]
-bedrock = ["boto3", "botocore"]
-bigquery = ["google-cloud-bigquery"]
-chromadb = ["chromadb"]
-clickhouse = ["clickhouse_connect"]
-duckdb = ["duckdb"]
-faiss-cpu = ["faiss-cpu"]
-faiss-gpu = ["faiss-gpu"]
-gemini = ["google-generativeai"]
-google = ["google-cloud-aiplatform", "google-generativeai"]
-hf = ["transformers"]
-marqo = ["marqo"]
-milvus = ["pymilvus[model]"]
-mistralai = ["mistralai (>=1.0.0)"]
-mysql = ["PyMySQL"]
-ollama = ["httpx", "ollama"]
-openai = ["openai"]
-opensearch = ["opensearch-dsl", "opensearch-py"]
-pgvector = ["langchain-postgres (>=0.0.12)"]
-pinecone = ["fastembed", "pinecone-client"]
-postgres = ["db-dtypes", "psycopg2-binary"]
-qdrant = ["fastembed", "qdrant-client"]
-qianfan = ["qianfan"]
-snowflake = ["snowflake-connector-python"]
-test = ["tox"]
-vllm = ["vllm"]
-weaviate = ["weaviate-client"]
-xinference-client = ["xinference-client"]
-zhipuai = ["zhipuai"]
-
[[package]]
name = "vine"
version = "5.1.0"
@@ -11530,110 +9427,86 @@ requests = ">=2.25.1"
retry = ">=0.9.2"
six = ">=1.0"
-[[package]]
-name = "volcengine-python-sdk"
-version = "1.0.103"
-description = "Volcengine SDK for Python"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "volcengine-python-sdk-1.0.103.tar.gz", hash = "sha256:49fa8572802724972e1cb47a7e692b184b055f41b09099358c1a0fad1d146af5"},
-]
-
-[package.dependencies]
-anyio = {version = ">=3.5.0,<5", optional = true, markers = "extra == \"ark\""}
-certifi = ">=2017.4.17"
-httpx = {version = ">=0.23.0,<1", optional = true, markers = "extra == \"ark\""}
-pydantic = {version = ">=1.9.0,<3", optional = true, markers = "extra == \"ark\""}
-python-dateutil = ">=2.1"
-six = ">=1.10"
-urllib3 = ">=1.23"
-
-[package.extras]
-ark = ["anyio (>=3.5.0,<5)", "cached-property", "httpx (>=0.23.0,<1)", "pydantic (>=1.9.0,<3)"]
-
[[package]]
name = "watchfiles"
-version = "1.0.3"
+version = "1.0.4"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
python-versions = ">=3.9"
groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42"},
- {file = "watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b"},
- {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d"},
- {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d"},
- {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552"},
- {file = "watchfiles-1.0.3-cp310-cp310-win32.whl", hash = "sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc"},
- {file = "watchfiles-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146"},
- {file = "watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f"},
- {file = "watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c"},
- {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77"},
- {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469"},
- {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780"},
- {file = "watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181"},
- {file = "watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c"},
- {file = "watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6"},
- {file = "watchfiles-1.0.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3"},
- {file = "watchfiles-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49"},
- {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0"},
- {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885"},
- {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5"},
- {file = "watchfiles-1.0.3-cp312-cp312-win32.whl", hash = "sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d"},
- {file = "watchfiles-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44"},
- {file = "watchfiles-1.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43"},
- {file = "watchfiles-1.0.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a"},
- {file = "watchfiles-1.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1"},
- {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6"},
- {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0"},
- {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868"},
- {file = "watchfiles-1.0.3-cp313-cp313-win32.whl", hash = "sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07"},
- {file = "watchfiles-1.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3"},
- {file = "watchfiles-1.0.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c05b021f7b5aa333124f2a64d56e4cb9963b6efdf44e8d819152237bbd93ba15"},
- {file = "watchfiles-1.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:310505ad305e30cb6c5f55945858cdbe0eb297fc57378f29bacceb534ac34199"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddff3f8b9fa24a60527c137c852d0d9a7da2a02cf2151650029fdc97c852c974"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46e86ed457c3486080a72bc837300dd200e18d08183f12b6ca63475ab64ed651"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f79fe7993e230a12172ce7d7c7db061f046f672f2b946431c81aff8f60b2758b"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea2b51c5f38bad812da2ec0cd7eec09d25f521a8b6b6843cbccedd9a1d8a5c15"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fe4e740ea94978b2b2ab308cbf9270a246bcbb44401f77cc8740348cbaeac3d"},
- {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af037d3df7188ae21dc1c7624501f2f90d81be6550904e07869d8d0e6766655"},
- {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52bb50a4c4ca2a689fdba84ba8ecc6a4e6210f03b6af93181bb61c4ec3abaf86"},
- {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c14a07bdb475eb696f85c715dbd0f037918ccbb5248290448488a0b4ef201aad"},
- {file = "watchfiles-1.0.3-cp39-cp39-win32.whl", hash = "sha256:be37f9b1f8934cd9e7eccfcb5612af9fb728fecbe16248b082b709a9d1b348bf"},
- {file = "watchfiles-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ef9ec8068cf23458dbf36a08e0c16f0a2df04b42a8827619646637be1769300a"},
- {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780"},
- {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29"},
- {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80"},
- {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098"},
- {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8af4b582d5fc1b8465d1d2483e5e7b880cc1a4e99f6ff65c23d64d070867ac58"},
- {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:127de3883bdb29dbd3b21f63126bb8fa6e773b74eaef46521025a9ce390e1073"},
- {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713f67132346bdcb4c12df185c30cf04bdf4bf6ea3acbc3ace0912cab6b7cb8c"},
- {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abd85de513eb83f5ec153a802348e7a5baa4588b818043848247e3e8986094e8"},
- {file = "watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56"},
+ {file = "watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08"},
+ {file = "watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2"},
+ {file = "watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899"},
+ {file = "watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff"},
+ {file = "watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f"},
+ {file = "watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f"},
+ {file = "watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161"},
+ {file = "watchfiles-1.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19"},
+ {file = "watchfiles-1.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49"},
+ {file = "watchfiles-1.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c"},
+ {file = "watchfiles-1.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1"},
+ {file = "watchfiles-1.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226"},
+ {file = "watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105"},
+ {file = "watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74"},
+ {file = "watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3"},
+ {file = "watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2"},
+ {file = "watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af"},
+ {file = "watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a"},
+ {file = "watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff"},
+ {file = "watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e"},
+ {file = "watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94"},
+ {file = "watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c"},
+ {file = "watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90"},
+ {file = "watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9"},
+ {file = "watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590"},
+ {file = "watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902"},
+ {file = "watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1"},
+ {file = "watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303"},
+ {file = "watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80"},
+ {file = "watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc"},
+ {file = "watchfiles-1.0.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d3452c1ec703aa1c61e15dfe9d482543e4145e7c45a6b8566978fbb044265a21"},
+ {file = "watchfiles-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7b75fee5a16826cf5c46fe1c63116e4a156924d668c38b013e6276f2582230f0"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e997802d78cdb02623b5941830ab06f8860038faf344f0d288d325cc9c5d2ff"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0611d244ce94d83f5b9aff441ad196c6e21b55f77f3c47608dcf651efe54c4a"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9745a4210b59e218ce64c91deb599ae8775c8a9da4e95fb2ee6fe745fc87d01a"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4810ea2ae622add560f4aa50c92fef975e475f7ac4900ce5ff5547b2434642d8"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d103cd01458f22462dedeb5a3382b7f2c57d07ff033fbc9465919e5e1d0f3"},
+ {file = "watchfiles-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbd912a61543a36aef85e34f212e5d2486e7c53ebfdb70d1e0b060cc50dd0bf"},
+ {file = "watchfiles-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0bc80d91ddaf95f70258cf78c471246846c1986bcc5fd33ccc4a1a67fcb40f9a"},
+ {file = "watchfiles-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab0311bb2ffcd9f74b6c9de2dda1612c13c84b996d032cd74799adb656af4e8b"},
+ {file = "watchfiles-1.0.4-cp39-cp39-win32.whl", hash = "sha256:02a526ee5b5a09e8168314c905fc545c9bc46509896ed282aeb5a8ba9bd6ca27"},
+ {file = "watchfiles-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:a5ae5706058b27c74bac987d615105da17724172d5aaacc6c362a40599b6de43"},
+ {file = "watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18"},
+ {file = "watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817"},
+ {file = "watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0"},
+ {file = "watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d"},
+ {file = "watchfiles-1.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9fe37a2de80aa785d340f2980276b17ef697ab8db6019b07ee4fd28a8359d2f3"},
+ {file = "watchfiles-1.0.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9d1ef56b56ed7e8f312c934436dea93bfa3e7368adfcf3df4c0da6d4de959a1e"},
+ {file = "watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b42cac65beae3a362629950c444077d1b44f1790ea2772beaea95451c086bb"},
+ {file = "watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e0227b8ed9074c6172cf55d85b5670199c99ab11fd27d2c473aa30aec67ee42"},
+ {file = "watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205"},
]
[package.dependencies]
@@ -11680,7 +9553,7 @@ version = "0.5.1"
description = "Character encoding aliases for legacy web content"
optional = false
python-versions = "*"
-groups = ["main", "tools"]
+groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
@@ -11689,117 +9562,100 @@ files = [
[[package]]
name = "websocket-client"
-version = "1.7.0"
+version = "1.8.0"
description = "WebSocket client for Python with low level API options"
optional = false
python-versions = ">=3.8"
-groups = ["main", "vdb"]
+groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"},
- {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"},
+ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"},
+ {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"},
]
[package.extras]
-docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"]
+docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"]
optional = ["python-socks", "wsaccel"]
test = ["websockets"]
[[package]]
name = "websockets"
-version = "13.1"
+version = "14.2"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"},
- {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"},
- {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"},
- {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"},
- {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"},
- {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"},
- {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"},
- {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"},
- {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"},
- {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"},
- {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"},
- {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"},
- {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"},
- {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"},
- {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"},
- {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"},
- {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"},
- {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"},
- {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"},
- {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"},
- {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"},
- {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"},
- {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"},
- {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"},
- {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"},
- {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"},
- {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"},
- {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"},
- {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"},
- {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"},
- {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"},
- {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"},
- {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"},
- {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"},
- {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"},
- {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"},
- {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"},
- {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"},
- {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"},
- {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"},
- {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"},
- {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"},
- {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"},
- {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"},
- {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"},
- {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"},
- {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"},
- {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"},
- {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"},
- {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"},
- {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"},
- {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"},
- {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"},
- {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"},
- {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"},
- {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"},
- {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"},
- {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"},
- {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"},
- {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"},
- {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"},
- {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"},
- {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"},
- {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"},
- {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"},
- {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"},
- {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"},
- {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"},
- {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"},
- {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"},
- {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"},
- {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"},
- {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"},
- {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"},
- {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"},
- {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"},
- {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"},
- {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"},
- {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"},
- {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"},
- {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"},
- {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"},
- {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"},
- {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"},
- {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"},
- {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"},
+ {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"},
+ {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"},
+ {file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"},
+ {file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"},
+ {file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"},
+ {file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"},
+ {file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"},
+ {file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"},
+ {file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"},
+ {file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"},
+ {file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"},
+ {file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"},
+ {file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"},
+ {file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"},
+ {file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"},
+ {file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"},
+ {file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"},
+ {file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"},
+ {file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"},
+ {file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"},
+ {file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"},
+ {file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"},
+ {file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"},
+ {file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"},
+ {file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"},
+ {file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"},
+ {file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"},
+ {file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"},
+ {file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"},
+ {file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"},
+ {file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"},
+ {file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"},
+ {file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"},
+ {file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"},
+ {file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"},
+ {file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"},
+ {file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"},
+ {file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"},
+ {file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"},
+ {file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"},
+ {file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"},
+ {file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"},
+ {file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"},
+ {file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"},
+ {file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"},
+ {file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"},
+ {file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"},
+ {file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"},
+ {file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"},
+ {file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"},
+ {file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"},
+ {file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"},
+ {file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"},
+ {file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"},
+ {file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"},
+ {file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"},
+ {file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"},
+ {file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"},
+ {file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"},
]
[[package]]
@@ -11808,7 +9664,7 @@ version = "3.1.3"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev", "tools"]
+groups = ["main", "dev"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"},
@@ -11821,141 +9677,107 @@ MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog (>=2.3)"]
-[[package]]
-name = "wikipedia"
-version = "1.4.0"
-description = "Wikipedia API for Python"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2"},
-]
-
-[package.dependencies]
-beautifulsoup4 = "*"
-requests = ">=2.0.0,<3.0.0"
-
-[[package]]
-name = "win32-setctime"
-version = "1.2.0"
-description = "A small Python utility to set file creation time on Windows"
-optional = false
-python-versions = ">=3.5"
-groups = ["main"]
-markers = "(python_version == \"3.11\" or python_version >= \"3.12\") and sys_platform == \"win32\""
-files = [
- {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"},
- {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"},
-]
-
-[package.extras]
-dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
-
[[package]]
name = "wrapt"
-version = "1.17.0"
+version = "1.17.2"
description = "Module for decorators, wrappers and monkey patching."
optional = false
python-versions = ">=3.8"
groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"},
- {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"},
- {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"},
- {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"},
- {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"},
- {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"},
- {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"},
- {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"},
- {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"},
- {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"},
- {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"},
- {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"},
- {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"},
- {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"},
- {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"},
- {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"},
- {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"},
- {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"},
- {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"},
- {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"},
- {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"},
- {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"},
- {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"},
- {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"},
- {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"},
- {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"},
- {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"},
- {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"},
- {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"},
- {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"},
- {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"},
- {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"},
- {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"},
- {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"},
- {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"},
- {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"},
- {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"},
- {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"},
- {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"},
- {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"},
- {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"},
- {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"},
- {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"},
- {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"},
- {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"},
- {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"},
- {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"},
- {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"},
- {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"},
- {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"},
- {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"},
- {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"},
- {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"},
- {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"},
- {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"},
- {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"},
- {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"},
- {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"},
- {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"},
- {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"},
- {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"},
- {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"},
- {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"},
- {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"},
- {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"},
-]
-
-[[package]]
-name = "wsproto"
-version = "1.2.0"
-description = "WebSockets state-machine based protocol implementation"
-optional = false
-python-versions = ">=3.7.0"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
- {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"},
+ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
+ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
+ {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"},
+ {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"},
+ {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"},
+ {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"},
+ {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"},
+ {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"},
+ {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"},
+ {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"},
+ {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"},
+ {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"},
+ {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"},
+ {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"},
+ {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"},
+ {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"},
+ {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"},
+ {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"},
+ {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"},
+ {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"},
+ {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"},
+ {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"},
+ {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"},
+ {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"},
+ {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"},
+ {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"},
+ {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"},
+ {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"},
+ {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"},
+ {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"},
+ {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"},
+ {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"},
+ {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"},
+ {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"},
+ {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"},
+ {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"},
+ {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"},
+ {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"},
+ {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"},
+ {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"},
+ {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"},
+ {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"},
+ {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"},
+ {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"},
+ {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"},
+ {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"},
+ {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"},
+ {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"},
+ {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"},
+ {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"},
+ {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"},
+ {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"},
+ {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"},
+ {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"},
+ {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"},
+ {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"},
+ {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"},
+ {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"},
+ {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"},
+ {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"},
+ {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"},
+ {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"},
+ {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"},
+ {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"},
+ {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"},
+ {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"},
+ {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"},
+ {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"},
+ {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"},
+ {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"},
+ {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"},
+ {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"},
+ {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"},
+ {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"},
+ {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"},
+ {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"},
+ {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"},
+ {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"},
+ {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"},
]
-[package.dependencies]
-h11 = ">=0.9.0,<1"
-
[[package]]
name = "xinference-client"
-version = "0.15.2"
+version = "1.2.2"
description = "Client for Xinference"
optional = false
python-versions = "*"
-groups = ["main"]
+groups = ["vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "xinference-client-0.15.2.tar.gz", hash = "sha256:5c2259bb133148d1cc9bd2b8ec6eb8b5bbeba7f11d6252959f4e6cd79baa53ed"},
- {file = "xinference_client-0.15.2-py3-none-any.whl", hash = "sha256:b6275adab695e75e75a33e21e0ad212488fc2d5a4d0f693d544c0e78469abbe3"},
+ {file = "xinference-client-1.2.2.tar.gz", hash = "sha256:85d2ba0fcbaae616b06719c422364123cbac97f3e3c82e614095fe6d0e630ed0"},
+ {file = "xinference_client-1.2.2-py3-none-any.whl", hash = "sha256:6941d87cf61283a9d6e81cee6cb2609a183d34c6b7d808c6ba0c33437520518f"},
]
[package.dependencies]
@@ -11986,15 +9808,15 @@ test = ["pytest", "pytest-cov"]
[[package]]
name = "xlsxwriter"
-version = "3.2.0"
+version = "3.2.2"
description = "A Python module for creating Excel XLSX files."
optional = false
python-versions = ">=3.6"
groups = ["main"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "XlsxWriter-3.2.0-py3-none-any.whl", hash = "sha256:ecfd5405b3e0e228219bcaf24c2ca0915e012ca9464a14048021d21a995d490e"},
- {file = "XlsxWriter-3.2.0.tar.gz", hash = "sha256:9977d0c661a72866a61f9f7a809e25ebbb0fb7036baa3b9fe74afcfca6b3cb8c"},
+ {file = "XlsxWriter-3.2.2-py3-none-any.whl", hash = "sha256:272ce861e7fa5e82a4a6ebc24511f2cb952fde3461f6c6e1a1e81d3272db1471"},
+ {file = "xlsxwriter-3.2.2.tar.gz", hash = "sha256:befc7f92578a85fed261639fb6cde1fd51b79c5e854040847dde59d4317077dc"},
]
[[package]]
@@ -12016,7 +9838,7 @@ version = "1.18.3"
description = "Yet another URL library"
optional = false
python-versions = ">=3.9"
-groups = ["main", "storage", "tools", "vdb"]
+groups = ["main", "storage", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"},
@@ -12108,73 +9930,6 @@ idna = ">=2.0"
multidict = ">=4.0"
propcache = ">=0.2.0"
-[[package]]
-name = "yfinance"
-version = "0.2.51"
-description = "Download market data from Yahoo! Finance API"
-optional = false
-python-versions = "*"
-groups = ["tools"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "yfinance-0.2.51-py2.py3-none-any.whl", hash = "sha256:d5cc7a970bb4bb43e4deee853514cbaa3c2b070a0dee6b2861c1ab5076f21dc1"},
- {file = "yfinance-0.2.51.tar.gz", hash = "sha256:7902cc9b23699a51efa50f1cc7a965220a56beccc00d189f929b4c7c5c189a60"},
-]
-
-[package.dependencies]
-beautifulsoup4 = ">=4.11.1"
-frozendict = ">=2.3.4"
-html5lib = ">=1.1"
-lxml = ">=4.9.1"
-multitasking = ">=0.0.7"
-numpy = ">=1.16.5"
-pandas = ">=1.3.0"
-peewee = ">=3.16.2"
-platformdirs = ">=2.0.0"
-pytz = ">=2022.5"
-requests = ">=2.31"
-
-[package.extras]
-nospam = ["requests_cache (>=1.0)", "requests_ratelimiter (>=0.3.1)"]
-repair = ["scipy (>=1.6.3)"]
-
-[[package]]
-name = "youtube-transcript-api"
-version = "0.6.3"
-description = "This is an python API which allows you to get the transcripts/subtitles for a given YouTube video. It also works for automatically generated subtitles, supports translating subtitles and it does not require a headless browser, like other selenium based solutions do!"
-optional = false
-python-versions = "<3.14,>=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "youtube_transcript_api-0.6.3-py3-none-any.whl", hash = "sha256:297a74c1863d9df88f6885229f33a7eda61493d73ecb13ec80e876b65423e9b4"},
- {file = "youtube_transcript_api-0.6.3.tar.gz", hash = "sha256:4d1f6451ae508390a5279f98519efb45e091bf60d3cca5ea0bb122800ab6a011"},
-]
-
-[package.dependencies]
-defusedxml = ">=0.7.1,<0.8.0"
-requests = "*"
-
-[[package]]
-name = "zhipuai"
-version = "2.1.5.20250106"
-description = "A SDK library for accessing big model apis from ZhipuAI"
-optional = false
-python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
-groups = ["main"]
-markers = "python_version == \"3.11\" or python_version >= \"3.12\""
-files = [
- {file = "zhipuai-2.1.5.20250106-py3-none-any.whl", hash = "sha256:ca76095f32db501e36038fc1ac4b287b88ed90c4cdd28902d3b1a9365fff879b"},
- {file = "zhipuai-2.1.5.20250106.tar.gz", hash = "sha256:45d391be336a210b360f126443f07882fa6d8184a148c46a8c7d0b7607d6d1f8"},
-]
-
-[package.dependencies]
-cachetools = ">=4.2.2"
-httpx = ">=0.23.0"
-pydantic = ">=1.9.0,<3.0"
-pydantic-core = ">=2.14.6"
-pyjwt = ">=2.8.0,<2.9.0"
-
[[package]]
name = "zipp"
version = "3.21.0"
@@ -12278,7 +10033,7 @@ version = "0.23.0"
description = "Zstandard bindings for Python"
optional = false
python-versions = ">=3.8"
-groups = ["main", "tools", "vdb"]
+groups = ["main", "vdb"]
markers = "python_version == \"3.11\" or python_version >= \"3.12\""
files = [
{file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"},
@@ -12389,4 +10144,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.11,<3.13"
-content-hash = "d197cdff507a70323c1d6aca11609188f54970f67715af744fe6def15b7776fd"
+content-hash = "a61e69fbd8c0306ed676d791b3783f8ba9914f871855543b265c4e7b5d2af0f9"
diff --git a/api/pyproject.toml b/api/pyproject.toml
index 72ec6d287e87bf..4a881a3e28b9c8 100644
--- a/api/pyproject.toml
+++ b/api/pyproject.toml
@@ -15,10 +15,7 @@ package-mode = false
############################################################
[tool.poetry.dependencies]
-anthropic = "~0.23.1"
authlib = "1.3.1"
-azure-ai-inference = "~1.0.0b8"
-azure-ai-ml = "~1.20.0"
azure-identity = "1.16.1"
beautifulsoup4 = "4.12.2"
boto3 = "1.36.12"
@@ -26,9 +23,6 @@ bs4 = "~0.0.1"
cachetools = "~5.3.0"
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.1.0"
flask-compress = "~1.17"
flask-cors = "~4.0.0"
@@ -38,24 +32,19 @@ flask-restful = "~0.3.10"
flask-sqlalchemy = "~3.1.1"
gevent = "~24.11.1"
gmpy2 = "~2.2.1"
-google-ai-generativelanguage = "0.6.9"
google-api-core = "2.18.0"
google-api-python-client = "2.90.0"
google-auth = "2.29.0"
google-auth-httplib2 = "0.2.0"
google-cloud-aiplatform = "1.49.0"
-google-generativeai = "0.8.1"
googleapis-common-protos = "1.63.0"
gunicorn = "~23.0.0"
httpx = { version = "~0.27.0", extras = ["socks"] }
-huggingface-hub = "~0.16.4"
jieba = "0.42.1"
langfuse = "~2.51.3"
langsmith = "~0.1.77"
mailchimp-transactional = "~1.0.50"
markdown = "~3.5.1"
-nomic = "~3.1.2"
-novita-client = "~0.5.7"
numpy = "~1.26.4"
oci = "~2.135.1"
openai = "~1.61.0"
@@ -77,25 +66,16 @@ python-dotenv = "1.0.1"
pyyaml = "~6.0.1"
readabilipy = "0.2.0"
redis = { version = "~5.0.3", extras = ["hiredis"] }
-replicate = "~0.22.0"
resend = "~0.7.0"
-sagemaker = "~2.231.0"
-scikit-learn = "~1.5.1"
sentry-sdk = { version = "~1.44.1", extras = ["flask"] }
sqlalchemy = "~2.0.29"
starlette = "0.41.0"
-tencentcloud-sdk-python-hunyuan = "~3.0.1294"
tiktoken = "~0.8.0"
tokenizers = "~0.15.0"
transformers = "~4.35.0"
unstructured = { version = "~0.16.1", extras = ["docx", "epub", "md", "msg", "ppt", "pptx"] }
validators = "0.21.0"
-volcengine-python-sdk = {extras = ["ark"], version = "~1.0.98"}
-websocket-client = "~1.7.0"
-xinference-client = "0.15.2"
yarl = "~1.18.3"
-youtube-transcript-api = "~0.6.2"
-zhipuai = "~2.1.5"
# Before adding new dependency, consider place it in alphabet order (a-z) and suitable group.
############################################################
@@ -112,21 +92,8 @@ safetensors = "~0.4.3"
# [ Tools ] dependency group
############################################################
[tool.poetry.group.tools.dependencies]
-arxiv = "2.1.0"
cloudscraper = "1.2.71"
-duckduckgo-search = "~6.3.0"
-jsonpath-ng = "1.6.1"
-matplotlib = "~3.8.2"
-mplfonts = "~0.0.8"
-newspaper3k = "0.2.8"
nltk = "3.9.1"
-numexpr = "~2.9.0"
-pydub = "~0.25.1"
-qrcode = "~7.4.2"
-twilio = "~9.0.4"
-vanna = { version = "0.7.5", extras = ["postgres", "mysql", "clickhouse", "duckdb", "oracle"] }
-wikipedia = "1.4.0"
-yfinance = "~0.2.40"
############################################################
# [ Storage ] dependency group
@@ -167,6 +134,7 @@ tidb-vector = "0.0.9"
upstash-vector = "0.6.0"
volcengine-compat = "~1.0.156"
weaviate-client = "~3.21.0"
+xinference-client = "~1.2.2"
############################################################
# [ Dev ] dependency group
diff --git a/api/pytest.ini b/api/pytest.ini
index 993da4c9a78935..3de16497987118 100644
--- a/api/pytest.ini
+++ b/api/pytest.ini
@@ -1,4 +1,5 @@
[pytest]
+continue-on-collection-errors = true
env =
ANTHROPIC_API_KEY = sk-ant-api11-IamNotARealKeyJustForMockTestKawaiiiiiiiiii-NotBaka-ASkksz
AZURE_OPENAI_API_BASE = https://difyai-openai.openai.azure.com
@@ -7,6 +8,12 @@ env =
CODE_EXECUTION_API_KEY = dify-sandbox
CODE_EXECUTION_ENDPOINT = http://127.0.0.1:8194
CODE_MAX_STRING_LENGTH = 80000
+ PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
+ PLUGIN_DAEMON_URL=http://127.0.0.1:5002
+ PLUGIN_MAX_PACKAGE_SIZE=15728640
+ INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
+ MARKETPLACE_ENABLED=true
+ MARKETPLACE_API_URL=https://marketplace.dify.ai
FIRECRAWL_API_KEY = fc-
FIREWORKS_API_KEY = fw_aaaaaaaaaaaaaaaaaaaa
GOOGLE_API_KEY = abcdefghijklmnopqrstuvwxyz
diff --git a/api/services/account_service.py b/api/services/account_service.py
index 5388e1878ed842..29237502989f4e 100644
--- a/api/services/account_service.py
+++ b/api/services/account_service.py
@@ -10,6 +10,7 @@
from pydantic import BaseModel
from sqlalchemy import func
+from sqlalchemy.orm import Session
from werkzeug.exceptions import Unauthorized
from configs import dify_config
@@ -101,7 +102,7 @@ def _delete_refresh_token(refresh_token: str, account_id: str) -> None:
@staticmethod
def load_user(user_id: str) -> None | Account:
- account = Account.query.filter_by(id=user_id).first()
+ account = db.session.query(Account).filter_by(id=user_id).first()
if not account:
return None
@@ -146,7 +147,7 @@ def get_account_jwt_token(account: Account) -> str:
def authenticate(email: str, password: str, invite_token: Optional[str] = None) -> Account:
"""authenticate account with email and password"""
- account = Account.query.filter_by(email=email).first()
+ account = db.session.query(Account).filter_by(email=email).first()
if not account:
raise AccountNotFoundError()
@@ -924,11 +925,14 @@ def register(
@classmethod
def invite_new_member(
- cls, tenant: Tenant, email: str, language: str, role: str = "normal", inviter: Optional[Account] = None
+ cls, tenant: Tenant, email: str, language: str, role: str = "normal", inviter: Account | None = None
) -> str:
+ if not inviter:
+ raise ValueError("Inviter is required")
+
"""Invite new member"""
- account = Account.query.filter_by(email=email).first()
- assert inviter is not None, "Inviter must be provided."
+ with Session(db.engine) as session:
+ account = session.query(Account).filter_by(email=email).first()
if not account:
TenantService.check_member_permission(tenant, inviter, None, "add")
diff --git a/api/services/agent_service.py b/api/services/agent_service.py
index b02f762ad267b8..0ff144052fce73 100644
--- a/api/services/agent_service.py
+++ b/api/services/agent_service.py
@@ -1,9 +1,13 @@
+import threading
from typing import Optional
import pytz
from flask_login import current_user # type: ignore
+import contexts
from core.app.app_config.easy_ui_based_app.agent.manager import AgentConfigManager
+from core.plugin.manager.agent import PluginAgentManager
+from core.plugin.manager.exc import PluginDaemonClientSideError
from core.tools.tool_manager import ToolManager
from extensions.ext_database import db
from models.account import Account
@@ -16,7 +20,10 @@ def get_agent_logs(cls, app_model: App, conversation_id: str, message_id: str) -
"""
Service to get agent logs
"""
- conversation: Optional[Conversation] = (
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
+
+ conversation: Conversation | None = (
db.session.query(Conversation)
.filter(
Conversation.id == conversation_id,
@@ -59,6 +66,10 @@ def get_agent_logs(cls, app_model: App, conversation_id: str, message_id: str) -
timezone = pytz.timezone(current_user.timezone)
+ app_model_config = app_model.app_model_config
+ if not app_model_config:
+ raise ValueError("App model config not found")
+
result = {
"meta": {
"status": "success",
@@ -66,16 +77,16 @@ def get_agent_logs(cls, app_model: App, conversation_id: str, message_id: str) -
"start_time": message.created_at.astimezone(timezone).isoformat(),
"elapsed_time": message.provider_response_latency,
"total_tokens": message.answer_tokens + message.message_tokens,
- "agent_mode": app_model.app_model_config.agent_mode_dict.get("strategy", "react"),
+ "agent_mode": app_model_config.agent_mode_dict.get("strategy", "react"),
"iterations": len(agent_thoughts),
},
"iterations": [],
"files": message.message_files,
}
- agent_config = AgentConfigManager.convert(app_model.app_model_config.to_dict())
+ agent_config = AgentConfigManager.convert(app_model_config.to_dict())
if not agent_config:
- return result
+ raise ValueError("Agent config not found")
agent_tools = agent_config.tools or []
@@ -89,7 +100,7 @@ def find_agent_tool(tool_name: str):
tool_labels = agent_thought.tool_labels
tool_meta = agent_thought.tool_meta
tool_inputs = agent_thought.tool_inputs_dict
- tool_outputs = agent_thought.tool_outputs_dict
+ tool_outputs = agent_thought.tool_outputs_dict or {}
tool_calls = []
for tool in tools:
tool_name = tool
@@ -144,3 +155,22 @@ def find_agent_tool(tool_name: str):
)
return result
+
+ @classmethod
+ def list_agent_providers(cls, user_id: str, tenant_id: str):
+ """
+ List agent providers
+ """
+ manager = PluginAgentManager()
+ return manager.fetch_agent_strategy_providers(tenant_id)
+
+ @classmethod
+ def get_agent_provider(cls, user_id: str, tenant_id: str, provider_name: str):
+ """
+ Get agent provider
+ """
+ manager = PluginAgentManager()
+ try:
+ return manager.fetch_agent_strategy_provider(tenant_id, provider_name)
+ except PluginDaemonClientSideError as e:
+ raise ValueError(str(e)) from e
diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py
index 15119247f887f0..4ddb56981eb09f 100644
--- a/api/services/app_dsl_service.py
+++ b/api/services/app_dsl_service.py
@@ -1,5 +1,6 @@
import logging
import uuid
+from collections.abc import Mapping
from enum import StrEnum
from typing import Optional
from urllib.parse import urlparse
@@ -7,22 +8,34 @@
import yaml # type: ignore
from packaging import version
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.orm import Session
from core.helper import ssrf_proxy
+from core.model_runtime.utils.encoders import jsonable_encoder
+from core.plugin.entities.plugin import PluginDependency
+from core.workflow.nodes.enums import NodeType
+from core.workflow.nodes.knowledge_retrieval.entities import KnowledgeRetrievalNodeData
+from core.workflow.nodes.llm.entities import LLMNodeData
+from core.workflow.nodes.parameter_extractor.entities import ParameterExtractorNodeData
+from core.workflow.nodes.question_classifier.entities import QuestionClassifierNodeData
+from core.workflow.nodes.tool.entities import ToolNodeData
from events.app_event import app_model_config_was_updated, app_was_created
from extensions.ext_redis import redis_client
from factories import variable_factory
from models import Account, App, AppMode
from models.model import AppModelConfig
+from models.workflow import Workflow
+from services.plugin.dependencies_analysis import DependenciesAnalysisService
from services.workflow_service import WorkflowService
logger = logging.getLogger(__name__)
IMPORT_INFO_REDIS_KEY_PREFIX = "app_import_info:"
-IMPORT_INFO_REDIS_EXPIRY = 180 # 3 minutes
+CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:"
+IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes
+DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB
CURRENT_DSL_VERSION = "0.1.5"
@@ -47,6 +60,10 @@ class Import(BaseModel):
error: str = ""
+class CheckDependenciesResult(BaseModel):
+ leaked_dependencies: list[PluginDependency] = Field(default_factory=list)
+
+
def _check_version_compatibility(imported_version: str) -> ImportStatus:
"""Determine import status based on version comparison"""
try:
@@ -76,6 +93,11 @@ class PendingData(BaseModel):
app_id: str | None
+class CheckDependenciesPendingData(BaseModel):
+ dependencies: list[PluginDependency]
+ app_id: str | None
+
+
class AppDslService:
def __init__(self, session: Session):
self._session = session
@@ -113,7 +135,6 @@ def import_app(
error="yaml_url is required when import_mode is yaml-url",
)
try:
- max_size = 10 * 1024 * 1024 # 10MB
parsed_url = urlparse(yaml_url)
if (
parsed_url.scheme == "https"
@@ -126,7 +147,7 @@ def import_app(
response.raise_for_status()
content = response.content.decode()
- if len(content) > max_size:
+ if len(content) > DSL_MAX_SIZE:
return Import(
id=import_id,
status=ImportStatus.FAILED,
@@ -208,7 +229,7 @@ def import_app(
# If major version mismatch, store import info in Redis
if status == ImportStatus.PENDING:
- panding_data = PendingData(
+ pending_data = PendingData(
import_mode=import_mode,
yaml_content=content,
name=name,
@@ -221,7 +242,7 @@ def import_app(
redis_client.setex(
f"{IMPORT_INFO_REDIS_KEY_PREFIX}{import_id}",
IMPORT_INFO_REDIS_EXPIRY,
- panding_data.model_dump_json(),
+ pending_data.model_dump_json(),
)
return Import(
@@ -231,6 +252,22 @@ def import_app(
imported_dsl_version=imported_version,
)
+ # Extract dependencies
+ dependencies = data.get("dependencies", [])
+ check_dependencies_pending_data = None
+ if dependencies:
+ check_dependencies_pending_data = [PluginDependency.model_validate(d) for d in dependencies]
+ elif imported_version <= "0.1.5":
+ if "workflow" in data:
+ graph = data.get("workflow", {}).get("graph", {})
+ dependencies_list = self._extract_dependencies_from_workflow_graph(graph)
+ else:
+ dependencies_list = self._extract_dependencies_from_model_config(data.get("model_config", {}))
+
+ check_dependencies_pending_data = DependenciesAnalysisService.generate_latest_dependencies(
+ dependencies_list
+ )
+
# Create or update app
app = self._create_or_update_app(
app=app,
@@ -241,6 +278,7 @@ def import_app(
icon_type=icon_type,
icon=icon,
icon_background=icon_background,
+ dependencies=check_dependencies_pending_data,
)
return Import(
@@ -325,6 +363,29 @@ def confirm_import(self, *, import_id: str, account: Account) -> Import:
error=str(e),
)
+ def check_dependencies(
+ self,
+ *,
+ app_model: App,
+ ) -> CheckDependenciesResult:
+ """Check dependencies"""
+ # Get dependencies from Redis
+ redis_key = f"{CHECK_DEPENDENCIES_REDIS_KEY_PREFIX}{app_model.id}"
+ dependencies = redis_client.get(redis_key)
+ if not dependencies:
+ return CheckDependenciesResult()
+
+ # Extract dependencies
+ dependencies = CheckDependenciesPendingData.model_validate_json(dependencies)
+
+ # Get leaked dependencies
+ leaked_dependencies = DependenciesAnalysisService.get_leaked_dependencies(
+ tenant_id=app_model.tenant_id, dependencies=dependencies.dependencies
+ )
+ return CheckDependenciesResult(
+ leaked_dependencies=leaked_dependencies,
+ )
+
def _create_or_update_app(
self,
*,
@@ -336,6 +397,7 @@ def _create_or_update_app(
icon_type: Optional[str] = None,
icon: Optional[str] = None,
icon_background: Optional[str] = None,
+ dependencies: Optional[list[PluginDependency]] = None,
) -> App:
"""Create a new app or update an existing one."""
app_data = data.get("app", {})
@@ -384,6 +446,14 @@ def _create_or_update_app(
self._session.commit()
app_was_created.send(app, account=account)
+ # save dependencies
+ if dependencies:
+ redis_client.setex(
+ f"{CHECK_DEPENDENCIES_REDIS_KEY_PREFIX}{app.id}",
+ IMPORT_INFO_REDIS_EXPIRY,
+ CheckDependenciesPendingData(app_id=app.id, dependencies=dependencies).model_dump_json(),
+ )
+
# Initialize app based on mode
if app_mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}:
workflow_data = data.get("workflow")
@@ -479,6 +549,13 @@ def _append_workflow_export_data(cls, *, export_data: dict, app_model: App, incl
raise ValueError("Missing draft workflow configuration, please check.")
export_data["workflow"] = workflow.to_dict(include_secret=include_secret)
+ dependencies = cls._extract_dependencies_from_workflow(workflow)
+ export_data["dependencies"] = [
+ jsonable_encoder(d.model_dump())
+ for d in DependenciesAnalysisService.generate_dependencies(
+ tenant_id=app_model.tenant_id, dependencies=dependencies
+ )
+ ]
@classmethod
def _append_model_config_export_data(cls, export_data: dict, app_model: App) -> None:
@@ -492,3 +569,154 @@ def _append_model_config_export_data(cls, export_data: dict, app_model: App) ->
raise ValueError("Missing app configuration, please check.")
export_data["model_config"] = app_model_config.to_dict()
+ dependencies = cls._extract_dependencies_from_model_config(app_model_config.to_dict())
+ export_data["dependencies"] = [
+ jsonable_encoder(d.model_dump())
+ for d in DependenciesAnalysisService.generate_dependencies(
+ tenant_id=app_model.tenant_id, dependencies=dependencies
+ )
+ ]
+
+ @classmethod
+ def _extract_dependencies_from_workflow(cls, workflow: Workflow) -> list[str]:
+ """
+ Extract dependencies from workflow
+ :param workflow: Workflow instance
+ :return: dependencies list format like ["langgenius/google"]
+ """
+ graph = workflow.graph_dict
+ dependencies = cls._extract_dependencies_from_workflow_graph(graph)
+ return dependencies
+
+ @classmethod
+ def _extract_dependencies_from_workflow_graph(cls, graph: Mapping) -> list[str]:
+ """
+ Extract dependencies from workflow graph
+ :param graph: Workflow graph
+ :return: dependencies list format like ["langgenius/google"]
+ """
+ dependencies = []
+ for node in graph.get("nodes", []):
+ try:
+ typ = node.get("data", {}).get("type")
+ match typ:
+ case NodeType.TOOL.value:
+ tool_entity = ToolNodeData(**node["data"])
+ dependencies.append(
+ DependenciesAnalysisService.analyze_tool_dependency(tool_entity.provider_id),
+ )
+ case NodeType.LLM.value:
+ llm_entity = LLMNodeData(**node["data"])
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(llm_entity.model.provider),
+ )
+ case NodeType.QUESTION_CLASSIFIER.value:
+ question_classifier_entity = QuestionClassifierNodeData(**node["data"])
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ question_classifier_entity.model.provider
+ ),
+ )
+ case NodeType.PARAMETER_EXTRACTOR.value:
+ parameter_extractor_entity = ParameterExtractorNodeData(**node["data"])
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ parameter_extractor_entity.model.provider
+ ),
+ )
+ case NodeType.KNOWLEDGE_RETRIEVAL.value:
+ knowledge_retrieval_entity = KnowledgeRetrievalNodeData(**node["data"])
+ if knowledge_retrieval_entity.retrieval_mode == "multiple":
+ if knowledge_retrieval_entity.multiple_retrieval_config:
+ if (
+ knowledge_retrieval_entity.multiple_retrieval_config.reranking_mode
+ == "reranking_model"
+ ):
+ if knowledge_retrieval_entity.multiple_retrieval_config.reranking_model:
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ knowledge_retrieval_entity.multiple_retrieval_config.reranking_model.provider
+ ),
+ )
+ elif (
+ knowledge_retrieval_entity.multiple_retrieval_config.reranking_mode
+ == "weighted_score"
+ ):
+ if knowledge_retrieval_entity.multiple_retrieval_config.weights:
+ vector_setting = (
+ knowledge_retrieval_entity.multiple_retrieval_config.weights.vector_setting
+ )
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ vector_setting.embedding_provider_name
+ ),
+ )
+ elif knowledge_retrieval_entity.retrieval_mode == "single":
+ model_config = knowledge_retrieval_entity.single_retrieval_config
+ if model_config:
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ model_config.model.provider
+ ),
+ )
+ case _:
+ # TODO: Handle default case or unknown node types
+ pass
+ except Exception as e:
+ logger.exception("Error extracting node dependency", exc_info=e)
+
+ return dependencies
+
+ @classmethod
+ def _extract_dependencies_from_model_config(cls, model_config: Mapping) -> list[str]:
+ """
+ Extract dependencies from model config
+ :param model_config: model config dict
+ :return: dependencies list format like ["langgenius/google"]
+ """
+ dependencies = []
+
+ try:
+ # completion model
+ model_dict = model_config.get("model", {})
+ if model_dict:
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(model_dict.get("provider", ""))
+ )
+
+ # reranking model
+ dataset_configs = model_config.get("dataset_configs", {})
+ if dataset_configs:
+ for dataset_config in dataset_configs.get("datasets", {}).get("datasets", []):
+ if dataset_config.get("reranking_model"):
+ dependencies.append(
+ DependenciesAnalysisService.analyze_model_provider_dependency(
+ dataset_config.get("reranking_model", {})
+ .get("reranking_provider_name", {})
+ .get("provider")
+ )
+ )
+
+ # tools
+ agent_configs = model_config.get("agent_mode", {})
+ if agent_configs:
+ for agent_config in agent_configs.get("tools", []):
+ dependencies.append(
+ DependenciesAnalysisService.analyze_tool_dependency(agent_config.get("provider_id"))
+ )
+
+ except Exception as e:
+ logger.exception("Error extracting model config dependency", exc_info=e)
+
+ return dependencies
+
+ @classmethod
+ def get_leaked_dependencies(cls, tenant_id: str, dsl_dependencies: list[dict]) -> list[PluginDependency]:
+ """
+ Returns the leaked dependencies in current workspace
+ """
+ dependencies = [PluginDependency(**dep) for dep in dsl_dependencies]
+ if not dependencies:
+ return []
+
+ return DependenciesAnalysisService.get_leaked_dependencies(tenant_id=tenant_id, dependencies=dependencies)
diff --git a/api/services/app_generate_service.py b/api/services/app_generate_service.py
index 51aef7ccab9a0c..39b4afa252ab7d 100644
--- a/api/services/app_generate_service.py
+++ b/api/services/app_generate_service.py
@@ -43,66 +43,62 @@ def generate(
request_id = rate_limit.enter(request_id)
if app_model.mode == AppMode.COMPLETION.value:
return rate_limit.generate(
- generator=CompletionAppGenerator().generate(
- app_model=app_model,
- user=user,
- args=args,
- invoke_from=invoke_from,
- streaming=streaming,
+ CompletionAppGenerator.convert_to_event_stream(
+ CompletionAppGenerator().generate(
+ app_model=app_model, user=user, args=args, invoke_from=invoke_from, streaming=streaming
+ ),
),
request_id=request_id,
)
elif app_model.mode == AppMode.AGENT_CHAT.value or app_model.is_agent:
- generator = AgentChatAppGenerator().generate(
- app_model=app_model,
- user=user,
- args=args,
- invoke_from=invoke_from,
- streaming=streaming,
- )
return rate_limit.generate(
- generator=generator,
- request_id=request_id,
+ AgentChatAppGenerator.convert_to_event_stream(
+ AgentChatAppGenerator().generate(
+ app_model=app_model, user=user, args=args, invoke_from=invoke_from, streaming=streaming
+ ),
+ ),
+ request_id,
)
elif app_model.mode == AppMode.CHAT.value:
return rate_limit.generate(
- generator=ChatAppGenerator().generate(
- app_model=app_model,
- user=user,
- args=args,
- invoke_from=invoke_from,
- streaming=streaming,
+ ChatAppGenerator.convert_to_event_stream(
+ ChatAppGenerator().generate(
+ app_model=app_model, user=user, args=args, invoke_from=invoke_from, streaming=streaming
+ ),
),
request_id=request_id,
)
elif app_model.mode == AppMode.ADVANCED_CHAT.value:
workflow = cls._get_workflow(app_model, invoke_from)
return rate_limit.generate(
- generator=AdvancedChatAppGenerator().generate(
- app_model=app_model,
- workflow=workflow,
- user=user,
- args=args,
- invoke_from=invoke_from,
- streaming=streaming,
+ AdvancedChatAppGenerator.convert_to_event_stream(
+ AdvancedChatAppGenerator().generate(
+ app_model=app_model,
+ workflow=workflow,
+ user=user,
+ args=args,
+ invoke_from=invoke_from,
+ streaming=streaming,
+ ),
),
request_id=request_id,
)
elif app_model.mode == AppMode.WORKFLOW.value:
workflow = cls._get_workflow(app_model, invoke_from)
- generator = WorkflowAppGenerator().generate(
- app_model=app_model,
- workflow=workflow,
- user=user,
- args=args,
- invoke_from=invoke_from,
- streaming=streaming,
- call_depth=0,
- workflow_thread_pool_id=None,
- )
return rate_limit.generate(
- generator=generator,
- request_id=request_id,
+ WorkflowAppGenerator.convert_to_event_stream(
+ WorkflowAppGenerator().generate(
+ app_model=app_model,
+ workflow=workflow,
+ user=user,
+ args=args,
+ invoke_from=invoke_from,
+ streaming=streaming,
+ call_depth=0,
+ workflow_thread_pool_id=None,
+ ),
+ ),
+ request_id,
)
else:
raise ValueError(f"Invalid app mode {app_model.mode}")
@@ -126,18 +122,17 @@ def _get_max_active_requests(app_model: App) -> int:
def generate_single_iteration(cls, app_model: App, user: Account, node_id: str, args: Any, streaming: bool = True):
if app_model.mode == AppMode.ADVANCED_CHAT.value:
workflow = cls._get_workflow(app_model, InvokeFrom.DEBUGGER)
- return AdvancedChatAppGenerator().single_iteration_generate(
- app_model=app_model,
- workflow=workflow,
- node_id=node_id,
- user=user,
- args=args,
- streaming=streaming,
+ return AdvancedChatAppGenerator.convert_to_event_stream(
+ AdvancedChatAppGenerator().single_iteration_generate(
+ app_model=app_model, workflow=workflow, node_id=node_id, user=user, args=args, streaming=streaming
+ )
)
elif app_model.mode == AppMode.WORKFLOW.value:
workflow = cls._get_workflow(app_model, InvokeFrom.DEBUGGER)
- return WorkflowAppGenerator().single_iteration_generate(
- app_model=app_model, workflow=workflow, node_id=node_id, user=user, args=args, streaming=streaming
+ return AdvancedChatAppGenerator.convert_to_event_stream(
+ WorkflowAppGenerator().single_iteration_generate(
+ app_model=app_model, workflow=workflow, node_id=node_id, user=user, args=args, streaming=streaming
+ )
)
else:
raise ValueError(f"Invalid app mode {app_model.mode}")
diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py
index 38025b5213aaa3..edf51851fb42f2 100644
--- a/api/services/dataset_service.py
+++ b/api/services/dataset_service.py
@@ -9,12 +9,14 @@
from flask_login import current_user # type: ignore
from sqlalchemy import func
+from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound
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.plugin.entities.plugin import ModelProviderID
from core.rag.index_processor.constant.index_type import IndexType
from core.rag.retrieval.retrieval_methods import RetrievalMethod
from events.dataset_event import dataset_was_deleted
@@ -268,7 +270,15 @@ def update_dataset(dataset_id, data, user):
external_knowledge_api_id = data.get("external_knowledge_api_id", None)
if not external_knowledge_api_id:
raise ValueError("External knowledge api id is required.")
- external_knowledge_binding = ExternalKnowledgeBindings.query.filter_by(dataset_id=dataset_id).first()
+
+ with Session(db.engine) as session:
+ external_knowledge_binding = (
+ session.query(ExternalKnowledgeBindings).filter_by(dataset_id=dataset_id).first()
+ )
+
+ if not external_knowledge_binding:
+ raise ValueError("External knowledge binding not found.")
+
if (
external_knowledge_binding.external_knowledge_id != external_knowledge_id
or external_knowledge_binding.external_knowledge_api_id != external_knowledge_api_id
@@ -316,8 +326,15 @@ def update_dataset(dataset_id, data, user):
except ProviderTokenNotInitError as ex:
raise ValueError(ex.description)
else:
+ # add default plugin id to both setting sets, to make sure the plugin model provider is consistent
+ plugin_model_provider = dataset.embedding_model_provider
+ plugin_model_provider = str(ModelProviderID(plugin_model_provider))
+
+ new_plugin_model_provider = data["embedding_model_provider"]
+ new_plugin_model_provider = str(ModelProviderID(new_plugin_model_provider))
+
if (
- data["embedding_model_provider"] != dataset.embedding_model_provider
+ new_plugin_model_provider != plugin_model_provider
or data["embedding_model"] != dataset.embedding_model
):
action = "update"
@@ -958,6 +975,8 @@ def save_document_with_dataset_id(
"notion_page_icon": page.page_icon.model_dump() if page.page_icon else None,
"type": page.type,
}
+ # Truncate page name to 255 characters to prevent DB field length errors
+ truncated_page_name = page.page_name[:255] if page.page_name else "nopagename"
document = DocumentService.build_document(
dataset,
dataset_process_rule.id, # type: ignore
@@ -968,7 +987,7 @@ def save_document_with_dataset_id(
created_from,
position,
account,
- page.page_name,
+ truncated_page_name,
batch,
knowledge_config.metadata,
)
@@ -1468,7 +1487,7 @@ def create_segment(cls, args: dict, document: Document, dataset: Dataset):
model=dataset.embedding_model,
)
# calc embedding use tokens
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])
+ tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])[0]
lock_name = "add_segment_lock_document_id_{}".format(document.id)
with redis_client.lock(lock_name, timeout=600):
max_position = (
@@ -1545,9 +1564,12 @@ def multi_create_segment(cls, segments: list, document: Document, dataset: Datas
if dataset.indexing_technique == "high_quality" and embedding_model:
# calc embedding use tokens
if document.doc_form == "qa_model":
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content + segment_item["answer"]])
+ tokens = embedding_model.get_text_embedding_num_tokens(
+ texts=[content + segment_item["answer"]]
+ )[0]
else:
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])
+ tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])[0]
+
segment_document = DocumentSegment(
tenant_id=current_user.current_tenant_id,
dataset_id=document.dataset_id,
@@ -1695,9 +1717,9 @@ def update_segment(cls, args: SegmentUpdateArgs, segment: DocumentSegment, docum
# calc embedding use tokens
if document.doc_form == "qa_model":
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content + segment.answer])
+ tokens = embedding_model.get_text_embedding_num_tokens(texts=[content + segment.answer])[0]
else:
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])
+ tokens = embedding_model.get_text_embedding_num_tokens(texts=[content])[0]
segment.content = content
segment.index_node_hash = segment_hash
segment.word_count = len(content)
diff --git a/api/services/entities/model_provider_entities.py b/api/services/entities/model_provider_entities.py
index f1417c6cb94b80..bc385b2e22bba1 100644
--- a/api/services/entities/model_provider_entities.py
+++ b/api/services/entities/model_provider_entities.py
@@ -8,7 +8,7 @@
ModelWithProviderEntity,
ProviderModelWithStatusEntity,
)
-from core.entities.provider_entities import QuotaConfiguration
+from core.entities.provider_entities import ProviderQuotaType, QuotaConfiguration
from core.model_runtime.entities.common_entities import I18nObject
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.entities.provider_entities import (
@@ -18,7 +18,7 @@
ProviderHelpEntity,
SimpleProviderEntity,
)
-from models.provider import ProviderQuotaType, ProviderType
+from models.provider import ProviderType
class CustomConfigurationStatus(Enum):
@@ -53,6 +53,7 @@ class ProviderResponse(BaseModel):
Model class for provider response.
"""
+ tenant_id: str
provider: str
label: I18nObject
description: Optional[I18nObject] = None
@@ -74,7 +75,9 @@ class ProviderResponse(BaseModel):
def __init__(self, **data) -> None:
super().__init__(**data)
- url_prefix = dify_config.CONSOLE_API_URL + f"/console/api/workspaces/current/model-providers/{self.provider}"
+ url_prefix = (
+ dify_config.CONSOLE_API_URL + f"/console/api/workspaces/{self.tenant_id}/model-providers/{self.provider}"
+ )
if self.icon_small is not None:
self.icon_small = I18nObject(
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
@@ -91,6 +94,7 @@ class ProviderWithModelsResponse(BaseModel):
Model class for provider with models response.
"""
+ tenant_id: str
provider: str
label: I18nObject
icon_small: Optional[I18nObject] = None
@@ -101,7 +105,9 @@ class ProviderWithModelsResponse(BaseModel):
def __init__(self, **data) -> None:
super().__init__(**data)
- url_prefix = dify_config.CONSOLE_API_URL + f"/console/api/workspaces/current/model-providers/{self.provider}"
+ url_prefix = (
+ dify_config.CONSOLE_API_URL + f"/console/api/workspaces/{self.tenant_id}/model-providers/{self.provider}"
+ )
if self.icon_small is not None:
self.icon_small = I18nObject(
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
@@ -118,10 +124,14 @@ class SimpleProviderEntityResponse(SimpleProviderEntity):
Simple provider entity response.
"""
+ tenant_id: str
+
def __init__(self, **data) -> None:
super().__init__(**data)
- url_prefix = dify_config.CONSOLE_API_URL + f"/console/api/workspaces/current/model-providers/{self.provider}"
+ url_prefix = (
+ dify_config.CONSOLE_API_URL + f"/console/api/workspaces/{self.tenant_id}/model-providers/{self.provider}"
+ )
if self.icon_small is not None:
self.icon_small = I18nObject(
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
@@ -146,13 +156,14 @@ class DefaultModelResponse(BaseModel):
model_config = ConfigDict(protected_namespaces=())
-class ModelWithProviderEntityResponse(ModelWithProviderEntity):
+class ModelWithProviderEntityResponse(ProviderModelWithStatusEntity):
"""
Model with provider entity.
"""
- # FIXME type error ignore here
- provider: SimpleProviderEntityResponse # type: ignore
+ provider: SimpleProviderEntityResponse
- def __init__(self, model: ModelWithProviderEntity) -> None:
- super().__init__(**model.model_dump())
+ def __init__(self, tenant_id: str, model: ModelWithProviderEntity) -> None:
+ dump_model = model.model_dump()
+ dump_model["provider"]["tenant_id"] = tenant_id
+ super().__init__(**dump_model)
diff --git a/api/services/feature_service.py b/api/services/feature_service.py
index b9261d19d7930e..a42b3020cd9b69 100644
--- a/api/services/feature_service.py
+++ b/api/services/feature_service.py
@@ -58,6 +58,8 @@ class SystemFeatureModel(BaseModel):
sso_enforced_for_web: bool = False
sso_enforced_for_web_protocol: str = ""
enable_web_sso_switch_component: bool = False
+ enable_marketplace: bool = True
+ max_plugin_package_size: int = dify_config.PLUGIN_MAX_PACKAGE_SIZE
enable_email_code_login: bool = False
enable_email_password_login: bool = True
enable_social_oauth_login: bool = False
@@ -90,6 +92,9 @@ def get_system_features(cls) -> SystemFeatureModel:
cls._fulfill_params_from_enterprise(system_features)
+ if dify_config.MARKETPLACE_ENABLED:
+ system_features.enable_marketplace = True
+
return system_features
@classmethod
diff --git a/api/services/hit_testing_service.py b/api/services/hit_testing_service.py
index e9176fc1c6015c..f8c1c1d297a411 100644
--- a/api/services/hit_testing_service.py
+++ b/api/services/hit_testing_service.py
@@ -47,7 +47,7 @@ def retrieve(
all_documents = RetrievalService.retrieve(
retrieval_method=retrieval_model.get("search_method", "semantic_search"),
dataset_id=dataset.id,
- query=cls.escape_query_for_search(query),
+ query=query,
top_k=retrieval_model.get("top_k", 2),
score_threshold=retrieval_model.get("score_threshold", 0.0)
if retrieval_model["score_threshold_enabled"]
diff --git a/api/services/message_service.py b/api/services/message_service.py
index c17122ef647ecd..480d03862334b6 100644
--- a/api/services/message_service.py
+++ b/api/services/message_service.py
@@ -46,6 +46,8 @@ def pagination_by_first_id(
app_model=app_model, user=user, conversation_id=conversation_id
)
+ fetch_limit = limit + 1
+
if first_id:
first_message = (
db.session.query(Message)
@@ -64,7 +66,7 @@ def pagination_by_first_id(
Message.id != first_message.id,
)
.order_by(Message.created_at.desc())
- .limit(limit)
+ .limit(fetch_limit)
.all()
)
else:
@@ -72,25 +74,14 @@ def pagination_by_first_id(
db.session.query(Message)
.filter(Message.conversation_id == conversation.id)
.order_by(Message.created_at.desc())
- .limit(limit)
+ .limit(fetch_limit)
.all()
)
has_more = False
- if len(history_messages) == limit:
- current_page_first_message = history_messages[-1]
- rest_count = (
- db.session.query(Message)
- .filter(
- Message.conversation_id == conversation.id,
- Message.created_at < current_page_first_message.created_at,
- Message.id != current_page_first_message.id,
- )
- .count()
- )
-
- if rest_count > 0:
- has_more = True
+ if len(history_messages) > limit:
+ has_more = True
+ history_messages = history_messages[:-1]
if order == "asc":
history_messages = list(reversed(history_messages))
@@ -112,6 +103,8 @@ def pagination_by_last_id(
base_query = db.session.query(Message)
+ fetch_limit = limit + 1
+
if conversation_id is not None:
conversation = ConversationService.get_conversation(
app_model=app_model, user=user, conversation_id=conversation_id
@@ -131,21 +124,16 @@ def pagination_by_last_id(
history_messages = (
base_query.filter(Message.created_at < last_message.created_at, Message.id != last_message.id)
.order_by(Message.created_at.desc())
- .limit(limit)
+ .limit(fetch_limit)
.all()
)
else:
- history_messages = base_query.order_by(Message.created_at.desc()).limit(limit).all()
+ history_messages = base_query.order_by(Message.created_at.desc()).limit(fetch_limit).all()
has_more = False
- if len(history_messages) == limit:
- current_page_first_message = history_messages[-1]
- rest_count = base_query.filter(
- Message.created_at < current_page_first_message.created_at, Message.id != current_page_first_message.id
- ).count()
-
- if rest_count > 0:
- has_more = True
+ if len(history_messages) > limit:
+ has_more = True
+ history_messages = history_messages[:-1]
return InfiniteScrollPagination(data=history_messages, limit=limit, has_more=has_more)
diff --git a/api/services/model_load_balancing_service.py b/api/services/model_load_balancing_service.py
index bacd3a8ec3d04f..26311a6377bfcd 100644
--- a/api/services/model_load_balancing_service.py
+++ b/api/services/model_load_balancing_service.py
@@ -14,7 +14,7 @@
ModelCredentialSchema,
ProviderCredentialSchema,
)
-from core.model_runtime.model_providers import model_provider_factory
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
from core.provider_manager import ProviderManager
from extensions.ext_database import db
from models.provider import LoadBalancingModelConfig
@@ -527,6 +527,7 @@ def _custom_credentials_validate(
credentials[key] = encrypter.decrypt_token(tenant_id, original_credentials[key])
if validate:
+ model_provider_factory = ModelProviderFactory(tenant_id)
if isinstance(credential_schemas, ModelCredentialSchema):
credentials = model_provider_factory.model_credentials_validate(
provider=provider_configuration.provider.provider,
diff --git a/api/services/model_provider_service.py b/api/services/model_provider_service.py
index b10c5ad2d616e9..0a0a5619e17c55 100644
--- a/api/services/model_provider_service.py
+++ b/api/services/model_provider_service.py
@@ -1,16 +1,9 @@
import logging
-import mimetypes
-import os
-from pathlib import Path
-from typing import Optional, cast
-
-import requests
-from flask import current_app
+from typing import Optional
from core.entities.model_entities import ModelStatus, ModelWithProviderEntity, ProviderModelWithStatusEntity
from core.model_runtime.entities.model_entities import ModelType, ParameterRule
-from core.model_runtime.model_providers import model_provider_factory
-from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
from core.provider_manager import ProviderManager
from models.provider import ProviderType
from services.entities.model_provider_entities import (
@@ -54,6 +47,7 @@ def get_provider_list(self, tenant_id: str, model_type: Optional[str] = None) ->
continue
provider_response = ProviderResponse(
+ tenant_id=tenant_id,
provider=provider_configuration.provider.provider,
label=provider_configuration.provider.label,
description=provider_configuration.provider.description,
@@ -97,10 +91,11 @@ def get_models_by_provider(self, tenant_id: str, provider: str) -> list[ModelWit
# Get provider available models
return [
- ModelWithProviderEntityResponse(model) for model in provider_configurations.get_models(provider=provider)
+ ModelWithProviderEntityResponse(tenant_id=tenant_id, model=model)
+ for model in provider_configurations.get_models(provider=provider)
]
- def get_provider_credentials(self, tenant_id: str, provider: str):
+ def get_provider_credentials(self, tenant_id: str, provider: str) -> Optional[dict]:
"""
get provider credentials.
"""
@@ -168,7 +163,7 @@ def remove_provider_credentials(self, tenant_id: str, provider: str) -> None:
# Remove custom provider credentials.
provider_configuration.delete_custom_credentials()
- def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str):
+ def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str) -> Optional[dict]:
"""
get model credentials.
@@ -302,6 +297,7 @@ def get_models_by_model_type(self, tenant_id: str, model_type: str) -> list[Prov
providers_with_models.append(
ProviderWithModelsResponse(
+ tenant_id=tenant_id,
provider=provider,
label=first_model.provider.label,
icon_small=first_model.provider.icon_small,
@@ -343,18 +339,17 @@ def get_model_parameter_rules(self, tenant_id: str, provider: str, model: str) -
if not provider_configuration:
raise ValueError(f"Provider {provider} does not exist.")
- # Get model instance of LLM
- model_type_instance = provider_configuration.get_model_type_instance(ModelType.LLM)
- model_type_instance = cast(LargeLanguageModel, model_type_instance)
-
# fetch credentials
credentials = provider_configuration.get_current_credentials(model_type=ModelType.LLM, model=model)
if not credentials:
return []
- # Call get_parameter_rules method of model instance to get model parameter rules
- return list(model_type_instance.get_parameter_rules(model=model, credentials=credentials))
+ model_schema = provider_configuration.get_model_schema(
+ model_type=ModelType.LLM, model=model, credentials=credentials
+ )
+
+ return model_schema.parameter_rules if model_schema else []
def get_default_model_of_model_type(self, tenant_id: str, model_type: str) -> Optional[DefaultModelResponse]:
"""
@@ -365,13 +360,15 @@ def get_default_model_of_model_type(self, tenant_id: str, model_type: str) -> Op
:return:
"""
model_type_enum = ModelType.value_of(model_type)
- result = self.provider_manager.get_default_model(tenant_id=tenant_id, model_type=model_type_enum)
+
try:
+ result = self.provider_manager.get_default_model(tenant_id=tenant_id, model_type=model_type_enum)
return (
DefaultModelResponse(
model=result.model,
model_type=result.model_type,
provider=SimpleProviderEntityResponse(
+ tenant_id=tenant_id,
provider=result.provider.provider,
label=result.provider.label,
icon_small=result.provider.icon_small,
@@ -383,7 +380,7 @@ def get_default_model_of_model_type(self, tenant_id: str, model_type: str) -> Op
else None
)
except Exception as e:
- logger.info(f"get_default_model_of_model_type error: {e}")
+ logger.debug(f"get_default_model_of_model_type error: {e}")
return None
def update_default_model_of_model_type(self, tenant_id: str, model_type: str, provider: str, model: str) -> None:
@@ -402,55 +399,21 @@ def update_default_model_of_model_type(self, tenant_id: str, model_type: str, pr
)
def get_model_provider_icon(
- self, provider: str, icon_type: str, lang: str
+ self, tenant_id: str, provider: str, icon_type: str, lang: str
) -> tuple[Optional[bytes], Optional[str]]:
"""
get model provider icon.
+ :param tenant_id: workspace id
:param provider: provider name
:param icon_type: icon type (icon_small or icon_large)
:param lang: language (zh_Hans or en_US)
:return:
"""
- provider_instance = model_provider_factory.get_provider_instance(provider)
- provider_schema = provider_instance.get_provider_schema()
- file_name: str | None = None
-
- if icon_type.lower() == "icon_small":
- if not provider_schema.icon_small:
- raise ValueError(f"Provider {provider} does not have small icon.")
-
- if lang.lower() == "zh_hans":
- file_name = provider_schema.icon_small.zh_Hans
- else:
- file_name = provider_schema.icon_small.en_US
- else:
- if not provider_schema.icon_large:
- raise ValueError(f"Provider {provider} does not have large icon.")
-
- if lang.lower() == "zh_hans":
- file_name = provider_schema.icon_large.zh_Hans
- else:
- file_name = provider_schema.icon_large.en_US
- if not file_name:
- return None, None
-
- root_path = current_app.root_path
- provider_instance_path = os.path.dirname(
- os.path.join(root_path, provider_instance.__class__.__module__.replace(".", "/"))
- )
- file_path = os.path.join(provider_instance_path, "_assets")
- file_path = os.path.join(file_path, file_name)
-
- if not os.path.exists(file_path):
- return None, None
+ model_provider_factory = ModelProviderFactory(tenant_id)
+ byte_data, mime_type = model_provider_factory.get_provider_icon(provider, icon_type, lang)
- mimetype, _ = mimetypes.guess_type(file_path)
- mimetype = mimetype or "application/octet-stream"
-
- # read binary from file
- byte_data = Path(file_path).read_bytes()
- return byte_data, mimetype
+ return byte_data, mime_type
def switch_preferred_provider(self, tenant_id: str, provider: str, preferred_provider_type: str) -> None:
"""
@@ -516,48 +479,3 @@ def disable_model(self, tenant_id: str, provider: str, model: str, model_type: s
# Enable model
provider_configuration.disable_model(model=model, model_type=ModelType.value_of(model_type))
-
- def free_quota_submit(self, tenant_id: str, provider: str):
- api_key = os.environ.get("FREE_QUOTA_APPLY_API_KEY")
- api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL", "")
- api_url = api_base_url + "/api/v1/providers/apply"
-
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
- response = requests.post(api_url, headers=headers, json={"workspace_id": tenant_id, "provider_name": provider})
- if not response.ok:
- logger.error(f"Request FREE QUOTA APPLY SERVER Error: {response.status_code} ")
- raise ValueError(f"Error: {response.status_code} ")
-
- if response.json()["code"] != "success":
- raise ValueError(f"error: {response.json()['message']}")
-
- rst = response.json()
-
- if rst["type"] == "redirect":
- return {"type": rst["type"], "redirect_url": rst["redirect_url"]}
- else:
- return {"type": rst["type"], "result": "success"}
-
- def free_quota_qualification_verify(self, tenant_id: str, provider: str, token: Optional[str]):
- api_key = os.environ.get("FREE_QUOTA_APPLY_API_KEY")
- api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL", "")
- api_url = api_base_url + "/api/v1/providers/qualification-verify"
-
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
- json_data = {"workspace_id": tenant_id, "provider_name": provider}
- if token:
- json_data["token"] = token
- response = requests.post(api_url, headers=headers, json=json_data)
- if not response.ok:
- logger.error(f"Request FREE QUOTA APPLY SERVER Error: {response.status_code} ")
- raise ValueError(f"Error: {response.status_code} ")
-
- rst = response.json()
- if rst["code"] != "success":
- raise ValueError(f"error: {rst['message']}")
-
- data = rst["data"]
- if data["qualified"] is True:
- return {"result": "success", "provider_name": provider, "flag": True}
- else:
- return {"result": "success", "provider_name": provider, "flag": False, "reason": data["reason"]}
diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py b/api/services/plugin/__init__.py
similarity index 100%
rename from api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py
rename to api/services/plugin/__init__.py
diff --git a/api/services/plugin/data_migration.py b/api/services/plugin/data_migration.py
new file mode 100644
index 00000000000000..7228a166320631
--- /dev/null
+++ b/api/services/plugin/data_migration.py
@@ -0,0 +1,185 @@
+import json
+import logging
+
+import click
+
+from core.entities import DEFAULT_PLUGIN_ID
+from models.engine import db
+
+logger = logging.getLogger(__name__)
+
+
+class PluginDataMigration:
+ @classmethod
+ def migrate(cls) -> None:
+ cls.migrate_db_records("providers", "provider_name") # large table
+ cls.migrate_db_records("provider_models", "provider_name")
+ cls.migrate_db_records("provider_orders", "provider_name")
+ cls.migrate_db_records("tenant_default_models", "provider_name")
+ cls.migrate_db_records("tenant_preferred_model_providers", "provider_name")
+ cls.migrate_db_records("provider_model_settings", "provider_name")
+ cls.migrate_db_records("load_balancing_model_configs", "provider_name")
+ cls.migrate_datasets()
+ cls.migrate_db_records("embeddings", "provider_name") # large table
+ cls.migrate_db_records("dataset_collection_bindings", "provider_name")
+ cls.migrate_db_records("tool_builtin_providers", "provider")
+
+ @classmethod
+ def migrate_datasets(cls) -> None:
+ table_name = "datasets"
+ provider_column_name = "embedding_model_provider"
+
+ click.echo(click.style(f"Migrating [{table_name}] data for plugin", fg="white"))
+
+ processed_count = 0
+ failed_ids = []
+ while True:
+ sql = f"""select id, {provider_column_name} as provider_name, retrieval_model from {table_name}
+where {provider_column_name} not like '%/%' and {provider_column_name} is not null and {provider_column_name} != ''
+limit 1000"""
+ with db.engine.begin() as conn:
+ rs = conn.execute(db.text(sql))
+
+ current_iter_count = 0
+ for i in rs:
+ record_id = str(i.id)
+ provider_name = str(i.provider_name)
+ retrieval_model = i.retrieval_model
+ print(type(retrieval_model))
+
+ if record_id in failed_ids:
+ continue
+
+ retrieval_model_changed = False
+ if retrieval_model:
+ if (
+ "reranking_model" in retrieval_model
+ and "reranking_provider_name" in retrieval_model["reranking_model"]
+ and retrieval_model["reranking_model"]["reranking_provider_name"]
+ and "/" not in retrieval_model["reranking_model"]["reranking_provider_name"]
+ ):
+ click.echo(
+ click.style(
+ f"[{processed_count}] Migrating {table_name} {record_id} "
+ f"(reranking_provider_name: "
+ f"{retrieval_model['reranking_model']['reranking_provider_name']})",
+ fg="white",
+ )
+ )
+ retrieval_model["reranking_model"]["reranking_provider_name"] = (
+ f"{DEFAULT_PLUGIN_ID}/{retrieval_model['reranking_model']['reranking_provider_name']}/{retrieval_model['reranking_model']['reranking_provider_name']}"
+ )
+ retrieval_model_changed = True
+
+ click.echo(
+ click.style(
+ f"[{processed_count}] Migrating [{table_name}] {record_id} ({provider_name})",
+ fg="white",
+ )
+ )
+
+ try:
+ # update provider name append with "langgenius/{provider_name}/{provider_name}"
+ params = {"record_id": record_id}
+ update_retrieval_model_sql = ""
+ if retrieval_model and retrieval_model_changed:
+ update_retrieval_model_sql = ", retrieval_model = :retrieval_model"
+ params["retrieval_model"] = json.dumps(retrieval_model)
+
+ sql = f"""update {table_name}
+ set {provider_column_name} =
+ concat('{DEFAULT_PLUGIN_ID}/', {provider_column_name}, '/', {provider_column_name})
+ {update_retrieval_model_sql}
+ where id = :record_id"""
+ conn.execute(db.text(sql), params)
+ click.echo(
+ click.style(
+ f"[{processed_count}] Migrated [{table_name}] {record_id} ({provider_name})",
+ fg="green",
+ )
+ )
+ except Exception:
+ failed_ids.append(record_id)
+ click.echo(
+ click.style(
+ f"[{processed_count}] Failed to migrate [{table_name}] {record_id} ({provider_name})",
+ fg="red",
+ )
+ )
+ logger.exception(
+ f"[{processed_count}] Failed to migrate [{table_name}] {record_id} ({provider_name})"
+ )
+ continue
+
+ current_iter_count += 1
+ processed_count += 1
+
+ if not current_iter_count:
+ break
+
+ click.echo(
+ click.style(f"Migrate [{table_name}] data for plugin completed, total: {processed_count}", fg="green")
+ )
+
+ @classmethod
+ def migrate_db_records(cls, table_name: str, provider_column_name: str) -> None:
+ click.echo(click.style(f"Migrating [{table_name}] data for plugin", fg="white"))
+
+ processed_count = 0
+ failed_ids = []
+ while True:
+ sql = f"""select id, {provider_column_name} as provider_name from {table_name}
+where {provider_column_name} not like '%/%' and {provider_column_name} is not null and {provider_column_name} != ''
+limit 1000"""
+ with db.engine.begin() as conn:
+ rs = conn.execute(db.text(sql))
+
+ current_iter_count = 0
+ for i in rs:
+ current_iter_count += 1
+ processed_count += 1
+ record_id = str(i.id)
+ provider_name = str(i.provider_name)
+
+ if record_id in failed_ids:
+ continue
+
+ click.echo(
+ click.style(
+ f"[{processed_count}] Migrating [{table_name}] {record_id} ({provider_name})",
+ fg="white",
+ )
+ )
+
+ try:
+ # update provider name append with "langgenius/{provider_name}/{provider_name}"
+ sql = f"""update {table_name}
+ set {provider_column_name} =
+ concat('{DEFAULT_PLUGIN_ID}/', {provider_column_name}, '/', {provider_column_name})
+ where id = :record_id"""
+ conn.execute(db.text(sql), {"record_id": record_id})
+ click.echo(
+ click.style(
+ f"[{processed_count}] Migrated [{table_name}] {record_id} ({provider_name})",
+ fg="green",
+ )
+ )
+ except Exception:
+ failed_ids.append(record_id)
+ click.echo(
+ click.style(
+ f"[{processed_count}] Failed to migrate [{table_name}] {record_id} ({provider_name})",
+ fg="red",
+ )
+ )
+ logger.exception(
+ f"[{processed_count}] Failed to migrate [{table_name}] {record_id} ({provider_name})"
+ )
+ continue
+
+ if not current_iter_count:
+ break
+
+ click.echo(
+ click.style(f"Migrate [{table_name}] data for plugin completed, total: {processed_count}", fg="green")
+ )
diff --git a/api/services/plugin/dependencies_analysis.py b/api/services/plugin/dependencies_analysis.py
new file mode 100644
index 00000000000000..778f05a0cd2e3e
--- /dev/null
+++ b/api/services/plugin/dependencies_analysis.py
@@ -0,0 +1,121 @@
+from core.helper import marketplace
+from core.plugin.entities.plugin import ModelProviderID, PluginDependency, PluginInstallationSource, ToolProviderID
+from core.plugin.manager.plugin import PluginInstallationManager
+
+
+class DependenciesAnalysisService:
+ @classmethod
+ def analyze_tool_dependency(cls, tool_id: str) -> str:
+ """
+ Analyze the dependency of a tool.
+
+ Convert the tool id to the plugin_id
+ """
+ try:
+ return ToolProviderID(tool_id).plugin_id
+ except Exception as e:
+ raise e
+
+ @classmethod
+ def analyze_model_provider_dependency(cls, model_provider_id: str) -> str:
+ """
+ Analyze the dependency of a model provider.
+
+ Convert the model provider id to the plugin_id
+ """
+ try:
+ return ModelProviderID(model_provider_id).plugin_id
+ except Exception as e:
+ raise e
+
+ @classmethod
+ def get_leaked_dependencies(cls, tenant_id: str, dependencies: list[PluginDependency]) -> list[PluginDependency]:
+ """
+ Check dependencies, returns the leaked dependencies in current workspace
+ """
+ required_plugin_unique_identifiers = []
+ for dependency in dependencies:
+ required_plugin_unique_identifiers.append(dependency.value.plugin_unique_identifier)
+
+ manager = PluginInstallationManager()
+
+ # get leaked dependencies
+ missing_plugins = manager.fetch_missing_dependencies(tenant_id, required_plugin_unique_identifiers)
+ missing_plugin_unique_identifiers = {plugin.plugin_unique_identifier: plugin for plugin in missing_plugins}
+
+ leaked_dependencies = []
+ for dependency in dependencies:
+ unique_identifier = dependency.value.plugin_unique_identifier
+ if unique_identifier in missing_plugin_unique_identifiers:
+ leaked_dependencies.append(
+ PluginDependency(
+ type=dependency.type,
+ value=dependency.value,
+ current_identifier=missing_plugin_unique_identifiers[unique_identifier].current_identifier,
+ )
+ )
+
+ return leaked_dependencies
+
+ @classmethod
+ def generate_dependencies(cls, tenant_id: str, dependencies: list[str]) -> list[PluginDependency]:
+ """
+ Generate dependencies through the list of plugin ids
+ """
+ dependencies = list(set(dependencies))
+ manager = PluginInstallationManager()
+ plugins = manager.fetch_plugin_installation_by_ids(tenant_id, dependencies)
+ result = []
+ for plugin in plugins:
+ if plugin.source == PluginInstallationSource.Github:
+ result.append(
+ PluginDependency(
+ type=PluginDependency.Type.Github,
+ value=PluginDependency.Github(
+ repo=plugin.meta["repo"],
+ version=plugin.meta["version"],
+ package=plugin.meta["package"],
+ github_plugin_unique_identifier=plugin.plugin_unique_identifier,
+ ),
+ )
+ )
+ elif plugin.source == PluginInstallationSource.Marketplace:
+ result.append(
+ PluginDependency(
+ type=PluginDependency.Type.Marketplace,
+ value=PluginDependency.Marketplace(
+ marketplace_plugin_unique_identifier=plugin.plugin_unique_identifier
+ ),
+ )
+ )
+ elif plugin.source == PluginInstallationSource.Package:
+ result.append(
+ PluginDependency(
+ type=PluginDependency.Type.Package,
+ value=PluginDependency.Package(plugin_unique_identifier=plugin.plugin_unique_identifier),
+ )
+ )
+ elif plugin.source == PluginInstallationSource.Remote:
+ raise ValueError(
+ f"You used a remote plugin: {plugin.plugin_unique_identifier} in the app, please remove it first"
+ " if you want to export the DSL."
+ )
+ else:
+ raise ValueError(f"Unknown plugin source: {plugin.source}")
+
+ return result
+
+ @classmethod
+ def generate_latest_dependencies(cls, dependencies: list[str]) -> list[PluginDependency]:
+ """
+ Generate the latest version of dependencies
+ """
+ dependencies = list(set(dependencies))
+ deps = marketplace.batch_fetch_plugin_manifests(dependencies)
+ return [
+ PluginDependency(
+ type=PluginDependency.Type.Marketplace,
+ value=PluginDependency.Marketplace(marketplace_plugin_unique_identifier=dep.latest_package_identifier),
+ )
+ for dep in deps
+ ]
diff --git a/api/services/plugin/endpoint_service.py b/api/services/plugin/endpoint_service.py
new file mode 100644
index 00000000000000..35961345a8cc17
--- /dev/null
+++ b/api/services/plugin/endpoint_service.py
@@ -0,0 +1,66 @@
+from core.plugin.manager.endpoint import PluginEndpointManager
+
+
+class EndpointService:
+ @classmethod
+ def create_endpoint(cls, tenant_id: str, user_id: str, plugin_unique_identifier: str, name: str, settings: dict):
+ return PluginEndpointManager().create_endpoint(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ plugin_unique_identifier=plugin_unique_identifier,
+ name=name,
+ settings=settings,
+ )
+
+ @classmethod
+ def list_endpoints(cls, tenant_id: str, user_id: str, page: int, page_size: int):
+ return PluginEndpointManager().list_endpoints(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ page=page,
+ page_size=page_size,
+ )
+
+ @classmethod
+ def list_endpoints_for_single_plugin(cls, tenant_id: str, user_id: str, plugin_id: str, page: int, page_size: int):
+ return PluginEndpointManager().list_endpoints_for_single_plugin(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ plugin_id=plugin_id,
+ page=page,
+ page_size=page_size,
+ )
+
+ @classmethod
+ def update_endpoint(cls, tenant_id: str, user_id: str, endpoint_id: str, name: str, settings: dict):
+ return PluginEndpointManager().update_endpoint(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ endpoint_id=endpoint_id,
+ name=name,
+ settings=settings,
+ )
+
+ @classmethod
+ def delete_endpoint(cls, tenant_id: str, user_id: str, endpoint_id: str):
+ return PluginEndpointManager().delete_endpoint(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ endpoint_id=endpoint_id,
+ )
+
+ @classmethod
+ def enable_endpoint(cls, tenant_id: str, user_id: str, endpoint_id: str):
+ return PluginEndpointManager().enable_endpoint(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ endpoint_id=endpoint_id,
+ )
+
+ @classmethod
+ def disable_endpoint(cls, tenant_id: str, user_id: str, endpoint_id: str):
+ return PluginEndpointManager().disable_endpoint(
+ tenant_id=tenant_id,
+ user_id=user_id,
+ endpoint_id=endpoint_id,
+ )
diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py b/api/services/plugin/github_service.py
similarity index 100%
rename from api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py
rename to api/services/plugin/github_service.py
diff --git a/api/services/plugin/plugin_migration.py b/api/services/plugin/plugin_migration.py
new file mode 100644
index 00000000000000..ec9e0aa8dc6b16
--- /dev/null
+++ b/api/services/plugin/plugin_migration.py
@@ -0,0 +1,501 @@
+import datetime
+import json
+import logging
+import time
+from collections.abc import Mapping, Sequence
+from concurrent.futures import ThreadPoolExecutor
+from pathlib import Path
+from typing import Any, Optional
+from uuid import uuid4
+
+import click
+import tqdm
+from flask import Flask, current_app
+from sqlalchemy.orm import Session
+
+from core.agent.entities import AgentToolEntity
+from core.helper import marketplace
+from core.plugin.entities.plugin import ModelProviderID, PluginInstallationSource, ToolProviderID
+from core.plugin.entities.plugin_daemon import PluginInstallTaskStatus
+from core.plugin.manager.plugin import PluginInstallationManager
+from core.tools.entities.tool_entities import ToolProviderType
+from models.account import Tenant
+from models.engine import db
+from models.model import App, AppMode, AppModelConfig
+from models.tools import BuiltinToolProvider
+from models.workflow import Workflow
+
+logger = logging.getLogger(__name__)
+
+excluded_providers = ["time", "audio", "code", "webscraper"]
+
+
+class PluginMigration:
+ @classmethod
+ def extract_plugins(cls, filepath: str, workers: int) -> None:
+ """
+ Migrate plugin.
+ """
+ from threading import Lock
+
+ click.echo(click.style("Migrating models/tools to new plugin Mechanism", fg="white"))
+ ended_at = datetime.datetime.now()
+ started_at = datetime.datetime(2023, 4, 3, 8, 59, 24)
+ current_time = started_at
+
+ with Session(db.engine) as session:
+ total_tenant_count = session.query(Tenant.id).count()
+
+ click.echo(click.style(f"Total tenant count: {total_tenant_count}", fg="white"))
+
+ handled_tenant_count = 0
+ file_lock = Lock()
+ counter_lock = Lock()
+
+ thread_pool = ThreadPoolExecutor(max_workers=workers)
+
+ def process_tenant(flask_app: Flask, tenant_id: str) -> None:
+ with flask_app.app_context():
+ nonlocal handled_tenant_count
+ try:
+ plugins = cls.extract_installed_plugin_ids(tenant_id)
+ # Use lock when writing to file
+ with file_lock:
+ with open(filepath, "a") as f:
+ f.write(json.dumps({"tenant_id": tenant_id, "plugins": plugins}) + "\n")
+
+ # Use lock when updating counter
+ with counter_lock:
+ nonlocal handled_tenant_count
+ handled_tenant_count += 1
+ click.echo(
+ click.style(
+ f"[{datetime.datetime.now()}] "
+ f"Processed {handled_tenant_count} tenants "
+ f"({(handled_tenant_count / total_tenant_count) * 100:.1f}%), "
+ f"{handled_tenant_count}/{total_tenant_count}",
+ fg="green",
+ )
+ )
+ except Exception:
+ logger.exception(f"Failed to process tenant {tenant_id}")
+
+ futures = []
+
+ while current_time < ended_at:
+ click.echo(click.style(f"Current time: {current_time}, Started at: {datetime.datetime.now()}", fg="white"))
+ # Initial interval of 1 day, will be dynamically adjusted based on tenant count
+ interval = datetime.timedelta(days=1)
+ # Process tenants in this batch
+ with Session(db.engine) as session:
+ # Calculate tenant count in next batch with current interval
+ # Try different intervals until we find one with a reasonable tenant count
+ test_intervals = [
+ datetime.timedelta(days=1),
+ datetime.timedelta(hours=12),
+ datetime.timedelta(hours=6),
+ datetime.timedelta(hours=3),
+ datetime.timedelta(hours=1),
+ ]
+
+ for test_interval in test_intervals:
+ tenant_count = (
+ session.query(Tenant.id)
+ .filter(Tenant.created_at.between(current_time, current_time + test_interval))
+ .count()
+ )
+ if tenant_count <= 100:
+ interval = test_interval
+ break
+ else:
+ # If all intervals have too many tenants, use minimum interval
+ interval = datetime.timedelta(hours=1)
+
+ # Adjust interval to target ~100 tenants per batch
+ if tenant_count > 0:
+ # Scale interval based on ratio to target count
+ interval = min(
+ datetime.timedelta(days=1), # Max 1 day
+ max(
+ datetime.timedelta(hours=1), # Min 1 hour
+ interval * (100 / tenant_count), # Scale to target 100
+ ),
+ )
+
+ batch_end = min(current_time + interval, ended_at)
+
+ rs = (
+ session.query(Tenant.id)
+ .filter(Tenant.created_at.between(current_time, batch_end))
+ .order_by(Tenant.created_at)
+ )
+
+ tenants = []
+ for row in rs:
+ tenant_id = str(row.id)
+ try:
+ tenants.append(tenant_id)
+ except Exception:
+ logger.exception(f"Failed to process tenant {tenant_id}")
+ continue
+
+ futures.append(
+ thread_pool.submit(
+ process_tenant,
+ current_app._get_current_object(), # type: ignore[attr-defined]
+ tenant_id,
+ )
+ )
+
+ current_time = batch_end
+
+ # wait for all threads to finish
+ for future in futures:
+ future.result()
+
+ @classmethod
+ def extract_installed_plugin_ids(cls, tenant_id: str) -> Sequence[str]:
+ """
+ Extract installed plugin ids.
+ """
+ tools = cls.extract_tool_tables(tenant_id)
+ models = cls.extract_model_tables(tenant_id)
+ workflows = cls.extract_workflow_tables(tenant_id)
+ apps = cls.extract_app_tables(tenant_id)
+
+ return list({*tools, *models, *workflows, *apps})
+
+ @classmethod
+ def extract_model_tables(cls, tenant_id: str) -> Sequence[str]:
+ """
+ Extract model tables.
+
+ """
+ models: list[str] = []
+ table_pairs = [
+ ("providers", "provider_name"),
+ ("provider_models", "provider_name"),
+ ("provider_orders", "provider_name"),
+ ("tenant_default_models", "provider_name"),
+ ("tenant_preferred_model_providers", "provider_name"),
+ ("provider_model_settings", "provider_name"),
+ ("load_balancing_model_configs", "provider_name"),
+ ]
+
+ for table, column in table_pairs:
+ models.extend(cls.extract_model_table(tenant_id, table, column))
+
+ # duplicate models
+ models = list(set(models))
+
+ return models
+
+ @classmethod
+ def extract_model_table(cls, tenant_id: str, table: str, column: str) -> Sequence[str]:
+ """
+ Extract model table.
+ """
+ with Session(db.engine) as session:
+ rs = session.execute(
+ db.text(f"SELECT DISTINCT {column} FROM {table} WHERE tenant_id = :tenant_id"), {"tenant_id": tenant_id}
+ )
+ result = []
+ for row in rs:
+ provider_name = str(row[0])
+ result.append(ModelProviderID(provider_name).plugin_id)
+
+ return result
+
+ @classmethod
+ def extract_tool_tables(cls, tenant_id: str) -> Sequence[str]:
+ """
+ Extract tool tables.
+ """
+ with Session(db.engine) as session:
+ rs = session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
+ result = []
+ for row in rs:
+ result.append(ToolProviderID(row.provider).plugin_id)
+
+ return result
+
+ @classmethod
+ def extract_workflow_tables(cls, tenant_id: str) -> Sequence[str]:
+ """
+ Extract workflow tables, only ToolNode is required.
+ """
+
+ with Session(db.engine) as session:
+ rs = session.query(Workflow).filter(Workflow.tenant_id == tenant_id).all()
+ result = []
+ for row in rs:
+ graph = row.graph_dict
+ # get nodes
+ nodes = graph.get("nodes", [])
+
+ for node in nodes:
+ data = node.get("data", {})
+ if data.get("type") == "tool":
+ provider_name = data.get("provider_name")
+ provider_type = data.get("provider_type")
+ if provider_name not in excluded_providers and provider_type == ToolProviderType.BUILT_IN.value:
+ result.append(ToolProviderID(provider_name).plugin_id)
+
+ return result
+
+ @classmethod
+ def extract_app_tables(cls, tenant_id: str) -> Sequence[str]:
+ """
+ Extract app tables.
+ """
+ with Session(db.engine) as session:
+ apps = session.query(App).filter(App.tenant_id == tenant_id).all()
+ if not apps:
+ return []
+
+ agent_app_model_config_ids = [
+ app.app_model_config_id for app in apps if app.is_agent or app.mode == AppMode.AGENT_CHAT.value
+ ]
+
+ rs = session.query(AppModelConfig).filter(AppModelConfig.id.in_(agent_app_model_config_ids)).all()
+ result = []
+ for row in rs:
+ agent_config = row.agent_mode_dict
+ if "tools" in agent_config and isinstance(agent_config["tools"], list):
+ for tool in agent_config["tools"]:
+ if isinstance(tool, dict):
+ try:
+ tool_entity = AgentToolEntity(**tool)
+ if (
+ tool_entity.provider_type == ToolProviderType.BUILT_IN.value
+ and tool_entity.provider_id not in excluded_providers
+ ):
+ result.append(ToolProviderID(tool_entity.provider_id).plugin_id)
+
+ except Exception:
+ logger.exception(f"Failed to process tool {tool}")
+ continue
+
+ return result
+
+ @classmethod
+ def _fetch_plugin_unique_identifier(cls, plugin_id: str) -> Optional[str]:
+ """
+ Fetch plugin unique identifier using plugin id.
+ """
+ plugin_manifest = marketplace.batch_fetch_plugin_manifests([plugin_id])
+ if not plugin_manifest:
+ return None
+
+ return plugin_manifest[0].latest_package_identifier
+
+ @classmethod
+ def extract_unique_plugins_to_file(cls, extracted_plugins: str, output_file: str) -> None:
+ """
+ Extract unique plugins.
+ """
+ Path(output_file).write_text(json.dumps(cls.extract_unique_plugins(extracted_plugins)))
+
+ @classmethod
+ def extract_unique_plugins(cls, extracted_plugins: str) -> Mapping[str, Any]:
+ plugins: dict[str, str] = {}
+ plugin_ids = []
+ plugin_not_exist = []
+ logger.info(f"Extracting unique plugins from {extracted_plugins}")
+ with open(extracted_plugins) as f:
+ for line in f:
+ data = json.loads(line)
+ new_plugin_ids = data.get("plugins", [])
+ for plugin_id in new_plugin_ids:
+ if plugin_id not in plugin_ids:
+ plugin_ids.append(plugin_id)
+
+ def fetch_plugin(plugin_id):
+ try:
+ unique_identifier = cls._fetch_plugin_unique_identifier(plugin_id)
+ if unique_identifier:
+ plugins[plugin_id] = unique_identifier
+ else:
+ plugin_not_exist.append(plugin_id)
+ except Exception:
+ logger.exception(f"Failed to fetch plugin unique identifier for {plugin_id}")
+ plugin_not_exist.append(plugin_id)
+
+ with ThreadPoolExecutor(max_workers=10) as executor:
+ list(tqdm.tqdm(executor.map(fetch_plugin, plugin_ids), total=len(plugin_ids)))
+
+ return {"plugins": plugins, "plugin_not_exist": plugin_not_exist}
+
+ @classmethod
+ def install_plugins(cls, extracted_plugins: str, output_file: str, workers: int = 100) -> None:
+ """
+ Install plugins.
+ """
+ manager = PluginInstallationManager()
+
+ plugins = cls.extract_unique_plugins(extracted_plugins)
+ not_installed = []
+ plugin_install_failed = []
+
+ # use a fake tenant id to install all the plugins
+ fake_tenant_id = uuid4().hex
+ logger.info(f"Installing {len(plugins['plugins'])} plugin instances for fake tenant {fake_tenant_id}")
+
+ thread_pool = ThreadPoolExecutor(max_workers=workers)
+
+ response = cls.handle_plugin_instance_install(fake_tenant_id, plugins["plugins"])
+ if response.get("failed"):
+ plugin_install_failed.extend(response.get("failed", []))
+
+ def install(tenant_id: str, plugin_ids: list[str]) -> None:
+ logger.info(f"Installing {len(plugin_ids)} plugins for tenant {tenant_id}")
+ # fetch plugin already installed
+ installed_plugins = manager.list_plugins(tenant_id)
+ installed_plugins_ids = [plugin.plugin_id for plugin in installed_plugins]
+ # at most 64 plugins one batch
+ for i in range(0, len(plugin_ids), 64):
+ batch_plugin_ids = plugin_ids[i : i + 64]
+ batch_plugin_identifiers = [
+ plugins["plugins"][plugin_id]
+ for plugin_id in batch_plugin_ids
+ if plugin_id not in installed_plugins_ids and plugin_id in plugins["plugins"]
+ ]
+ manager.install_from_identifiers(
+ tenant_id,
+ batch_plugin_identifiers,
+ PluginInstallationSource.Marketplace,
+ metas=[
+ {
+ "plugin_unique_identifier": identifier,
+ }
+ for identifier in batch_plugin_identifiers
+ ],
+ )
+
+ with open(extracted_plugins) as f:
+ """
+ Read line by line, and install plugins for each tenant.
+ """
+ for line in f:
+ data = json.loads(line)
+ tenant_id = data.get("tenant_id")
+ plugin_ids = data.get("plugins", [])
+ current_not_installed = {
+ "tenant_id": tenant_id,
+ "plugin_not_exist": [],
+ }
+ # get plugin unique identifier
+ for plugin_id in plugin_ids:
+ unique_identifier = plugins.get(plugin_id)
+ if unique_identifier:
+ current_not_installed["plugin_not_exist"].append(plugin_id)
+
+ if current_not_installed["plugin_not_exist"]:
+ not_installed.append(current_not_installed)
+
+ thread_pool.submit(install, tenant_id, plugin_ids)
+
+ thread_pool.shutdown(wait=True)
+
+ logger.info("Uninstall plugins")
+
+ # get installation
+ try:
+ installation = manager.list_plugins(fake_tenant_id)
+ while installation:
+ for plugin in installation:
+ manager.uninstall(fake_tenant_id, plugin.installation_id)
+
+ installation = manager.list_plugins(fake_tenant_id)
+ except Exception:
+ logger.exception(f"Failed to get installation for tenant {fake_tenant_id}")
+
+ Path(output_file).write_text(
+ json.dumps(
+ {
+ "not_installed": not_installed,
+ "plugin_install_failed": plugin_install_failed,
+ }
+ )
+ )
+
+ @classmethod
+ def handle_plugin_instance_install(
+ cls, tenant_id: str, plugin_identifiers_map: Mapping[str, str]
+ ) -> Mapping[str, Any]:
+ """
+ Install plugins for a tenant.
+ """
+ manager = PluginInstallationManager()
+
+ # download all the plugins and upload
+ thread_pool = ThreadPoolExecutor(max_workers=10)
+ futures = []
+
+ for plugin_id, plugin_identifier in plugin_identifiers_map.items():
+
+ def download_and_upload(tenant_id, plugin_id, plugin_identifier):
+ plugin_package = marketplace.download_plugin_pkg(plugin_identifier)
+ if not plugin_package:
+ raise Exception(f"Failed to download plugin {plugin_identifier}")
+
+ # upload
+ manager.upload_pkg(tenant_id, plugin_package, verify_signature=True)
+
+ futures.append(thread_pool.submit(download_and_upload, tenant_id, plugin_id, plugin_identifier))
+
+ # Wait for all downloads to complete
+ for future in futures:
+ future.result() # This will raise any exceptions that occurred
+
+ thread_pool.shutdown(wait=True)
+ success = []
+ failed = []
+
+ reverse_map = {v: k for k, v in plugin_identifiers_map.items()}
+
+ # at most 8 plugins one batch
+ for i in range(0, len(plugin_identifiers_map), 8):
+ batch_plugin_ids = list(plugin_identifiers_map.keys())[i : i + 8]
+ batch_plugin_identifiers = [plugin_identifiers_map[plugin_id] for plugin_id in batch_plugin_ids]
+
+ try:
+ response = manager.install_from_identifiers(
+ tenant_id=tenant_id,
+ identifiers=batch_plugin_identifiers,
+ source=PluginInstallationSource.Marketplace,
+ metas=[
+ {
+ "plugin_unique_identifier": identifier,
+ }
+ for identifier in batch_plugin_identifiers
+ ],
+ )
+ except Exception:
+ # add to failed
+ failed.extend(batch_plugin_identifiers)
+ continue
+
+ if response.all_installed:
+ success.extend(batch_plugin_identifiers)
+ continue
+
+ task_id = response.task_id
+ done = False
+ while not done:
+ status = manager.fetch_plugin_installation_task(tenant_id, task_id)
+ if status.status in [PluginInstallTaskStatus.Failed, PluginInstallTaskStatus.Success]:
+ for plugin in status.plugins:
+ if plugin.status == PluginInstallTaskStatus.Success:
+ success.append(reverse_map[plugin.plugin_unique_identifier])
+ else:
+ failed.append(reverse_map[plugin.plugin_unique_identifier])
+ logger.error(
+ f"Failed to install plugin {plugin.plugin_unique_identifier}, error: {plugin.message}"
+ )
+
+ done = True
+ else:
+ time.sleep(1)
+
+ return {"success": success, "failed": failed}
diff --git a/api/services/plugin/plugin_permission_service.py b/api/services/plugin/plugin_permission_service.py
new file mode 100644
index 00000000000000..275e496037271a
--- /dev/null
+++ b/api/services/plugin/plugin_permission_service.py
@@ -0,0 +1,34 @@
+from sqlalchemy.orm import Session
+
+from extensions.ext_database import db
+from models.account import TenantPluginPermission
+
+
+class PluginPermissionService:
+ @staticmethod
+ def get_permission(tenant_id: str) -> TenantPluginPermission | None:
+ with Session(db.engine) as session:
+ return session.query(TenantPluginPermission).filter(TenantPluginPermission.tenant_id == tenant_id).first()
+
+ @staticmethod
+ def change_permission(
+ tenant_id: str,
+ install_permission: TenantPluginPermission.InstallPermission,
+ debug_permission: TenantPluginPermission.DebugPermission,
+ ):
+ with Session(db.engine) as session:
+ permission = (
+ session.query(TenantPluginPermission).filter(TenantPluginPermission.tenant_id == tenant_id).first()
+ )
+ if not permission:
+ permission = TenantPluginPermission(
+ tenant_id=tenant_id, install_permission=install_permission, debug_permission=debug_permission
+ )
+
+ session.add(permission)
+ else:
+ permission.install_permission = install_permission
+ permission.debug_permission = debug_permission
+
+ session.commit()
+ return True
diff --git a/api/services/plugin/plugin_service.py b/api/services/plugin/plugin_service.py
new file mode 100644
index 00000000000000..f84baf6b812a75
--- /dev/null
+++ b/api/services/plugin/plugin_service.py
@@ -0,0 +1,301 @@
+import logging
+from collections.abc import Sequence
+from mimetypes import guess_type
+
+from configs import dify_config
+from core.helper import marketplace
+from core.helper.download import download_with_size_limit
+from core.helper.marketplace import download_plugin_pkg
+from core.plugin.entities.bundle import PluginBundleDependency
+from core.plugin.entities.plugin import (
+ GenericProviderID,
+ PluginDeclaration,
+ PluginEntity,
+ PluginInstallation,
+ PluginInstallationSource,
+)
+from core.plugin.entities.plugin_daemon import PluginInstallTask, PluginUploadResponse
+from core.plugin.manager.asset import PluginAssetManager
+from core.plugin.manager.debugging import PluginDebuggingManager
+from core.plugin.manager.plugin import PluginInstallationManager
+
+logger = logging.getLogger(__name__)
+
+
+class PluginService:
+ @staticmethod
+ def get_debugging_key(tenant_id: str) -> str:
+ """
+ get the debugging key of the tenant
+ """
+ manager = PluginDebuggingManager()
+ return manager.get_debugging_key(tenant_id)
+
+ @staticmethod
+ def list(tenant_id: str) -> list[PluginEntity]:
+ """
+ list all plugins of the tenant
+ """
+ manager = PluginInstallationManager()
+ plugins = manager.list_plugins(tenant_id)
+ plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace]
+ try:
+ manifests = {
+ manifest.plugin_id: manifest for manifest in marketplace.batch_fetch_plugin_manifests(plugin_ids)
+ }
+ except Exception:
+ manifests = {}
+ logger.exception("failed to fetch plugin manifests")
+
+ for plugin in plugins:
+ if plugin.source == PluginInstallationSource.Marketplace:
+ if plugin.plugin_id in manifests:
+ # set latest_version
+ plugin.latest_version = manifests[plugin.plugin_id].latest_version
+ plugin.latest_unique_identifier = manifests[plugin.plugin_id].latest_package_identifier
+
+ return plugins
+
+ @staticmethod
+ def list_installations_from_ids(tenant_id: str, ids: Sequence[str]) -> Sequence[PluginInstallation]:
+ """
+ List plugin installations from ids
+ """
+ manager = PluginInstallationManager()
+ return manager.fetch_plugin_installation_by_ids(tenant_id, ids)
+
+ @staticmethod
+ def get_asset(tenant_id: str, asset_file: str) -> tuple[bytes, str]:
+ """
+ get the asset file of the plugin
+ """
+ manager = PluginAssetManager()
+ # guess mime type
+ mime_type, _ = guess_type(asset_file)
+ return manager.fetch_asset(tenant_id, asset_file), mime_type or "application/octet-stream"
+
+ @staticmethod
+ def check_plugin_unique_identifier(tenant_id: str, plugin_unique_identifier: str) -> bool:
+ """
+ check if the plugin unique identifier is already installed by other tenant
+ """
+ manager = PluginInstallationManager()
+ return manager.fetch_plugin_by_identifier(tenant_id, plugin_unique_identifier)
+
+ @staticmethod
+ def fetch_plugin_manifest(tenant_id: str, plugin_unique_identifier: str) -> PluginDeclaration:
+ """
+ Fetch plugin manifest
+ """
+ manager = PluginInstallationManager()
+ return manager.fetch_plugin_manifest(tenant_id, plugin_unique_identifier)
+
+ @staticmethod
+ def fetch_install_tasks(tenant_id: str, page: int, page_size: int) -> Sequence[PluginInstallTask]:
+ """
+ Fetch plugin installation tasks
+ """
+ manager = PluginInstallationManager()
+ return manager.fetch_plugin_installation_tasks(tenant_id, page, page_size)
+
+ @staticmethod
+ def fetch_install_task(tenant_id: str, task_id: str) -> PluginInstallTask:
+ manager = PluginInstallationManager()
+ return manager.fetch_plugin_installation_task(tenant_id, task_id)
+
+ @staticmethod
+ def delete_install_task(tenant_id: str, task_id: str) -> bool:
+ """
+ Delete a plugin installation task
+ """
+ manager = PluginInstallationManager()
+ return manager.delete_plugin_installation_task(tenant_id, task_id)
+
+ @staticmethod
+ def delete_all_install_task_items(
+ tenant_id: str,
+ ) -> bool:
+ """
+ Delete all plugin installation task items
+ """
+ manager = PluginInstallationManager()
+ return manager.delete_all_plugin_installation_task_items(tenant_id)
+
+ @staticmethod
+ def delete_install_task_item(tenant_id: str, task_id: str, identifier: str) -> bool:
+ """
+ Delete a plugin installation task item
+ """
+ manager = PluginInstallationManager()
+ return manager.delete_plugin_installation_task_item(tenant_id, task_id, identifier)
+
+ @staticmethod
+ def upgrade_plugin_with_marketplace(
+ tenant_id: str, original_plugin_unique_identifier: str, new_plugin_unique_identifier: str
+ ):
+ """
+ Upgrade plugin with marketplace
+ """
+ if original_plugin_unique_identifier == new_plugin_unique_identifier:
+ raise ValueError("you should not upgrade plugin with the same plugin")
+
+ # check if plugin pkg is already downloaded
+ manager = PluginInstallationManager()
+
+ try:
+ manager.fetch_plugin_manifest(tenant_id, new_plugin_unique_identifier)
+ # already downloaded, skip, and record install event
+ marketplace.record_install_plugin_event(new_plugin_unique_identifier)
+ except Exception:
+ # plugin not installed, download and upload pkg
+ pkg = download_plugin_pkg(new_plugin_unique_identifier)
+ manager.upload_pkg(tenant_id, pkg, verify_signature=False)
+
+ return manager.upgrade_plugin(
+ tenant_id,
+ original_plugin_unique_identifier,
+ new_plugin_unique_identifier,
+ PluginInstallationSource.Marketplace,
+ {
+ "plugin_unique_identifier": new_plugin_unique_identifier,
+ },
+ )
+
+ @staticmethod
+ def upgrade_plugin_with_github(
+ tenant_id: str,
+ original_plugin_unique_identifier: str,
+ new_plugin_unique_identifier: str,
+ repo: str,
+ version: str,
+ package: str,
+ ):
+ """
+ Upgrade plugin with github
+ """
+ manager = PluginInstallationManager()
+ return manager.upgrade_plugin(
+ tenant_id,
+ original_plugin_unique_identifier,
+ new_plugin_unique_identifier,
+ PluginInstallationSource.Github,
+ {
+ "repo": repo,
+ "version": version,
+ "package": package,
+ },
+ )
+
+ @staticmethod
+ def upload_pkg(tenant_id: str, pkg: bytes, verify_signature: bool = False) -> PluginUploadResponse:
+ """
+ Upload plugin package files
+
+ returns: plugin_unique_identifier
+ """
+ manager = PluginInstallationManager()
+ return manager.upload_pkg(tenant_id, pkg, verify_signature)
+
+ @staticmethod
+ def upload_pkg_from_github(
+ tenant_id: str, repo: str, version: str, package: str, verify_signature: bool = False
+ ) -> PluginUploadResponse:
+ """
+ Install plugin from github release package files,
+ returns plugin_unique_identifier
+ """
+ pkg = download_with_size_limit(
+ f"https://github.com/{repo}/releases/download/{version}/{package}", dify_config.PLUGIN_MAX_PACKAGE_SIZE
+ )
+
+ manager = PluginInstallationManager()
+ return manager.upload_pkg(
+ tenant_id,
+ pkg,
+ verify_signature,
+ )
+
+ @staticmethod
+ def upload_bundle(
+ tenant_id: str, bundle: bytes, verify_signature: bool = False
+ ) -> Sequence[PluginBundleDependency]:
+ """
+ Upload a plugin bundle and return the dependencies.
+ """
+ manager = PluginInstallationManager()
+ return manager.upload_bundle(tenant_id, bundle, verify_signature)
+
+ @staticmethod
+ def install_from_local_pkg(tenant_id: str, plugin_unique_identifiers: Sequence[str]):
+ manager = PluginInstallationManager()
+ return manager.install_from_identifiers(
+ tenant_id,
+ plugin_unique_identifiers,
+ PluginInstallationSource.Package,
+ [{}],
+ )
+
+ @staticmethod
+ def install_from_github(tenant_id: str, plugin_unique_identifier: str, repo: str, version: str, package: str):
+ """
+ Install plugin from github release package files,
+ returns plugin_unique_identifier
+ """
+ manager = PluginInstallationManager()
+ return manager.install_from_identifiers(
+ tenant_id,
+ [plugin_unique_identifier],
+ PluginInstallationSource.Github,
+ [
+ {
+ "repo": repo,
+ "version": version,
+ "package": package,
+ }
+ ],
+ )
+
+ @staticmethod
+ def install_from_marketplace_pkg(
+ tenant_id: str, plugin_unique_identifiers: Sequence[str], verify_signature: bool = False
+ ):
+ """
+ Install plugin from marketplace package files,
+ returns installation task id
+ """
+ manager = PluginInstallationManager()
+
+ # check if already downloaded
+ for plugin_unique_identifier in plugin_unique_identifiers:
+ try:
+ manager.fetch_plugin_manifest(tenant_id, plugin_unique_identifier)
+ # already downloaded, skip
+ except Exception:
+ # plugin not installed, download and upload pkg
+ pkg = download_plugin_pkg(plugin_unique_identifier)
+ manager.upload_pkg(tenant_id, pkg, verify_signature)
+
+ return manager.install_from_identifiers(
+ tenant_id,
+ plugin_unique_identifiers,
+ PluginInstallationSource.Marketplace,
+ [
+ {
+ "plugin_unique_identifier": plugin_unique_identifier,
+ }
+ for plugin_unique_identifier in plugin_unique_identifiers
+ ],
+ )
+
+ @staticmethod
+ def uninstall(tenant_id: str, plugin_installation_id: str) -> bool:
+ manager = PluginInstallationManager()
+ return manager.uninstall(tenant_id, plugin_installation_id)
+
+ @staticmethod
+ def check_tools_existence(tenant_id: str, provider_ids: Sequence[GenericProviderID]) -> Sequence[bool]:
+ """
+ Check if the tools exist
+ """
+ manager = PluginInstallationManager()
+ return manager.check_tools_existence(tenant_id, provider_ids)
diff --git a/api/services/tools/api_tools_manage_service.py b/api/services/tools/api_tools_manage_service.py
index 988f9df927e4f7..6f848d49c4d304 100644
--- a/api/services/tools/api_tools_manage_service.py
+++ b/api/services/tools/api_tools_manage_service.py
@@ -1,24 +1,24 @@
import json
import logging
from collections.abc import Mapping
-from typing import Any, Optional, cast
+from typing import Any, cast
from httpx import get
+from core.entities.provider_entities import ProviderConfig
from core.model_runtime.utils.encoders import jsonable_encoder
-from core.tools.entities.api_entities import UserTool, UserToolProvider
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.custom_tool.provider import ApiToolProviderController
+from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_bundle import ApiToolBundle
from core.tools.entities.tool_entities import (
ApiProviderAuthType,
ApiProviderSchemaType,
- ToolCredentialsOption,
- ToolProviderCredentials,
)
-from core.tools.provider.api_tool_provider import ApiToolProviderController
from core.tools.tool_label_manager import ToolLabelManager
from core.tools.tool_manager import ToolManager
-from core.tools.utils.configuration import ToolConfigurationManager
+from core.tools.utils.configuration import ProviderConfigEncrypter
from core.tools.utils.parser import ApiBasedToolSchemaParser
from extensions.ext_database import db
from models.tools import ApiToolProvider
@@ -41,28 +41,28 @@ def parser_api_schema(schema: str) -> Mapping[str, Any]:
raise ValueError(f"invalid schema: {str(e)}")
credentials_schema = [
- ToolProviderCredentials(
+ ProviderConfig(
name="auth_type",
- type=ToolProviderCredentials.CredentialsType.SELECT,
+ type=ProviderConfig.Type.SELECT,
required=True,
default="none",
options=[
- ToolCredentialsOption(value="none", label=I18nObject(en_US="None", zh_Hans="无")),
- ToolCredentialsOption(value="api_key", label=I18nObject(en_US="Api Key", zh_Hans="Api Key")),
+ ProviderConfig.Option(value="none", label=I18nObject(en_US="None", zh_Hans="无")),
+ ProviderConfig.Option(value="api_key", label=I18nObject(en_US="Api Key", zh_Hans="Api Key")),
],
placeholder=I18nObject(en_US="Select auth type", zh_Hans="选择认证方式"),
),
- ToolProviderCredentials(
+ ProviderConfig(
name="api_key_header",
- type=ToolProviderCredentials.CredentialsType.TEXT_INPUT,
+ type=ProviderConfig.Type.TEXT_INPUT,
required=False,
placeholder=I18nObject(en_US="Enter api key header", zh_Hans="输入 api key header,如:X-API-KEY"),
default="api_key",
help=I18nObject(en_US="HTTP header name for api key", zh_Hans="HTTP 头部字段名,用于传递 api key"),
),
- ToolProviderCredentials(
+ ProviderConfig(
name="api_key_value",
- type=ToolProviderCredentials.CredentialsType.TEXT_INPUT,
+ type=ProviderConfig.Type.TEXT_INPUT,
required=False,
placeholder=I18nObject(en_US="Enter api key", zh_Hans="输入 api key"),
default="",
@@ -84,17 +84,14 @@ def parser_api_schema(schema: str) -> Mapping[str, Any]:
raise ValueError(f"invalid schema: {str(e)}")
@staticmethod
- def convert_schema_to_tool_bundles(
- schema: str, extra_info: Optional[dict] = None
- ) -> tuple[list[ApiToolBundle], str]:
+ def convert_schema_to_tool_bundles(schema: str, extra_info: dict | None = None) -> tuple[list[ApiToolBundle], str]:
"""
convert schema to tool bundles
:return: the list of tool bundles, description
"""
try:
- tool_bundles = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, extra_info=extra_info)
- return tool_bundles
+ return ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, extra_info=extra_info)
except Exception as e:
raise ValueError(f"invalid schema: {str(e)}")
@@ -167,8 +164,14 @@ def create_api_tool_provider(
provider_controller.load_bundled_tools(tool_bundles)
# encrypt credentials
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
- encrypted_credentials = tool_configuration.encrypt_tool_credentials(credentials)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=list(provider_controller.get_credentials_schema()),
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
+
+ encrypted_credentials = tool_configuration.encrypt(credentials)
db_provider.credentials_str = json.dumps(encrypted_credentials)
db.session.add(db_provider)
@@ -198,18 +201,18 @@ def get_api_tool_provider_remote_schema(user_id: str, tenant_id: str, url: str):
# try to parse schema, avoid SSRF attack
ApiToolManageService.parser_api_schema(schema)
- except Exception as e:
+ except Exception:
logger.exception("parse api schema error")
raise ValueError("invalid schema, please check the url you provided")
return {"schema": schema}
@staticmethod
- def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider_name: str) -> list[UserTool]:
+ def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider_name: str) -> list[ToolApiEntity]:
"""
list api tool provider tools
"""
- provider = (
+ provider: ApiToolProvider | None = (
db.session.query(ApiToolProvider)
.filter(
ApiToolProvider.tenant_id == tenant_id,
@@ -225,8 +228,9 @@ def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider_name: st
labels = ToolLabelManager.get_tool_labels(controller)
return [
- ToolTransformService.tool_to_user_tool(
+ ToolTransformService.convert_tool_entity_to_api_entity(
tool_bundle,
+ tenant_id=tenant_id,
labels=labels,
)
for tool_bundle in provider.tools
@@ -293,16 +297,21 @@ def update_api_tool_provider(
provider_controller.load_bundled_tools(tool_bundles)
# get original credentials if exists
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=list(provider_controller.get_credentials_schema()),
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
- original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
+ original_credentials = tool_configuration.decrypt(provider.credentials)
masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
# check if the credential has changed, save the original credential
for name, value in credentials.items():
if name in masked_credentials and value == masked_credentials[name]:
credentials[name] = original_credentials[name]
- credentials = tool_configuration.encrypt_tool_credentials(credentials)
+ credentials = tool_configuration.encrypt(credentials)
provider.credentials_str = json.dumps(credentials)
db.session.add(provider)
@@ -363,7 +372,7 @@ def test_api_tool_preview(
try:
tool_bundles, _ = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema)
- except Exception as e:
+ except Exception:
raise ValueError("invalid schema")
# get tool bundle
@@ -407,8 +416,13 @@ def test_api_tool_preview(
# decrypt credentials
if db_provider.id:
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=list(provider_controller.get_credentials_schema()),
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
+ decrypted_credentials = tool_configuration.decrypt(credentials)
# check if the credential has changed, save the original credential
masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
for name, value in credentials.items():
@@ -419,20 +433,20 @@ def test_api_tool_preview(
provider_controller.validate_credentials_format(credentials)
# get tool
tool = provider_controller.get_tool(tool_name)
- runtime_tool = tool.fork_tool_runtime(
- runtime={
- "credentials": credentials,
- "tenant_id": tenant_id,
- }
+ tool = tool.fork_tool_runtime(
+ runtime=ToolRuntime(
+ credentials=credentials,
+ tenant_id=tenant_id,
+ )
)
- result = runtime_tool.validate_credentials(credentials, parameters)
+ result = tool.validate_credentials(credentials, parameters)
except Exception as e:
return {"error": str(e)}
return {"result": result or "empty response"}
@staticmethod
- def list_api_tools(user_id: str, tenant_id: str) -> list[UserToolProvider]:
+ def list_api_tools(user_id: str, tenant_id: str) -> list[ToolProviderApiEntity]:
"""
list api tools
"""
@@ -441,7 +455,7 @@ def list_api_tools(user_id: str, tenant_id: str) -> list[UserToolProvider]:
db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all() or []
)
- result: list[UserToolProvider] = []
+ result: list[ToolProviderApiEntity] = []
for provider in db_providers:
# convert provider controller to user provider
@@ -453,13 +467,13 @@ def list_api_tools(user_id: str, tenant_id: str) -> list[UserToolProvider]:
user_provider.labels = labels
# add icon
- ToolTransformService.repack_provider(user_provider)
+ ToolTransformService.repack_provider(tenant_id=tenant_id, provider=user_provider)
- tools = provider_controller.get_tools(user_id=user_id, tenant_id=tenant_id)
+ tools = provider_controller.get_tools(tenant_id=tenant_id)
for tool in tools or []:
user_provider.tools.append(
- ToolTransformService.tool_to_user_tool(
+ ToolTransformService.convert_tool_entity_to_api_entity(
tenant_id=tenant_id, tool=tool, credentials=user_provider.original_credentials, labels=labels
)
)
diff --git a/api/services/tools/builtin_tools_manage_service.py b/api/services/tools/builtin_tools_manage_service.py
index 21adbb0074724e..51b56ab5864998 100644
--- a/api/services/tools/builtin_tools_manage_service.py
+++ b/api/services/tools/builtin_tools_manage_service.py
@@ -2,19 +2,19 @@
import logging
from pathlib import Path
-from sqlalchemy import select
from sqlalchemy.orm import Session
from configs import dify_config
from core.helper.position_helper import is_filtered
from core.model_runtime.utils.encoders import jsonable_encoder
-from core.tools.entities.api_entities import UserTool, UserToolProvider
+from core.plugin.entities.plugin import GenericProviderID, ToolProviderID
+from core.plugin.manager.exc import PluginDaemonClientSideError
+from core.tools.builtin_tool.providers._positions import BuiltinToolProviderSort
+from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
from core.tools.errors import ToolNotFoundError, ToolProviderCredentialValidationError, ToolProviderNotFoundError
-from core.tools.provider.builtin._positions import BuiltinToolProviderSort
-from core.tools.provider.tool_provider import ToolProviderController
from core.tools.tool_label_manager import ToolLabelManager
from core.tools.tool_manager import ToolManager
-from core.tools.utils.configuration import ToolConfigurationManager
+from core.tools.utils.configuration import ProviderConfigEncrypter
from extensions.ext_database import db
from models.tools import BuiltinToolProvider
from services.tools.tools_transform_service import ToolTransformService
@@ -24,36 +24,38 @@
class BuiltinToolManageService:
@staticmethod
- def list_builtin_tool_provider_tools(user_id: str, tenant_id: str, provider: str) -> list[UserTool]:
+ def list_builtin_tool_provider_tools(tenant_id: str, provider: str) -> list[ToolApiEntity]:
"""
list builtin tool provider tools
+
+ :param user_id: the id of the user
+ :param tenant_id: the id of the tenant
+ :param provider: the name of the provider
+
+ :return: the list of tools
"""
- provider_controller: ToolProviderController = ToolManager.get_builtin_provider(provider)
+ provider_controller = ToolManager.get_builtin_provider(provider, tenant_id)
tools = provider_controller.get_tools()
- tool_provider_configurations = ToolConfigurationManager(
- tenant_id=tenant_id, provider_controller=provider_controller
+ tool_provider_configurations = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
)
# check if user has added the provider
- builtin_provider = (
- db.session.query(BuiltinToolProvider)
- .filter(
- BuiltinToolProvider.tenant_id == tenant_id,
- BuiltinToolProvider.provider == provider,
- )
- .first()
- )
+ builtin_provider = BuiltinToolManageService._fetch_builtin_provider(provider, tenant_id)
credentials = {}
if builtin_provider is not None:
# get credentials
credentials = builtin_provider.credentials
- credentials = tool_provider_configurations.decrypt_tool_credentials(credentials)
+ credentials = tool_provider_configurations.decrypt(credentials)
- result: list[UserTool] = []
+ result: list[ToolApiEntity] = []
for tool in tools or []:
result.append(
- ToolTransformService.tool_to_user_tool(
+ ToolTransformService.convert_tool_entity_to_api_entity(
tool=tool,
credentials=credentials,
tenant_id=tenant_id,
@@ -64,14 +66,47 @@ def list_builtin_tool_provider_tools(user_id: str, tenant_id: str, provider: str
return result
@staticmethod
- def list_builtin_provider_credentials_schema(provider_name):
+ def get_builtin_tool_provider_info(user_id: str, tenant_id: str, provider: str):
+ """
+ get builtin tool provider info
+ """
+ provider_controller = ToolManager.get_builtin_provider(provider, tenant_id)
+ tool_provider_configurations = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
+ # check if user has added the provider
+ builtin_provider = BuiltinToolManageService._fetch_builtin_provider(provider, tenant_id)
+
+ credentials = {}
+ if builtin_provider is not None:
+ # get credentials
+ credentials = builtin_provider.credentials
+ credentials = tool_provider_configurations.decrypt(credentials)
+
+ entity = ToolTransformService.builtin_provider_to_user_provider(
+ provider_controller=provider_controller,
+ db_provider=builtin_provider,
+ decrypt_credentials=True,
+ )
+
+ entity.original_credentials = {}
+
+ return entity
+
+ @staticmethod
+ def list_builtin_provider_credentials_schema(provider_name: str, tenant_id: str):
"""
list builtin provider credentials schema
+ :param provider_name: the name of the provider
+ :param tenant_id: the id of the tenant
:return: the list of tool providers
"""
- provider = ToolManager.get_builtin_provider(provider_name)
- return jsonable_encoder([v for _, v in (provider.credentials_schema or {}).items()])
+ provider = ToolManager.get_builtin_provider(provider_name, tenant_id)
+ return jsonable_encoder(provider.get_credentials_schema())
@staticmethod
def update_builtin_tool_provider(
@@ -81,31 +116,38 @@ def update_builtin_tool_provider(
update builtin tool provider
"""
# get if the provider exists
- stmt = select(BuiltinToolProvider).where(
- BuiltinToolProvider.tenant_id == tenant_id,
- BuiltinToolProvider.provider == provider_name,
- )
- provider = session.scalar(stmt)
+ provider = BuiltinToolManageService._fetch_builtin_provider(provider_name, tenant_id)
try:
# get provider
- provider_controller = ToolManager.get_builtin_provider(provider_name)
+ provider_controller = ToolManager.get_builtin_provider(provider_name, tenant_id)
if not provider_controller.need_credentials:
raise ValueError(f"provider {provider_name} does not need credentials")
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
+
# get original credentials if exists
if provider is not None:
- original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
+ original_credentials = tool_configuration.decrypt(provider.credentials)
masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
# check if the credential has changed, save the original credential
for name, value in credentials.items():
if name in masked_credentials and value == masked_credentials[name]:
credentials[name] = original_credentials[name]
# validate credentials
- provider_controller.validate_credentials(credentials)
+ provider_controller.validate_credentials(user_id, credentials)
# encrypt credentials
- credentials = tool_configuration.encrypt_tool_credentials(credentials)
- except (ToolProviderNotFoundError, ToolNotFoundError, ToolProviderCredentialValidationError) as e:
+ credentials = tool_configuration.encrypt(credentials)
+ except (
+ PluginDaemonClientSideError,
+ ToolProviderNotFoundError,
+ ToolNotFoundError,
+ ToolProviderCredentialValidationError,
+ ) as e:
raise ValueError(str(e))
if provider is None:
@@ -117,14 +159,14 @@ def update_builtin_tool_provider(
encrypted_credentials=json.dumps(credentials),
)
- session.add(provider)
-
+ db.session.add(provider)
else:
provider.encrypted_credentials = json.dumps(credentials)
# delete cache
tool_configuration.delete_tool_credentials_cache()
+ db.session.commit()
return {"result": "success"}
@staticmethod
@@ -132,21 +174,19 @@ def get_builtin_tool_provider_credentials(tenant_id: str, provider_name: str):
"""
get builtin tool provider credentials
"""
- provider = (
- db.session.query(BuiltinToolProvider)
- .filter(
- BuiltinToolProvider.tenant_id == tenant_id,
- BuiltinToolProvider.provider == provider_name,
- )
- .first()
- )
+ provider_obj = BuiltinToolManageService._fetch_builtin_provider(provider_name, tenant_id)
- if provider is None:
+ if provider_obj is None:
return {}
- provider_controller = ToolManager.get_builtin_provider(provider.provider)
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
- credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
+ provider_controller = ToolManager.get_builtin_provider(provider_obj.provider, tenant_id)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
+ credentials = tool_configuration.decrypt(provider_obj.credentials)
credentials = tool_configuration.mask_tool_credentials(credentials)
return credentials
@@ -155,24 +195,22 @@ def delete_builtin_tool_provider(user_id: str, tenant_id: str, provider_name: st
"""
delete tool provider
"""
- provider = (
- db.session.query(BuiltinToolProvider)
- .filter(
- BuiltinToolProvider.tenant_id == tenant_id,
- BuiltinToolProvider.provider == provider_name,
- )
- .first()
- )
+ provider_obj = BuiltinToolManageService._fetch_builtin_provider(provider_name, tenant_id)
- if provider is None:
+ if provider_obj is None:
raise ValueError(f"you have not added provider {provider_name}")
- db.session.delete(provider)
+ db.session.delete(provider_obj)
db.session.commit()
# delete cache
- provider_controller = ToolManager.get_builtin_provider(provider_name)
- tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
+ provider_controller = ToolManager.get_builtin_provider(provider_name, tenant_id)
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
+ )
tool_configuration.delete_tool_credentials_cache()
return {"result": "success"}
@@ -182,67 +220,111 @@ def get_builtin_tool_provider_icon(provider: str):
"""
get tool provider icon and it's mimetype
"""
- icon_path, mime_type = ToolManager.get_builtin_provider_icon(provider)
+ icon_path, mime_type = ToolManager.get_hardcoded_provider_icon(provider)
icon_bytes = Path(icon_path).read_bytes()
return icon_bytes, mime_type
@staticmethod
- def list_builtin_tools(user_id: str, tenant_id: str) -> list[UserToolProvider]:
+ def list_builtin_tools(user_id: str, tenant_id: str) -> list[ToolProviderApiEntity]:
"""
list builtin tools
"""
# get all builtin providers
- provider_controllers = ToolManager.list_builtin_providers()
-
- # get all user added providers
- db_providers: list[BuiltinToolProvider] = (
- db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all() or []
- )
+ provider_controllers = ToolManager.list_builtin_providers(tenant_id)
- # find provider
- find_provider = lambda provider: next(
- filter(lambda db_provider: db_provider.provider == provider, db_providers), None
- )
+ with db.session.no_autoflush:
+ # get all user added providers
+ db_providers: list[BuiltinToolProvider] = (
+ db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all() or []
+ )
- result: list[UserToolProvider] = []
-
- for provider_controller in provider_controllers:
- try:
- # handle include, exclude
- if is_filtered(
- include_set=dify_config.POSITION_TOOL_INCLUDES_SET,
- exclude_set=dify_config.POSITION_TOOL_EXCLUDES_SET,
- data=provider_controller,
- name_func=lambda x: x.identity.name,
- ):
- continue
- if provider_controller.identity is None:
- continue
-
- # convert provider controller to user provider
- user_builtin_provider = ToolTransformService.builtin_provider_to_user_provider(
- provider_controller=provider_controller,
- db_provider=find_provider(provider_controller.identity.name),
- decrypt_credentials=True,
- )
+ # rewrite db_providers
+ for db_provider in db_providers:
+ db_provider.provider = str(ToolProviderID(db_provider.provider))
+
+ # find provider
+ def find_provider(provider):
+ return next(filter(lambda db_provider: db_provider.provider == provider, db_providers), None)
+
+ result: list[ToolProviderApiEntity] = []
+
+ for provider_controller in provider_controllers:
+ try:
+ # handle include, exclude
+ if is_filtered(
+ include_set=dify_config.POSITION_TOOL_INCLUDES_SET, # type: ignore
+ exclude_set=dify_config.POSITION_TOOL_EXCLUDES_SET, # type: ignore
+ data=provider_controller,
+ name_func=lambda x: x.identity.name,
+ ):
+ continue
+
+ # convert provider controller to user provider
+ user_builtin_provider = ToolTransformService.builtin_provider_to_user_provider(
+ provider_controller=provider_controller,
+ db_provider=find_provider(provider_controller.entity.identity.name),
+ decrypt_credentials=True,
+ )
- # add icon
- ToolTransformService.repack_provider(user_builtin_provider)
-
- tools = provider_controller.get_tools()
- for tool in tools or []:
- user_builtin_provider.tools.append(
- ToolTransformService.tool_to_user_tool(
- tenant_id=tenant_id,
- tool=tool,
- credentials=user_builtin_provider.original_credentials,
- labels=ToolLabelManager.get_tool_labels(provider_controller),
+ # add icon
+ ToolTransformService.repack_provider(tenant_id=tenant_id, provider=user_builtin_provider)
+
+ tools = provider_controller.get_tools()
+ for tool in tools or []:
+ user_builtin_provider.tools.append(
+ ToolTransformService.convert_tool_entity_to_api_entity(
+ tenant_id=tenant_id,
+ tool=tool,
+ credentials=user_builtin_provider.original_credentials,
+ labels=ToolLabelManager.get_tool_labels(provider_controller),
+ )
)
- )
- result.append(user_builtin_provider)
- except Exception as e:
- raise e
+ result.append(user_builtin_provider)
+ except Exception as e:
+ raise e
return BuiltinToolProviderSort.sort(result)
+
+ @staticmethod
+ def _fetch_builtin_provider(provider_name: str, tenant_id: str) -> BuiltinToolProvider | None:
+ try:
+ full_provider_name = provider_name
+ provider_id_entity = GenericProviderID(provider_name)
+ provider_name = provider_id_entity.provider_name
+ if provider_id_entity.organization != "langgenius":
+ provider_obj = (
+ db.session.query(BuiltinToolProvider)
+ .filter(
+ BuiltinToolProvider.tenant_id == tenant_id,
+ BuiltinToolProvider.provider == full_provider_name,
+ )
+ .first()
+ )
+ else:
+ provider_obj = (
+ db.session.query(BuiltinToolProvider)
+ .filter(
+ BuiltinToolProvider.tenant_id == tenant_id,
+ (BuiltinToolProvider.provider == provider_name)
+ | (BuiltinToolProvider.provider == full_provider_name),
+ )
+ .first()
+ )
+
+ if provider_obj is None:
+ return None
+
+ provider_obj.provider = GenericProviderID(provider_obj.provider).to_string()
+ return provider_obj
+ except Exception:
+ # it's an old provider without organization
+ return (
+ db.session.query(BuiltinToolProvider)
+ .filter(
+ BuiltinToolProvider.tenant_id == tenant_id,
+ (BuiltinToolProvider.provider == provider_name),
+ )
+ .first()
+ )
diff --git a/api/services/tools/tools_manage_service.py b/api/services/tools/tools_manage_service.py
index 1c67f7648ca99f..59d5b50e23200a 100644
--- a/api/services/tools/tools_manage_service.py
+++ b/api/services/tools/tools_manage_service.py
@@ -1,6 +1,6 @@
import logging
-from core.tools.entities.api_entities import UserToolProviderTypeLiteral
+from core.tools.entities.api_entities import ToolProviderTypeApiLiteral
from core.tools.tool_manager import ToolManager
from services.tools.tools_transform_service import ToolTransformService
@@ -9,17 +9,17 @@
class ToolCommonService:
@staticmethod
- def list_tool_providers(user_id: str, tenant_id: str, typ: UserToolProviderTypeLiteral = None):
+ def list_tool_providers(user_id: str, tenant_id: str, typ: ToolProviderTypeApiLiteral = None):
"""
list tool providers
:return: the list of tool providers
"""
- providers = ToolManager.user_list_providers(user_id, tenant_id, typ)
+ providers = ToolManager.list_providers_from_api(user_id, tenant_id, typ)
# add icon
for provider in providers:
- ToolTransformService.repack_provider(provider)
+ ToolTransformService.repack_provider(tenant_id=tenant_id, provider=provider)
result = [provider.to_dict() for provider in providers]
diff --git a/api/services/tools/tools_transform_service.py b/api/services/tools/tools_transform_service.py
index 6e3a45be0da1c4..83a42ddfcbcab3 100644
--- a/api/services/tools/tools_transform_service.py
+++ b/api/services/tools/tools_transform_service.py
@@ -2,47 +2,57 @@
import logging
from typing import Optional, Union, cast
+from yarl import URL
+
from configs import dify_config
-from core.tools.entities.api_entities import UserTool, UserToolProvider
+from core.tools.__base.tool import Tool
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.builtin_tool.provider import BuiltinToolProviderController
+from core.tools.custom_tool.provider import ApiToolProviderController
+from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_bundle import ApiToolBundle
from core.tools.entities.tool_entities import (
ApiProviderAuthType,
ToolParameter,
- ToolProviderCredentials,
ToolProviderType,
)
-from core.tools.provider.api_tool_provider import ApiToolProviderController
-from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
-from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController
-from core.tools.tool.tool import Tool
-from core.tools.tool.workflow_tool import WorkflowTool
-from core.tools.utils.configuration import ToolConfigurationManager
+from core.tools.plugin_tool.provider import PluginToolProviderController
+from core.tools.utils.configuration import ProviderConfigEncrypter
+from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
+from core.tools.workflow_as_tool.tool import WorkflowTool
from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider
logger = logging.getLogger(__name__)
class ToolTransformService:
- @staticmethod
- def get_tool_provider_icon_url(provider_type: str, provider_name: str, icon: str) -> Union[str, dict]:
+ @classmethod
+ def get_plugin_icon_url(cls, tenant_id: str, filename: str) -> str:
+ url_prefix = URL(dify_config.CONSOLE_API_URL) / "console" / "api" / "workspaces" / "current" / "plugin" / "icon"
+ return str(url_prefix % {"tenant_id": tenant_id, "filename": filename})
+
+ @classmethod
+ def get_tool_provider_icon_url(cls, provider_type: str, provider_name: str, icon: str | dict) -> Union[str, dict]:
"""
get tool provider icon url
"""
- url_prefix = dify_config.CONSOLE_API_URL + "/console/api/workspaces/current/tool-provider/"
+ url_prefix = URL(dify_config.CONSOLE_API_URL) / "console" / "api" / "workspaces" / "current" / "tool-provider"
if provider_type == ToolProviderType.BUILT_IN.value:
- return url_prefix + "builtin/" + provider_name + "/icon"
+ return str(url_prefix / "builtin" / provider_name / "icon")
elif provider_type in {ToolProviderType.API.value, ToolProviderType.WORKFLOW.value}:
try:
- return cast(dict, json.loads(icon))
- except:
+ if isinstance(icon, str):
+ return cast(dict, json.loads(icon))
+ return icon
+ except Exception:
return {"background": "#252525", "content": "\ud83d\ude01"}
return ""
@staticmethod
- def repack_provider(provider: Union[dict, UserToolProvider]):
+ def repack_provider(tenant_id: str, provider: Union[dict, ToolProviderApiEntity]):
"""
repack provider
@@ -52,55 +62,52 @@ def repack_provider(provider: Union[dict, UserToolProvider]):
provider["icon"] = ToolTransformService.get_tool_provider_icon_url(
provider_type=provider["type"], provider_name=provider["name"], icon=provider["icon"]
)
- elif isinstance(provider, UserToolProvider):
- provider.icon = cast(
- str,
- ToolTransformService.get_tool_provider_icon_url(
+ elif isinstance(provider, ToolProviderApiEntity):
+ if provider.plugin_id:
+ if isinstance(provider.icon, str):
+ provider.icon = ToolTransformService.get_plugin_icon_url(
+ tenant_id=tenant_id, filename=provider.icon
+ )
+ else:
+ provider.icon = ToolTransformService.get_tool_provider_icon_url(
provider_type=provider.type.value, provider_name=provider.name, icon=provider.icon
- ),
- )
+ )
- @staticmethod
+ @classmethod
def builtin_provider_to_user_provider(
- provider_controller: BuiltinToolProviderController,
+ cls,
+ provider_controller: BuiltinToolProviderController | PluginToolProviderController,
db_provider: Optional[BuiltinToolProvider],
decrypt_credentials: bool = True,
- ) -> UserToolProvider:
+ ) -> ToolProviderApiEntity:
"""
convert provider controller to user provider
"""
- if provider_controller.identity is None:
- raise ValueError("provider identity is None")
-
- result = UserToolProvider(
- id=provider_controller.identity.name,
- author=provider_controller.identity.author,
- name=provider_controller.identity.name,
- description=I18nObject(
- en_US=provider_controller.identity.description.en_US,
- zh_Hans=provider_controller.identity.description.zh_Hans,
- pt_BR=provider_controller.identity.description.pt_BR,
- ja_JP=provider_controller.identity.description.ja_JP,
- ),
- icon=provider_controller.identity.icon,
- label=I18nObject(
- en_US=provider_controller.identity.label.en_US,
- zh_Hans=provider_controller.identity.label.zh_Hans,
- pt_BR=provider_controller.identity.label.pt_BR,
- ja_JP=provider_controller.identity.label.ja_JP,
- ),
+ result = ToolProviderApiEntity(
+ id=provider_controller.entity.identity.name,
+ author=provider_controller.entity.identity.author,
+ name=provider_controller.entity.identity.name,
+ description=provider_controller.entity.identity.description,
+ icon=provider_controller.entity.identity.icon,
+ label=provider_controller.entity.identity.label,
type=ToolProviderType.BUILT_IN,
masked_credentials={},
is_team_authorization=False,
+ plugin_id=None,
tools=[],
labels=provider_controller.tool_labels,
)
+ if isinstance(provider_controller, PluginToolProviderController):
+ result.plugin_id = provider_controller.plugin_id
+ result.plugin_unique_identifier = provider_controller.plugin_unique_identifier
+
# get credentials schema
- schema = provider_controller.get_credentials_schema()
+ schema = {x.to_basic_provider_config().name: x for x in provider_controller.get_credentials_schema()}
+
for name, value in schema.items():
- assert result.masked_credentials is not None, "masked credentials is None"
- result.masked_credentials[name] = ToolProviderCredentials.CredentialsType.default(str(value.type))
+ if result.masked_credentials:
+ result.masked_credentials[name] = ""
# check if the provider need credentials
if not provider_controller.need_credentials:
@@ -113,12 +120,15 @@ def builtin_provider_to_user_provider(
credentials = db_provider.credentials
# init tool configuration
- tool_configuration = ToolConfigurationManager(
- tenant_id=db_provider.tenant_id, provider_controller=provider_controller
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=db_provider.tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
)
# decrypt the credentials and mask the credentials
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials=credentials)
- masked_credentials = tool_configuration.mask_tool_credentials(credentials=decrypted_credentials)
+ decrypted_credentials = tool_configuration.decrypt(data=credentials)
+ masked_credentials = tool_configuration.mask_tool_credentials(data=decrypted_credentials)
result.masked_credentials = masked_credentials
result.original_credentials = decrypted_credentials
@@ -151,41 +161,35 @@ def workflow_provider_to_controller(db_provider: WorkflowToolProvider) -> Workfl
@staticmethod
def workflow_provider_to_user_provider(
- provider_controller: WorkflowToolProviderController, labels: Optional[list[str]] = None
+ provider_controller: WorkflowToolProviderController, labels: list[str] | None = None
):
"""
convert provider controller to user provider
"""
- if provider_controller.identity is None:
- raise ValueError("provider identity is None")
-
- return UserToolProvider(
+ return ToolProviderApiEntity(
id=provider_controller.provider_id,
- author=provider_controller.identity.author,
- name=provider_controller.identity.name,
- description=I18nObject(
- en_US=provider_controller.identity.description.en_US,
- zh_Hans=provider_controller.identity.description.zh_Hans,
- ),
- icon=provider_controller.identity.icon,
- label=I18nObject(
- en_US=provider_controller.identity.label.en_US,
- zh_Hans=provider_controller.identity.label.zh_Hans,
- ),
+ author=provider_controller.entity.identity.author,
+ name=provider_controller.entity.identity.name,
+ description=provider_controller.entity.identity.description,
+ icon=provider_controller.entity.identity.icon,
+ label=provider_controller.entity.identity.label,
type=ToolProviderType.WORKFLOW,
masked_credentials={},
is_team_authorization=True,
+ plugin_id=None,
+ plugin_unique_identifier=None,
tools=[],
labels=labels or [],
)
- @staticmethod
+ @classmethod
def api_provider_to_user_provider(
+ cls,
provider_controller: ApiToolProviderController,
db_provider: ApiToolProvider,
decrypt_credentials: bool = True,
- labels: Optional[list[str]] = None,
- ) -> UserToolProvider:
+ labels: list[str] | None = None,
+ ) -> ToolProviderApiEntity:
"""
convert provider controller to user provider
"""
@@ -193,12 +197,16 @@ def api_provider_to_user_provider(
if db_provider.user is None:
raise ValueError(f"user is None for api provider {db_provider.id}")
try:
- username = db_provider.user.name
- except Exception as e:
+ user = db_provider.user
+ if not user:
+ raise ValueError("user not found")
+
+ username = user.name
+ except Exception:
logger.exception(f"failed to get user name for api provider {db_provider.id}")
# add provider into providers
credentials = db_provider.credentials
- result = UserToolProvider(
+ result = ToolProviderApiEntity(
id=db_provider.id,
author=username,
name=db_provider.name,
@@ -212,6 +220,8 @@ def api_provider_to_user_provider(
zh_Hans=db_provider.name,
),
type=ToolProviderType.API,
+ plugin_id=None,
+ plugin_unique_identifier=None,
masked_credentials={},
is_team_authorization=True,
tools=[],
@@ -220,39 +230,42 @@ def api_provider_to_user_provider(
if decrypt_credentials:
# init tool configuration
- tool_configuration = ToolConfigurationManager(
- tenant_id=db_provider.tenant_id, provider_controller=provider_controller
+ tool_configuration = ProviderConfigEncrypter(
+ tenant_id=db_provider.tenant_id,
+ config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
+ provider_type=provider_controller.provider_type.value,
+ provider_identity=provider_controller.entity.identity.name,
)
# decrypt the credentials and mask the credentials
- decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials=credentials)
- masked_credentials = tool_configuration.mask_tool_credentials(credentials=decrypted_credentials)
+ decrypted_credentials = tool_configuration.decrypt(data=credentials)
+ masked_credentials = tool_configuration.mask_tool_credentials(data=decrypted_credentials)
result.masked_credentials = masked_credentials
return result
@staticmethod
- def tool_to_user_tool(
+ def convert_tool_entity_to_api_entity(
tool: Union[ApiToolBundle, WorkflowTool, Tool],
- credentials: Optional[dict] = None,
- tenant_id: Optional[str] = None,
- labels: Optional[list[str]] = None,
- ) -> UserTool:
+ tenant_id: str,
+ credentials: dict | None = None,
+ labels: list[str] | None = None,
+ ) -> ToolApiEntity:
"""
convert tool to user tool
"""
if isinstance(tool, Tool):
# fork tool runtime
tool = tool.fork_tool_runtime(
- runtime={
- "credentials": credentials,
- "tenant_id": tenant_id,
- }
+ runtime=ToolRuntime(
+ credentials=credentials or {},
+ tenant_id=tenant_id,
+ )
)
# get tool parameters
- parameters = tool.parameters or []
+ parameters = tool.entity.parameters or []
# get tool runtime parameters
runtime_parameters = tool.get_runtime_parameters()
# override parameters
@@ -268,23 +281,21 @@ def tool_to_user_tool(
if not found and runtime_parameter.form == ToolParameter.ToolParameterForm.FORM:
current_parameters.append(runtime_parameter)
- if tool.identity is None:
- raise ValueError("tool identity is None")
-
- return UserTool(
- author=tool.identity.author,
- name=tool.identity.name,
- label=tool.identity.label,
- description=tool.description.human if tool.description else "", # type: ignore
+ return ToolApiEntity(
+ author=tool.entity.identity.author,
+ name=tool.entity.identity.name,
+ label=tool.entity.identity.label,
+ description=tool.entity.description.human if tool.entity.description else I18nObject(en_US=""),
+ output_schema=tool.entity.output_schema,
parameters=current_parameters,
- labels=labels,
+ labels=labels or [],
)
if isinstance(tool, ApiToolBundle):
- return UserTool(
+ return ToolApiEntity(
author=tool.author,
name=tool.operation_id or "",
- label=I18nObject(en_US=tool.operation_id or "", zh_Hans=tool.operation_id or ""),
+ label=I18nObject(en_US=tool.operation_id, zh_Hans=tool.operation_id),
description=I18nObject(en_US=tool.summary or "", zh_Hans=tool.summary or ""),
parameters=tool.parameters,
- labels=labels,
+ labels=labels or [],
)
diff --git a/api/services/tools/workflow_tools_manage_service.py b/api/services/tools/workflow_tools_manage_service.py
index 69430de432b143..e486ed7b8c53f2 100644
--- a/api/services/tools/workflow_tools_manage_service.py
+++ b/api/services/tools/workflow_tools_manage_service.py
@@ -1,17 +1,17 @@
import json
from collections.abc import Mapping
from datetime import datetime
-from typing import Any, Optional
+from typing import Any
from sqlalchemy import or_
from core.model_runtime.utils.encoders import jsonable_encoder
-from core.tools.entities.api_entities import UserTool, UserToolProvider
-from core.tools.provider.tool_provider import ToolProviderController
-from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController
-from core.tools.tool.tool import Tool
+from core.tools.__base.tool_provider import ToolProviderController
+from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
from core.tools.tool_label_manager import ToolLabelManager
from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils
+from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
+from core.tools.workflow_as_tool.tool import WorkflowTool
from extensions.ext_database import db
from models.model import App
from models.tools import WorkflowToolProvider
@@ -36,7 +36,7 @@ def create_workflow_tool(
description: str,
parameters: list[Mapping[str, Any]],
privacy_policy: str = "",
- labels: Optional[list[str]] = None,
+ labels: list[str] | None = None,
) -> dict:
WorkflowToolConfigurationUtils.check_parameter_configurations(parameters)
@@ -54,11 +54,12 @@ def create_workflow_tool(
if existing_workflow_tool_provider is not None:
raise ValueError(f"Tool with name {name} or app_id {workflow_app_id} already exists")
- app = db.session.query(App).filter(App.id == workflow_app_id, App.tenant_id == tenant_id).first()
+ app: App | None = db.session.query(App).filter(App.id == workflow_app_id, App.tenant_id == tenant_id).first()
+
if app is None:
raise ValueError(f"App {workflow_app_id} not found")
- workflow = app.workflow
+ workflow: Workflow | None = app.workflow
if workflow is None:
raise ValueError(f"Workflow not found for app {workflow_app_id}")
@@ -101,7 +102,7 @@ def update_workflow_tool(
description: str,
parameters: list[Mapping[str, Any]],
privacy_policy: str = "",
- labels: Optional[list[str]] = None,
+ labels: list[str] | None = None,
) -> dict:
"""
Update a workflow tool.
@@ -133,7 +134,7 @@ def update_workflow_tool(
if existing_workflow_tool_provider is not None:
raise ValueError(f"Tool with name {name} already exists")
- workflow_tool_provider: Optional[WorkflowToolProvider] = (
+ workflow_tool_provider: WorkflowToolProvider | None = (
db.session.query(WorkflowToolProvider)
.filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id)
.first()
@@ -142,14 +143,14 @@ def update_workflow_tool(
if workflow_tool_provider is None:
raise ValueError(f"Tool {workflow_tool_id} not found")
- app: Optional[App] = (
+ app: App | None = (
db.session.query(App).filter(App.id == workflow_tool_provider.app_id, App.tenant_id == tenant_id).first()
)
if app is None:
raise ValueError(f"App {workflow_tool_provider.app_id} not found")
- workflow: Optional[Workflow] = app.workflow
+ workflow: Workflow | None = app.workflow
if workflow is None:
raise ValueError(f"Workflow not found for app {workflow_tool_provider.app_id}")
@@ -178,7 +179,7 @@ def update_workflow_tool(
return {"result": "success"}
@classmethod
- def list_tenant_workflow_tools(cls, user_id: str, tenant_id: str) -> list[UserToolProvider]:
+ def list_tenant_workflow_tools(cls, user_id: str, tenant_id: str) -> list[ToolProviderApiEntity]:
"""
List workflow tools.
:param user_id: the user id
@@ -187,11 +188,11 @@ def list_tenant_workflow_tools(cls, user_id: str, tenant_id: str) -> list[UserTo
"""
db_tools = db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
- tools = []
+ tools: list[WorkflowToolProviderController] = []
for provider in db_tools:
try:
tools.append(ToolTransformService.workflow_provider_to_controller(provider))
- except:
+ except Exception:
# skip deleted tools
pass
@@ -203,12 +204,13 @@ def list_tenant_workflow_tools(cls, user_id: str, tenant_id: str) -> list[UserTo
user_tool_provider = ToolTransformService.workflow_provider_to_user_provider(
provider_controller=tool, labels=labels.get(tool.provider_id, [])
)
- ToolTransformService.repack_provider(user_tool_provider)
- to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id)
- if to_user_tool is None or len(to_user_tool) == 0:
- continue
+ ToolTransformService.repack_provider(tenant_id=tenant_id, provider=user_tool_provider)
user_tool_provider.tools = [
- ToolTransformService.tool_to_user_tool(to_user_tool[0], labels=labels.get(tool.provider_id, []))
+ ToolTransformService.convert_tool_entity_to_api_entity(
+ tool=tool.get_tools(tenant_id)[0],
+ labels=labels.get(tool.provider_id, []),
+ tenant_id=tenant_id,
+ )
]
result.append(user_tool_provider)
@@ -239,42 +241,12 @@ def get_workflow_tool_by_tool_id(cls, user_id: str, tenant_id: str, workflow_too
:param workflow_app_id: the workflow app id
:return: the tool
"""
- db_tool: Optional[WorkflowToolProvider] = (
+ db_tool: WorkflowToolProvider | None = (
db.session.query(WorkflowToolProvider)
.filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id)
.first()
)
-
- if db_tool is None:
- raise ValueError(f"Tool {workflow_tool_id} not found")
-
- workflow_app: Optional[App] = (
- db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first()
- )
-
- if workflow_app is None:
- raise ValueError(f"App {db_tool.app_id} not found")
-
- tool = ToolTransformService.workflow_provider_to_controller(db_tool)
-
- to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id)
- if to_user_tool is None or len(to_user_tool) == 0:
- raise ValueError(f"Tool {workflow_tool_id} not found")
-
- return {
- "name": db_tool.name,
- "label": db_tool.label,
- "workflow_tool_id": db_tool.id,
- "workflow_app_id": db_tool.app_id,
- "icon": json.loads(db_tool.icon),
- "description": db_tool.description,
- "parameters": jsonable_encoder(db_tool.parameter_configurations),
- "tool": ToolTransformService.tool_to_user_tool(
- to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool)
- ),
- "synced": workflow_app.workflow.version == db_tool.version if workflow_app.workflow else False,
- "privacy_policy": db_tool.privacy_policy,
- }
+ return cls._get_workflow_tool(tenant_id, db_tool)
@classmethod
def get_workflow_tool_by_app_id(cls, user_id: str, tenant_id: str, workflow_app_id: str) -> dict:
@@ -285,26 +257,38 @@ def get_workflow_tool_by_app_id(cls, user_id: str, tenant_id: str, workflow_app_
:param workflow_app_id: the workflow app id
:return: the tool
"""
- db_tool: Optional[WorkflowToolProvider] = (
+ db_tool: WorkflowToolProvider | None = (
db.session.query(WorkflowToolProvider)
.filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.app_id == workflow_app_id)
.first()
)
+ return cls._get_workflow_tool(tenant_id, db_tool)
+ @classmethod
+ def _get_workflow_tool(cls, tenant_id: str, db_tool: WorkflowToolProvider | None) -> dict:
+ """
+ Get a workflow tool.
+ :db_tool: the database tool
+ :return: the tool
+ """
if db_tool is None:
- raise ValueError(f"Tool {workflow_app_id} not found")
+ raise ValueError("Tool not found")
- workflow_app: Optional[App] = (
- db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first()
+ workflow_app: App | None = (
+ db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == db_tool.tenant_id).first()
)
if workflow_app is None:
raise ValueError(f"App {db_tool.app_id} not found")
+ workflow = workflow_app.workflow
+ if not workflow:
+ raise ValueError("Workflow not found")
+
tool = ToolTransformService.workflow_provider_to_controller(db_tool)
- to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id)
- if to_user_tool is None or len(to_user_tool) == 0:
- raise ValueError(f"Tool {workflow_app_id} not found")
+ workflow_tools: list[WorkflowTool] = tool.get_tools(tenant_id)
+ if len(workflow_tools) == 0:
+ raise ValueError(f"Tool {db_tool.id} not found")
return {
"name": db_tool.name,
@@ -314,15 +298,17 @@ def get_workflow_tool_by_app_id(cls, user_id: str, tenant_id: str, workflow_app_
"icon": json.loads(db_tool.icon),
"description": db_tool.description,
"parameters": jsonable_encoder(db_tool.parameter_configurations),
- "tool": ToolTransformService.tool_to_user_tool(
- to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool)
+ "tool": ToolTransformService.convert_tool_entity_to_api_entity(
+ tool=tool.get_tools(db_tool.tenant_id)[0],
+ labels=ToolLabelManager.get_tool_labels(tool),
+ tenant_id=tenant_id,
),
- "synced": workflow_app.workflow.version == db_tool.version if workflow_app.workflow else False,
+ "synced": workflow.version == db_tool.version,
"privacy_policy": db_tool.privacy_policy,
}
@classmethod
- def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_id: str) -> list[UserTool]:
+ def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_id: str) -> list[ToolApiEntity]:
"""
List workflow tool provider tools.
:param user_id: the user id
@@ -330,7 +316,7 @@ def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_
:param workflow_app_id: the workflow app id
:return: the list of tools
"""
- db_tool: Optional[WorkflowToolProvider] = (
+ db_tool: WorkflowToolProvider | None = (
db.session.query(WorkflowToolProvider)
.filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id)
.first()
@@ -340,8 +326,14 @@ def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_
raise ValueError(f"Tool {workflow_tool_id} not found")
tool = ToolTransformService.workflow_provider_to_controller(db_tool)
- to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id)
- if to_user_tool is None or len(to_user_tool) == 0:
+ workflow_tools: list[WorkflowTool] = tool.get_tools(tenant_id)
+ if len(workflow_tools) == 0:
raise ValueError(f"Tool {workflow_tool_id} not found")
- return [ToolTransformService.tool_to_user_tool(to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool))]
+ return [
+ ToolTransformService.convert_tool_entity_to_api_entity(
+ tool=tool.get_tools(db_tool.tenant_id)[0],
+ labels=ToolLabelManager.get_tool_labels(tool),
+ tenant_id=tenant_id,
+ )
+ ]
diff --git a/api/services/workflow_run_service.py b/api/services/workflow_run_service.py
index 4343596a236f5f..0ddd18ea27e004 100644
--- a/api/services/workflow_run_service.py
+++ b/api/services/workflow_run_service.py
@@ -1,5 +1,7 @@
+import threading
from typing import Optional
+import contexts
from extensions.ext_database import db
from libs.infinite_scroll_pagination import InfiniteScrollPagination
from models.enums import WorkflowRunTriggeredFrom
@@ -119,6 +121,9 @@ def get_workflow_run_node_executions(self, app_model: App, run_id: str) -> list[
"""
workflow_run = self.get_workflow_run(app_model, run_id)
+ contexts.plugin_tool_providers.set({})
+ contexts.plugin_tool_providers_lock.set(threading.Lock())
+
if not workflow_run:
return []
diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py
index 9f7a9c770d9306..8380e465998321 100644
--- a/api/services/workflow_service.py
+++ b/api/services/workflow_service.py
@@ -1,8 +1,8 @@
import json
import time
-from collections.abc import Sequence
+from collections.abc import Callable, Generator, Sequence
from datetime import UTC, datetime
-from typing import Any, Optional, cast
+from typing import Any, Optional
from uuid import uuid4
from sqlalchemy import desc
@@ -13,11 +13,12 @@
from core.variables import Variable
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.errors import WorkflowNodeRunFailedError
+from core.workflow.graph_engine.entities.event import InNodeEvent
from core.workflow.nodes import NodeType
-from core.workflow.nodes.base.entities import BaseNodeData
from core.workflow.nodes.base.node import BaseNode
from core.workflow.nodes.enums import ErrorStrategy
from core.workflow.nodes.event import RunCompletedEvent
+from core.workflow.nodes.event.types import NodeEvent
from core.workflow.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from core.workflow.workflow_entry import WorkflowEntry
from events.app_event import app_draft_workflow_was_synced, app_published_workflow_was_updated
@@ -246,14 +247,69 @@ def run_draft_workflow_node(
# run draft workflow node
start_at = time.perf_counter()
- try:
- node_instance, generator = WorkflowEntry.single_step_run(
+ workflow_node_execution = self._handle_node_run_result(
+ getter=lambda: WorkflowEntry.single_step_run(
workflow=draft_workflow,
node_id=node_id,
user_inputs=user_inputs,
user_id=account.id,
- )
- node_instance = cast(BaseNode[BaseNodeData], node_instance)
+ ),
+ start_at=start_at,
+ tenant_id=app_model.tenant_id,
+ node_id=node_id,
+ )
+
+ workflow_node_execution.app_id = app_model.id
+ workflow_node_execution.created_by = account.id
+ workflow_node_execution.workflow_id = draft_workflow.id
+
+ db.session.add(workflow_node_execution)
+ db.session.commit()
+
+ return workflow_node_execution
+
+ def run_free_workflow_node(
+ self, node_data: dict, tenant_id: str, user_id: str, node_id: str, user_inputs: dict[str, Any]
+ ) -> WorkflowNodeExecution:
+ """
+ Run draft workflow node
+ """
+ # run draft workflow node
+ start_at = time.perf_counter()
+
+ workflow_node_execution = self._handle_node_run_result(
+ getter=lambda: WorkflowEntry.run_free_node(
+ node_id=node_id,
+ node_data=node_data,
+ tenant_id=tenant_id,
+ user_id=user_id,
+ user_inputs=user_inputs,
+ ),
+ start_at=start_at,
+ tenant_id=tenant_id,
+ node_id=node_id,
+ )
+
+ return workflow_node_execution
+
+ def _handle_node_run_result(
+ self,
+ getter: Callable[[], tuple[BaseNode, Generator[NodeEvent | InNodeEvent, None, None]]],
+ start_at: float,
+ tenant_id: str,
+ node_id: str,
+ ) -> WorkflowNodeExecution:
+ """
+ Handle node run result
+
+ :param getter: Callable[[], tuple[BaseNode, Generator[RunEvent | InNodeEvent, None, None]]]
+ :param start_at: float
+ :param tenant_id: str
+ :param node_id: str
+ """
+ try:
+ node_instance, generator = getter()
+
node_run_result: NodeRunResult | None = None
for event in generator:
if isinstance(event, RunCompletedEvent):
@@ -303,9 +359,7 @@ def run_draft_workflow_node(
workflow_node_execution = WorkflowNodeExecution()
workflow_node_execution.id = str(uuid4())
- workflow_node_execution.tenant_id = app_model.tenant_id
- workflow_node_execution.app_id = app_model.id
- workflow_node_execution.workflow_id = draft_workflow.id
+ workflow_node_execution.tenant_id = tenant_id
workflow_node_execution.triggered_from = WorkflowNodeExecutionTriggeredFrom.SINGLE_STEP.value
workflow_node_execution.index = 1
workflow_node_execution.node_id = node_id
@@ -313,7 +367,6 @@ def run_draft_workflow_node(
workflow_node_execution.title = node_instance.node_data.title
workflow_node_execution.elapsed_time = time.perf_counter() - start_at
workflow_node_execution.created_by_role = CreatedByRole.ACCOUNT.value
- workflow_node_execution.created_by = account.id
workflow_node_execution.created_at = datetime.now(UTC).replace(tzinfo=None)
workflow_node_execution.finished_at = datetime.now(UTC).replace(tzinfo=None)
if run_succeeded and node_run_result:
@@ -342,9 +395,6 @@ def run_draft_workflow_node(
workflow_node_execution.status = WorkflowNodeExecutionStatus.FAILED.value
workflow_node_execution.error = error
- db.session.add(workflow_node_execution)
- db.session.commit()
-
return workflow_node_execution
def convert_to_workflow(self, app_model: App, account: Account, args: dict) -> App:
diff --git a/api/tasks/batch_create_segment_to_index_task.py b/api/tasks/batch_create_segment_to_index_task.py
index e2d23927973c5f..40356b9731f4ef 100644
--- a/api/tasks/batch_create_segment_to_index_task.py
+++ b/api/tasks/batch_create_segment_to_index_task.py
@@ -73,43 +73,49 @@ def batch_create_segment_to_index_task(
max_position_stmt = select(func.max(DocumentSegment.position)).where(
DocumentSegment.document_id == dataset_document.id
)
- max_position = session.scalar(max_position_stmt) or 1
- for segment in content:
- content_str = segment["content"]
- doc_id = str(uuid.uuid4())
- segment_hash = helper.generate_text_hash(content_str)
- # calc embedding use tokens
- tokens = embedding_model.get_text_embedding_num_tokens(texts=[content_str]) if embedding_model else 0
- segment_document = DocumentSegment(
- tenant_id=tenant_id,
- dataset_id=dataset_id,
- document_id=document_id,
- index_node_id=doc_id,
- index_node_hash=segment_hash,
- position=max_position,
- content=content_str,
- word_count=len(content_str),
- tokens=tokens,
- created_by=user_id,
- indexing_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
- status="completed",
- completed_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
- )
- max_position += 1
- 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
- session.add(segment_document)
- document_segments.append(segment_document)
- segments_to_insert.append(str(segment)) # Cast to string if needed
- # update document word count
- dataset_document.word_count += word_count_change
- session.add(dataset_document)
- # add index to db
- VectorService.create_segments_vector(None, document_segments, dataset, dataset_document.doc_form)
- session.commit()
-
+ word_count_change = 0
+ if embedding_model:
+ tokens_list = embedding_model.get_text_embedding_num_tokens(
+ texts=[segment["content"] for segment in content]
+ )
+ else:
+ tokens_list = [0] * len(content)
+ for segment, tokens in zip(content, tokens_list):
+ content = segment["content"]
+ doc_id = str(uuid.uuid4())
+ segment_hash = helper.generate_text_hash(content) # type: ignore
+ max_position = (
+ db.session.query(func.max(DocumentSegment.position))
+ .filter(DocumentSegment.document_id == dataset_document.id)
+ .scalar()
+ )
+ segment_document = DocumentSegment(
+ tenant_id=tenant_id,
+ dataset_id=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,
+ content=content,
+ word_count=len(content),
+ tokens=tokens,
+ created_by=user_id,
+ indexing_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
+ status="completed",
+ completed_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
+ )
+ 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
+ VectorService.create_segments_vector(None, document_segments, dataset, dataset_document.doc_form)
+ db.session.commit()
redis_client.setex(indexing_cache_key, 600, "completed")
end_at = time.perf_counter()
logging.info(
@@ -118,6 +124,6 @@ def batch_create_segment_to_index_task(
fg="green",
)
)
- except Exception as e:
+ except Exception:
logging.exception("Segments batch created index failed")
redis_client.setex(indexing_cache_key, 600, "error")
diff --git a/api/tasks/clean_dataset_task.py b/api/tasks/clean_dataset_task.py
index dfc7a896fc05aa..4d77f1fb6519b6 100644
--- a/api/tasks/clean_dataset_task.py
+++ b/api/tasks/clean_dataset_task.py
@@ -5,7 +5,7 @@
from celery import shared_task # type: ignore
from core.rag.index_processor.index_processor_factory import IndexProcessorFactory
-from core.tools.utils.web_reader_tool import get_image_upload_file_ids
+from core.tools.utils.rag_web_reader import get_image_upload_file_ids
from extensions.ext_database import db
from extensions.ext_storage import storage
from models.dataset import (
diff --git a/api/tasks/clean_document_task.py b/api/tasks/clean_document_task.py
index 7a536f74265757..5a4d7a52b2c2a5 100644
--- a/api/tasks/clean_document_task.py
+++ b/api/tasks/clean_document_task.py
@@ -6,7 +6,7 @@
from celery import shared_task # type: ignore
from core.rag.index_processor.index_processor_factory import IndexProcessorFactory
-from core.tools.utils.web_reader_tool import get_image_upload_file_ids
+from core.tools.utils.rag_web_reader import get_image_upload_file_ids
from extensions.ext_database import db
from extensions.ext_storage import storage
from models.dataset import Dataset, DocumentSegment
diff --git a/api/tests/integration_tests/.env.example b/api/tests/integration_tests/.env.example
index 6fd144c5c2f349..3c2ccb9dad490f 100644
--- a/api/tests/integration_tests/.env.example
+++ b/api/tests/integration_tests/.env.example
@@ -84,6 +84,13 @@ VOLC_EMBEDDING_ENDPOINT_ID=
# 360 AI Credentials
ZHINAO_API_KEY=
+# Plugin configuration
+PLUGIN_DAEMON_KEY=
+PLUGIN_DAEMON_URL=
+INNER_API_KEY=
+
+# Marketplace configuration
+MARKETPLACE_API_URL=
# VESSL AI Credentials
VESSL_AI_MODEL_NAME=
VESSL_AI_API_KEY=
diff --git a/api/tests/integration_tests/model_runtime/__init__.py b/api/tests/integration_tests/model_runtime/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/__mock/anthropic.py b/api/tests/integration_tests/model_runtime/__mock/anthropic.py
deleted file mode 100644
index 5092af4f13b2ff..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/anthropic.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import os
-from collections.abc import Iterable
-from typing import Any, Literal, Union
-
-import anthropic
-import pytest
-from _pytest.monkeypatch import MonkeyPatch
-from anthropic import Stream
-from anthropic.resources import Messages
-from anthropic.types import (
- ContentBlock,
- ContentBlockDeltaEvent,
- Message,
- MessageDeltaEvent,
- MessageDeltaUsage,
- MessageParam,
- MessageStartEvent,
- MessageStopEvent,
- MessageStreamEvent,
- TextDelta,
- Usage,
-)
-from anthropic.types.message_delta_event import Delta
-
-MOCK = os.getenv("MOCK_SWITCH", "false") == "true"
-
-
-class MockAnthropicClass:
- @staticmethod
- def mocked_anthropic_chat_create_sync(model: str) -> Message:
- return Message(
- id="msg-123",
- type="message",
- role="assistant",
- content=[ContentBlock(text="hello, I'm a chatbot from anthropic", type="text")],
- model=model,
- stop_reason="stop_sequence",
- usage=Usage(input_tokens=1, output_tokens=1),
- )
-
- @staticmethod
- def mocked_anthropic_chat_create_stream(model: str) -> Stream[MessageStreamEvent]:
- full_response_text = "hello, I'm a chatbot from anthropic"
-
- yield MessageStartEvent(
- type="message_start",
- message=Message(
- id="msg-123",
- content=[],
- role="assistant",
- model=model,
- stop_reason=None,
- type="message",
- usage=Usage(input_tokens=1, output_tokens=1),
- ),
- )
-
- index = 0
- for i in range(0, len(full_response_text)):
- yield ContentBlockDeltaEvent(
- type="content_block_delta", delta=TextDelta(text=full_response_text[i], type="text_delta"), index=index
- )
-
- index += 1
-
- yield MessageDeltaEvent(
- type="message_delta", delta=Delta(stop_reason="stop_sequence"), usage=MessageDeltaUsage(output_tokens=1)
- )
-
- yield MessageStopEvent(type="message_stop")
-
- def mocked_anthropic(
- self: Messages,
- *,
- max_tokens: int,
- messages: Iterable[MessageParam],
- model: str,
- stream: Literal[True],
- **kwargs: Any,
- ) -> Union[Message, Stream[MessageStreamEvent]]:
- if len(self._client.api_key) < 18:
- raise anthropic.AuthenticationError("Invalid API key")
-
- if stream:
- return MockAnthropicClass.mocked_anthropic_chat_create_stream(model=model)
- else:
- return MockAnthropicClass.mocked_anthropic_chat_create_sync(model=model)
-
-
-@pytest.fixture
-def setup_anthropic_mock(request, monkeypatch: MonkeyPatch):
- if MOCK:
- monkeypatch.setattr(Messages, "create", MockAnthropicClass.mocked_anthropic)
-
- yield
-
- if MOCK:
- monkeypatch.undo()
diff --git a/api/tests/integration_tests/model_runtime/__mock/fishaudio.py b/api/tests/integration_tests/model_runtime/__mock/fishaudio.py
deleted file mode 100644
index bec3babeafddab..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/fishaudio.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import os
-from collections.abc import Callable
-from typing import Literal
-
-import httpx
-import pytest
-from _pytest.monkeypatch import MonkeyPatch
-
-
-def mock_get(*args, **kwargs):
- if kwargs.get("headers", {}).get("Authorization") != "Bearer test":
- raise httpx.HTTPStatusError(
- "Invalid API key",
- request=httpx.Request("GET", ""),
- response=httpx.Response(401),
- )
-
- return httpx.Response(
- 200,
- json={
- "items": [
- {"title": "Model 1", "_id": "model1"},
- {"title": "Model 2", "_id": "model2"},
- ]
- },
- request=httpx.Request("GET", ""),
- )
-
-
-def mock_stream(*args, **kwargs):
- class MockStreamResponse:
- def __init__(self):
- self.status_code = 200
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- pass
-
- def iter_bytes(self):
- yield b"Mocked audio data"
-
- return MockStreamResponse()
-
-
-def mock_fishaudio(
- monkeypatch: MonkeyPatch,
- methods: list[Literal["list-models", "tts"]],
-) -> Callable[[], None]:
- """
- mock fishaudio module
-
- :param monkeypatch: pytest monkeypatch fixture
- :return: unpatch function
- """
-
- def unpatch() -> None:
- monkeypatch.undo()
-
- if "list-models" in methods:
- monkeypatch.setattr(httpx, "get", mock_get)
-
- if "tts" in methods:
- monkeypatch.setattr(httpx, "stream", mock_stream)
-
- return unpatch
-
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_fishaudio_mock(request, monkeypatch):
- methods = request.param if hasattr(request, "param") else []
- if MOCK:
- unpatch = mock_fishaudio(monkeypatch, methods=methods)
-
- yield
-
- if MOCK:
- unpatch()
diff --git a/api/tests/integration_tests/model_runtime/__mock/google.py b/api/tests/integration_tests/model_runtime/__mock/google.py
deleted file mode 100644
index 3a26b99e37507c..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/google.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from unittest.mock import MagicMock
-
-import google.generativeai.types.generation_types as generation_config_types # type: ignore
-import pytest
-from _pytest.monkeypatch import MonkeyPatch
-from google.ai import generativelanguage as glm
-from google.ai.generativelanguage_v1beta.types import content as gag_content
-from google.generativeai import GenerativeModel
-from google.generativeai.types import GenerateContentResponse, content_types, safety_types
-from google.generativeai.types.generation_types import BaseGenerateContentResponse
-
-from extensions import ext_redis
-
-
-class MockGoogleResponseClass:
- _done = False
-
- def __iter__(self):
- full_response_text = "it's google!"
-
- for i in range(0, len(full_response_text) + 1, 1):
- if i == len(full_response_text):
- self._done = True
- yield GenerateContentResponse(
- done=True, iterator=None, result=glm.GenerateContentResponse({}), chunks=[]
- )
- else:
- yield GenerateContentResponse(
- done=False, iterator=None, result=glm.GenerateContentResponse({}), chunks=[]
- )
-
-
-class MockGoogleResponseCandidateClass:
- finish_reason = "stop"
-
- @property
- def content(self) -> gag_content.Content:
- return gag_content.Content(parts=[gag_content.Part(text="it's google!")])
-
-
-class MockGoogleClass:
- @staticmethod
- def generate_content_sync() -> GenerateContentResponse:
- return GenerateContentResponse(done=True, iterator=None, result=glm.GenerateContentResponse({}), chunks=[])
-
- @staticmethod
- def generate_content_stream() -> MockGoogleResponseClass:
- return MockGoogleResponseClass()
-
- def generate_content(
- self: GenerativeModel,
- contents: content_types.ContentsType,
- *,
- generation_config: generation_config_types.GenerationConfigType | None = None,
- safety_settings: safety_types.SafetySettingOptions | None = None,
- stream: bool = False,
- **kwargs,
- ) -> GenerateContentResponse:
- if stream:
- return MockGoogleClass.generate_content_stream()
-
- return MockGoogleClass.generate_content_sync()
-
- @property
- def generative_response_text(self) -> str:
- return "it's google!"
-
- @property
- def generative_response_candidates(self) -> list[MockGoogleResponseCandidateClass]:
- return [MockGoogleResponseCandidateClass()]
-
-
-def mock_configure(api_key: str):
- if len(api_key) < 16:
- raise Exception("Invalid API key")
-
-
-class MockFileState:
- def __init__(self):
- self.name = "FINISHED"
-
-
-class MockGoogleFile:
- def __init__(self, name: str = "mock_file_name"):
- self.name = name
- self.state = MockFileState()
-
-
-def mock_get_file(name: str) -> MockGoogleFile:
- return MockGoogleFile(name)
-
-
-def mock_upload_file(path: str, mime_type: str) -> MockGoogleFile:
- return MockGoogleFile()
-
-
-@pytest.fixture
-def setup_google_mock(request, monkeypatch: MonkeyPatch):
- monkeypatch.setattr(BaseGenerateContentResponse, "text", MockGoogleClass.generative_response_text)
- monkeypatch.setattr(BaseGenerateContentResponse, "candidates", MockGoogleClass.generative_response_candidates)
- monkeypatch.setattr(GenerativeModel, "generate_content", MockGoogleClass.generate_content)
- monkeypatch.setattr("google.generativeai.configure", mock_configure)
- monkeypatch.setattr("google.generativeai.get_file", mock_get_file)
- monkeypatch.setattr("google.generativeai.upload_file", mock_upload_file)
-
- yield
-
- monkeypatch.undo()
-
-
-@pytest.fixture
-def setup_mock_redis() -> None:
- ext_redis.redis_client.get = MagicMock(return_value=None)
- ext_redis.redis_client.setex = MagicMock(return_value=None)
- ext_redis.redis_client.exists = MagicMock(return_value=True)
diff --git a/api/tests/integration_tests/model_runtime/__mock/huggingface.py b/api/tests/integration_tests/model_runtime/__mock/huggingface.py
deleted file mode 100644
index 4de52514408a06..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/huggingface.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import os
-
-import pytest
-from _pytest.monkeypatch import MonkeyPatch
-from huggingface_hub import InferenceClient # type: ignore
-
-from tests.integration_tests.model_runtime.__mock.huggingface_chat import MockHuggingfaceChatClass
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_huggingface_mock(request, monkeypatch: MonkeyPatch):
- if MOCK:
- monkeypatch.setattr(InferenceClient, "text_generation", MockHuggingfaceChatClass.text_generation)
-
- yield
-
- if MOCK:
- monkeypatch.undo()
diff --git a/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py b/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py
deleted file mode 100644
index 77c7e7f5e4089c..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import re
-from collections.abc import Generator
-from typing import Any, Literal, Optional, Union
-
-from _pytest.monkeypatch import MonkeyPatch
-from huggingface_hub import InferenceClient # type: ignore
-from huggingface_hub.inference._text_generation import ( # type: ignore
- Details,
- StreamDetails,
- TextGenerationResponse,
- TextGenerationStreamResponse,
- Token,
-)
-from huggingface_hub.utils import BadRequestError # type: ignore
-
-
-class MockHuggingfaceChatClass:
- @staticmethod
- def generate_create_sync(model: str) -> TextGenerationResponse:
- response = TextGenerationResponse(
- generated_text="You can call me Miku Miku o~e~o~",
- details=Details(
- finish_reason="length",
- generated_tokens=6,
- tokens=[Token(id=0, text="You", logprob=0.0, special=False) for i in range(0, 6)],
- ),
- )
-
- return response
-
- @staticmethod
- def generate_create_stream(model: str) -> Generator[TextGenerationStreamResponse, None, None]:
- full_text = "You can call me Miku Miku o~e~o~"
-
- for i in range(0, len(full_text)):
- response = TextGenerationStreamResponse(
- token=Token(id=i, text=full_text[i], logprob=0.0, special=False),
- )
- response.generated_text = full_text[i]
- response.details = StreamDetails(finish_reason="stop_sequence", generated_tokens=1)
-
- yield response
-
- def text_generation(
- self: InferenceClient, prompt: str, *, stream: Literal[False] = ..., model: Optional[str] = None, **kwargs: Any
- ) -> Union[TextGenerationResponse, Generator[TextGenerationStreamResponse, None, None]]:
- # check if key is valid
- if not re.match(r"Bearer\shf\-[a-zA-Z0-9]{16,}", self.headers["authorization"]):
- raise BadRequestError("Invalid API key")
-
- if model is None:
- raise BadRequestError("Invalid model")
-
- if stream:
- return MockHuggingfaceChatClass.generate_create_stream(model)
- return MockHuggingfaceChatClass.generate_create_sync(model)
diff --git a/api/tests/integration_tests/model_runtime/__mock/huggingface_tei.py b/api/tests/integration_tests/model_runtime/__mock/huggingface_tei.py
deleted file mode 100644
index b9a721c803fc52..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/huggingface_tei.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from core.model_runtime.model_providers.huggingface_tei.tei_helper import TeiModelExtraParameter
-
-
-class MockTEIClass:
- @staticmethod
- def get_tei_extra_parameter(server_url: str, model_name: str) -> TeiModelExtraParameter:
- # During mock, we don't have a real server to query, so we just return a dummy value
- if "rerank" in model_name:
- model_type = "reranker"
- else:
- model_type = "embedding"
-
- return TeiModelExtraParameter(model_type=model_type, max_input_length=512, max_client_batch_size=1)
-
- @staticmethod
- def invoke_tokenize(server_url: str, texts: list[str]) -> list[list[dict]]:
- # Use space as token separator, and split the text into tokens
- tokenized_texts = []
- for text in texts:
- tokens = text.split(" ")
- current_index = 0
- tokenized_text = []
- for idx, token in enumerate(tokens):
- s_token = {
- "id": idx,
- "text": token,
- "special": False,
- "start": current_index,
- "stop": current_index + len(token),
- }
- current_index += len(token) + 1
- tokenized_text.append(s_token)
- tokenized_texts.append(tokenized_text)
- return tokenized_texts
-
- @staticmethod
- def invoke_embeddings(server_url: str, texts: list[str]) -> dict:
- # {
- # "object": "list",
- # "data": [
- # {
- # "object": "embedding",
- # "embedding": [...],
- # "index": 0
- # }
- # ],
- # "model": "MODEL_NAME",
- # "usage": {
- # "prompt_tokens": 3,
- # "total_tokens": 3
- # }
- # }
- embeddings = []
- for idx in range(len(texts)):
- embedding = [0.1] * 768
- embeddings.append(
- {
- "object": "embedding",
- "embedding": embedding,
- "index": idx,
- }
- )
- return {
- "object": "list",
- "data": embeddings,
- "model": "MODEL_NAME",
- "usage": {
- "prompt_tokens": sum(len(text.split(" ")) for text in texts),
- "total_tokens": sum(len(text.split(" ")) for text in texts),
- },
- }
-
- @staticmethod
- def invoke_rerank(server_url: str, query: str, texts: list[str]) -> list[dict]:
- # Example response:
- # [
- # {
- # "index": 0,
- # "text": "Deep Learning is ...",
- # "score": 0.9950755
- # }
- # ]
- reranked_docs = []
- for idx, text in enumerate(texts):
- reranked_docs.append(
- {
- "index": idx,
- "text": text,
- "score": 0.9,
- }
- )
- # For mock, only return the first document
- break
- return reranked_docs
diff --git a/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py b/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py
deleted file mode 100644
index 4e00660a29162f..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import os
-from collections.abc import Callable
-from typing import Any, Literal
-
-import pytest
-
-# import monkeypatch
-from _pytest.monkeypatch import MonkeyPatch
-from nomic import embed # type: ignore
-
-
-def create_embedding(texts: list[str], model: str, **kwargs: Any) -> dict:
- texts_len = len(texts)
-
- foo_embedding_sample = 0.123456
-
- combined = {
- "embeddings": [[foo_embedding_sample for _ in range(768)] for _ in range(texts_len)],
- "usage": {"prompt_tokens": texts_len, "total_tokens": texts_len},
- "model": model,
- "inference_mode": "remote",
- }
-
- return combined
-
-
-def mock_nomic(
- monkeypatch: MonkeyPatch,
- methods: list[Literal["text_embedding"]],
-) -> Callable[[], None]:
- """
- mock nomic module
-
- :param monkeypatch: pytest monkeypatch fixture
- :return: unpatch function
- """
-
- def unpatch() -> None:
- monkeypatch.undo()
-
- if "text_embedding" in methods:
- monkeypatch.setattr(embed, "text", create_embedding)
-
- return unpatch
-
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_nomic_mock(request, monkeypatch):
- methods = request.param if hasattr(request, "param") else []
- if MOCK:
- unpatch = mock_nomic(monkeypatch, methods=methods)
-
- yield
-
- if MOCK:
- unpatch()
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai.py b/api/tests/integration_tests/model_runtime/__mock/openai.py
deleted file mode 100644
index 6637f4f212a50e..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import os
-from collections.abc import Callable
-from typing import Literal
-
-import pytest
-
-# import monkeypatch
-from _pytest.monkeypatch import MonkeyPatch
-from openai.resources.audio.transcriptions import Transcriptions
-from openai.resources.chat import Completions as ChatCompletions
-from openai.resources.completions import Completions
-from openai.resources.embeddings import Embeddings
-from openai.resources.models import Models
-from openai.resources.moderations import Moderations
-
-from tests.integration_tests.model_runtime.__mock.openai_chat import MockChatClass
-from tests.integration_tests.model_runtime.__mock.openai_completion import MockCompletionsClass
-from tests.integration_tests.model_runtime.__mock.openai_embeddings import MockEmbeddingsClass
-from tests.integration_tests.model_runtime.__mock.openai_moderation import MockModerationClass
-from tests.integration_tests.model_runtime.__mock.openai_remote import MockModelClass
-from tests.integration_tests.model_runtime.__mock.openai_speech2text import MockSpeech2TextClass
-
-
-def mock_openai(
- monkeypatch: MonkeyPatch,
- methods: list[Literal["completion", "chat", "remote", "moderation", "speech2text", "text_embedding"]],
-) -> Callable[[], None]:
- """
- mock openai module
-
- :param monkeypatch: pytest monkeypatch fixture
- :return: unpatch function
- """
-
- def unpatch() -> None:
- monkeypatch.undo()
-
- if "completion" in methods:
- monkeypatch.setattr(Completions, "create", MockCompletionsClass.completion_create)
-
- if "chat" in methods:
- monkeypatch.setattr(ChatCompletions, "create", MockChatClass.chat_create)
-
- if "remote" in methods:
- monkeypatch.setattr(Models, "list", MockModelClass.list)
-
- if "moderation" in methods:
- monkeypatch.setattr(Moderations, "create", MockModerationClass.moderation_create)
-
- if "speech2text" in methods:
- monkeypatch.setattr(Transcriptions, "create", MockSpeech2TextClass.speech2text_create)
-
- if "text_embedding" in methods:
- monkeypatch.setattr(Embeddings, "create", MockEmbeddingsClass.create_embeddings)
-
- return unpatch
-
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_openai_mock(request, monkeypatch):
- methods = request.param if hasattr(request, "param") else []
- if MOCK:
- unpatch = mock_openai(monkeypatch, methods=methods)
-
- yield
-
- if MOCK:
- unpatch()
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_chat.py b/api/tests/integration_tests/model_runtime/__mock/openai_chat.py
deleted file mode 100644
index 1dc5df766748ec..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_chat.py
+++ /dev/null
@@ -1,267 +0,0 @@
-import re
-from collections.abc import Generator
-from json import dumps
-from time import time
-
-# import monkeypatch
-from typing import Any, Literal, Optional, Union
-
-from openai import AzureOpenAI, OpenAI
-from openai._types import NOT_GIVEN, NotGiven
-from openai.resources.chat.completions import Completions
-from openai.types import Completion as CompletionMessage
-from openai.types.chat import (
- ChatCompletionChunk,
- ChatCompletionMessageParam,
- ChatCompletionMessageToolCall,
- ChatCompletionToolParam,
- completion_create_params,
-)
-from openai.types.chat.chat_completion import ChatCompletion as _ChatCompletion
-from openai.types.chat.chat_completion import Choice as _ChatCompletionChoice
-from openai.types.chat.chat_completion_chunk import (
- Choice,
- ChoiceDelta,
- ChoiceDeltaFunctionCall,
- ChoiceDeltaToolCall,
- ChoiceDeltaToolCallFunction,
-)
-from openai.types.chat.chat_completion_message import ChatCompletionMessage, FunctionCall
-from openai.types.chat.chat_completion_message_tool_call import Function
-from openai.types.completion_usage import CompletionUsage
-
-from core.model_runtime.errors.invoke import InvokeAuthorizationError
-
-
-class MockChatClass:
- @staticmethod
- def generate_function_call(
- functions: list[completion_create_params.Function] | NotGiven = NOT_GIVEN,
- ) -> Optional[FunctionCall]:
- if not functions or len(functions) == 0:
- return None
- function: completion_create_params.Function = functions[0]
- function_name = function["name"]
- function_description = function["description"]
- function_parameters = function["parameters"]
- function_parameters_type = function_parameters["type"]
- if function_parameters_type != "object":
- return None
- function_parameters_properties = function_parameters["properties"]
- function_parameters_required = function_parameters["required"]
- parameters = {}
- for parameter_name, parameter in function_parameters_properties.items():
- if parameter_name not in function_parameters_required:
- continue
- parameter_type = parameter["type"]
- if parameter_type == "string":
- if "enum" in parameter:
- if len(parameter["enum"]) == 0:
- continue
- parameters[parameter_name] = parameter["enum"][0]
- else:
- parameters[parameter_name] = "kawaii"
- elif parameter_type == "integer":
- parameters[parameter_name] = 114514
- elif parameter_type == "number":
- parameters[parameter_name] = 1919810.0
- elif parameter_type == "boolean":
- parameters[parameter_name] = True
-
- return FunctionCall(name=function_name, arguments=dumps(parameters))
-
- @staticmethod
- def generate_tool_calls(tools=NOT_GIVEN) -> Optional[list[ChatCompletionMessageToolCall]]:
- list_tool_calls = []
- if not tools or len(tools) == 0:
- return None
- tool = tools[0]
-
- if "type" in tools and tools["type"] != "function":
- return None
-
- function = tool["function"]
-
- function_call = MockChatClass.generate_function_call(functions=[function])
- if function_call is None:
- return None
-
- list_tool_calls.append(
- ChatCompletionMessageToolCall(
- id="sakurajima-mai",
- function=Function(
- name=function_call.name,
- arguments=function_call.arguments,
- ),
- type="function",
- )
- )
-
- return list_tool_calls
-
- @staticmethod
- def mocked_openai_chat_create_sync(
- model: str,
- functions: list[completion_create_params.Function] | NotGiven = NOT_GIVEN,
- tools: list[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
- ) -> CompletionMessage:
- tool_calls = []
- function_call = MockChatClass.generate_function_call(functions=functions)
- if not function_call:
- tool_calls = MockChatClass.generate_tool_calls(tools=tools)
-
- return _ChatCompletion(
- id="cmpl-3QJQa5jXJ5Z5X",
- choices=[
- _ChatCompletionChoice(
- finish_reason="content_filter",
- index=0,
- message=ChatCompletionMessage(
- content="elaina", role="assistant", function_call=function_call, tool_calls=tool_calls
- ),
- )
- ],
- created=int(time()),
- model=model,
- object="chat.completion",
- system_fingerprint="",
- usage=CompletionUsage(
- prompt_tokens=2,
- completion_tokens=1,
- total_tokens=3,
- ),
- )
-
- @staticmethod
- def mocked_openai_chat_create_stream(
- model: str,
- functions: list[completion_create_params.Function] | NotGiven = NOT_GIVEN,
- tools: list[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
- ) -> Generator[ChatCompletionChunk, None, None]:
- tool_calls = []
- function_call = MockChatClass.generate_function_call(functions=functions)
- if not function_call:
- tool_calls = MockChatClass.generate_tool_calls(tools=tools)
-
- full_text = "Hello, world!\n\n```python\nprint('Hello, world!')\n```"
- for i in range(0, len(full_text) + 1):
- if i == len(full_text):
- yield ChatCompletionChunk(
- id="cmpl-3QJQa5jXJ5Z5X",
- choices=[
- Choice(
- delta=ChoiceDelta(
- content="",
- function_call=ChoiceDeltaFunctionCall(
- name=function_call.name,
- arguments=function_call.arguments,
- )
- if function_call
- else None,
- role="assistant",
- tool_calls=[
- ChoiceDeltaToolCall(
- index=0,
- id="misaka-mikoto",
- function=ChoiceDeltaToolCallFunction(
- name=tool_calls[0].function.name,
- arguments=tool_calls[0].function.arguments,
- ),
- type="function",
- )
- ]
- if tool_calls and len(tool_calls) > 0
- else None,
- ),
- finish_reason="function_call",
- index=0,
- )
- ],
- created=int(time()),
- model=model,
- object="chat.completion.chunk",
- system_fingerprint="",
- usage=CompletionUsage(
- prompt_tokens=2,
- completion_tokens=17,
- total_tokens=19,
- ),
- )
- else:
- yield ChatCompletionChunk(
- id="cmpl-3QJQa5jXJ5Z5X",
- choices=[
- Choice(
- delta=ChoiceDelta(
- content=full_text[i],
- role="assistant",
- ),
- finish_reason="content_filter",
- index=0,
- )
- ],
- created=int(time()),
- model=model,
- object="chat.completion.chunk",
- system_fingerprint="",
- )
-
- def chat_create(
- self: Completions,
- *,
- messages: list[ChatCompletionMessageParam],
- model: Union[
- str,
- Literal[
- "gpt-4-1106-preview",
- "gpt-4-vision-preview",
- "gpt-4",
- "gpt-4-0314",
- "gpt-4-0613",
- "gpt-4-32k",
- "gpt-4-32k-0314",
- "gpt-4-32k-0613",
- "gpt-3.5-turbo-1106",
- "gpt-3.5-turbo",
- "gpt-3.5-turbo-16k",
- "gpt-3.5-turbo-0301",
- "gpt-3.5-turbo-0613",
- "gpt-3.5-turbo-16k-0613",
- ],
- ],
- functions: list[completion_create_params.Function] | NotGiven = NOT_GIVEN,
- response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
- stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
- tools: list[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
- **kwargs: Any,
- ):
- openai_models = [
- "gpt-4-1106-preview",
- "gpt-4-vision-preview",
- "gpt-4",
- "gpt-4-0314",
- "gpt-4-0613",
- "gpt-4-32k",
- "gpt-4-32k-0314",
- "gpt-4-32k-0613",
- "gpt-3.5-turbo-1106",
- "gpt-3.5-turbo",
- "gpt-3.5-turbo-16k",
- "gpt-3.5-turbo-0301",
- "gpt-3.5-turbo-0613",
- "gpt-3.5-turbo-16k-0613",
- ]
- azure_openai_models = ["gpt35", "gpt-4v", "gpt-35-turbo"]
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", str(self._client.base_url)):
- raise InvokeAuthorizationError("Invalid base url")
- if model in openai_models + azure_openai_models:
- if not re.match(r"sk-[a-zA-Z0-9]{24,}$", self._client.api_key) and type(self._client) == OpenAI:
- # sometime, provider use OpenAI compatible API will not have api key or have different api key format
- # so we only check if model is in openai_models
- raise InvokeAuthorizationError("Invalid api key")
- if len(self._client.api_key) < 18 and type(self._client) == AzureOpenAI:
- raise InvokeAuthorizationError("Invalid api key")
- if stream:
- return MockChatClass.mocked_openai_chat_create_stream(model=model, functions=functions, tools=tools)
-
- return MockChatClass.mocked_openai_chat_create_sync(model=model, functions=functions, tools=tools)
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_completion.py b/api/tests/integration_tests/model_runtime/__mock/openai_completion.py
deleted file mode 100644
index 14223668e036d9..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_completion.py
+++ /dev/null
@@ -1,130 +0,0 @@
-import re
-from collections.abc import Generator
-from time import time
-
-# import monkeypatch
-from typing import Any, Literal, Optional, Union
-
-from openai import AzureOpenAI, BadRequestError, OpenAI
-from openai._types import NOT_GIVEN, NotGiven
-from openai.resources.completions import Completions
-from openai.types import Completion as CompletionMessage
-from openai.types.completion import CompletionChoice
-from openai.types.completion_usage import CompletionUsage
-
-from core.model_runtime.errors.invoke import InvokeAuthorizationError
-
-
-class MockCompletionsClass:
- @staticmethod
- def mocked_openai_completion_create_sync(model: str) -> CompletionMessage:
- return CompletionMessage(
- id="cmpl-3QJQa5jXJ5Z5X",
- object="text_completion",
- created=int(time()),
- model=model,
- system_fingerprint="",
- choices=[
- CompletionChoice(
- text="mock",
- index=0,
- logprobs=None,
- finish_reason="stop",
- )
- ],
- usage=CompletionUsage(
- prompt_tokens=2,
- completion_tokens=1,
- total_tokens=3,
- ),
- )
-
- @staticmethod
- def mocked_openai_completion_create_stream(model: str) -> Generator[CompletionMessage, None, None]:
- full_text = "Hello, world!\n\n```python\nprint('Hello, world!')\n```"
- for i in range(0, len(full_text) + 1):
- if i == len(full_text):
- yield CompletionMessage(
- id="cmpl-3QJQa5jXJ5Z5X",
- object="text_completion",
- created=int(time()),
- model=model,
- system_fingerprint="",
- choices=[
- CompletionChoice(
- text="",
- index=0,
- logprobs=None,
- finish_reason="stop",
- )
- ],
- usage=CompletionUsage(
- prompt_tokens=2,
- completion_tokens=17,
- total_tokens=19,
- ),
- )
- else:
- yield CompletionMessage(
- id="cmpl-3QJQa5jXJ5Z5X",
- object="text_completion",
- created=int(time()),
- model=model,
- system_fingerprint="",
- choices=[
- CompletionChoice(text=full_text[i], index=0, logprobs=None, finish_reason="content_filter")
- ],
- )
-
- def completion_create(
- self: Completions,
- *,
- model: Union[
- str,
- Literal[
- "babbage-002",
- "davinci-002",
- "gpt-3.5-turbo-instruct",
- "text-davinci-003",
- "text-davinci-002",
- "text-davinci-001",
- "code-davinci-002",
- "text-curie-001",
- "text-babbage-001",
- "text-ada-001",
- ],
- ],
- prompt: Union[str, list[str], list[int], list[list[int]], None],
- stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
- **kwargs: Any,
- ):
- openai_models = [
- "babbage-002",
- "davinci-002",
- "gpt-3.5-turbo-instruct",
- "text-davinci-003",
- "text-davinci-002",
- "text-davinci-001",
- "code-davinci-002",
- "text-curie-001",
- "text-babbage-001",
- "text-ada-001",
- ]
- azure_openai_models = ["gpt-35-turbo-instruct"]
-
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", str(self._client.base_url)):
- raise InvokeAuthorizationError("Invalid base url")
- if model in openai_models + azure_openai_models:
- if not re.match(r"sk-[a-zA-Z0-9]{24,}$", self._client.api_key) and type(self._client) == OpenAI:
- # sometime, provider use OpenAI compatible API will not have api key or have different api key format
- # so we only check if model is in openai_models
- raise InvokeAuthorizationError("Invalid api key")
- if len(self._client.api_key) < 18 and type(self._client) == AzureOpenAI:
- raise InvokeAuthorizationError("Invalid api key")
-
- if not prompt:
- raise BadRequestError("Invalid prompt")
- if stream:
- return MockCompletionsClass.mocked_openai_completion_create_stream(model=model)
-
- return MockCompletionsClass.mocked_openai_completion_create_sync(model=model)
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_embeddings.py b/api/tests/integration_tests/model_runtime/__mock/openai_embeddings.py
deleted file mode 100644
index 3cc1fa9ff1db49..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_embeddings.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import re
-from typing import Any, Literal, Union
-
-from openai._types import NOT_GIVEN, NotGiven
-from openai.resources.embeddings import Embeddings
-from openai.types.create_embedding_response import CreateEmbeddingResponse, Usage
-from openai.types.embedding import Embedding
-
-from core.model_runtime.errors.invoke import InvokeAuthorizationError
-
-
-class MockEmbeddingsClass:
- def create_embeddings(
- self: Embeddings,
- *,
- input: Union[str, list[str], list[int], list[list[int]]],
- model: Union[str, Literal["text-embedding-ada-002"]],
- encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN,
- **kwargs: Any,
- ) -> CreateEmbeddingResponse:
- if isinstance(input, str):
- input = [input]
-
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", str(self._client.base_url)):
- raise InvokeAuthorizationError("Invalid base url")
-
- if len(self._client.api_key) < 18:
- raise InvokeAuthorizationError("Invalid API key")
-
- if encoding_format == "float":
- return CreateEmbeddingResponse(
- data=[
- Embedding(embedding=[0.23333 for _ in range(233)], index=i, object="embedding")
- for i in range(len(input))
- ],
- model=model,
- object="list",
- # marked: usage of embeddings should equal the number of testcase
- usage=Usage(prompt_tokens=2, total_tokens=2),
- )
-
- embeddings = "VEfNvMLUnrwFleO8hcj9vEE/yrzyjOA84E1MvNfoCrxjrI+8sZUKvNgrBT17uY07gJ/IvNvhHLrUemc8KXXGumalIT3YKwU7ZsnbPMhATrwTt6u8JEwRPNMmCjxGREW7TRKvu6/MG7zAyDU8wXLkuuMDZDsXsL28zHzaOw0IArzOiMO8LtASvPKM4Dul5l+80V0bPGVDZ7wYNrI89ucsvJZdYztzRm+8P8ysOyGbc7zrdgK9sdiEPKQ8sbulKdq7KIgdvKIMDj25dNc8k0AXPBn/oLzrdgK8IXe5uz0Dvrt50V68tTjLO4ZOcjoG9x29oGfZufiwmzwMDXy8EL6ZPHvdx7nKjzE8+LCbPG22hTs3EZq7TM+0POrRzTxVZo084wPkO8Nak7z8cpw8pDwxvA2T8LvBC7C72fltvC8Atjp3fYE8JHDLvEYgC7xAdls8YiabPPkEeTzPUbK8gOLCPEBSIbyt5Oy8CpreusNakzywUhA824vLPHRlr7zAhTs7IZtzvHd9AT2xY/O6ok8IvOihqrql5l88K4EvuknWorvYKwW9iXkbvGMTRLw5qPG7onPCPLgNIzwAbK67ftbZPMxYILvAyDW9TLB0vIid1buzCKi7u+d0u8iDSLxNVam8PZyJPNxnETvVANw8Oi5mu9nVszzl65I7DIKNvLGVirxsMJE7tPXQu2PvCT1zRm87p1l9uyRMkbsdfqe8U52ePHRlr7wt9Mw8/C8ivTu02rwJFGq8tpoFPWnC7blWumq7sfy+vG1zCzy9Nlg8iv+PuvxT3DuLU228kVhoOkmTqDrv1kg8ocmTu1WpBzsKml48DzglvI8ECzxwTd27I+pWvIWkQ7xUR007GqlPPBFEDrzGECu865q8PI7BkDwNxYc8tgG6ullMSLsIajs84lk1PNLjD70mv648ZmInO2tnIjzvb5Q8o5KCPLo9xrwKMyq9QqGEvI8ECzxO2508ATUdPRAlTry5kxc8KVGMPJyBHjxIUC476KGqvIU9DzwX87c88PUIParrWrzdlzS/G3K+uzEw2TxB2BU86AhfPAMiRj2dK808a85WPPCft7xU4Bg95Q9NPDxZjzwrpek7yNkZvHa0EjyQ0nM6Nq9fuyjvUbsRq8I7CAMHO3VSWLyuauE7U1qkvPkEeTxs7ZY7B6FMO48Eizy75/S7ieBPvB07rTxmyVu8onPCO5rc6Tu7XIa7oEMfPYngT7u24vk7/+W5PE8eGDxJ1iI9t4cuvBGHiLyH1GY7jfghu+oUSDwa7Mk7iXmbuut2grrq8I2563v8uyofdTxRTrs44lm1vMeWnzukf6s7r4khvEKhhDyhyZO8G5Z4Oy56wTz4sBs81Zknuz3fg7wnJuO74n1vvASEADu98128gUl3vBtyvrtZCU47yep8u5FYaDx2G0e8a85WO5cmUjz3kds8qgqbPCUaerx50d67WKIZPI7BkDua3Om74vKAvL3zXbzXpRA9CI51vLo9xryKzXg7tXtFO9RWLTwnJuM854LqPEIs8zuO5cq8d8V1u9P0cjrQ++C8cGwdPDdUlLoOGeW8auEtu8Z337nlzFK8aRg/vFCkDD0nRSM879bIvKUFID1iStU8EL6ZvLufgLtKgNE7KVEMvJOnSzwahRU895HbvJiIjLvc8n88bmC0PPLP2rywM9C7jTscOoS3mjy/Znu7dhvHuu5Q1Dyq61o6CI71u09hkry0jhw8gb6IPI8EC7uoVAM8gs9rvGM3fjx2G8e81FYtu/ojubyYRRK72Riuu83elDtNNmk70/TyuzUFsbvgKZI7onNCvAehzLumr8679R6+urr6SztX2So8Bl5SOwSEgLv5NpA8LwC2PGPvibzJ6vw7H2tQvOtXwrzXpRC8j0z/uxwcbTy2vr+8VWYNu+t2ArwKmt68NKN2O3XrIzw9A747UU47vaavzjwU+qW8YBqyvE02aTyEt5o8cCmjOxtyPrxs7ZY775NOu+SJWLxMJQY8/bWWu6IMDrzSSsQ7GSPbPLlQnbpVzcE7Pka4PJ96sLycxJg8v/9GPO2HZTyeW3C8Vpawtx2iYTwWBg87/qI/OviwGzxyWcY7M9WNPIA4FD32C2e8tNGWPJ43trxCoYS8FGHavItTbbu7n4C80NemPLm30Ty1OMu7vG1pvG3aPztBP0o75Q/NPJhFEj2V9i683PL/O97+aLz6iu27cdPRum/mKLwvVgc89fqDu3LA+jvm2Ls8mVZ1PIuFBD3ZGK47Cpreut7+aLziWTU8XSEgPMvSKzzO73e5040+vBlmVTxS1K+8mQ4BPZZ8o7w8FpW6OR0DPSSPCz21Vwu99fqDOjMYiDy7XAY8oYaZO+aVwTyX49c84OaXOqdZfTunEQk7B8AMvMDs7zo/D6e8OP5CvN9gIzwNCII8FefOPE026TpzIjU8XsvOO+J9b7rkIiQ8is34O+e0AbxBpv67hcj9uiPq1jtCoQQ8JfY/u86nAz0Wkf28LnrBPJlW9Tt8P4K7BbSjO9grhbyAOJS8G3K+vJLe3LzXpZA7NQUxPJs+JDz6vAS8QHZbvYNVYDrj3yk88PWIPOJ97zuSIVc8ZUPnPMqPsbx2cZi7QfzPOxYGDz2hqtO6H2tQO543NjyFPY+7JRUAOt0wgDyJeZu8MpKTu6AApTtg1ze82JI5vKllZjvrV0I7HX6nu7vndDxg1ze8jwQLu1ZTNjuJvBU7BXGpvAP+C7xJk6g8j2u/vBABlLzlqBi8M9WNutRWLTx0zGM9sHbKPLoZDDtmyVu8tpqFOvPumjyuRqe87lBUvFU0drxs7Za8ejMZOzJPGbyC7qu863v8PDPVjTxJ1iI7Ca01PLuAQLuNHFy7At9LOwP+i7tYxlO80NemO9elkDx45LU8h9TmuzxZjzz/5bk8p84OurvndLwAkGi7XL9luCSzRTwMgg08vrxMPKIwyDwdomG8K6VpPGPvCTxkmTi7M/lHPGxUSzxwKSM8wQuwvOqtkzrLFSa8SbdivAMixjw2r9+7xWt2vAyCDT1NEi87B8CMvG1zi7xpwm27MrbNO9R6Z7xJt+K7jNnhu9ZiFrve/ug55CKkvCwHJLqsOr47+ortvPwvIr2v8NW8YmmVOE+FTLywUhA8MTBZvMiDyLtx8hG8OEE9vMDsbzroCF88DelBOobnPbx+b6U8sbnEOywr3ro93wO9dMzjup2xwbwnRaO7cRZMu8Z337vS44+7VpYwvFWphzxKgNE8L1aHPLPFLbunzo66zFggPN+jHbs7tFo8nW7HO9JKRLyoeD28Fm1DPGZip7u5dNe7KMsXvFnlkzxQpAw7MrZNPHpX0zwSyoK7ayQovPR0Dz3gClK8/juLPDjaCLvqrZO7a4vcO9HEzzvife88KKzXvDmocbwpMkw7t2huvaIMjjznguo7Gy/EOzxZjzoLuZ48qi5VvCjLFzuDmNo654LquyrXgDy7XAa8e7mNvJ7QAb0Rq8K7ojBIvBN0MTuOfha8GoUVveb89bxMsHS8jV9WPPKM4LyAOJS8me9AvZv7qbsbcr47tuL5uaXmXzweKNa7rkYnPINV4Lxcv+W8tVcLvI8oxbzvbxS7oYaZu9+jHT0cHO08c7uAPCSzRTywUhA85xu2u+wBcTuJvJU8PBYVusTghzsnAim8acJtPFQE0zzFIwI9C7meO1DIRry7XAY8MKpkPJZd47suN0e5JTm6u6BDn7zfx1e8AJDoOr9CQbwaQps7x/1TPLTRFryqLtU8JybjPIXI/Tz6I7k6mVb1PMWKNryd1fs8Ok0mPHt2kzy9Ep48TTZpvPS3ibwGOpi8Ns4fPBqFlbr3Kqc8+QR5vHLA+rt7uY289YXyPI6iULxL4gu8Tv/XuycCKbwCnFG8C7kevVG1b7zIXw68GoWVO4rNeDnrM4i8MxgIPUNLs7zSoJW86ScfO+rRzbs6Cqw8NxGautP0cjw0wjY8CGq7vAkU6rxKgNG5+uA+vJXXbrwKM6o86vCNOu+yjjoQAZS8xATCOQVxKbynzo68wxcZvMhATjzS4488ArsRvNEaobwRh4i7t4euvAvd2DwnAik8UtQvvBFEDrz4sJs79gtnvOknnzy+vEy8D3sfPLH8vjzmLo28KVGMvOtXwjvpapm8HBxtPH3K8Lu753Q8/l9FvLvn9DomoG48fET8u9zy/7wMpke8zmQJu3oU2TzlD828KteAPAwNfLu+mBI5ldduPNZDVjq+vEy8eEvqvDHJpLwUPaC6qi7VPABsLjwFcSm72sJcu+bYO7v41NW8RiALvYB7DjzL0is7qLs3us1FSbzaf2K8MnNTuxABFDzF8Wo838fXvOBNzDzre3w8afQEvQE1nbulBaC78zEVvG5B9LzH/VM82Riuuwu5nrwsByQ8Y6yPvHXro7yQ0nM8nStNPJkyOzwnJmM80m7+O1VmjTzqrZM8dhvHOyAQBbz3baG8KTJMPOlqmbxsVEs8Pq3suy56QbzUVq08X3CDvAE1nTwUHuA7hue9vF8tCbvwOAO6F7A9ugd9kryqLtW7auEtu9ONPryPa7+8o9r2O570OzyFpEO8ntCBPOqtk7sykhO7lC1AOw2TcLswhiq6vx4HvP5fRbwuesG7Mk8ZvA4Z5TlfcAM9DrIwPL//xrzMm5q8JEwRPHBsnbxL4gu8jyjFu99gozrkZZ483GeRPLuAwDuYiIw8iv8PvK5Gpzx+b6W87Yflu3NGbzyE+hQ8a4tcPItT7bsoy5e8L1YHvWQyBDwrga86kPEzvBQ9oDxtl0W8lwKYvGpIYrxQ5wY8AJDovOLyALyw3f489JjJvMdTpTkKMyo8V9mqvH3K8LpyNYy8JHDLOixu2LpQ54Y8Q0uzu8LUnrs0wrY84vIAveihqjwfihA8DIKNvLDd/jywM1C7FB7gOxsLirxAUqE7sulnvH3K8DkAkGg8jsGQvO+TzrynWf287CCxvK4Drbwg8UQ8JRr6vFEqAbskjwu76q2TPNP0cjopDhK8dVJYvFIXKrxLn5G8AK8oPAb3HbxbOXE8Bvedun5Q5ThHyjk8QdiVvBXDlLw0o/Y7aLGKupkOgTxKPdc81kNWPtUAXLxUR827X1FDPf47izxsEVE8akhiPIhaWzxYX5+7hT0PPSrXgLxQC0E8i4WEvKUp2jtCLHM8DcWHO768zLxnK5a89R6+vH9czrorpem73h0pvAnwr7yKzXi8gDgUPf47Czq9zyO8728UOf34EDy6PUY76OSkvKZIGr2ZDgE8gzEmPG3av7v77Ce7/oP/O3MiNTtas/w8x1OlO/D1CDvDfs27ll1jO2Ufrbv1hXK8WINZuxN0sbuxlYq8OYS3uia/rjyiTwi9O7TaO+/WyDyiDA49E7erO3fF9bj6I7k7qHi9O3SoKbyBSfc7drSSvGPvCT2pQay7t2huPGnC7byUCQY8CEaBu6rHoDhx8hE8/fgQvCjLl7zdeHS8x/3TO0Isc7tas3y8jwQLvUKhhDz+foU8fCDCPC+ZgTywD5Y7ZR8tOla66rtCCLm8gWg3vDoKrLxbWDE76SefPBkj2zrlqJi7pebfuv6Df7zWQ9a7lHA6PGDXtzzMv1Q8mtxpOwJ4lzxKGZ28mGnMPDw6z7yxY/O7m2Leu7juYjwvVge8zFigPGpIYjtWumo5xs2wOgyCjbxrZ6K8bbaFvKzTCbsks8W7C7mePIU9DzxQyEY8posUvAW0ozrHlh88CyBTPJRwursxySQ757SBuqcRCbwNCIK8EL6ZvIG+iLsIRgE8rF74vOJZtbuUcDq8r/DVPMpMt7sL3Vi8eWqquww/kzqj2vY5auGtu85kiTwMPxM66KGqvBIxNzuwUpA8v2b7u09C0rx7ms08NUirvFYQPLxKPdc68mimvP5fRTtoPPm7XuqOOgOJ+jxfLYm7u58AvXz8B72PR4W6ldfuuys+tbvYKwW7pkiaPLB2SjvKj7G875POvA6yML7qFEg9Eu68O6Up2rz77Kc84CmSPP6ivzz4sJu6/C+iOaUpWjwq14A84E3MOYB7Dr2d1Xu775NOvC6e+7spUYw8PzPhO5TGizt29ww9yNkZPY7lyrz020M7QRsQu3z8BzwkCZe79YXyO8jZmTzvGUM8HgQcO9kYrrzxBmy8hLeaPLYBOjz+oj88flBlO6GqUzuiMMi8fxlUvCr7ujz41NU8DA38PBeMAzx7uY28TTZpvFG1bzxtc4s89ucsPEereTwfipC82p4iPKtNFbzo5KQ7pcKlOW5gtDzO73c7B6FMOzRbgjxCXoo8v0JBOSl1RrwxDJ+7XWSaPD3Aw7sOsjA8tuJ5vKw6Pry5k5c8ZUNnvG/H6DyVTAA8Shkdvd7+aDvtpiW9qUGsPFTgmDwbcr68TTbpO1DnhryNX9a7mrivvIqpPjxsqhy81HrnOzv31Dvth+U6UtQvPBz4MrvtpqW84OYXvRz4sjxwkFe8zSGPuycCqbyFPY8818nKOw84JTy8bWk8USqBvBGHiLtosQo8BOs0u9skl7xQ54Y8uvrLPOknn7w705o8Jny0PAd9EjxhoKa8Iv2tu2M3/jtsVEs8DcUHPQSEADs3eE48GkKbupRR+rvdeHQ7Xy2JvO1jKz0xMFm8sWPzux07LbyrTZW7bdq/O6Pa9r0ahRW9CyDTOjSjdjyQ8bO8yaIIPfupLTz/CfQ7xndfvJs+JD0zPEK8KO/RvMpw8bwObzY7fm+lPJtiXrz5BHm8WmsIvKlBrLuDdKA7hWHJOgd9Ers0o/Y7nlvwu5NAl7u8BrW6utYRO2SZuDxyNYw8CppevAY6GDxVqQe9oGdZPFa6ary3RLS70NcmO2PQSb36ZrM86q2TPML42LwewaE8k2RRPDmocTsi/S29o/k2PHRlr7zjnC+8gHsOPUpcFzxtl8W6tuL5vHw/gry/2wy9yaIIvINV4Dx3fQG7ISFoPO7pnzwGXlK8HPiyPGAaMjzBC7A7MQyfu+eC6jyV1+67pDyxvBWkVLxrJKg754LqOScCKbwpUQy8KIgdOJDSc7zDfk08tLLWvNZDVjyh7c28ShmdvMnlgjs2NdS8ISHovP5+hbxGIIs8ayQouyKnXDzBcmS6zw44u86IQ7yl5l+7cngGvWvOVrsEhIC7yNkZPJODkbuAn0g8XN6lPOaVwbuTgxG8OR2DPAb3HTzlqJi8nUoNvCAVf73Mmxo9afSEu4FotzveHSk8c0ZvOMFOqjwP9Sq87iwavIEBg7xIUK68IbozuozZ4btg17c7vx4Hvarr2rtp9IQ8Rt0QO+1jqzyeNzY8kNLzO8sVpry98108OCL9uyisV7vhr4Y8FgaPvLFjczw42og8gWg3vPX6gzsNk/C83GeRPCUVgDy0jpw7yNkZu2VD5zvh93o81h+cuw3Fhzyl5t+86Y7TvHa0EjyzCCi7WmsIPIy1Jzy00Ra6NUiru50rTTx50d47/HKcO2wwETw0f7y8sFIQvNxnkbzS4w855pVBu9FdGzx9yvC6TM80vFQjkzy/Zvs7BhtYPLjKKLqPa787A/6LOyiInbzooSq8728UPIFJ97wq+7q8R6v5u1tYMbwdomG6iSPKPAb3HTx3oTu7fGO8POqtk7ze/ug84wNkPMnq/DsB8iK9ogwOu6lBrDznguo8NQUxvHKcwDo28tm7yNmZPN1UurxCoYS80m7+Oy+9OzzGzTC836MdvCDNCrtaawi7dVLYPEfKuTxzRm88cCmjOyXSBbwGOpi879ZIO8dTJbtqnrO8NMI2vR1+J7xwTV087umfPFG17zsC30s8oYaZPKllZrzZGK47zss9vP21FryZywa9bbYFPVNapDt2G0e7E3SxPMUjgry5dNc895Hbu0H8z7ueN7a7OccxPFhfH7vC1B48n3owvEhQLrzu6Z+8HTutvEBSITw6Taa5g1XgPCzEqbxfLYk9OYQ3vBlm1bvPUTI8wIU7PIy1pzyFyP07gzGmO3NGb7yS3ty7O5CguyEhaLyWoF28pmxUOaZImrz+g/87mnU1vFbsgTxvo668PFmPO2KNTzy09VC8LG5YPHhL6rsvJPC7kTQuvEGCxDlhB9s6u58AvfCAd7z0t4k7kVjoOCkOkrxMjDq8iPOmPL0SnrxsMJG7OEG9vCUa+rvx4rE7cpxAPDCGqjukf6u8TEnAvNn57TweBBw7JdKFvIy1p7vIg8i7" # noqa: E501
-
- data = []
- for i, text in enumerate(input):
- obj = Embedding(embedding=[], index=i, object="embedding")
- obj.embedding = embeddings
-
- data.append(obj)
-
- return CreateEmbeddingResponse(
- data=data,
- model=model,
- object="list",
- # marked: usage of embeddings should equal the number of testcase
- usage=Usage(prompt_tokens=2, total_tokens=2),
- )
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_moderation.py b/api/tests/integration_tests/model_runtime/__mock/openai_moderation.py
deleted file mode 100644
index e26855344e63bf..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_moderation.py
+++ /dev/null
@@ -1,140 +0,0 @@
-import re
-from typing import Any, Literal, Union
-
-from openai._types import NOT_GIVEN, NotGiven
-from openai.resources.moderations import Moderations
-from openai.types import ModerationCreateResponse
-from openai.types.moderation import Categories, CategoryScores, Moderation
-
-from core.model_runtime.errors.invoke import InvokeAuthorizationError
-
-
-class MockModerationClass:
- def moderation_create(
- self: Moderations,
- *,
- input: Union[str, list[str]],
- model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] | NotGiven = NOT_GIVEN,
- **kwargs: Any,
- ) -> ModerationCreateResponse:
- if isinstance(input, str):
- input = [input]
-
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", str(self._client.base_url)):
- raise InvokeAuthorizationError("Invalid base url")
-
- if len(self._client.api_key) < 18:
- raise InvokeAuthorizationError("Invalid API key")
-
- for text in input:
- result = []
- if "kill" in text:
- moderation_categories = {
- "harassment": False,
- "harassment/threatening": False,
- "hate": False,
- "hate/threatening": False,
- "self-harm": False,
- "self-harm/instructions": False,
- "self-harm/intent": False,
- "sexual": False,
- "sexual/minors": False,
- "violence": False,
- "violence/graphic": False,
- "illicit": False,
- "illicit/violent": False,
- }
- moderation_categories_scores = {
- "harassment": 1.0,
- "harassment/threatening": 1.0,
- "hate": 1.0,
- "hate/threatening": 1.0,
- "self-harm": 1.0,
- "self-harm/instructions": 1.0,
- "self-harm/intent": 1.0,
- "sexual": 1.0,
- "sexual/minors": 1.0,
- "violence": 1.0,
- "violence/graphic": 1.0,
- "illicit": 1.0,
- "illicit/violent": 1.0,
- }
- category_applied_input_types = {
- "sexual": ["text", "image"],
- "hate": ["text"],
- "harassment": ["text"],
- "self-harm": ["text", "image"],
- "sexual/minors": ["text"],
- "hate/threatening": ["text"],
- "violence/graphic": ["text", "image"],
- "self-harm/intent": ["text", "image"],
- "self-harm/instructions": ["text", "image"],
- "harassment/threatening": ["text"],
- "violence": ["text", "image"],
- "illicit": ["text"],
- "illicit/violent": ["text"],
- }
- result.append(
- Moderation(
- flagged=True,
- categories=Categories(**moderation_categories),
- category_scores=CategoryScores(**moderation_categories_scores),
- category_applied_input_types=category_applied_input_types,
- )
- )
- else:
- moderation_categories = {
- "harassment": False,
- "harassment/threatening": False,
- "hate": False,
- "hate/threatening": False,
- "self-harm": False,
- "self-harm/instructions": False,
- "self-harm/intent": False,
- "sexual": False,
- "sexual/minors": False,
- "violence": False,
- "violence/graphic": False,
- "illicit": False,
- "illicit/violent": False,
- }
- moderation_categories_scores = {
- "harassment": 0.0,
- "harassment/threatening": 0.0,
- "hate": 0.0,
- "hate/threatening": 0.0,
- "self-harm": 0.0,
- "self-harm/instructions": 0.0,
- "self-harm/intent": 0.0,
- "sexual": 0.0,
- "sexual/minors": 0.0,
- "violence": 0.0,
- "violence/graphic": 0.0,
- "illicit": 0.0,
- "illicit/violent": 0.0,
- }
- category_applied_input_types = {
- "sexual": ["text", "image"],
- "hate": ["text"],
- "harassment": ["text"],
- "self-harm": ["text", "image"],
- "sexual/minors": ["text"],
- "hate/threatening": ["text"],
- "violence/graphic": ["text", "image"],
- "self-harm/intent": ["text", "image"],
- "self-harm/instructions": ["text", "image"],
- "harassment/threatening": ["text"],
- "violence": ["text", "image"],
- "illicit": ["text"],
- "illicit/violent": ["text"],
- }
- result.append(
- Moderation(
- flagged=False,
- categories=Categories(**moderation_categories),
- category_scores=CategoryScores(**moderation_categories_scores),
- category_applied_input_types=category_applied_input_types,
- )
- )
-
- return ModerationCreateResponse(id="shiroii kuloko", model=model, results=result)
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_remote.py b/api/tests/integration_tests/model_runtime/__mock/openai_remote.py
deleted file mode 100644
index 704dbad5d288ca..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_remote.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from time import time
-
-from openai.types.model import Model
-
-
-class MockModelClass:
- """
- mock class for openai.models.Models
- """
-
- def list(
- self,
- **kwargs,
- ) -> list[Model]:
- return [
- Model(
- id="ft:gpt-3.5-turbo-0613:personal::8GYJLPDQ",
- created=int(time()),
- object="model",
- owned_by="organization:org-123",
- )
- ]
diff --git a/api/tests/integration_tests/model_runtime/__mock/openai_speech2text.py b/api/tests/integration_tests/model_runtime/__mock/openai_speech2text.py
deleted file mode 100644
index a51dcab4be7467..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/openai_speech2text.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import re
-from typing import Any, Literal, Union
-
-from openai._types import NOT_GIVEN, FileTypes, NotGiven
-from openai.resources.audio.transcriptions import Transcriptions
-from openai.types.audio.transcription import Transcription
-
-from core.model_runtime.errors.invoke import InvokeAuthorizationError
-
-
-class MockSpeech2TextClass:
- def speech2text_create(
- self: Transcriptions,
- *,
- file: FileTypes,
- model: Union[str, Literal["whisper-1"]],
- language: str | NotGiven = NOT_GIVEN,
- prompt: str | NotGiven = NOT_GIVEN,
- response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN,
- temperature: float | NotGiven = NOT_GIVEN,
- **kwargs: Any,
- ) -> Transcription:
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", str(self._client.base_url)):
- raise InvokeAuthorizationError("Invalid base url")
-
- if len(self._client.api_key) < 18:
- raise InvokeAuthorizationError("Invalid API key")
-
- return Transcription(text="1, 2, 3, 4, 5, 6, 7, 8, 9, 10")
diff --git a/api/tests/integration_tests/model_runtime/__mock/plugin_daemon.py b/api/tests/integration_tests/model_runtime/__mock/plugin_daemon.py
new file mode 100644
index 00000000000000..6dfc01ab4cddb7
--- /dev/null
+++ b/api/tests/integration_tests/model_runtime/__mock/plugin_daemon.py
@@ -0,0 +1,44 @@
+import os
+from collections.abc import Callable
+
+import pytest
+
+# import monkeypatch
+from _pytest.monkeypatch import MonkeyPatch
+
+from core.plugin.manager.model import PluginModelManager
+from tests.integration_tests.model_runtime.__mock.plugin_model import MockModelClass
+
+
+def mock_plugin_daemon(
+ monkeypatch: MonkeyPatch,
+) -> Callable[[], None]:
+ """
+ mock openai module
+
+ :param monkeypatch: pytest monkeypatch fixture
+ :return: unpatch function
+ """
+
+ def unpatch() -> None:
+ monkeypatch.undo()
+
+ monkeypatch.setattr(PluginModelManager, "invoke_llm", MockModelClass.invoke_llm)
+ monkeypatch.setattr(PluginModelManager, "fetch_model_providers", MockModelClass.fetch_model_providers)
+ monkeypatch.setattr(PluginModelManager, "get_model_schema", MockModelClass.get_model_schema)
+
+ return unpatch
+
+
+MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
+
+
+@pytest.fixture
+def setup_model_mock(monkeypatch):
+ if MOCK:
+ unpatch = mock_plugin_daemon(monkeypatch)
+
+ yield
+
+ if MOCK:
+ unpatch()
diff --git a/api/tests/integration_tests/model_runtime/__mock/plugin_model.py b/api/tests/integration_tests/model_runtime/__mock/plugin_model.py
new file mode 100644
index 00000000000000..50913662e293da
--- /dev/null
+++ b/api/tests/integration_tests/model_runtime/__mock/plugin_model.py
@@ -0,0 +1,249 @@
+import datetime
+import uuid
+from collections.abc import Generator, Sequence
+from decimal import Decimal
+from json import dumps
+
+# import monkeypatch
+from typing import Optional
+
+from core.model_runtime.entities.common_entities import I18nObject
+from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage
+from core.model_runtime.entities.message_entities import AssistantPromptMessage, PromptMessage, PromptMessageTool
+from core.model_runtime.entities.model_entities import (
+ AIModelEntity,
+ FetchFrom,
+ ModelFeature,
+ ModelPropertyKey,
+ ModelType,
+)
+from core.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity
+from core.plugin.entities.plugin_daemon import PluginModelProviderEntity
+from core.plugin.manager.model import PluginModelManager
+
+
+class MockModelClass(PluginModelManager):
+ def fetch_model_providers(self, tenant_id: str) -> Sequence[PluginModelProviderEntity]:
+ """
+ Fetch model providers for the given tenant.
+ """
+ return [
+ PluginModelProviderEntity(
+ id=uuid.uuid4().hex,
+ created_at=datetime.datetime.now(),
+ updated_at=datetime.datetime.now(),
+ provider="openai",
+ tenant_id=tenant_id,
+ plugin_unique_identifier="langgenius/openai/openai",
+ plugin_id="langgenius/openai",
+ declaration=ProviderEntity(
+ provider="openai",
+ label=I18nObject(
+ en_US="OpenAI",
+ zh_Hans="OpenAI",
+ ),
+ description=I18nObject(
+ en_US="OpenAI",
+ zh_Hans="OpenAI",
+ ),
+ icon_small=I18nObject(
+ en_US="https://example.com/icon_small.png",
+ zh_Hans="https://example.com/icon_small.png",
+ ),
+ icon_large=I18nObject(
+ en_US="https://example.com/icon_large.png",
+ zh_Hans="https://example.com/icon_large.png",
+ ),
+ supported_model_types=[ModelType.LLM],
+ configurate_methods=[ConfigurateMethod.PREDEFINED_MODEL],
+ models=[
+ AIModelEntity(
+ model="gpt-3.5-turbo",
+ label=I18nObject(
+ en_US="gpt-3.5-turbo",
+ zh_Hans="gpt-3.5-turbo",
+ ),
+ model_type=ModelType.LLM,
+ fetch_from=FetchFrom.PREDEFINED_MODEL,
+ model_properties={},
+ features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL],
+ ),
+ AIModelEntity(
+ model="gpt-3.5-turbo-instruct",
+ label=I18nObject(
+ en_US="gpt-3.5-turbo-instruct",
+ zh_Hans="gpt-3.5-turbo-instruct",
+ ),
+ model_type=ModelType.LLM,
+ fetch_from=FetchFrom.PREDEFINED_MODEL,
+ model_properties={
+ ModelPropertyKey.MODE: LLMMode.COMPLETION,
+ },
+ features=[],
+ ),
+ ],
+ ),
+ )
+ ]
+
+ def get_model_schema(
+ self,
+ tenant_id: str,
+ user_id: str,
+ plugin_id: str,
+ provider: str,
+ model_type: str,
+ model: str,
+ credentials: dict,
+ ) -> AIModelEntity | None:
+ """
+ Get model schema
+ """
+ return AIModelEntity(
+ model=model,
+ label=I18nObject(
+ en_US="OpenAI",
+ zh_Hans="OpenAI",
+ ),
+ model_type=ModelType(model_type),
+ fetch_from=FetchFrom.PREDEFINED_MODEL,
+ model_properties={},
+ features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL] if model == "gpt-3.5-turbo" else [],
+ )
+
+ @staticmethod
+ def generate_function_call(
+ tools: Optional[list[PromptMessageTool]],
+ ) -> Optional[AssistantPromptMessage.ToolCall]:
+ if not tools or len(tools) == 0:
+ return None
+ function: PromptMessageTool = tools[0]
+ function_name = function.name
+ function_parameters = function.parameters
+ function_parameters_type = function_parameters["type"]
+ if function_parameters_type != "object":
+ return None
+ function_parameters_properties = function_parameters["properties"]
+ function_parameters_required = function_parameters["required"]
+ parameters = {}
+ for parameter_name, parameter in function_parameters_properties.items():
+ if parameter_name not in function_parameters_required:
+ continue
+ parameter_type = parameter["type"]
+ if parameter_type == "string":
+ if "enum" in parameter:
+ if len(parameter["enum"]) == 0:
+ continue
+ parameters[parameter_name] = parameter["enum"][0]
+ else:
+ parameters[parameter_name] = "kawaii"
+ elif parameter_type == "integer":
+ parameters[parameter_name] = 114514
+ elif parameter_type == "number":
+ parameters[parameter_name] = 1919810.0
+ elif parameter_type == "boolean":
+ parameters[parameter_name] = True
+
+ return AssistantPromptMessage.ToolCall(
+ id=str(uuid.uuid4()),
+ type="function",
+ function=AssistantPromptMessage.ToolCall.ToolCallFunction(
+ name=function_name,
+ arguments=dumps(parameters),
+ ),
+ )
+
+ @staticmethod
+ def mocked_chat_create_sync(
+ model: str,
+ prompt_messages: list[PromptMessage],
+ tools: Optional[list[PromptMessageTool]] = None,
+ ) -> LLMResult:
+ tool_call = MockModelClass.generate_function_call(tools=tools)
+
+ return LLMResult(
+ id=str(uuid.uuid4()),
+ model=model,
+ prompt_messages=prompt_messages,
+ message=AssistantPromptMessage(content="elaina", tool_calls=[tool_call] if tool_call else []),
+ usage=LLMUsage(
+ prompt_tokens=2,
+ completion_tokens=1,
+ total_tokens=3,
+ prompt_unit_price=Decimal(0.0001),
+ completion_unit_price=Decimal(0.0002),
+ prompt_price_unit=Decimal(1),
+ prompt_price=Decimal(0.0001),
+ completion_price_unit=Decimal(1),
+ completion_price=Decimal(0.0002),
+ total_price=Decimal(0.0003),
+ currency="USD",
+ latency=0.001,
+ ),
+ )
+
+ @staticmethod
+ def mocked_chat_create_stream(
+ model: str,
+ prompt_messages: list[PromptMessage],
+ tools: Optional[list[PromptMessageTool]] = None,
+ ) -> Generator[LLMResultChunk, None, None]:
+ tool_call = MockModelClass.generate_function_call(tools=tools)
+
+ full_text = "Hello, world!\n\n```python\nprint('Hello, world!')\n```"
+ for i in range(0, len(full_text) + 1):
+ if i == len(full_text):
+ yield LLMResultChunk(
+ model=model,
+ prompt_messages=prompt_messages,
+ delta=LLMResultChunkDelta(
+ index=0,
+ message=AssistantPromptMessage(
+ content="",
+ tool_calls=[tool_call] if tool_call else [],
+ ),
+ ),
+ )
+ else:
+ yield LLMResultChunk(
+ model=model,
+ prompt_messages=prompt_messages,
+ delta=LLMResultChunkDelta(
+ index=0,
+ message=AssistantPromptMessage(
+ content=full_text[i],
+ tool_calls=[tool_call] if tool_call else [],
+ ),
+ usage=LLMUsage(
+ prompt_tokens=2,
+ completion_tokens=17,
+ total_tokens=19,
+ prompt_unit_price=Decimal(0.0001),
+ completion_unit_price=Decimal(0.0002),
+ prompt_price_unit=Decimal(1),
+ prompt_price=Decimal(0.0001),
+ completion_price_unit=Decimal(1),
+ completion_price=Decimal(0.0002),
+ total_price=Decimal(0.0003),
+ currency="USD",
+ latency=0.001,
+ ),
+ ),
+ )
+
+ def invoke_llm(
+ self: PluginModelManager,
+ *,
+ tenant_id: str,
+ user_id: str,
+ plugin_id: str,
+ provider: str,
+ model: str,
+ credentials: dict,
+ prompt_messages: list[PromptMessage],
+ model_parameters: Optional[dict] = None,
+ tools: Optional[list[PromptMessageTool]] = None,
+ stop: Optional[list[str]] = None,
+ stream: bool = True,
+ ):
+ return MockModelClass.mocked_chat_create_stream(model=model, prompt_messages=prompt_messages, tools=tools)
diff --git a/api/tests/integration_tests/model_runtime/__mock/xinference.py b/api/tests/integration_tests/model_runtime/__mock/xinference.py
deleted file mode 100644
index e2abaa52b939a6..00000000000000
--- a/api/tests/integration_tests/model_runtime/__mock/xinference.py
+++ /dev/null
@@ -1,169 +0,0 @@
-import os
-import re
-from typing import Union
-
-import pytest
-from _pytest.monkeypatch import MonkeyPatch
-from requests import Response
-from requests.sessions import Session
-from xinference_client.client.restful.restful_client import ( # type: ignore
- Client,
- RESTfulChatModelHandle,
- RESTfulEmbeddingModelHandle,
- RESTfulGenerateModelHandle,
- RESTfulRerankModelHandle,
-)
-from xinference_client.types import Embedding, EmbeddingData, EmbeddingUsage # type: ignore
-
-
-class MockXinferenceClass:
- def get_chat_model(self: Client, model_uid: str) -> Union[RESTfulGenerateModelHandle, RESTfulChatModelHandle]:
- if not re.match(r"https?:\/\/[^\s\/$.?#].[^\s]*$", self.base_url):
- raise RuntimeError("404 Not Found")
-
- if model_uid == "generate":
- return RESTfulGenerateModelHandle(model_uid, base_url=self.base_url, auth_headers={})
- if model_uid == "chat":
- return RESTfulChatModelHandle(model_uid, base_url=self.base_url, auth_headers={})
- if model_uid == "embedding":
- return RESTfulEmbeddingModelHandle(model_uid, base_url=self.base_url, auth_headers={})
- if model_uid == "rerank":
- return RESTfulRerankModelHandle(model_uid, base_url=self.base_url, auth_headers={})
- raise RuntimeError("404 Not Found")
-
- def get(self: Session, url: str, **kwargs):
- response = Response()
- if "v1/models/" in url:
- # get model uid
- model_uid = url.split("/")[-1] or ""
- if not re.match(
- r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", model_uid
- ) and model_uid not in {"generate", "chat", "embedding", "rerank"}:
- response.status_code = 404
- response._content = b"{}"
- return response
-
- # check if url is valid
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", url):
- response.status_code = 404
- response._content = b"{}"
- return response
-
- if model_uid in {"generate", "chat"}:
- response.status_code = 200
- response._content = b"""{
- "model_type": "LLM",
- "address": "127.0.0.1:43877",
- "accelerators": [
- "0",
- "1"
- ],
- "model_name": "chatglm3-6b",
- "model_lang": [
- "en"
- ],
- "model_ability": [
- "generate",
- "chat"
- ],
- "model_description": "latest chatglm3",
- "model_format": "pytorch",
- "model_size_in_billions": 7,
- "quantization": "none",
- "model_hub": "huggingface",
- "revision": null,
- "context_length": 2048,
- "replica": 1
- }"""
- return response
-
- elif model_uid == "embedding":
- response.status_code = 200
- response._content = b"""{
- "model_type": "embedding",
- "address": "127.0.0.1:43877",
- "accelerators": [
- "0",
- "1"
- ],
- "model_name": "bge",
- "model_lang": [
- "en"
- ],
- "revision": null,
- "max_tokens": 512
- }"""
- return response
-
- elif "v1/cluster/auth" in url:
- response.status_code = 200
- response._content = b"""{
- "auth": true
- }"""
- return response
-
- def _check_cluster_authenticated(self):
- self._cluster_authed = True
-
- def rerank(
- self: RESTfulRerankModelHandle, documents: list[str], query: str, top_n: int, return_documents: bool
- ) -> dict:
- # check if self._model_uid is a valid uuid
- if (
- not re.match(r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", self._model_uid)
- and self._model_uid != "rerank"
- ):
- raise RuntimeError("404 Not Found")
-
- if not re.match(r"^(https?):\/\/[^\s\/$.?#].[^\s]*$", self._base_url):
- raise RuntimeError("404 Not Found")
-
- if top_n is None:
- top_n = 1
-
- return {
- "results": [
- {"index": i, "document": doc, "relevance_score": 0.9} for i, doc in enumerate(documents[:top_n])
- ]
- }
-
- def create_embedding(self: RESTfulGenerateModelHandle, input: Union[str, list[str]], **kwargs) -> dict:
- # check if self._model_uid is a valid uuid
- if (
- not re.match(r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", self._model_uid)
- and self._model_uid != "embedding"
- ):
- raise RuntimeError("404 Not Found")
-
- if isinstance(input, str):
- input = [input]
- ipt_len = len(input)
-
- embedding = Embedding(
- object="list",
- model=self._model_uid,
- data=[
- EmbeddingData(index=i, object="embedding", embedding=[1919.810 for _ in range(768)])
- for i in range(ipt_len)
- ],
- usage=EmbeddingUsage(prompt_tokens=ipt_len, total_tokens=ipt_len),
- )
-
- return embedding
-
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_xinference_mock(request, monkeypatch: MonkeyPatch):
- if MOCK:
- monkeypatch.setattr(Client, "get_model", MockXinferenceClass.get_chat_model)
- monkeypatch.setattr(Client, "_check_cluster_authenticated", MockXinferenceClass._check_cluster_authenticated)
- monkeypatch.setattr(Session, "get", MockXinferenceClass.get)
- monkeypatch.setattr(RESTfulEmbeddingModelHandle, "create_embedding", MockXinferenceClass.create_embedding)
- monkeypatch.setattr(RESTfulRerankModelHandle, "rerank", MockXinferenceClass.rerank)
- yield
-
- if MOCK:
- monkeypatch.undo()
diff --git a/api/tests/integration_tests/model_runtime/anthropic/__init__.py b/api/tests/integration_tests/model_runtime/anthropic/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/anthropic/test_llm.py b/api/tests/integration_tests/model_runtime/anthropic/test_llm.py
deleted file mode 100644
index 8f7e9ec48743bf..00000000000000
--- a/api/tests/integration_tests/model_runtime/anthropic/test_llm.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.anthropic.llm.llm import AnthropicLargeLanguageModel
-from tests.integration_tests.model_runtime.__mock.anthropic import setup_anthropic_mock
-
-
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_anthropic_mock):
- model = AnthropicLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="claude-instant-1.2", credentials={"anthropic_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="claude-instant-1.2", credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_anthropic_mock):
- model = AnthropicLargeLanguageModel()
-
- response = model.invoke(
- model="claude-instant-1.2",
- credentials={
- "anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY"),
- "anthropic_api_url": os.environ.get("ANTHROPIC_API_URL"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "top_p": 1.0, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_invoke_stream_model(setup_anthropic_mock):
- model = AnthropicLargeLanguageModel()
-
- response = model.invoke(
- model="claude-instant-1.2",
- credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = AnthropicLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="claude-instant-1.2",
- credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 18
diff --git a/api/tests/integration_tests/model_runtime/anthropic/test_provider.py b/api/tests/integration_tests/model_runtime/anthropic/test_provider.py
deleted file mode 100644
index 6f1e50f431849f..00000000000000
--- a/api/tests/integration_tests/model_runtime/anthropic/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.anthropic.anthropic import AnthropicProvider
-from tests.integration_tests.model_runtime.__mock.anthropic import setup_anthropic_mock
-
-
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_validate_provider_credentials(setup_anthropic_mock):
- provider = AnthropicProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/assets/audio.mp3 b/api/tests/integration_tests/model_runtime/assets/audio.mp3
deleted file mode 100644
index 7c86e02e160909..00000000000000
Binary files a/api/tests/integration_tests/model_runtime/assets/audio.mp3 and /dev/null differ
diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/__init__.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py
deleted file mode 100644
index b995077984e910..00000000000000
--- a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.azure_ai_studio.llm.llm import AzureAIStudioLargeLanguageModel
-
-
-@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True)
-def test_validate_credentials(setup_azure_ai_studio_mock):
- model = AzureAIStudioLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="gpt-35-turbo",
- credentials={"api_key": "invalid_key", "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")},
- )
-
- model.validate_credentials(
- model="gpt-35-turbo",
- credentials={
- "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"),
- "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"),
- },
- )
-
-
-@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True)
-def test_invoke_model(setup_azure_ai_studio_mock):
- model = AzureAIStudioLargeLanguageModel()
-
- result = model.invoke(
- model="gpt-35-turbo",
- credentials={
- "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"),
- "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True)
-def test_invoke_stream_model(setup_azure_ai_studio_mock):
- model = AzureAIStudioLargeLanguageModel()
-
- result = model.invoke(
- model="gpt-35-turbo",
- credentials={
- "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"),
- "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = AzureAIStudioLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="gpt-35-turbo",
- credentials={
- "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"),
- "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py
deleted file mode 100644
index 8afe38b09b9f02..00000000000000
--- a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.azure_ai_studio.azure_ai_studio import AzureAIStudioProvider
-
-
-def test_validate_provider_credentials():
- provider = AzureAIStudioProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={"api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")}
- )
diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py
deleted file mode 100644
index 4d72327c0ec43c..00000000000000
--- a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.azure_ai_studio.rerank.rerank import AzureRerankModel
-
-
-def test_validate_credentials():
- model = AzureRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="azure-ai-studio-rerank-v1",
- credentials={"api_key": "invalid_key", "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")},
- )
-
-
-def test_invoke_model():
- model = AzureRerankModel()
-
- result = model.invoke(
- model="azure-ai-studio-rerank-v1",
- credentials={
- "api_key": os.getenv("AZURE_AI_STUDIO_JWT_TOKEN"),
- "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"),
- },
- query="What is the capital of the United States?",
- docs=[
- "Carson City is the capital city of the American state of Nevada. At the 2010 United States "
- "Census, Carson City had a population of 55,274.",
- "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
- "are a political division controlled by the United States. Its capital is Saipan.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 1
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/azure_openai/__init__.py b/api/tests/integration_tests/model_runtime/azure_openai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/azure_openai/test_llm.py b/api/tests/integration_tests/model_runtime/azure_openai/test_llm.py
deleted file mode 100644
index 216c50a1823c8d..00000000000000
--- a/api/tests/integration_tests/model_runtime/azure_openai/test_llm.py
+++ /dev/null
@@ -1,292 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- ImagePromptMessageContent,
- PromptMessageTool,
- SystemPromptMessage,
- TextPromptMessageContent,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.azure_openai.llm.llm import AzureOpenAILargeLanguageModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="gpt35",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": "invalid_key",
- "base_model_name": "gpt-35-turbo",
- },
- )
-
- model.validate_credentials(
- model="gpt35",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo",
- },
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_validate_credentials_for_completion_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="gpt-35-turbo-instruct",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": "invalid_key",
- "base_model_name": "gpt-35-turbo-instruct",
- },
- )
-
- model.validate_credentials(
- model="gpt-35-turbo-instruct",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo-instruct",
- },
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_invoke_completion_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-35-turbo-instruct",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo-instruct",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 1},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_invoke_stream_completion_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-35-turbo-instruct",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo-instruct",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt35",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt35",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_vision(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-4v",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-4-vision-preview",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(
- data="Hello World!",
- ),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_tools(setup_openai_mock):
- model = AzureOpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-35-turbo",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "gpt-35-turbo",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
-
-
-def test_get_num_tokens():
- model = AzureOpenAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="gpt-35-turbo-instruct",
- credentials={"base_model_name": "gpt-35-turbo-instruct"},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 3
-
- num_tokens = model.get_num_tokens(
- model="gpt35",
- credentials={"base_model_name": "gpt-35-turbo"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/azure_openai/test_text_embedding.py b/api/tests/integration_tests/model_runtime/azure_openai/test_text_embedding.py
deleted file mode 100644
index a1ae2b2e5b740c..00000000000000
--- a/api/tests/integration_tests/model_runtime/azure_openai/test_text_embedding.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.azure_openai.text_embedding.text_embedding import AzureOpenAITextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = AzureOpenAITextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="embedding",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": "invalid_key",
- "base_model_name": "text-embedding-ada-002",
- },
- )
-
- model.validate_credentials(
- model="embedding",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "text-embedding-ada-002",
- },
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = AzureOpenAITextEmbeddingModel()
-
- result = model.invoke(
- model="embedding",
- credentials={
- "openai_api_base": os.environ.get("AZURE_OPENAI_API_BASE"),
- "openai_api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
- "base_model_name": "text-embedding-ada-002",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = AzureOpenAITextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="embedding", credentials={"base_model_name": "text-embedding-ada-002"}, texts=["hello", "world"]
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/baichuan/__init__.py b/api/tests/integration_tests/model_runtime/baichuan/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/baichuan/test_llm.py b/api/tests/integration_tests/model_runtime/baichuan/test_llm.py
deleted file mode 100644
index fe7fe968911439..00000000000000
--- a/api/tests/integration_tests/model_runtime/baichuan/test_llm.py
+++ /dev/null
@@ -1,172 +0,0 @@
-import os
-from collections.abc import Generator
-from time import sleep
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.baichuan.llm.llm import BaichuanLanguageModel
-
-
-def test_predefined_models():
- model = BaichuanLanguageModel()
- model_schemas = model.predefined_models()
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-def test_validate_credentials_for_chat_model():
- sleep(3)
- model = BaichuanLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="baichuan2-turbo", credentials={"api_key": "invalid_key", "secret_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- )
-
-
-def test_invoke_model():
- sleep(3)
- model = BaichuanLanguageModel()
-
- response = model.invoke(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_model_with_system_message():
- sleep(3)
- model = BaichuanLanguageModel()
-
- response = model.invoke(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(content="请记住你是Kasumi。"),
- UserPromptMessage(content="现在告诉我你是谁?"),
- ],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_model():
- sleep(3)
- model = BaichuanLanguageModel()
-
- response = model.invoke(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_with_search():
- sleep(3)
- model = BaichuanLanguageModel()
-
- response = model.invoke(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="北京今天的天气怎么样")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- "with_search_enhance": True,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- total_message = ""
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if not chunk.delta.finish_reason else True
- total_message += chunk.delta.message.content
-
- assert "不" not in total_message
-
-
-def test_get_num_tokens():
- sleep(3)
- model = BaichuanLanguageModel()
-
- response = model.get_num_tokens(
- model="baichuan2-turbo",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- "secret_key": os.environ.get("BAICHUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- tools=[],
- )
-
- assert isinstance(response, int)
- assert response == 9
diff --git a/api/tests/integration_tests/model_runtime/baichuan/test_provider.py b/api/tests/integration_tests/model_runtime/baichuan/test_provider.py
deleted file mode 100644
index 4036edfb7a7062..00000000000000
--- a/api/tests/integration_tests/model_runtime/baichuan/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.baichuan.baichuan import BaichuanProvider
-
-
-def test_validate_provider_credentials():
- provider = BaichuanProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "hahahaha"})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("BAICHUAN_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/baichuan/test_text_embedding.py b/api/tests/integration_tests/model_runtime/baichuan/test_text_embedding.py
deleted file mode 100644
index cbc63f3978fb99..00000000000000
--- a/api/tests/integration_tests/model_runtime/baichuan/test_text_embedding.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.baichuan.text_embedding.text_embedding import BaichuanTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = BaichuanTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="baichuan-text-embedding", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(
- model="baichuan-text-embedding", credentials={"api_key": os.environ.get("BAICHUAN_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = BaichuanTextEmbeddingModel()
-
- result = model.invoke(
- model="baichuan-text-embedding",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 6
-
-
-def test_get_num_tokens():
- model = BaichuanTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="baichuan-text-embedding",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
-
-
-def test_max_chunks():
- model = BaichuanTextEmbeddingModel()
-
- result = model.invoke(
- model="baichuan-text-embedding",
- credentials={
- "api_key": os.environ.get("BAICHUAN_API_KEY"),
- },
- texts=[
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- ],
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 22
diff --git a/api/tests/integration_tests/model_runtime/bedrock/__init__.py b/api/tests/integration_tests/model_runtime/bedrock/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/bedrock/test_llm.py b/api/tests/integration_tests/model_runtime/bedrock/test_llm.py
deleted file mode 100644
index c19ec35a6e45fc..00000000000000
--- a/api/tests/integration_tests/model_runtime/bedrock/test_llm.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.bedrock.llm.llm import BedrockLargeLanguageModel
-
-
-def test_validate_credentials():
- model = BedrockLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="meta.llama2-13b-chat-v1", credentials={"anthropic_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="meta.llama2-13b-chat-v1",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = BedrockLargeLanguageModel()
-
- response = model.invoke(
- model="meta.llama2-13b-chat-v1",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "top_p": 1.0, "max_tokens_to_sample": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = BedrockLargeLanguageModel()
-
- response = model.invoke(
- model="meta.llama2-13b-chat-v1",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens_to_sample": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- print(chunk)
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = BedrockLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="meta.llama2-13b-chat-v1",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 18
diff --git a/api/tests/integration_tests/model_runtime/bedrock/test_provider.py b/api/tests/integration_tests/model_runtime/bedrock/test_provider.py
deleted file mode 100644
index 080727829e9e2f..00000000000000
--- a/api/tests/integration_tests/model_runtime/bedrock/test_provider.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.bedrock.bedrock import BedrockProvider
-
-
-def test_validate_provider_credentials():
- provider = BedrockProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/chatglm/__init__.py b/api/tests/integration_tests/model_runtime/chatglm/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/chatglm/test_llm.py b/api/tests/integration_tests/model_runtime/chatglm/test_llm.py
deleted file mode 100644
index a7c5229e051418..00000000000000
--- a/api/tests/integration_tests/model_runtime/chatglm/test_llm.py
+++ /dev/null
@@ -1,229 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.chatglm.llm.llm import ChatGLMLargeLanguageModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def test_predefined_models():
- model = ChatGLMLargeLanguageModel()
- model_schemas = model.predefined_models()
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = ChatGLMLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="chatglm2-6b", credentials={"api_base": "invalid_key"})
-
- model.validate_credentials(model="chatglm2-6b", credentials={"api_base": os.environ.get("CHATGLM_API_BASE")})
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = ChatGLMLargeLanguageModel()
-
- response = model.invoke(
- model="chatglm2-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_model(setup_openai_mock):
- model = ChatGLMLargeLanguageModel()
-
- response = model.invoke(
- model="chatglm2-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_model_with_functions(setup_openai_mock):
- model = ChatGLMLargeLanguageModel()
-
- response = model.invoke(
- model="chatglm3-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="你是一个天气机器人,你不知道今天的天气怎么样,你需要通过调用一个函数来获取天气信息。"
- ),
- UserPromptMessage(content="波士顿天气如何?"),
- ],
- model_parameters={
- "temperature": 0,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=True,
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(response, Generator)
-
- call: LLMResultChunk = None
- chunks = []
-
- for chunk in response:
- chunks.append(chunk)
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
- if chunk.delta.message.tool_calls and len(chunk.delta.message.tool_calls) > 0:
- call = chunk
- break
-
- assert call is not None
- assert call.delta.message.tool_calls[0].function.name == "get_current_weather"
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_model_with_functions(setup_openai_mock):
- model = ChatGLMLargeLanguageModel()
-
- response = model.invoke(
- model="chatglm3-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[UserPromptMessage(content="What is the weather like in San Francisco?")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
- assert response.message.tool_calls[0].function.name == "get_current_weather"
-
-
-def test_get_num_tokens():
- model = ChatGLMLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="chatglm2-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 77
-
- num_tokens = model.get_num_tokens(
- model="chatglm2-6b",
- credentials={"api_base": os.environ.get("CHATGLM_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/chatglm/test_provider.py b/api/tests/integration_tests/model_runtime/chatglm/test_provider.py
deleted file mode 100644
index 7907805d072772..00000000000000
--- a/api/tests/integration_tests/model_runtime/chatglm/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.chatglm.chatglm import ChatGLMProvider
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_provider_credentials(setup_openai_mock):
- provider = ChatGLMProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_base": "hahahaha"})
-
- provider.validate_provider_credentials(credentials={"api_base": os.environ.get("CHATGLM_API_BASE")})
diff --git a/api/tests/integration_tests/model_runtime/cohere/__init__.py b/api/tests/integration_tests/model_runtime/cohere/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/cohere/test_llm.py b/api/tests/integration_tests/model_runtime/cohere/test_llm.py
deleted file mode 100644
index b7f707e935dbea..00000000000000
--- a/api/tests/integration_tests/model_runtime/cohere/test_llm.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.cohere.llm.llm import CohereLargeLanguageModel
-
-
-def test_validate_credentials_for_chat_model():
- model = CohereLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="command-light-chat", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="command-light-chat", credentials={"api_key": os.environ.get("COHERE_API_KEY")})
-
-
-def test_validate_credentials_for_completion_model():
- model = CohereLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="command-light", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="command-light", credentials={"api_key": os.environ.get("COHERE_API_KEY")})
-
-
-def test_invoke_completion_model():
- model = CohereLargeLanguageModel()
-
- credentials = {"api_key": os.environ.get("COHERE_API_KEY")}
-
- result = model.invoke(
- model="command-light",
- credentials=credentials,
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 1},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
- assert model._num_tokens_from_string("command-light", credentials, result.message.content) == 1
-
-
-def test_invoke_stream_completion_model():
- model = CohereLargeLanguageModel()
-
- result = model.invoke(
- model="command-light",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_chat_model():
- model = CohereLargeLanguageModel()
-
- result = model.invoke(
- model="command-light-chat",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "p": 0.99,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-def test_invoke_stream_chat_model():
- model = CohereLargeLanguageModel()
-
- result = model.invoke(
- model="command-light-chat",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = CohereLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="command-light",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 3
-
- num_tokens = model.get_num_tokens(
- model="command-light-chat",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 15
-
-
-def test_fine_tuned_model():
- model = CohereLargeLanguageModel()
-
- # test invoke
- result = model.invoke(
- model="85ec47be-6139-4f75-a4be-0f0ec1ef115c-ft",
- credentials={"api_key": os.environ.get("COHERE_API_KEY"), "mode": "completion"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
-
-
-def test_fine_tuned_chat_model():
- model = CohereLargeLanguageModel()
-
- # test invoke
- result = model.invoke(
- model="94f2d55a-4c79-4c00-bde4-23962e74b170-ft",
- credentials={"api_key": os.environ.get("COHERE_API_KEY"), "mode": "chat"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
diff --git a/api/tests/integration_tests/model_runtime/cohere/test_provider.py b/api/tests/integration_tests/model_runtime/cohere/test_provider.py
deleted file mode 100644
index fb7e6d34984a61..00000000000000
--- a/api/tests/integration_tests/model_runtime/cohere/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.cohere.cohere import CohereProvider
-
-
-def test_validate_provider_credentials():
- provider = CohereProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("COHERE_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/cohere/test_rerank.py b/api/tests/integration_tests/model_runtime/cohere/test_rerank.py
deleted file mode 100644
index a1b6922128570e..00000000000000
--- a/api/tests/integration_tests/model_runtime/cohere/test_rerank.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.cohere.rerank.rerank import CohereRerankModel
-
-
-def test_validate_credentials():
- model = CohereRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="rerank-english-v2.0", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="rerank-english-v2.0", credentials={"api_key": os.environ.get("COHERE_API_KEY")})
-
-
-def test_invoke_model():
- model = CohereRerankModel()
-
- result = model.invoke(
- model="rerank-english-v2.0",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- query="What is the capital of the United States?",
- docs=[
- "Carson City is the capital city of the American state of Nevada. At the 2010 United States "
- "Census, Carson City had a population of 55,274.",
- "Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) "
- "is the capital of the United States. It is a federal district. The President of the USA and many major "
- "national government offices are in the territory. This makes it the political center of the United "
- "States of America.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 1
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/cohere/test_text_embedding.py b/api/tests/integration_tests/model_runtime/cohere/test_text_embedding.py
deleted file mode 100644
index ae26d36635d1b5..00000000000000
--- a/api/tests/integration_tests/model_runtime/cohere/test_text_embedding.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.cohere.text_embedding.text_embedding import CohereTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = CohereTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="embed-multilingual-v3.0", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(
- model="embed-multilingual-v3.0", credentials={"api_key": os.environ.get("COHERE_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = CohereTextEmbeddingModel()
-
- result = model.invoke(
- model="embed-multilingual-v3.0",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- assert result.usage.total_tokens == 811
-
-
-def test_get_num_tokens():
- model = CohereTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="embed-multilingual-v3.0",
- credentials={"api_key": os.environ.get("COHERE_API_KEY")},
- texts=["hello", "world"],
- )
-
- assert num_tokens == 3
diff --git a/api/tests/integration_tests/model_runtime/fireworks/__init__.py b/api/tests/integration_tests/model_runtime/fireworks/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/fireworks/test_llm.py b/api/tests/integration_tests/model_runtime/fireworks/test_llm.py
deleted file mode 100644
index 699ca293a2fca8..00000000000000
--- a/api/tests/integration_tests/model_runtime/fireworks/test_llm.py
+++ /dev/null
@@ -1,186 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.fireworks.llm.llm import FireworksLargeLanguageModel
-
-"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def test_predefined_models():
- model = FireworksLargeLanguageModel()
- model_schemas = model.predefined_models()
-
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = FireworksLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- # model name to gpt-3.5-turbo because of mocking
- model.validate_credentials(model="gpt-3.5-turbo", credentials={"fireworks_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model(setup_openai_mock):
- model = FireworksLargeLanguageModel()
-
- result = model.invoke(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="foo",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_tools(setup_openai_mock):
- model = FireworksLargeLanguageModel()
-
- result = model.invoke(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="foo",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock):
- model = FireworksLargeLanguageModel()
-
- result = model.invoke(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="foo",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = FireworksLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 10
-
- num_tokens = model.get_num_tokens(
- model="accounts/fireworks/models/llama-v3p1-8b-instruct",
- credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- ],
- )
-
- assert num_tokens == 77
diff --git a/api/tests/integration_tests/model_runtime/fireworks/test_provider.py b/api/tests/integration_tests/model_runtime/fireworks/test_provider.py
deleted file mode 100644
index a68cf1a1a8fbda..00000000000000
--- a/api/tests/integration_tests/model_runtime/fireworks/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.fireworks.fireworks import FireworksProvider
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_provider_credentials(setup_openai_mock):
- provider = FireworksProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/fireworks/test_text_embedding.py b/api/tests/integration_tests/model_runtime/fireworks/test_text_embedding.py
deleted file mode 100644
index 7bf723b3a93742..00000000000000
--- a/api/tests/integration_tests/model_runtime/fireworks/test_text_embedding.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.fireworks.text_embedding.text_embedding import FireworksTextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = FireworksTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="nomic-ai/nomic-embed-text-v1.5", credentials={"fireworks_api_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="nomic-ai/nomic-embed-text-v1.5", credentials={"fireworks_api_key": os.environ.get("FIREWORKS_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = FireworksTextEmbeddingModel()
-
- result = model.invoke(
- model="nomic-ai/nomic-embed-text-v1.5",
- credentials={
- "fireworks_api_key": os.environ.get("FIREWORKS_API_KEY"),
- },
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="foo",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = FireworksTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="nomic-ai/nomic-embed-text-v1.5",
- credentials={
- "fireworks_api_key": os.environ.get("FIREWORKS_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/fishaudio/__init__.py b/api/tests/integration_tests/model_runtime/fishaudio/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/fishaudio/test_provider.py b/api/tests/integration_tests/model_runtime/fishaudio/test_provider.py
deleted file mode 100644
index 3526574b613238..00000000000000
--- a/api/tests/integration_tests/model_runtime/fishaudio/test_provider.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import os
-
-import httpx
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.fishaudio.fishaudio import FishAudioProvider
-from tests.integration_tests.model_runtime.__mock.fishaudio import setup_fishaudio_mock
-
-
-@pytest.mark.parametrize("setup_fishaudio_mock", [["list-models"]], indirect=True)
-def test_validate_provider_credentials(setup_fishaudio_mock):
- print("-----", httpx.get)
- provider = FishAudioProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(
- credentials={
- "api_key": "bad_api_key",
- "api_base": os.environ.get("FISH_AUDIO_API_BASE", "https://api.fish.audio"),
- "use_public_models": "false",
- "latency": "normal",
- }
- )
-
- provider.validate_provider_credentials(
- credentials={
- "api_key": os.environ.get("FISH_AUDIO_API_KEY", "test"),
- "api_base": os.environ.get("FISH_AUDIO_API_BASE", "https://api.fish.audio"),
- "use_public_models": "false",
- "latency": "normal",
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/fishaudio/test_tts.py b/api/tests/integration_tests/model_runtime/fishaudio/test_tts.py
deleted file mode 100644
index f61fee28b98e30..00000000000000
--- a/api/tests/integration_tests/model_runtime/fishaudio/test_tts.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.model_providers.fishaudio.tts.tts import (
- FishAudioText2SpeechModel,
-)
-from tests.integration_tests.model_runtime.__mock.fishaudio import setup_fishaudio_mock
-
-
-@pytest.mark.parametrize("setup_fishaudio_mock", [["tts"]], indirect=True)
-def test_invoke_model(setup_fishaudio_mock):
- model = FishAudioText2SpeechModel()
-
- result = model.invoke(
- model="tts-default",
- tenant_id="test",
- credentials={
- "api_key": os.environ.get("FISH_AUDIO_API_KEY", "test"),
- "api_base": os.environ.get("FISH_AUDIO_API_BASE", "https://api.fish.audio"),
- "use_public_models": "false",
- "latency": "normal",
- },
- content_text="Hello, world!",
- voice="03397b4c4be74759b72533b663fbd001",
- )
-
- content = b""
- for chunk in result:
- content += chunk
-
- assert content != b""
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/__init__.py b/api/tests/integration_tests/model_runtime/gitee_ai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_llm.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_llm.py
deleted file mode 100644
index 753c52ce31d452..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_llm.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gitee_ai.llm.llm import GiteeAILargeLanguageModel
-
-
-def test_predefined_models():
- model = GiteeAILargeLanguageModel()
- model_schemas = model.predefined_models()
-
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-def test_validate_credentials_for_chat_model():
- model = GiteeAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- # model name to gpt-3.5-turbo because of mocking
- model.validate_credentials(model="gpt-3.5-turbo", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(
- model="Qwen2-7B-Instruct",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- )
-
-
-def test_invoke_chat_model():
- model = GiteeAILargeLanguageModel()
-
- result = model.invoke(
- model="Qwen2-7B-Instruct",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- "stream": False,
- },
- stop=["How"],
- stream=False,
- user="foo",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-def test_invoke_stream_chat_model():
- model = GiteeAILargeLanguageModel()
-
- result = model.invoke(
- model="Qwen2-7B-Instruct",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100, "stream": False},
- stream=True,
- user="foo",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
-
-
-def test_get_num_tokens():
- model = GiteeAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="Qwen2-7B-Instruct",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 10
-
- num_tokens = model.get_num_tokens(
- model="Qwen2-7B-Instruct",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- ],
- )
-
- assert num_tokens == 77
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_provider.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_provider.py
deleted file mode 100644
index f12ed54a457896..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gitee_ai.gitee_ai import GiteeAIProvider
-
-
-def test_validate_provider_credentials():
- provider = GiteeAIProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "invalid_key"})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_rerank.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_rerank.py
deleted file mode 100644
index 0e5914a61f7f43..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_rerank.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gitee_ai.rerank.rerank import GiteeAIRerankModel
-
-
-def test_validate_credentials():
- model = GiteeAIRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-reranker-v2-m3",
- credentials={"api_key": "invalid_key"},
- )
-
- model.validate_credentials(
- model="bge-reranker-v2-m3",
- credentials={
- "api_key": os.environ.get("GITEE_AI_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = GiteeAIRerankModel()
- result = model.invoke(
- model="bge-reranker-v2-m3",
- credentials={
- "api_key": os.environ.get("GITEE_AI_API_KEY"),
- },
- query="What is the capital of the United States?",
- docs=[
- "Carson City is the capital city of the American state of Nevada. At the 2010 United States "
- "Census, Carson City had a population of 55,274.",
- "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
- "are a political division controlled by the United States. Its capital is Saipan.",
- ],
- top_n=1,
- score_threshold=0.01,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].score >= 0.01
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_speech2text.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_speech2text.py
deleted file mode 100644
index 4a01453fdd1cda..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_speech2text.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gitee_ai.speech2text.speech2text import GiteeAISpeech2TextModel
-
-
-def test_validate_credentials():
- model = GiteeAISpeech2TextModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="whisper-base",
- credentials={"api_key": "invalid_key"},
- )
-
- model.validate_credentials(
- model="whisper-base",
- credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")},
- )
-
-
-def test_invoke_model():
- model = GiteeAISpeech2TextModel()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- # Open the file and get the file object
- with open(audio_file_path, "rb") as audio_file:
- file = audio_file
-
- result = model.invoke(
- model="whisper-base", credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")}, file=file
- )
-
- assert isinstance(result, str)
- assert result == "1 2 3 4 5 6 7 8 9 10"
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_text_embedding.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_text_embedding.py
deleted file mode 100644
index 34648f0bc8ae78..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_text_embedding.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gitee_ai.text_embedding.text_embedding import GiteeAIEmbeddingModel
-
-
-def test_validate_credentials():
- model = GiteeAIEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="bge-large-zh-v1.5", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="bge-large-zh-v1.5", credentials={"api_key": os.environ.get("GITEE_AI_API_KEY")})
-
-
-def test_invoke_model():
- model = GiteeAIEmbeddingModel()
-
- result = model.invoke(
- model="bge-large-zh-v1.5",
- credentials={
- "api_key": os.environ.get("GITEE_AI_API_KEY"),
- },
- texts=["hello", "world"],
- user="user",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
-
-
-def test_get_num_tokens():
- model = GiteeAIEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="bge-large-zh-v1.5",
- credentials={
- "api_key": os.environ.get("GITEE_AI_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/gitee_ai/test_tts.py b/api/tests/integration_tests/model_runtime/gitee_ai/test_tts.py
deleted file mode 100644
index 9f18161a7bb74e..00000000000000
--- a/api/tests/integration_tests/model_runtime/gitee_ai/test_tts.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import os
-
-from core.model_runtime.model_providers.gitee_ai.tts.tts import GiteeAIText2SpeechModel
-
-
-def test_invoke_model():
- model = GiteeAIText2SpeechModel()
-
- result = model.invoke(
- model="speecht5_tts",
- tenant_id="test",
- credentials={
- "api_key": os.environ.get("GITEE_AI_API_KEY"),
- },
- content_text="Hello, world!",
- voice="",
- )
-
- content = b""
- for chunk in result:
- content += chunk
-
- assert content != b""
diff --git a/api/tests/integration_tests/model_runtime/google/__init__.py b/api/tests/integration_tests/model_runtime/google/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/google/test_llm.py b/api/tests/integration_tests/model_runtime/google/test_llm.py
deleted file mode 100644
index 65357be6586143..00000000000000
--- a/api/tests/integration_tests/model_runtime/google/test_llm.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- ImagePromptMessageContent,
- SystemPromptMessage,
- TextPromptMessageContent,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.google.llm.llm import GoogleLargeLanguageModel
-from tests.integration_tests.model_runtime.__mock.google import setup_google_mock, setup_mock_redis
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_google_mock):
- model = GoogleLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="gemini-pro", credentials={"google_api_key": "invalid_key"})
-
- model.validate_credentials(model="gemini-pro", credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")})
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_google_mock):
- model = GoogleLargeLanguageModel()
-
- response = model.invoke(
- model="gemini-1.5-pro",
- credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Give me your worst dad joke or i will unplug you"),
- AssistantPromptMessage(
- content="Why did the scarecrow win an award? Because he was outstanding in his field!"
- ),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data="ok something snarkier pls"),
- TextPromptMessageContent(data="i may still unplug you"),
- ]
- ),
- ],
- model_parameters={"temperature": 0.5, "top_p": 1.0, "max_output_tokens": 2048},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_invoke_stream_model(setup_google_mock):
- model = GoogleLargeLanguageModel()
-
- response = model.invoke(
- model="gemini-1.5-pro",
- credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Give me your worst dad joke or i will unplug you"),
- AssistantPromptMessage(
- content="Why did the scarecrow win an award? Because he was outstanding in his field!"
- ),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data="ok something snarkier pls"),
- TextPromptMessageContent(data="i may still unplug you"),
- ]
- ),
- ],
- model_parameters={"temperature": 0.2, "top_k": 5, "max_tokens": 2048},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_invoke_chat_model_with_vision(setup_google_mock, setup_mock_redis):
- model = GoogleLargeLanguageModel()
-
- result = model.invoke(
- model="gemini-1.5-pro",
- credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data="what do you see?"),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- ),
- ],
- model_parameters={"temperature": 0.3, "top_p": 0.2, "top_k": 3, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_invoke_chat_model_with_vision_multi_pics(setup_google_mock, setup_mock_redis):
- model = GoogleLargeLanguageModel()
-
- result = model.invoke(
- model="gemini-1.5-pro",
- credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(content="You are a helpful AI assistant."),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data="what do you see?"),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- ),
- AssistantPromptMessage(content="I see a blue letter 'D' with a gradient from light blue to dark blue."),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data="what about now?"),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAABAAAAAQBPJcTWAAADl0lEQVR4nC3Uf0zUdRjA8S9W6w//bGs1DUd5RT+gIY0oYeEqY0QCy5EbAnF4IEgyAnGuCBANWOjih6YOlK0BbtLAX+iAENFgUBLMkzs8uDuO+wEcxx3cgdx9v3fvvn/0x+v5PM+z56/n2T6CIAgIQUEECVsICnqOoC0v8PyLW3n5lW28GhLG9hAFwYowdoRsJ+Tzv3hdEcpOxVvsfDscheI1BIXKy5t7OwiPiCI8IZaIL+OISPKxK/IDdiU6ifwqjqj4WKISP5VN8mHSFNHJA7KnfJQYh7A7+g1i9hXw2dcX2JuSxhcJnxCfnEJ8ygESqtfYl3qA5O/1pKaX8E2Rn7R0JWnKXFkRaX0OhIOqUtJVRWQoj5ChyiOjb4XMQ0fIVB0lM6eEzMO5ZN5x8W1xD1nZh1Fm55OtzOdQTgEqZR6CSi5UjSI5hTnk3bWSX/gj+ccaKCgspaDkNIWlpygc3OTYtZc4fqKcE5Vn+eFkDWUp8ZS1ryOUn66lvGmCyt/8nLwxTlXZcapqL1Nd10B1Uy01FbnUnFVS+2sLvzTWUXfRRMOAgcb6KhovdSA0XnHRdL6Zcy1/0lyTS3NfgJbWNq6cu0nrPyu0FSlpu9pF21037ZFhXLtYT+eNIbp61+jq70bofv8drvf0c2vQz+3O3+nRrNI78JD+/psMfLefe0MG7p+a5v6tP3g48ojhC7mMXP2Y0YoZRitnEcbkMPaglzEnPAoNZrw4hXH1LBOtOiYfa3gcugO1+gnqZwGeaHRMTcyhaduKRjOBxiJfQSsnWq0W7YwVrd3PtH6BaeMST40adJ3V6OwBZlR7mNUvMWswYsiKxTA1gWHOgsGiRzCmRGOcW8QoD855JObWJUxmHSb5nfd4Mc+ZMFv1MjtmuWepSMNiMmAxz2LN2o1gbdmDdV6NdVnE1p6EzajHZp7BtjCLbSnAgsMtE1k8H8OiwyuTWPL4sLduwz5vRLA7XCzbLCw7PTiswzgWJnBsijhNwzhtw6xmRLLmdLC27sU9dBC324un/iieSyF4rPIS1/8eZOOego0NL898Epv14Wz2nMHrsOB12/Glh+Mrfg/fqgufKCHmxSC21SE6JxFdKwjihhFxw4O4aUf0bSKVRyN1pyKNXEcaDUbS3EZan5Sp/zeFtLGO5LUiSRKCJAXwZ0bg73oXv+kBfrsOv8uOXxIJ/JRG4N/9sjME1B3QXAjzd8CqhqWfkT8C4T8Z5+ciRtwo8gAAAABJRU5ErkJggg==",
- ),
- ]
- ),
- ],
- model_parameters={"temperature": 0.3, "top_p": 0.2, "top_k": 3, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- print(f"result: {result.message.content}")
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-def test_get_num_tokens():
- model = GoogleLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="gemini-1.5-pro",
- credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens > 0 # The exact number of tokens may vary based on the model's tokenization
diff --git a/api/tests/integration_tests/model_runtime/google/test_provider.py b/api/tests/integration_tests/model_runtime/google/test_provider.py
deleted file mode 100644
index c217e4fe058870..00000000000000
--- a/api/tests/integration_tests/model_runtime/google/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.google.google import GoogleProvider
-from tests.integration_tests.model_runtime.__mock.google import setup_google_mock
-
-
-@pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True)
-def test_validate_provider_credentials(setup_google_mock):
- provider = GoogleProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"google_api_key": os.environ.get("GOOGLE_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/gpustack/__init__.py b/api/tests/integration_tests/model_runtime/gpustack/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/gpustack/test_embedding.py b/api/tests/integration_tests/model_runtime/gpustack/test_embedding.py
deleted file mode 100644
index f56ad0dadcbe20..00000000000000
--- a/api/tests/integration_tests/model_runtime/gpustack/test_embedding.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gpustack.text_embedding.text_embedding import (
- GPUStackTextEmbeddingModel,
-)
-
-
-def test_validate_credentials():
- model = GPUStackTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-m3",
- credentials={
- "endpoint_url": "invalid_url",
- "api_key": "invalid_api_key",
- },
- )
-
- model.validate_credentials(
- model="bge-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = GPUStackTextEmbeddingModel()
-
- result = model.invoke(
- model="bge-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "context_size": 8192,
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 7
diff --git a/api/tests/integration_tests/model_runtime/gpustack/test_llm.py b/api/tests/integration_tests/model_runtime/gpustack/test_llm.py
deleted file mode 100644
index 326b7b16f04dda..00000000000000
--- a/api/tests/integration_tests/model_runtime/gpustack/test_llm.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import (
- LLMResult,
- LLMResultChunk,
- LLMResultChunkDelta,
-)
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gpustack.llm.llm import GPUStackLanguageModel
-
-
-def test_validate_credentials_for_chat_model():
- model = GPUStackLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="llama-3.2-1b-instruct",
- credentials={
- "endpoint_url": "invalid_url",
- "api_key": "invalid_api_key",
- "mode": "chat",
- },
- )
-
- model.validate_credentials(
- model="llama-3.2-1b-instruct",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "chat",
- },
- )
-
-
-def test_invoke_completion_model():
- model = GPUStackLanguageModel()
-
- response = model.invoke(
- model="llama-3.2-1b-instruct",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "completion",
- },
- prompt_messages=[UserPromptMessage(content="ping")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=[],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_chat_model():
- model = GPUStackLanguageModel()
-
- response = model.invoke(
- model="llama-3.2-1b-instruct",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "chat",
- },
- prompt_messages=[UserPromptMessage(content="ping")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=[],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_chat_model():
- model = GPUStackLanguageModel()
-
- response = model.invoke(
- model="llama-3.2-1b-instruct",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "chat",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = GPUStackLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="????",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {
- "type": "string",
- "description": "The city and state e.g. San Francisco, CA",
- },
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 80
-
- num_tokens = model.get_num_tokens(
- model="????",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- "mode": "chat",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 10
diff --git a/api/tests/integration_tests/model_runtime/gpustack/test_rerank.py b/api/tests/integration_tests/model_runtime/gpustack/test_rerank.py
deleted file mode 100644
index f5c2d2d21ca825..00000000000000
--- a/api/tests/integration_tests/model_runtime/gpustack/test_rerank.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gpustack.rerank.rerank import (
- GPUStackRerankModel,
-)
-
-
-def test_validate_credentials_for_rerank_model():
- model = GPUStackRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-reranker-v2-m3",
- credentials={
- "endpoint_url": "invalid_url",
- "api_key": "invalid_api_key",
- },
- )
-
- model.validate_credentials(
- model="bge-reranker-v2-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- )
-
-
-def test_invoke_rerank_model():
- model = GPUStackRerankModel()
-
- response = model.invoke(
- model="bge-reranker-v2-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- query="Organic skincare products for sensitive skin",
- docs=[
- "Eco-friendly kitchenware for modern homes",
- "Biodegradable cleaning supplies for eco-conscious consumers",
- "Organic cotton baby clothes for sensitive skin",
- "Natural organic skincare range for sensitive skin",
- "Tech gadgets for smart homes: 2024 edition",
- "Sustainable gardening tools and compost solutions",
- "Sensitive skin-friendly facial cleansers and toners",
- "Organic food wraps and storage solutions",
- "Yoga mats made from recycled materials",
- ],
- top_n=3,
- score_threshold=-0.75,
- user="abc-123",
- )
-
- assert isinstance(response, RerankResult)
- assert len(response.docs) == 3
-
-
-def test__invoke():
- model = GPUStackRerankModel()
-
- # Test case 1: Empty docs
- result = model._invoke(
- model="bge-reranker-v2-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- query="Organic skincare products for sensitive skin",
- docs=[],
- top_n=3,
- score_threshold=0.75,
- user="abc-123",
- )
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 0
-
- # Test case 2: Expected docs
- result = model._invoke(
- model="bge-reranker-v2-m3",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- query="Organic skincare products for sensitive skin",
- docs=[
- "Eco-friendly kitchenware for modern homes",
- "Biodegradable cleaning supplies for eco-conscious consumers",
- "Organic cotton baby clothes for sensitive skin",
- "Natural organic skincare range for sensitive skin",
- "Tech gadgets for smart homes: 2024 edition",
- "Sustainable gardening tools and compost solutions",
- "Sensitive skin-friendly facial cleansers and toners",
- "Organic food wraps and storage solutions",
- "Yoga mats made from recycled materials",
- ],
- top_n=3,
- score_threshold=-0.75,
- user="abc-123",
- )
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 3
- assert all(isinstance(doc, RerankDocument) for doc in result.docs)
diff --git a/api/tests/integration_tests/model_runtime/gpustack/test_speech2text.py b/api/tests/integration_tests/model_runtime/gpustack/test_speech2text.py
deleted file mode 100644
index c215e9b73988be..00000000000000
--- a/api/tests/integration_tests/model_runtime/gpustack/test_speech2text.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import os
-from pathlib import Path
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.gpustack.speech2text.speech2text import GPUStackSpeech2TextModel
-
-
-def test_validate_credentials():
- model = GPUStackSpeech2TextModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="faster-whisper-medium",
- credentials={
- "endpoint_url": "invalid_url",
- "api_key": "invalid_api_key",
- },
- )
-
- model.validate_credentials(
- model="faster-whisper-medium",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = GPUStackSpeech2TextModel()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- file = Path(audio_file_path).read_bytes()
-
- result = model.invoke(
- model="faster-whisper-medium",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- file=file,
- )
-
- assert isinstance(result, str)
- assert result == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
diff --git a/api/tests/integration_tests/model_runtime/gpustack/test_tts.py b/api/tests/integration_tests/model_runtime/gpustack/test_tts.py
deleted file mode 100644
index 8997ad074cb1bd..00000000000000
--- a/api/tests/integration_tests/model_runtime/gpustack/test_tts.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import os
-
-from core.model_runtime.model_providers.gpustack.tts.tts import GPUStackText2SpeechModel
-
-
-def test_invoke_model():
- model = GPUStackText2SpeechModel()
-
- result = model.invoke(
- model="cosyvoice-300m-sft",
- tenant_id="test",
- credentials={
- "endpoint_url": os.environ.get("GPUSTACK_SERVER_URL"),
- "api_key": os.environ.get("GPUSTACK_API_KEY"),
- },
- content_text="Hello world",
- voice="Chinese Female",
- )
-
- content = b""
- for chunk in result:
- content += chunk
-
- assert content != b""
diff --git a/api/tests/integration_tests/model_runtime/huggingface_hub/__init__.py b/api/tests/integration_tests/model_runtime/huggingface_hub/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/huggingface_hub/test_llm.py b/api/tests/integration_tests/model_runtime/huggingface_hub/test_llm.py
deleted file mode 100644
index 8f90c680295727..00000000000000
--- a/api/tests/integration_tests/model_runtime/huggingface_hub/test_llm.py
+++ /dev/null
@@ -1,278 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.huggingface_hub.llm.llm import HuggingfaceHubLargeLanguageModel
-from tests.integration_tests.model_runtime.__mock.huggingface import setup_huggingface_mock
-
-
-@pytest.mark.skip
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_hosted_inference_api_validate_credentials(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="HuggingFaceH4/zephyr-7b-beta",
- credentials={"huggingfacehub_api_type": "hosted_inference_api", "huggingfacehub_api_token": "invalid_key"},
- )
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="fake-model",
- credentials={"huggingfacehub_api_type": "hosted_inference_api", "huggingfacehub_api_token": "invalid_key"},
- )
-
- model.validate_credentials(
- model="HuggingFaceH4/zephyr-7b-beta",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- },
- )
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_hosted_inference_api_invoke_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="HuggingFaceH4/zephyr-7b-beta",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_hosted_inference_api_invoke_stream_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="HuggingFaceH4/zephyr-7b-beta",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text_generation_validate_credentials(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="openchat/openchat_3.5",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": "invalid_key",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text-generation",
- },
- )
-
- model.validate_credentials(
- model="openchat/openchat_3.5",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text-generation",
- },
- )
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text_generation_invoke_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="openchat/openchat_3.5",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text-generation",
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text_generation_invoke_stream_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="openchat/openchat_3.5",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text-generation",
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text2text_generation_validate_credentials(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="google/mt5-base",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": "invalid_key",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT2TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text2text-generation",
- },
- )
-
- model.validate_credentials(
- model="google/mt5-base",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT2TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text2text-generation",
- },
- )
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text2text_generation_invoke_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="google/mt5-base",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT2TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text2text-generation",
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_huggingface_mock", [["none"]], indirect=True)
-def test_inference_endpoints_text2text_generation_invoke_stream_model(setup_huggingface_mock):
- model = HuggingfaceHubLargeLanguageModel()
-
- response = model.invoke(
- model="google/mt5-base",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT2TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text2text-generation",
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = HuggingfaceHubLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="google/mt5-base",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_TEXT2TEXT_GEN_ENDPOINT_URL"),
- "task_type": "text2text-generation",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 7
diff --git a/api/tests/integration_tests/model_runtime/huggingface_hub/test_text_embedding.py b/api/tests/integration_tests/model_runtime/huggingface_hub/test_text_embedding.py
deleted file mode 100644
index 0ee593f38a494a..00000000000000
--- a/api/tests/integration_tests/model_runtime/huggingface_hub/test_text_embedding.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.huggingface_hub.text_embedding.text_embedding import (
- HuggingfaceHubTextEmbeddingModel,
-)
-
-
-def test_hosted_inference_api_validate_credentials():
- model = HuggingfaceHubTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="facebook/bart-base",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": "invalid_key",
- },
- )
-
- model.validate_credentials(
- model="facebook/bart-base",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- },
- )
-
-
-def test_hosted_inference_api_invoke_model():
- model = HuggingfaceHubTextEmbeddingModel()
-
- result = model.invoke(
- model="facebook/bart-base",
- credentials={
- "huggingfacehub_api_type": "hosted_inference_api",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_inference_endpoints_validate_credentials():
- model = HuggingfaceHubTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="all-MiniLM-L6-v2",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": "invalid_key",
- "huggingface_namespace": "Dify-AI",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_EMBEDDINGS_ENDPOINT_URL"),
- "task_type": "feature-extraction",
- },
- )
-
- model.validate_credentials(
- model="all-MiniLM-L6-v2",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingface_namespace": "Dify-AI",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_EMBEDDINGS_ENDPOINT_URL"),
- "task_type": "feature-extraction",
- },
- )
-
-
-def test_inference_endpoints_invoke_model():
- model = HuggingfaceHubTextEmbeddingModel()
-
- result = model.invoke(
- model="all-MiniLM-L6-v2",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingface_namespace": "Dify-AI",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_EMBEDDINGS_ENDPOINT_URL"),
- "task_type": "feature-extraction",
- },
- texts=["hello", "world"],
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 0
-
-
-def test_get_num_tokens():
- model = HuggingfaceHubTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="all-MiniLM-L6-v2",
- credentials={
- "huggingfacehub_api_type": "inference_endpoints",
- "huggingfacehub_api_token": os.environ.get("HUGGINGFACE_API_KEY"),
- "huggingface_namespace": "Dify-AI",
- "huggingfacehub_endpoint_url": os.environ.get("HUGGINGFACE_EMBEDDINGS_ENDPOINT_URL"),
- "task_type": "feature-extraction",
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/huggingface_tei/__init__.py b/api/tests/integration_tests/model_runtime/huggingface_tei/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/huggingface_tei/test_embeddings.py b/api/tests/integration_tests/model_runtime/huggingface_tei/test_embeddings.py
deleted file mode 100644
index 33160062e57996..00000000000000
--- a/api/tests/integration_tests/model_runtime/huggingface_tei/test_embeddings.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.huggingface_tei.text_embedding.text_embedding import (
- HuggingfaceTeiTextEmbeddingModel,
- TeiHelper,
-)
-from tests.integration_tests.model_runtime.__mock.huggingface_tei import MockTEIClass
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_tei_mock(request, monkeypatch: pytest.MonkeyPatch):
- if MOCK:
- monkeypatch.setattr(TeiHelper, "get_tei_extra_parameter", MockTEIClass.get_tei_extra_parameter)
- monkeypatch.setattr(TeiHelper, "invoke_tokenize", MockTEIClass.invoke_tokenize)
- monkeypatch.setattr(TeiHelper, "invoke_embeddings", MockTEIClass.invoke_embeddings)
- monkeypatch.setattr(TeiHelper, "invoke_rerank", MockTEIClass.invoke_rerank)
- yield
-
- if MOCK:
- monkeypatch.undo()
-
-
-@pytest.mark.parametrize("setup_tei_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_tei_mock):
- model = HuggingfaceTeiTextEmbeddingModel()
- # model name is only used in mock
- model_name = "embedding"
-
- if MOCK:
- # TEI Provider will check model type by API endpoint, at real server, the model type is correct.
- # So we dont need to check model type here. Only check in mock
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="reranker",
- credentials={
- "server_url": os.environ.get("TEI_EMBEDDING_SERVER_URL", ""),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- )
-
- model.validate_credentials(
- model=model_name,
- credentials={
- "server_url": os.environ.get("TEI_EMBEDDING_SERVER_URL", ""),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- )
-
-
-@pytest.mark.parametrize("setup_tei_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_tei_mock):
- model = HuggingfaceTeiTextEmbeddingModel()
- model_name = "embedding"
-
- result = model.invoke(
- model=model_name,
- credentials={
- "server_url": os.environ.get("TEI_EMBEDDING_SERVER_URL", ""),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens > 0
diff --git a/api/tests/integration_tests/model_runtime/huggingface_tei/test_rerank.py b/api/tests/integration_tests/model_runtime/huggingface_tei/test_rerank.py
deleted file mode 100644
index 9777367063f5fa..00000000000000
--- a/api/tests/integration_tests/model_runtime/huggingface_tei/test_rerank.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.huggingface_tei.rerank.rerank import (
- HuggingfaceTeiRerankModel,
-)
-from core.model_runtime.model_providers.huggingface_tei.text_embedding.text_embedding import TeiHelper
-from tests.integration_tests.model_runtime.__mock.huggingface_tei import MockTEIClass
-
-MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
-
-
-@pytest.fixture
-def setup_tei_mock(request, monkeypatch: pytest.MonkeyPatch):
- if MOCK:
- monkeypatch.setattr(TeiHelper, "get_tei_extra_parameter", MockTEIClass.get_tei_extra_parameter)
- monkeypatch.setattr(TeiHelper, "invoke_tokenize", MockTEIClass.invoke_tokenize)
- monkeypatch.setattr(TeiHelper, "invoke_embeddings", MockTEIClass.invoke_embeddings)
- monkeypatch.setattr(TeiHelper, "invoke_rerank", MockTEIClass.invoke_rerank)
- yield
-
- if MOCK:
- monkeypatch.undo()
-
-
-@pytest.mark.parametrize("setup_tei_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_tei_mock):
- model = HuggingfaceTeiRerankModel()
- # model name is only used in mock
- model_name = "reranker"
-
- if MOCK:
- # TEI Provider will check model type by API endpoint, at real server, the model type is correct.
- # So we dont need to check model type here. Only check in mock
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="embedding",
- credentials={
- "server_url": os.environ.get("TEI_RERANK_SERVER_URL"),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- )
-
- model.validate_credentials(
- model=model_name,
- credentials={
- "server_url": os.environ.get("TEI_RERANK_SERVER_URL"),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- )
-
-
-@pytest.mark.parametrize("setup_tei_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_tei_mock):
- model = HuggingfaceTeiRerankModel()
- # model name is only used in mock
- model_name = "reranker"
-
- result = model.invoke(
- model=model_name,
- credentials={
- "server_url": os.environ.get("TEI_RERANK_SERVER_URL"),
- "api_key": os.environ.get("TEI_API_KEY", ""),
- },
- query="Who is Kasumi?",
- docs=[
- 'Kasumi is a girl\'s name of Japanese origin meaning "mist".',
- "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music ",
- "and she leads a team named PopiParty.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/hunyuan/__init__.py b/api/tests/integration_tests/model_runtime/hunyuan/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/hunyuan/test_llm.py b/api/tests/integration_tests/model_runtime/hunyuan/test_llm.py
deleted file mode 100644
index b3049a06d9b98a..00000000000000
--- a/api/tests/integration_tests/model_runtime/hunyuan/test_llm.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.hunyuan.llm.llm import HunyuanLargeLanguageModel
-
-
-def test_validate_credentials():
- model = HunyuanLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="hunyuan-standard", credentials={"secret_id": "invalid_key", "secret_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="hunyuan-standard",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = HunyuanLargeLanguageModel()
-
- response = model.invoke(
- model="hunyuan-standard",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hi")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = HunyuanLargeLanguageModel()
-
- response = model.invoke(
- model="hunyuan-standard",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hi")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = HunyuanLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="hunyuan-standard",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 14
diff --git a/api/tests/integration_tests/model_runtime/hunyuan/test_provider.py b/api/tests/integration_tests/model_runtime/hunyuan/test_provider.py
deleted file mode 100644
index e3748c2ce713d4..00000000000000
--- a/api/tests/integration_tests/model_runtime/hunyuan/test_provider.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.hunyuan.hunyuan import HunyuanProvider
-
-
-def test_validate_provider_credentials():
- provider = HunyuanProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"secret_id": "invalid_key", "secret_key": "invalid_key"})
-
- provider.validate_provider_credentials(
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/hunyuan/test_text_embedding.py b/api/tests/integration_tests/model_runtime/hunyuan/test_text_embedding.py
deleted file mode 100644
index 69d14dffeebf35..00000000000000
--- a/api/tests/integration_tests/model_runtime/hunyuan/test_text_embedding.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.hunyuan.text_embedding.text_embedding import HunyuanTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = HunyuanTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="hunyuan-embedding", credentials={"secret_id": "invalid_key", "secret_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="hunyuan-embedding",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = HunyuanTextEmbeddingModel()
-
- result = model.invoke(
- model="hunyuan-embedding",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 6
-
-
-def test_get_num_tokens():
- model = HunyuanTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="hunyuan-embedding",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
-
-
-def test_max_chunks():
- model = HunyuanTextEmbeddingModel()
-
- result = model.invoke(
- model="hunyuan-embedding",
- credentials={
- "secret_id": os.environ.get("HUNYUAN_SECRET_ID"),
- "secret_key": os.environ.get("HUNYUAN_SECRET_KEY"),
- },
- texts=[
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- "hello",
- "world",
- ],
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 22
diff --git a/api/tests/integration_tests/model_runtime/jina/__init__.py b/api/tests/integration_tests/model_runtime/jina/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/jina/test_provider.py b/api/tests/integration_tests/model_runtime/jina/test_provider.py
deleted file mode 100644
index e3b6128c59d8df..00000000000000
--- a/api/tests/integration_tests/model_runtime/jina/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.jina.jina import JinaProvider
-
-
-def test_validate_provider_credentials():
- provider = JinaProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "hahahaha"})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("JINA_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/jina/test_text_embedding.py b/api/tests/integration_tests/model_runtime/jina/test_text_embedding.py
deleted file mode 100644
index 290735ec49e625..00000000000000
--- a/api/tests/integration_tests/model_runtime/jina/test_text_embedding.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.jina.text_embedding.text_embedding import JinaTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = JinaTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="jina-embeddings-v2-base-en", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(
- model="jina-embeddings-v2-base-en", credentials={"api_key": os.environ.get("JINA_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = JinaTextEmbeddingModel()
-
- result = model.invoke(
- model="jina-embeddings-v2-base-en",
- credentials={
- "api_key": os.environ.get("JINA_API_KEY"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 6
-
-
-def test_get_num_tokens():
- model = JinaTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="jina-embeddings-v2-base-en",
- credentials={
- "api_key": os.environ.get("JINA_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 6
diff --git a/api/tests/integration_tests/model_runtime/localai/__init__.py b/api/tests/integration_tests/model_runtime/localai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/localai/test_embedding.py b/api/tests/integration_tests/model_runtime/localai/test_embedding.py
deleted file mode 100644
index 7fd9f2b3000a31..00000000000000
--- a/api/tests/integration_tests/model_runtime/localai/test_embedding.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""
-LocalAI Embedding Interface is temporarily unavailable due to
-we could not find a way to test it for now.
-"""
diff --git a/api/tests/integration_tests/model_runtime/localai/test_llm.py b/api/tests/integration_tests/model_runtime/localai/test_llm.py
deleted file mode 100644
index 51e899fd5186cf..00000000000000
--- a/api/tests/integration_tests/model_runtime/localai/test_llm.py
+++ /dev/null
@@ -1,172 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.localai.llm.llm import LocalAILanguageModel
-
-
-def test_validate_credentials_for_chat_model():
- model = LocalAILanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": "hahahaha",
- "completion_type": "completion",
- },
- )
-
- model.validate_credentials(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "completion",
- },
- )
-
-
-def test_invoke_completion_model():
- model = LocalAILanguageModel()
-
- response = model.invoke(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "completion",
- },
- prompt_messages=[UserPromptMessage(content="ping")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=[],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_chat_model():
- model = LocalAILanguageModel()
-
- response = model.invoke(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "chat_completion",
- },
- prompt_messages=[UserPromptMessage(content="ping")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=[],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_completion_model():
- model = LocalAILanguageModel()
-
- response = model.invoke(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "completion",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_stream_chat_model():
- model = LocalAILanguageModel()
-
- response = model.invoke(
- model="chinese-llama-2-7b",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "chat_completion",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.7, "top_p": 1.0, "max_tokens": 10},
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = LocalAILanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="????",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "chat_completion",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 77
-
- num_tokens = model.get_num_tokens(
- model="????",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "chat_completion",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 10
diff --git a/api/tests/integration_tests/model_runtime/localai/test_rerank.py b/api/tests/integration_tests/model_runtime/localai/test_rerank.py
deleted file mode 100644
index 13c7df6d1473b0..00000000000000
--- a/api/tests/integration_tests/model_runtime/localai/test_rerank.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.localai.rerank.rerank import LocalaiRerankModel
-
-
-def test_validate_credentials_for_chat_model():
- model = LocalaiRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-reranker-v2-m3",
- credentials={
- "server_url": "hahahaha",
- "completion_type": "completion",
- },
- )
-
- model.validate_credentials(
- model="bge-reranker-base",
- credentials={
- "server_url": os.environ.get("LOCALAI_SERVER_URL"),
- "completion_type": "completion",
- },
- )
-
-
-def test_invoke_rerank_model():
- model = LocalaiRerankModel()
-
- response = model.invoke(
- model="bge-reranker-base",
- credentials={"server_url": os.environ.get("LOCALAI_SERVER_URL")},
- query="Organic skincare products for sensitive skin",
- docs=[
- "Eco-friendly kitchenware for modern homes",
- "Biodegradable cleaning supplies for eco-conscious consumers",
- "Organic cotton baby clothes for sensitive skin",
- "Natural organic skincare range for sensitive skin",
- "Tech gadgets for smart homes: 2024 edition",
- "Sustainable gardening tools and compost solutions",
- "Sensitive skin-friendly facial cleansers and toners",
- "Organic food wraps and storage solutions",
- "Yoga mats made from recycled materials",
- ],
- top_n=3,
- score_threshold=0.75,
- user="abc-123",
- )
-
- assert isinstance(response, RerankResult)
- assert len(response.docs) == 3
-
-
-def test__invoke():
- model = LocalaiRerankModel()
-
- # Test case 1: Empty docs
- result = model._invoke(
- model="bge-reranker-base",
- credentials={"server_url": "https://example.com", "api_key": "1234567890"},
- query="Organic skincare products for sensitive skin",
- docs=[],
- top_n=3,
- score_threshold=0.75,
- user="abc-123",
- )
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 0
-
- # Test case 2: Valid invocation
- result = model._invoke(
- model="bge-reranker-base",
- credentials={"server_url": "https://example.com", "api_key": "1234567890"},
- query="Organic skincare products for sensitive skin",
- docs=[
- "Eco-friendly kitchenware for modern homes",
- "Biodegradable cleaning supplies for eco-conscious consumers",
- "Organic cotton baby clothes for sensitive skin",
- "Natural organic skincare range for sensitive skin",
- "Tech gadgets for smart homes: 2024 edition",
- "Sustainable gardening tools and compost solutions",
- "Sensitive skin-friendly facial cleansers and toners",
- "Organic food wraps and storage solutions",
- "Yoga mats made from recycled materials",
- ],
- top_n=3,
- score_threshold=0.75,
- user="abc-123",
- )
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 3
- assert all(isinstance(doc, RerankDocument) for doc in result.docs)
diff --git a/api/tests/integration_tests/model_runtime/localai/test_speech2text.py b/api/tests/integration_tests/model_runtime/localai/test_speech2text.py
deleted file mode 100644
index 91b7a5752ce973..00000000000000
--- a/api/tests/integration_tests/model_runtime/localai/test_speech2text.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.localai.speech2text.speech2text import LocalAISpeech2text
-
-
-def test_validate_credentials():
- model = LocalAISpeech2text()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="whisper-1", credentials={"server_url": "invalid_url"})
-
- model.validate_credentials(model="whisper-1", credentials={"server_url": os.environ.get("LOCALAI_SERVER_URL")})
-
-
-def test_invoke_model():
- model = LocalAISpeech2text()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- # Open the file and get the file object
- with open(audio_file_path, "rb") as audio_file:
- file = audio_file
-
- result = model.invoke(
- model="whisper-1",
- credentials={"server_url": os.environ.get("LOCALAI_SERVER_URL")},
- file=file,
- user="abc-123",
- )
-
- assert isinstance(result, str)
- assert result == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
diff --git a/api/tests/integration_tests/model_runtime/minimax/__init__.py b/api/tests/integration_tests/model_runtime/minimax/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/minimax/test_embedding.py b/api/tests/integration_tests/model_runtime/minimax/test_embedding.py
deleted file mode 100644
index cf2a28eb9eb2fe..00000000000000
--- a/api/tests/integration_tests/model_runtime/minimax/test_embedding.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.minimax.text_embedding.text_embedding import MinimaxTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = MinimaxTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="embo-01",
- credentials={"minimax_api_key": "invalid_key", "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID")},
- )
-
- model.validate_credentials(
- model="embo-01",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- )
-
-
-def test_invoke_model():
- model = MinimaxTextEmbeddingModel()
-
- result = model.invoke(
- model="embo-01",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 16
-
-
-def test_get_num_tokens():
- model = MinimaxTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="embo-01",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/minimax/test_llm.py b/api/tests/integration_tests/model_runtime/minimax/test_llm.py
deleted file mode 100644
index aacde04d326caf..00000000000000
--- a/api/tests/integration_tests/model_runtime/minimax/test_llm.py
+++ /dev/null
@@ -1,143 +0,0 @@
-import os
-from collections.abc import Generator
-from time import sleep
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, UserPromptMessage
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.minimax.llm.llm import MinimaxLargeLanguageModel
-
-
-def test_predefined_models():
- model = MinimaxLargeLanguageModel()
- model_schemas = model.predefined_models()
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-def test_validate_credentials_for_chat_model():
- sleep(3)
- model = MinimaxLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="abab5.5-chat", credentials={"minimax_api_key": "invalid_key", "minimax_group_id": "invalid_key"}
- )
-
- model.validate_credentials(
- model="abab5.5-chat",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- )
-
-
-def test_invoke_model():
- sleep(3)
- model = MinimaxLargeLanguageModel()
-
- response = model.invoke(
- model="abab5-chat",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_model():
- sleep(3)
- model = MinimaxLargeLanguageModel()
-
- response = model.invoke(
- model="abab5.5-chat",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_with_search():
- sleep(3)
- model = MinimaxLargeLanguageModel()
-
- response = model.invoke(
- model="abab5.5-chat",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- prompt_messages=[UserPromptMessage(content="北京今天的天气怎么样")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- "plugin_web_search": True,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- total_message = ""
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- total_message += chunk.delta.message.content
- assert len(chunk.delta.message.content) > 0 if not chunk.delta.finish_reason else True
-
- assert "参考资料" in total_message
-
-
-def test_get_num_tokens():
- sleep(3)
- model = MinimaxLargeLanguageModel()
-
- response = model.get_num_tokens(
- model="abab5.5-chat",
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- tools=[],
- )
-
- assert isinstance(response, int)
- assert response == 30
diff --git a/api/tests/integration_tests/model_runtime/minimax/test_provider.py b/api/tests/integration_tests/model_runtime/minimax/test_provider.py
deleted file mode 100644
index 575ed13eef124a..00000000000000
--- a/api/tests/integration_tests/model_runtime/minimax/test_provider.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.minimax.minimax import MinimaxProvider
-
-
-def test_validate_provider_credentials():
- provider = MinimaxProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(
- credentials={
- "minimax_api_key": "hahahaha",
- "minimax_group_id": "123",
- }
- )
-
- provider.validate_provider_credentials(
- credentials={
- "minimax_api_key": os.environ.get("MINIMAX_API_KEY"),
- "minimax_group_id": os.environ.get("MINIMAX_GROUP_ID"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/mixedbread/__init__.py b/api/tests/integration_tests/model_runtime/mixedbread/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/mixedbread/test_provider.py b/api/tests/integration_tests/model_runtime/mixedbread/test_provider.py
deleted file mode 100644
index 25c9f3ce8dffa9..00000000000000
--- a/api/tests/integration_tests/model_runtime/mixedbread/test_provider.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.mixedbread.mixedbread import MixedBreadProvider
-
-
-def test_validate_provider_credentials():
- provider = MixedBreadProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "hahahaha"})
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "usage": {"prompt_tokens": 3, "total_tokens": 3},
- "model": "mixedbread-ai/mxbai-embed-large-v1",
- "data": [{"embedding": [0.23333 for _ in range(1024)], "index": 0, "object": "embedding"}],
- "object": "list",
- "normalized": "true",
- "encoding_format": "float",
- "dimensions": 1024,
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("MIXEDBREAD_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/mixedbread/test_rerank.py b/api/tests/integration_tests/model_runtime/mixedbread/test_rerank.py
deleted file mode 100644
index b65aab74aa96d3..00000000000000
--- a/api/tests/integration_tests/model_runtime/mixedbread/test_rerank.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.mixedbread.rerank.rerank import MixedBreadRerankModel
-
-
-def test_validate_credentials():
- model = MixedBreadRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mxbai-rerank-large-v1",
- credentials={"api_key": "invalid_key"},
- )
- with patch("httpx.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "usage": {"prompt_tokens": 86, "total_tokens": 86},
- "model": "mixedbread-ai/mxbai-rerank-large-v1",
- "data": [
- {
- "index": 0,
- "score": 0.06762695,
- "input": "Carson City is the capital city of the American state of Nevada. At the 2010 United "
- "States Census, Carson City had a population of 55,274.",
- "object": "text_document",
- },
- {
- "index": 1,
- "score": 0.057403564,
- "input": "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific "
- "Ocean that are a political division controlled by the United States. Its capital is "
- "Saipan.",
- "object": "text_document",
- },
- ],
- "object": "list",
- "top_k": 2,
- "return_input": True,
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- model.validate_credentials(
- model="mxbai-rerank-large-v1",
- credentials={
- "api_key": os.environ.get("MIXEDBREAD_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = MixedBreadRerankModel()
- with patch("httpx.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "usage": {"prompt_tokens": 56, "total_tokens": 56},
- "model": "mixedbread-ai/mxbai-rerank-large-v1",
- "data": [
- {
- "index": 0,
- "score": 0.6044922,
- "input": "Kasumi is a girl name of Japanese origin meaning mist.",
- "object": "text_document",
- },
- {
- "index": 1,
- "score": 0.0703125,
- "input": "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music and she leads a "
- "team named PopiParty.",
- "object": "text_document",
- },
- ],
- "object": "list",
- "top_k": 2,
- "return_input": "true",
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- result = model.invoke(
- model="mxbai-rerank-large-v1",
- credentials={
- "api_key": os.environ.get("MIXEDBREAD_API_KEY"),
- },
- query="Who is Kasumi?",
- docs=[
- "Kasumi is a girl name of Japanese origin meaning mist.",
- "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music and she leads a team named "
- "PopiParty.",
- ],
- score_threshold=0.5,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.5
diff --git a/api/tests/integration_tests/model_runtime/mixedbread/test_text_embedding.py b/api/tests/integration_tests/model_runtime/mixedbread/test_text_embedding.py
deleted file mode 100644
index ca97a1895113f0..00000000000000
--- a/api/tests/integration_tests/model_runtime/mixedbread/test_text_embedding.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.mixedbread.text_embedding.text_embedding import MixedBreadTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = MixedBreadTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="mxbai-embed-large-v1", credentials={"api_key": "invalid_key"})
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "usage": {"prompt_tokens": 3, "total_tokens": 3},
- "model": "mixedbread-ai/mxbai-embed-large-v1",
- "data": [{"embedding": [0.23333 for _ in range(1024)], "index": 0, "object": "embedding"}],
- "object": "list",
- "normalized": "true",
- "encoding_format": "float",
- "dimensions": 1024,
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- model.validate_credentials(
- model="mxbai-embed-large-v1", credentials={"api_key": os.environ.get("MIXEDBREAD_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = MixedBreadTextEmbeddingModel()
-
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "usage": {"prompt_tokens": 6, "total_tokens": 6},
- "model": "mixedbread-ai/mxbai-embed-large-v1",
- "data": [
- {"embedding": [0.23333 for _ in range(1024)], "index": 0, "object": "embedding"},
- {"embedding": [0.23333 for _ in range(1024)], "index": 1, "object": "embedding"},
- ],
- "object": "list",
- "normalized": "true",
- "encoding_format": "float",
- "dimensions": 1024,
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- result = model.invoke(
- model="mxbai-embed-large-v1",
- credentials={
- "api_key": os.environ.get("MIXEDBREAD_API_KEY"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 6
-
-
-def test_get_num_tokens():
- model = MixedBreadTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="mxbai-embed-large-v1",
- credentials={
- "api_key": os.environ.get("MIXEDBREAD_API_KEY"),
- },
- texts=["ping"],
- )
-
- assert num_tokens == 1
diff --git a/api/tests/integration_tests/model_runtime/nomic/__init__.py b/api/tests/integration_tests/model_runtime/nomic/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/nomic/test_embeddings.py b/api/tests/integration_tests/model_runtime/nomic/test_embeddings.py
deleted file mode 100644
index 52dc96ee95c1bc..00000000000000
--- a/api/tests/integration_tests/model_runtime/nomic/test_embeddings.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.nomic.text_embedding.text_embedding import NomicTextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.nomic_embeddings import setup_nomic_mock
-
-
-@pytest.mark.parametrize("setup_nomic_mock", [["text_embedding"]], indirect=True)
-def test_validate_credentials(setup_nomic_mock):
- model = NomicTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="nomic-embed-text-v1.5",
- credentials={
- "nomic_api_key": "invalid_key",
- },
- )
-
- model.validate_credentials(
- model="nomic-embed-text-v1.5",
- credentials={
- "nomic_api_key": os.environ.get("NOMIC_API_KEY"),
- },
- )
-
-
-@pytest.mark.parametrize("setup_nomic_mock", [["text_embedding"]], indirect=True)
-def test_invoke_model(setup_nomic_mock):
- model = NomicTextEmbeddingModel()
-
- result = model.invoke(
- model="nomic-embed-text-v1.5",
- credentials={
- "nomic_api_key": os.environ.get("NOMIC_API_KEY"),
- },
- texts=["hello", "world"],
- user="foo",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert result.model == "nomic-embed-text-v1.5"
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-@pytest.mark.parametrize("setup_nomic_mock", [["text_embedding"]], indirect=True)
-def test_get_num_tokens(setup_nomic_mock):
- model = NomicTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="nomic-embed-text-v1.5",
- credentials={
- "nomic_api_key": os.environ.get("NOMIC_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/nomic/test_provider.py b/api/tests/integration_tests/model_runtime/nomic/test_provider.py
deleted file mode 100644
index ece4bb920080b3..00000000000000
--- a/api/tests/integration_tests/model_runtime/nomic/test_provider.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.nomic.nomic import NomicAtlasProvider
-from tests.integration_tests.model_runtime.__mock.nomic_embeddings import setup_nomic_mock
-
-
-@pytest.mark.parametrize("setup_nomic_mock", [["text_embedding"]], indirect=True)
-def test_validate_provider_credentials(setup_nomic_mock):
- provider = NomicAtlasProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={
- "nomic_api_key": os.environ.get("NOMIC_API_KEY"),
- },
- )
diff --git a/api/tests/integration_tests/model_runtime/novita/__init__.py b/api/tests/integration_tests/model_runtime/novita/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/novita/test_llm.py b/api/tests/integration_tests/model_runtime/novita/test_llm.py
deleted file mode 100644
index 9f92679cd5ccfa..00000000000000
--- a/api/tests/integration_tests/model_runtime/novita/test_llm.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.novita.llm.llm import NovitaLargeLanguageModel
-
-
-def test_validate_credentials():
- model = NovitaLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="meta-llama/llama-3-8b-instruct", credentials={"api_key": "invalid_key", "mode": "chat"}
- )
-
- model.validate_credentials(
- model="meta-llama/llama-3-8b-instruct",
- credentials={"api_key": os.environ.get("NOVITA_API_KEY"), "mode": "chat"},
- )
-
-
-def test_invoke_model():
- model = NovitaLargeLanguageModel()
-
- response = model.invoke(
- model="meta-llama/llama-3-8b-instruct",
- credentials={"api_key": os.environ.get("NOVITA_API_KEY"), "mode": "completion"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_p": 0.5,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="novita",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = NovitaLargeLanguageModel()
-
- response = model.invoke(
- model="meta-llama/llama-3-8b-instruct",
- credentials={"api_key": os.environ.get("NOVITA_API_KEY"), "mode": "chat"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={"temperature": 1.0, "top_k": 2, "top_p": 0.5, "max_tokens": 100},
- stream=True,
- user="novita",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_get_num_tokens():
- model = NovitaLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="meta-llama/llama-3-8b-instruct",
- credentials={
- "api_key": os.environ.get("NOVITA_API_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/novita/test_provider.py b/api/tests/integration_tests/model_runtime/novita/test_provider.py
deleted file mode 100644
index 191af99db20bd9..00000000000000
--- a/api/tests/integration_tests/model_runtime/novita/test_provider.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.novita.novita import NovitaProvider
-
-
-def test_validate_provider_credentials():
- provider = NovitaProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={
- "api_key": os.environ.get("NOVITA_API_KEY"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/oci/__init__.py b/api/tests/integration_tests/model_runtime/oci/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/oci/test_llm.py b/api/tests/integration_tests/model_runtime/oci/test_llm.py
deleted file mode 100644
index bd5d27eb0f2a02..00000000000000
--- a/api/tests/integration_tests/model_runtime/oci/test_llm.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.oci.llm.llm import OCILargeLanguageModel
-
-
-def test_validate_credentials():
- model = OCILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="cohere.command-r-plus",
- credentials={"oci_config_content": "invalid_key", "oci_key_content": "invalid_key"},
- )
-
- model.validate_credentials(
- model="cohere.command-r-plus",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- )
-
-
-def test_invoke_model():
- model = OCILargeLanguageModel()
-
- response = model.invoke(
- model="cohere.command-r-plus",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- prompt_messages=[UserPromptMessage(content="Hi")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = OCILargeLanguageModel()
-
- response = model.invoke(
- model="meta.llama-3-70b-instruct",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- prompt_messages=[UserPromptMessage(content="Hi")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_model_with_function():
- model = OCILargeLanguageModel()
-
- response = model.invoke(
- model="cohere.command-r-plus",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- prompt_messages=[UserPromptMessage(content="Hi")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=False,
- user="abc-123",
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_get_num_tokens():
- model = OCILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="cohere.command-r-plus",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 18
diff --git a/api/tests/integration_tests/model_runtime/oci/test_provider.py b/api/tests/integration_tests/model_runtime/oci/test_provider.py
deleted file mode 100644
index 2c7107c7ccfe45..00000000000000
--- a/api/tests/integration_tests/model_runtime/oci/test_provider.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.oci.oci import OCIGENAIProvider
-
-
-def test_validate_provider_credentials():
- provider = OCIGENAIProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/oci/test_text_embedding.py b/api/tests/integration_tests/model_runtime/oci/test_text_embedding.py
deleted file mode 100644
index 032c5c681a7aeb..00000000000000
--- a/api/tests/integration_tests/model_runtime/oci/test_text_embedding.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.oci.text_embedding.text_embedding import OCITextEmbeddingModel
-
-
-def test_validate_credentials():
- model = OCITextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="cohere.embed-multilingual-v3.0",
- credentials={"oci_config_content": "invalid_key", "oci_key_content": "invalid_key"},
- )
-
- model.validate_credentials(
- model="cohere.embed-multilingual-v3.0",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- )
-
-
-def test_invoke_model():
- model = OCITextEmbeddingModel()
-
- result = model.invoke(
- model="cohere.embed-multilingual-v3.0",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- # assert result.usage.total_tokens == 811
-
-
-def test_get_num_tokens():
- model = OCITextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="cohere.embed-multilingual-v3.0",
- credentials={
- "oci_config_content": os.environ.get("OCI_CONFIG_CONTENT"),
- "oci_key_content": os.environ.get("OCI_KEY_CONTENT"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/ollama/__init__.py b/api/tests/integration_tests/model_runtime/ollama/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/ollama/test_llm.py b/api/tests/integration_tests/model_runtime/ollama/test_llm.py
deleted file mode 100644
index 979751afceaca4..00000000000000
--- a/api/tests/integration_tests/model_runtime/ollama/test_llm.py
+++ /dev/null
@@ -1,226 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- ImagePromptMessageContent,
- SystemPromptMessage,
- TextPromptMessageContent,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.ollama.llm.llm import OllamaLargeLanguageModel
-
-
-def test_validate_credentials():
- model = OllamaLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mistral:text",
- credentials={
- "base_url": "http://localhost:21434",
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- )
-
- model.validate_credentials(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- )
-
-
-def test_invoke_model():
- model = OllamaLargeLanguageModel()
-
- response = model.invoke(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 1.0, "top_k": 2, "top_p": 0.5, "num_predict": 10},
- stop=["How"],
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = OllamaLargeLanguageModel()
-
- response = model.invoke(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={"temperature": 1.0, "top_k": 2, "top_p": 0.5, "num_predict": 10},
- stop=["How"],
- stream=True,
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_invoke_completion_model():
- model = OllamaLargeLanguageModel()
-
- response = model.invoke(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "completion",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 1.0, "top_k": 2, "top_p": 0.5, "num_predict": 10},
- stop=["How"],
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_completion_model():
- model = OllamaLargeLanguageModel()
-
- response = model.invoke(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "completion",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={"temperature": 1.0, "top_k": 2, "top_p": 0.5, "num_predict": 10},
- stop=["How"],
- stream=True,
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_invoke_completion_model_with_vision():
- model = OllamaLargeLanguageModel()
-
- result = model.invoke(
- model="llava",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "completion",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[
- UserPromptMessage(
- content=[
- TextPromptMessageContent(
- data="What is this in this picture?",
- ),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- )
- ],
- model_parameters={"temperature": 0.1, "num_predict": 100},
- stream=False,
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-def test_invoke_chat_model_with_vision():
- model = OllamaLargeLanguageModel()
-
- result = model.invoke(
- model="llava",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[
- UserPromptMessage(
- content=[
- TextPromptMessageContent(
- data="What is this in this picture?",
- ),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- )
- ],
- model_parameters={"temperature": 0.1, "num_predict": 100},
- stream=False,
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-def test_get_num_tokens():
- model = OllamaLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 2048,
- "max_tokens": 2048,
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 6
diff --git a/api/tests/integration_tests/model_runtime/ollama/test_text_embedding.py b/api/tests/integration_tests/model_runtime/ollama/test_text_embedding.py
deleted file mode 100644
index 3c4f740a4fd09c..00000000000000
--- a/api/tests/integration_tests/model_runtime/ollama/test_text_embedding.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.ollama.text_embedding.text_embedding import OllamaEmbeddingModel
-
-
-def test_validate_credentials():
- model = OllamaEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mistral:text",
- credentials={
- "base_url": "http://localhost:21434",
- "mode": "chat",
- "context_size": 4096,
- },
- )
-
- model.validate_credentials(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 4096,
- },
- )
-
-
-def test_invoke_model():
- model = OllamaEmbeddingModel()
-
- result = model.invoke(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 4096,
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = OllamaEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="mistral:text",
- credentials={
- "base_url": os.environ.get("OLLAMA_BASE_URL"),
- "mode": "chat",
- "context_size": 4096,
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/openai/__init__.py b/api/tests/integration_tests/model_runtime/openai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/openai/test_llm.py b/api/tests/integration_tests/model_runtime/openai/test_llm.py
deleted file mode 100644
index 9e83b9d434359d..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai/test_llm.py
+++ /dev/null
@@ -1,314 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- ImagePromptMessageContent,
- PromptMessageTool,
- SystemPromptMessage,
- TextPromptMessageContent,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity, ModelType
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel
-
-"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def test_predefined_models():
- model = OpenAILargeLanguageModel()
- model_schemas = model.predefined_models()
-
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="gpt-3.5-turbo", credentials={"openai_api_key": "invalid_key"})
-
- model.validate_credentials(model="gpt-3.5-turbo", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_validate_credentials_for_completion_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="text-davinci-003", credentials={"openai_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="text-davinci-003", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_invoke_completion_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo-instruct",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY"), "openai_api_base": "https://api.openai.com"},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 1},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
- assert model._num_tokens_from_string("gpt-3.5-turbo-instruct", result.message.content) == 1
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_invoke_stream_completion_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo-instruct",
- credentials={
- "openai_api_key": os.environ.get("OPENAI_API_KEY"),
- "openai_organization": os.environ.get("OPENAI_ORGANIZATION"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_vision(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-4-vision-preview",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content=[
- TextPromptMessageContent(
- data="Hello World!",
- ),
- ImagePromptMessageContent(
- mime_type="image/png",
- format="png",
- base64_data="iVBORw0KGgoAAAANSUhEUgAAAE4AAABMCAYAAADDYoEWAAAMQGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggt9I4gKiEJEEqMgaBiRxcVXLuIgA1dFVGwAmJBETuLYu+LBRVlXSzYlTcpoOu+8r35vrnz33/O/OfMmbllAFA7zhGJclF1APKEBeLYYH/6uOQUOukpIAEdoAy0gA2Hmy9iRkeHA1iG2r+Xd9cBIm2v2Eu1/tn/X4sGj5/PBQCJhjidl8/Ng/gAAHg1VyQuAIAo5c2mFoikGFagJYYBQrxIijPluFqK0+V4j8wmPpYFcTsASiocjjgTANVLkKcXcjOhhmo/xI5CnkAIgBodYp+8vMk8iNMgtoY2Ioil+oz0H3Qy/6aZPqzJ4WQOY/lcZEUpQJAvyuVM/z/T8b9LXq5kyIclrCpZ4pBY6Zxh3m7mTA6TYhWI+4TpkVEQa0L8QcCT2UOMUrIkIQlye9SAm8+COYMrDVBHHicgDGIDiIOEuZHhCj49QxDEhhjuEHSaoIAdD7EuxIv4+YFxCptN4smxCl9oY4aYxVTwZzlimV+pr/uSnASmQv91Fp+t0MdUi7LikyCmQGxeKEiMhFgVYof8nLgwhc3YoixW5JCNWBIrjd8c4li+MNhfro8VZoiDYhX2pXn5Q/PFNmUJ2JEKvK8gKz5Enh+sncuRxQ/ngl3iC5kJQzr8/HHhQ3Ph8QMC5XPHnvGFCXEKnQ+iAv9Y+VicIsqNVtjjpvzcYClvCrFLfmGcYiyeWAA3pFwfzxAVRMfL48SLsjmh0fJ48OUgHLBAAKADCazpYDLIBoLOvqY+eCfvCQIcIAaZgA/sFczQiCRZjxBe40AR+BMiPsgfHucv6+WDQsh/HWblV3uQIestlI3IAU8gzgNhIBfeS2SjhMPeEsFjyAj+4Z0DKxfGmwurtP/f80Psd4YJmXAFIxnySFcbsiQGEgOIIcQgog2uj/vgXng4vPrB6oQzcI+heXy3JzwhdBEeEq4Rugm3JgmKxT9FGQG6oX6QIhfpP+YCt4Sarrg/7g3VoTKug+sDe9wF+mHivtCzK2RZirilWaH/pP23GfywGgo7siMZJY8g+5Gtfx6paqvqOqwizfWP+ZHHmj6cb9Zwz8/+WT9knwfbsJ8tsUXYfuwMdgI7hx3BmgAda8WasQ7sqBQP767Hst015C1WFk8O1BH8w9/Qykozme9Y59jr+EXeV8CfJn1HA9Zk0XSxIDOrgM6EXwQ+nS3kOoyiOzk6OQMg/b7IX19vYmTfDUSn4zs3/w8AvFsHBwcPf+dCWwHY6w4f/0PfOWsG/HQoA3D2EFciLpRzuPRCgG8JNfik6QEjYAas4XycgBvwAn4gEISCKBAPksFEGH0W3OdiMBXMBPNACSgDy8EaUAk2gi1gB9gN9oEmcAScAKfBBXAJXAN34O7pAS9AP3gHPiMIQkKoCA3RQ4wRC8QOcUIYiA8SiIQjsUgykoZkIkJEgsxE5iNlyEqkEtmM1CJ7kUPICeQc0oXcQh4gvchr5BOKoSqoFmqIWqKjUQbKRMPQeHQCmolOQYvQBehStAKtQXehjegJ9AJ6De1GX6ADGMCUMR3MBLPHGBgLi8JSsAxMjM3GSrFyrAarx1rgOl/BurE+7CNOxGk4HbeHOzgET8C5+BR8Nr4Er8R34I14O34Ff4D3498IVIIBwY7gSWATxhEyCVMJJYRywjbCQcIp+Cz1EN4RiUQdohXRHT6LycRs4gziEuJ6YgPxOLGL+Ig4QCKR9Eh2JG9SFIlDKiCVkNaRdpFaSZdJPaQPSspKxkpOSkFKKUpCpWKlcqWdSseULis9VfpMVidbkD3JUWQeeTp5GXkruYV8kdxD/kzRoFhRvCnxlGzKPEoFpZ5yinKX8kZZWdlU2UM5RlmgPFe5QnmP8lnlB8ofVTRVbFVYKqkqEpWlKttVjqvcUnlDpVItqX7UFGoBdSm1lnqSep/6QZWm6qDKVuWpzlGtUm1Uvaz6Uo2sZqHGVJuoVqRWrrZf7aJanzpZ3VKdpc5Rn61epX5I/Yb6gAZNY4xGlEaexhKNnRrnNJ5pkjQtNQM1eZoLNLdontR8RMNoZjQWjUubT9tKO0Xr0SJqWWmxtbK1yrR2a3Vq9WtrartoJ2pP067SPqrdrYPpWOqwdXJ1luns07mu82mE4QjmCP6IxSPqR1we8V53pK6fLl+3VLdB95ruJz26XqBejt4KvSa9e/q4vq1+jP5U/Q36p/T7RmqN9BrJHVk6ct/I2waoga1BrMEMgy0GHQYDhkaGwYYiw3WGJw37jHSM/IyyjVYbHTPqNaYZ+xgLjFcbtxo/p2vTmfRcegW9nd5vYmASYiIx2WzSafLZ1Mo0wbTYtMH0nhnFjGGWYbbarM2s39zYPMJ8pnmd+W0LsgXDIstircUZi/eWVpZJlgstmyyfWelasa2KrOqs7lpTrX2tp1jXWF+1IdowbHJs1ttcskVtXW2zbKtsL9qhdm52Arv1dl2jCKM8RglH1Yy6Ya9iz7QvtK+zf+Cg4xDuUOzQ5PBytPnolNErRp8Z/c3R1THXcavjnTGaY0LHFI9pGfPaydaJ61TldNWZ6hzkPMe52fmVi50L32WDy01XmmuE60LXNtevbu5uYrd6t153c/c092r3GwwtRjRjCeOsB8HD32OOxxGPj55ungWe+zz/8rL3yvHa6fVsrNVY/titYx95m3pzvDd7d/vQfdJ8Nvl0+5r4cnxrfB/6mfnx/Lb5PWXaMLOZu5gv/R39xf4H/d+zPFmzWMcDsIDggNKAzkDNwITAysD7QaZBmUF1Qf3BrsEzgo+HEELCQlaE3GAbsrnsWnZ/qHvorND2MJWwuLDKsIfhtuHi8JYINCI0YlXE3UiLSGFkUxSIYketiroXbRU9JfpwDDEmOqYq5knsmNiZsWfiaHGT4nbGvYv3j18WfyfBOkGS0JaolpiaWJv4PikgaWVS97jR42aNu5CsnyxIbk4hpSSmbEsZGB84fs34nlTX1JLU6xOsJkybcG6i/sTciUcnqU3iTNqfRkhLStuZ9oUTxanhDKSz06vT+7ks7lruC54fbzWvl+/NX8l/muGdsTLjWaZ35qrM3izfrPKsPgFLUCl4lR2SvTH7fU5Uzvacwdyk3IY8pby0vENCTWGOsH2y0eRpk7tEdqISUfcUzylrpvSLw8Tb8pH8CfnNBVrwR75DYi35RfKg0KewqvDD1MSp+6dpTBNO65huO33x9KdFQUW/zcBncGe0zTSZOW/mg1nMWZtnI7PTZ7fNMZuzYE7P3OC5O+ZR5uXM+73YsXhl8dv5SfNbFhgumLvg0S/Bv9SVqJaIS24s9Fq4cRG+SLCoc7Hz4nWLv5XySs+XOZaVl31Zwl1y/tcxv1b8Org0Y2nnMrdlG5YTlwuXX1/hu2LHSo2VRSsfrYpY1biavrp09ds1k9acK3cp37iWslaytrsivKJ5nfm65eu+VGZVXqvyr2qoNqheXP1+PW/95Q1+G+o3Gm4s2/hpk2DTzc3BmxtrLGvKtxC3FG55sjVx65nfGL/VbtPfVrbt63bh9u4dsTvaa91ra3ca7FxWh9ZJ6np3pe66tDtgd3O9ff3mBp2Gsj1gj2TP871pe6/vC9vXtp+xv/6AxYHqg7SDpY1I4/TG/qaspu7m5OauQ6GH2lq8Wg4edji8/YjJkaqj2keXHaMcW3BssLWodeC46HjficwTj9omtd05Oe7k1faY9s5TYafOng46ffIM80zrWe+zR855njt0nnG+6YLbhcYO146Dv7v+frDTrbPxovvF5ksel1q6xnYdu+x7+cSVgCunr7KvXrgWea3resL1mzdSb3Tf5N18div31qvbhbc/35l7l3C39J76vfL7Bvdr/rD5o6Hbrfvog4AHHQ/jHt55xH304nH+4y89C55Qn5Q/NX5a+8zp2ZHeoN5Lz8c/73khevG5r+RPjT+rX1q/PPCX318d/eP6e16JXw2+XvJG7832ty5v2waiB+6/y3v3+X3pB70POz4yPp75lPTp6eepX0hfKr7afG35Fvbt7mDe4KCII+bIfgUwWNGMDABebweAmgwADZ7PKOPl5z9ZQeRnVhkC/wnLz4iy4gZAPfx/j+mDfzc3ANizFR6/oL5aKgDRVADiPQDq7Dxch85qsnOltBDhOWBT5Nf0vHTwb4r8zPlD3D+3QKrqAn5u/wWdZ3xtG7qP3QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAATqADAAQAAAABAAAATAAAAADhTXUdAAARnUlEQVR4Ae2c245bR3aGi4fulizFHgUzQAYIggBB5klymfeaZ8hDBYjvAiRxkMAGkowRWx7JktjcZL7vX1Uku62Burkl5YbV5q7Tqqq1/v3XqgMpL95tbvftEh6NwPLRLS4NgsAFuDOJcAHuAtyZCJzZ7MK4C3BnInBmswvjLsCdicCZzS6MOxO49Znt0uz3//CPbbv6srXFrq0W9Q6Wi0VbLPn4R8x/jSLiu3nrl8s9dcartlwtKdmTbm21XranN6v27Mm6XV8t25fP1+3Pn1+1r4if3Czbk+t9u1rR6f9jmAXc1P6sbaevQGbfdgGJeA8ke0AQsCYYgiYgPR1QyVO+3wvcMm2WO0G2PeWkX79btp839AG4//UjYC62gDsB2rI9f7pov3q2bX/9F1ftBWAufTufOcwCrnTtR90dOdHoNgCJeAbUkuM5TsWAW5W9gfkE83ZkUHg0oAyAwbm927a2ebVoP/xx2f7jD1uYuG9/89tF+/VXK1hq+88TZgG32O1g2r7tpRdBM8fUTM7pyR8SYddgxkJErUszHti7U44CpzyEo16syNtx+qgy+1og7RMetpev9+3rb3bt+c2u/ebFsv3uL1ftiqn+qcMs4HY7jNQpEfadNU5VqeHUTJkgUbaPDxRADdZ8jU9LHoJYnwLUtgWN4ObDC7Kdr8Hp7d9qMTW8gt23V1zyvPrD1H56e9t+99vr9uJLprBDfaIw69U4dQRCIw2JdVIjbUzecj+7qYyPpZHiAbDaJwsXyMhQEQ0pq6sAp7hMS2XGqykdA2iy4EUtF6v206ur9k/fbNo//+frtt2OaW/rjxtmAaeNGqihBY5xfVQzQEZfoSH0KHgkrbD/CX6vPIqlSTU61vVCovRSbEwbIS851vj23Q+tff3vu/bzu5I7tvs4qVnADTa5FCbNC86qCLN2E1MxKKroYB2pgSz2RLbbVcVkSJhOKxIDjGxn+nSuqes2JlKuG8fA/IzPXazbj68X7et/27UfX7GifORwOuSju47h/c3beKfRFO74CNA04YP0ZT2/YzERFGojc9pmDG47/wyDZwJjiX4wwJNer1dZPJbs5/xzK5Ppzp7SQZBszNy22U7tX7/dtFdvJrv8aGE2cDJLoPycBgHSgICJUQLo8nmUo6y7oH0S5Lu/FGhDQULCfIooATw3yyOQQ46eYVpYiaBMTFtAFPR307r9y3fbdvsRfd5Rg6HJI2Lt1qaAF6TEqoxWdVdYSHawezCvAHLjW7Jh2QGcUkDDT4Og2OfSFRVkxipcAJUZARC5FVRbeRpB1hVY6r25XQHexIZ96Hfa++PTs4Dbi8rQg7imWQG27/uEgCTCssk/WWg7GwJWwDQ36PceGzQ+x7jOtgNogkIIpsZiFMdXoEfOPUlh3l5ulu2/X6bJ7Mc84Bw+xgOKzJqM0VKm8WYlVMqt61gFKNtQKeZ6o7Ls/aqEeYooJXDIZ9uiT0uZ5UxPUJNlYdoAK62qHfM7unz3/bb9/Ha+v3u/tn3AD0XOrnxAZdpNYZILgoxyGk4BqMCbssq66dXv6RdFkiB6Rj2u3N1npiMw1dQjF4oJW/kzy6VdMRFA9Xd8VvhCLxCyYUYkvhHZb7+fotvdUR6XmwXcYI1DangAA6yspgBj/dRjp6L+RbmSPaaxuuMnGEeVAhBF4pSapAFG5gUo60rAHmpVtcz0sR2aBZW8NAB9+W7dXr9N0dmPmUcu10pWrq7kQQvBQXn1dUsgoM4ej12TtyBknG51PEMGOV2TLLVZ/GLvLMBYHsYJhg7fuMBx6tq3LFu7aBxxD9jKFiO7Thbwcv7n5dS+/ML0eWEWcBqoptk+mEQp2aTG+rbmBYA+D6MyMwMAdepKsX5QpnglFZyZ5k4tDYsI/Y1pF7CRq22HoHXgGEOwgodvgH79INnW3tlFIVVQvkBXg1dvF3z27fkTGzw+zALOPZluVoVkV4yLHoBB3VBJUNyo6uEWXAyIkruC2OQjbVeppxkm8+iti2mySsM1EPYGKBcEyul3LKTW1+pr+wLRstwP0J8a2K95Txf/+6q1ZzeUDEXt/oFhHnA4fJYCBtawYlWmlsrJBEHhP43bi9Rq1Z0ymlK3Z/QCRqA5YfaNLZJWEACn929eluXlUGO8CgMrHWYi441S2tsFebLRL5RWL0e0nL64SEEf2sjMR4ZZwA0Ddfziclz1eN8yDn1qAaHSq3G0FEQXjABDo51sJVNyGnA0QlAPL4LOApzMo0mY1sUFbQBj8xTzYhKrROYF5VGIftR1uW3+3uiWU8XnBw7l3HIYVG/P/djYgMZoyrTJrci0n2qPZVnNFV913viW6btGzsXBT6aW3VKmsauVTFOc2DxpP5YJYLBBeCUixE71IlGBR2EF+6OugHbP12Ddoj29HgIPj+cxDiPDFGINzB8sKhLh0Ui4gOgDI8deb8FiwYxlteWhLHWTlmOzhkxLAObPIkFqS8+bbG5BdgWiAmJTwXdqZ7oysktzdKC/BWMWiAJNpyP0ZPTMItRy7fTi2RB4eDwLuIkpCma1gob/Dsw7zcKAMf3txiCot8c42ZCDPu3WAqRMJAGEk4cACaLzSZsFRhAE9QoAtXcwTX92XDT0sxTQXJYHdDJin0KfVN8PmzNvnOYBx5XNlik4giumihb7tJ60ezgNhgXuXgRNttxunZYAj7uzbL3nUA67rm5KJWrJCyTfIVwBMh3bTkD8TqFYp6uv8RwrgJpAZmHHScqv0qWeKT48NujhAuELekyYBdz9gXJQ53DvDh3tU62xTtN8bQhzzE9OccAK8wA2ez2k3cNtN7wM/RZs9M5NkNZoee0H2rmhLr8miPV9roAZtN1RHV/gDb7EoUtXKeXjYXUBN0oeFs8CbrtlhZRGPZSSZNyI9gA+TBFkelFNWxgEgCtG3wDiFqEr5Jz6y/U1DAM4QLxi2l7DNhl3w/epNTUFWGbXC7HrMQMz7WUbf8AaDQ46DYXuxLoJX6CFRzvuiPyJzCzgZIoKyqgKAx1yAGPQUWfa+GoDsqwDJNnHLF9juSz0i5VrpvqSwmsQul5dtyfrfX1zL3i0WdHHSjaKVjf0T5k7ABtxlEHbwxusgjydAY8N84BjvAx5GLfMqBW0VJEZ+pwKskQnbpnFHPzpwWo/bzkGvX51296+bu1v/+qL9usXT9rTJ07Bzh9k9HEPsxNhwhh6xLXKo3fXWf3iMkrBBz9nAbflbHm6ONxhXp8/NW26lkSleIEV9FBVI+o6ihjmffPDt+3v/+5Z+82vnsZw/fyercweB2d7wzA8mfuPEknpXTnHvQsoPd1v/aD8LODw+AxbAw/QjnEfv69u5kz6dtOiW2R6YmW7vd0C3qK94wcjf/zxZ1bRXfvqGT6U3f2G/Z6AesqotgJX477PNVmTmxfiwTSS5irqz2ybEHD6PzbMAk7lS/0BxgkTqPAUYBiAkQpTLLdKxe1D4Lbsp968uW1vXk+ZrnpsN7yL1TbmbvCl4GcPPPStZWyNcM9s++9y92ruZu2CT21q7lZ9KDcLuC3WbmGG42uA30EISOVkFynt1BBialOliF/wZHqGTa1tOfq8fbMHPL6N2iBPW2d7HfxZdWnreiN49UL0dfhLR6tBSVVwNo+TQ1U5IsHvQU4Dcry7bGNOix+SngVcwAhYpZjTQxaNMABLLLtUFEAMEwi4kk63fGDbLTcVm82ubd7hNylzEXCa6SPdz2Vf5iUobe0jAFIq8+JHT8CjGeUjHFOj5E7MIO4THxvOaHIcwu2IOKiznyg89BTEXi6WssO8B36vkLa33Pv7/QRbEtm21c/BtIm9Yb4ho19PDg4g09aeucySdpzq3BfVx6WQqh7MkLOSkHLf2olEKni4n7xznh0VH4jnAYdy6hfVSZTvUmF54f2cU9d9XmlhvUyTlbkxIT0BWtgH4wRRgPMy7EFbAwi8ojzbNyqtH/7coWxnUHyE+rmYjbs3NCnqdwIbbM/GZ4RZwDleVskO3viSBhWjSu2Pxj7JU4bsqrzTU5YZQ7xKu73Bb8bAbo+s28NStxEyb8e+K1UAKXhOVivK7x0RUANf3zEw/smJpsr37cad9RlhFnCbzQYwfN36I+5qwxgVwRA/vOHxlneeMiaux9lymN5tTTttkZN5mbZwCYsLM550taA+zJM5gsdHsGSdQTbngN7ZlC/JrRhXIcorRJvVcp2pnjzdy+0nnErOCbOAE5x8d4oVCy4xMSFGetjfgWJ3MQFHdomxZbUwwC4B84YlzBNojUEmxmqO1tVC4VcVopUzKuXK+XArUeDVTyq85wv7xKqHsel1dfIUkl8zUXcFm8eUH7IPjWcBp8J5mYxWcWmbclhlyEIAMJm2HbSwDCHZGD9IuR1UH4MhaZ4HOAIQIJOrIxfjxOFRUMNQq8wI9EH5WNVJdcEje22ofxs3K6PlQ+OZwA2ghrFSKhiEVSqh/5JJcfodKBnntLac7wb5CKLpAs+0RguYuAhoNh2CRV1dTVFhqWhRn/u+tOsMtTph6JhOkAWsQDz1K3NHeHyYBZyK70BG5oy3SyqGumoaAhr1Aiggnm8FzXr3cQWSq++p8seM10v6LW9Elgh5kyGINXMdi1xspw2LRHwqMjJTV2KdU9c2eQ1SkXDDHL2aYf2MprVp1dFrtcBlAWB/sNuxMoJIzEfRqhMk04qXfM0n8yVDaa/DRLp1GuGSKhNz65ZEOQUSdyD0Y/adRSojsxjoz2jnNFdN3l/S+sUvnqbDsx+zgCvQMJzhPaCrlouCLBvbA43x68DhsAc7DxpTr0y39VAMBCfpSlpSUMggzRe8X4bIAWRYJqVJj6t7feMV/9Bkfeb+bYw2Czg78S3GwWtEQEPRWFMMEDAZhVTiMaWLnZZRxSexfaStPR9DAXbMj5Qs479Dm8PqqYCNEpUTVAe/GpLC3vH16hI64zkLuB1XQVsdFkED8ps40oLjj2sMAdbFwGlKRjbW6UHAFZaRJVegIpeWVafZhQ4yHahUm+5VyfOwXYFHTX8DKUNSn+fCcsN3qOd8AT3GGPEs4EYnxho9YlOnU1WTUj98GbLKWCawI5wk71DiBMoh+qjYfgXUc+nNlW+rXuqjOrknPAs4sRoHcvvNguDZNEChYOoBUUZ175z9nMBZnQ6cnncgS7uDnt3BJ49Y8axqPYLZ0gVEb2DaICyHtOUM5t2eP7AJexWaGWYBVzcdsqneoAAViyzzo3ZsC1Jeq2qBKVhlkIxDsuSRrSY6/6S6eaaFjD+B4BGmMo9X9M06kcAdMq0qU5eT+lBBc8+GqaVmCc989iHP6yVvOcr4qE8ZLijVZ8VleC/5xWDWFmN6ow6aIKX75EfdL5rfKxBJgAcwwV/zeXrFjyqqo3uy52dnMa5oU4O7svo7YMNgWrFKdsk6WBXmmS82HuKsuADjHZFGi5iBIv+9qnn/qt+qSh3JTFNjPvWDiqpnA0SexYB/ijm6q5qP85wFnIZrXQHgillpVesHh9QVaAWWAJccfo/VNrOcbmrbYn/vCR9gy2m1aUH2WOa/rv4UoKnhPODowC2Gx6jQo4Nox4ZinDL392ssIHFSZWa1rTZJD/wSy0Kn34eDpwZvP1w96+dmH25zrsQs4KSLP4GAawWSjhnFZZQFmUZxOZSTj/ne2yUhIHCjRIlFKcIU0x852RjZTGGlDdaQrkxk7MPrJr/gzg17r4vgJ3rMAk4/wmQDE7wJhg+fFV1xaMGiMqnXaFc5jd4FjCCIRAEmAO5aPE7lzsw0ZelHYJB0PCWscErqOJcsrbllGmhmzE/7mAXcPof544Wlqg6wTuORtvKQzjV2gVC+shaNMhc24v8iIloGmS3ogc7bD9sS884Oi0kEP89jFnDX++/hCtPVtT7kwaxOkZpmxQ/L9vgdj1r+NCtAwQ6/A9DXMXnBqZgoHDdXP7Wna/Id6PRCum7DiREqcg1UPw9Yp6MsLv/HwlM4Hp7WQ1/CGQhcgDsDNJtcgLsAdyYCZza7MO4C3JkInNnswrgLcGcicGazC+POBO7/AH5zPa/ivytzAAAAAElFTkSuQmCC",
- ),
- ]
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_tools(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = OpenAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="gpt-3.5-turbo-instruct",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 3
-
- num_tokens = model.get_num_tokens(
- model="gpt-3.5-turbo",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- ],
- )
-
- assert num_tokens == 72
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat", "remote"]], indirect=True)
-def test_fine_tuned_models(setup_openai_mock):
- model = OpenAILargeLanguageModel()
-
- remote_models = model.remote_models(credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
-
- if not remote_models:
- assert isinstance(remote_models, list)
- else:
- assert isinstance(remote_models[0], AIModelEntity)
-
- for llm_model in remote_models:
- if llm_model.model_type == ModelType.LLM:
- break
-
- assert isinstance(llm_model, AIModelEntity)
-
- # test invoke
- result = model.invoke(
- model=llm_model.model,
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
-
-
-def test__get_num_tokens_by_gpt2():
- model = OpenAILargeLanguageModel()
- num_tokens = model._get_num_tokens_by_gpt2("Hello World!")
-
- assert num_tokens == 3
diff --git a/api/tests/integration_tests/model_runtime/openai/test_moderation.py b/api/tests/integration_tests/model_runtime/openai/test_moderation.py
deleted file mode 100644
index 6de262471798ad..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai/test_moderation.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai.moderation.moderation import OpenAIModerationModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["moderation"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = OpenAIModerationModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="text-moderation-stable", credentials={"openai_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="text-moderation-stable", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["moderation"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = OpenAIModerationModel()
-
- result = model.invoke(
- model="text-moderation-stable",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- text="hello",
- user="abc-123",
- )
-
- assert isinstance(result, bool)
- assert result is False
-
- result = model.invoke(
- model="text-moderation-stable",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- text="i will kill you",
- user="abc-123",
- )
-
- assert isinstance(result, bool)
- assert result is True
diff --git a/api/tests/integration_tests/model_runtime/openai/test_provider.py b/api/tests/integration_tests/model_runtime/openai/test_provider.py
deleted file mode 100644
index 4d56cfcf3c25f0..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai.openai import OpenAIProvider
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_provider_credentials(setup_openai_mock):
- provider = OpenAIProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/openai/test_speech2text.py b/api/tests/integration_tests/model_runtime/openai/test_speech2text.py
deleted file mode 100644
index aa92c8b61fb684..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai/test_speech2text.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai.speech2text.speech2text import OpenAISpeech2TextModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["speech2text"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = OpenAISpeech2TextModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="whisper-1", credentials={"openai_api_key": "invalid_key"})
-
- model.validate_credentials(model="whisper-1", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["speech2text"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = OpenAISpeech2TextModel()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- # Open the file and get the file object
- with open(audio_file_path, "rb") as audio_file:
- file = audio_file
-
- result = model.invoke(
- model="whisper-1",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
- file=file,
- user="abc-123",
- )
-
- assert isinstance(result, str)
- assert result == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
diff --git a/api/tests/integration_tests/model_runtime/openai/test_text_embedding.py b/api/tests/integration_tests/model_runtime/openai/test_text_embedding.py
deleted file mode 100644
index f5dd73f2d4cd60..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai/test_text_embedding.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai.text_embedding.text_embedding import OpenAITextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = OpenAITextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="text-embedding-ada-002", credentials={"openai_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="text-embedding-ada-002", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = OpenAITextEmbeddingModel()
-
- result = model.invoke(
- model="text-embedding-ada-002",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY"), "openai_api_base": "https://api.openai.com"},
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = OpenAITextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="text-embedding-ada-002",
- credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY"), "openai_api_base": "https://api.openai.com"},
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/openai_api_compatible/__init__.py b/api/tests/integration_tests/model_runtime/openai_api_compatible/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_llm.py b/api/tests/integration_tests/model_runtime/openai_api_compatible/test_llm.py
deleted file mode 100644
index f2302ef05e06de..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_llm.py
+++ /dev/null
@@ -1,197 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel
-
-"""
-Using Together.ai's OpenAI-compatible API as testing endpoint
-"""
-
-
-def test_validate_credentials():
- model = OAIAPICompatLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={"api_key": "invalid_key", "endpoint_url": "https://api.together.xyz/v1/", "mode": "chat"},
- )
-
- model.validate_credentials(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- "endpoint_url": "https://api.together.xyz/v1/",
- "mode": "chat",
- },
- )
-
-
-def test_invoke_model():
- model = OAIAPICompatLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- "endpoint_url": "https://api.together.xyz/v1/",
- "mode": "completion",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = OAIAPICompatLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- "endpoint_url": "https://api.together.xyz/v1/",
- "mode": "chat",
- "stream_mode_delimiter": "\\n\\n",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_invoke_stream_model_without_delimiter():
- model = OAIAPICompatLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- "endpoint_url": "https://api.together.xyz/v1/",
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-# using OpenAI's ChatGPT-3.5 as testing endpoint
-def test_invoke_chat_model_with_tools():
- model = OAIAPICompatLargeLanguageModel()
-
- result = model.invoke(
- model="gpt-3.5-turbo",
- credentials={
- "api_key": os.environ.get("OPENAI_API_KEY"),
- "endpoint_url": "https://api.openai.com/v1/",
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
- },
- "required": ["location"],
- },
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 1024},
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
-
-
-def test_get_num_tokens():
- model = OAIAPICompatLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={"api_key": os.environ.get("OPENAI_API_KEY"), "endpoint_url": "https://api.openai.com/v1/"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_speech2text.py b/api/tests/integration_tests/model_runtime/openai_api_compatible/test_speech2text.py
deleted file mode 100644
index cf805eafff4968..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_speech2text.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai_api_compatible.speech2text.speech2text import (
- OAICompatSpeech2TextModel,
-)
-
-
-def test_validate_credentials():
- model = OAICompatSpeech2TextModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="whisper-1",
- credentials={"api_key": "invalid_key", "endpoint_url": "https://api.openai.com/v1/"},
- )
-
- model.validate_credentials(
- model="whisper-1",
- credentials={"api_key": os.environ.get("OPENAI_API_KEY"), "endpoint_url": "https://api.openai.com/v1/"},
- )
-
-
-def test_invoke_model():
- model = OAICompatSpeech2TextModel()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- # Open the file and get the file object
- with open(audio_file_path, "rb") as audio_file:
- file = audio_file
-
- result = model.invoke(
- model="whisper-1",
- credentials={"api_key": os.environ.get("OPENAI_API_KEY"), "endpoint_url": "https://api.openai.com/v1/"},
- file=file,
- user="abc-123",
- )
-
- assert isinstance(result, str)
- assert result == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
diff --git a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_text_embedding.py b/api/tests/integration_tests/model_runtime/openai_api_compatible/test_text_embedding.py
deleted file mode 100644
index 052b41605f6da2..00000000000000
--- a/api/tests/integration_tests/model_runtime/openai_api_compatible/test_text_embedding.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openai_api_compatible.text_embedding.text_embedding import (
- OAICompatEmbeddingModel,
-)
-
-"""
-Using OpenAI's API as testing endpoint
-"""
-
-
-def test_validate_credentials():
- model = OAICompatEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="text-embedding-ada-002",
- credentials={"api_key": "invalid_key", "endpoint_url": "https://api.openai.com/v1/", "context_size": 8184},
- )
-
- model.validate_credentials(
- model="text-embedding-ada-002",
- credentials={
- "api_key": os.environ.get("OPENAI_API_KEY"),
- "endpoint_url": "https://api.openai.com/v1/",
- "context_size": 8184,
- },
- )
-
-
-def test_invoke_model():
- model = OAICompatEmbeddingModel()
-
- result = model.invoke(
- model="text-embedding-ada-002",
- credentials={
- "api_key": os.environ.get("OPENAI_API_KEY"),
- "endpoint_url": "https://api.openai.com/v1/",
- "context_size": 8184,
- },
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- assert result.usage.total_tokens == 502
-
-
-def test_get_num_tokens():
- model = OAICompatEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="text-embedding-ada-002",
- credentials={
- "api_key": os.environ.get("OPENAI_API_KEY"),
- "endpoint_url": "https://api.openai.com/v1/embeddings",
- "context_size": 8184,
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/openllm/__init__.py b/api/tests/integration_tests/model_runtime/openllm/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/openllm/test_embedding.py b/api/tests/integration_tests/model_runtime/openllm/test_embedding.py
deleted file mode 100644
index 14d47217af62c8..00000000000000
--- a/api/tests/integration_tests/model_runtime/openllm/test_embedding.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openllm.text_embedding.text_embedding import OpenLLMTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = OpenLLMTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "server_url": "ww" + os.environ.get("OPENLLM_SERVER_URL"),
- },
- )
-
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- )
-
-
-def test_invoke_model():
- model = OpenLLMTextEmbeddingModel()
-
- result = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens > 0
-
-
-def test_get_num_tokens():
- model = OpenLLMTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/openllm/test_llm.py b/api/tests/integration_tests/model_runtime/openllm/test_llm.py
deleted file mode 100644
index 35939e3cfe8bfd..00000000000000
--- a/api/tests/integration_tests/model_runtime/openllm/test_llm.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openllm.llm.llm import OpenLLMLargeLanguageModel
-
-
-def test_validate_credentials_for_chat_model():
- model = OpenLLMLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "server_url": "invalid_key",
- },
- )
-
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- )
-
-
-def test_invoke_model():
- model = OpenLLMLargeLanguageModel()
-
- response = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_model():
- model = OpenLLMLargeLanguageModel()
-
- response = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = OpenLLMLargeLanguageModel()
-
- response = model.get_num_tokens(
- model="NOT IMPORTANT",
- credentials={
- "server_url": os.environ.get("OPENLLM_SERVER_URL"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- tools=[],
- )
-
- assert isinstance(response, int)
- assert response == 3
diff --git a/api/tests/integration_tests/model_runtime/openrouter/__init__.py b/api/tests/integration_tests/model_runtime/openrouter/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/openrouter/test_llm.py b/api/tests/integration_tests/model_runtime/openrouter/test_llm.py
deleted file mode 100644
index 1b0cc6bf4b8e76..00000000000000
--- a/api/tests/integration_tests/model_runtime/openrouter/test_llm.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.openrouter.llm.llm import OpenRouterLargeLanguageModel
-
-
-def test_validate_credentials():
- model = OpenRouterLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mistralai/mixtral-8x7b-instruct", credentials={"api_key": "invalid_key", "mode": "chat"}
- )
-
- model.validate_credentials(
- model="mistralai/mixtral-8x7b-instruct",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "chat"},
- )
-
-
-def test_invoke_model():
- model = OpenRouterLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/mixtral-8x7b-instruct",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "completion"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = OpenRouterLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/mixtral-8x7b-instruct",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "chat"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_get_num_tokens():
- model = OpenRouterLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="mistralai/mixtral-8x7b-instruct",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/replicate/__init__.py b/api/tests/integration_tests/model_runtime/replicate/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/replicate/test_llm.py b/api/tests/integration_tests/model_runtime/replicate/test_llm.py
deleted file mode 100644
index b940005b715760..00000000000000
--- a/api/tests/integration_tests/model_runtime/replicate/test_llm.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.replicate.llm.llm import ReplicateLargeLanguageModel
-
-
-def test_validate_credentials():
- model = ReplicateLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="meta/llama-2-13b-chat",
- credentials={
- "replicate_api_token": "invalid_key",
- "model_version": "f4e2de70d66816a838a89eeeb621910adffb0dd0baba3976c96980970978018d",
- },
- )
-
- model.validate_credentials(
- model="meta/llama-2-13b-chat",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "f4e2de70d66816a838a89eeeb621910adffb0dd0baba3976c96980970978018d",
- },
- )
-
-
-def test_invoke_model():
- model = ReplicateLargeLanguageModel()
-
- response = model.invoke(
- model="meta/llama-2-13b-chat",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "f4e2de70d66816a838a89eeeb621910adffb0dd0baba3976c96980970978018d",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = ReplicateLargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/mixtral-8x7b-instruct-v0.1",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "2b56576fcfbe32fa0526897d8385dd3fb3d36ba6fd0dbe033c72886b81ade93e",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_get_num_tokens():
- model = ReplicateLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "2b56576fcfbe32fa0526897d8385dd3fb3d36ba6fd0dbe033c72886b81ade93e",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 14
diff --git a/api/tests/integration_tests/model_runtime/replicate/test_text_embedding.py b/api/tests/integration_tests/model_runtime/replicate/test_text_embedding.py
deleted file mode 100644
index 397715f2252083..00000000000000
--- a/api/tests/integration_tests/model_runtime/replicate/test_text_embedding.py
+++ /dev/null
@@ -1,136 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.replicate.text_embedding.text_embedding import ReplicateEmbeddingModel
-
-
-def test_validate_credentials_one():
- model = ReplicateEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="replicate/all-mpnet-base-v2",
- credentials={
- "replicate_api_token": "invalid_key",
- "model_version": "b6b7585c9640cd7a9572c6e129c9549d79c9c31f0d3fdce7baac7c67ca38f305",
- },
- )
-
- model.validate_credentials(
- model="replicate/all-mpnet-base-v2",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "b6b7585c9640cd7a9572c6e129c9549d79c9c31f0d3fdce7baac7c67ca38f305",
- },
- )
-
-
-def test_validate_credentials_two():
- model = ReplicateEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="nateraw/bge-large-en-v1.5",
- credentials={
- "replicate_api_token": "invalid_key",
- "model_version": "9cf9f015a9cb9c61d1a2610659cdac4a4ca222f2d3707a68517b18c198a9add1",
- },
- )
-
- model.validate_credentials(
- model="nateraw/bge-large-en-v1.5",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "9cf9f015a9cb9c61d1a2610659cdac4a4ca222f2d3707a68517b18c198a9add1",
- },
- )
-
-
-def test_invoke_model_one():
- model = ReplicateEmbeddingModel()
-
- result = model.invoke(
- model="nateraw/bge-large-en-v1.5",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "9cf9f015a9cb9c61d1a2610659cdac4a4ca222f2d3707a68517b18c198a9add1",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_invoke_model_two():
- model = ReplicateEmbeddingModel()
-
- result = model.invoke(
- model="andreasjansson/clip-features",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "75b33f253f7714a281ad3e9b28f63e3232d583716ef6718f2e46641077ea040a",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_invoke_model_three():
- model = ReplicateEmbeddingModel()
-
- result = model.invoke(
- model="replicate/all-mpnet-base-v2",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "b6b7585c9640cd7a9572c6e129c9549d79c9c31f0d3fdce7baac7c67ca38f305",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_invoke_model_four():
- model = ReplicateEmbeddingModel()
-
- result = model.invoke(
- model="nateraw/jina-embeddings-v2-base-en",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "f8367a1c072ba2bc28af549d1faeacfe9b88b3f0e475add7a75091dac507f79e",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = ReplicateEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="nateraw/jina-embeddings-v2-base-en",
- credentials={
- "replicate_api_token": os.environ.get("REPLICATE_API_KEY"),
- "model_version": "f8367a1c072ba2bc28af549d1faeacfe9b88b3f0e475add7a75091dac507f79e",
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/sagemaker/__init__.py b/api/tests/integration_tests/model_runtime/sagemaker/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/sagemaker/test_provider.py b/api/tests/integration_tests/model_runtime/sagemaker/test_provider.py
deleted file mode 100644
index 41de2a17fda047..00000000000000
--- a/api/tests/integration_tests/model_runtime/sagemaker/test_provider.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.sagemaker.sagemaker import SageMakerProvider
-
-
-def test_validate_provider_credentials():
- provider = SageMakerProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={})
diff --git a/api/tests/integration_tests/model_runtime/sagemaker/test_rerank.py b/api/tests/integration_tests/model_runtime/sagemaker/test_rerank.py
deleted file mode 100644
index d5a6798a1ef735..00000000000000
--- a/api/tests/integration_tests/model_runtime/sagemaker/test_rerank.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.sagemaker.rerank.rerank import SageMakerRerankModel
-
-
-def test_validate_credentials():
- model = SageMakerRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-m3-rerank-v2",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- query="What is the capital of the United States?",
- docs=[
- "Carson City is the capital city of the American state of Nevada. At the 2010 United States "
- "Census, Carson City had a population of 55,274.",
- "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
- "are a political division controlled by the United States. Its capital is Saipan.",
- ],
- score_threshold=0.8,
- )
-
-
-def test_invoke_model():
- model = SageMakerRerankModel()
-
- result = model.invoke(
- model="bge-m3-rerank-v2",
- credentials={
- "aws_region": os.getenv("AWS_REGION"),
- "aws_access_key": os.getenv("AWS_ACCESS_KEY"),
- "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
- },
- query="What is the capital of the United States?",
- docs=[
- "Carson City is the capital city of the American state of Nevada. At the 2010 United States "
- "Census, Carson City had a population of 55,274.",
- "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
- "are a political division controlled by the United States. Its capital is Saipan.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 1
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/sagemaker/test_text_embedding.py b/api/tests/integration_tests/model_runtime/sagemaker/test_text_embedding.py
deleted file mode 100644
index f77601eea2c263..00000000000000
--- a/api/tests/integration_tests/model_runtime/sagemaker/test_text_embedding.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.sagemaker.text_embedding.text_embedding import SageMakerEmbeddingModel
-
-
-def test_validate_credentials():
- model = SageMakerEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="bge-m3", credentials={})
-
- model.validate_credentials(model="bge-m3-embedding", credentials={})
-
-
-def test_invoke_model():
- model = SageMakerEmbeddingModel()
-
- result = model.invoke(model="bge-m3-embedding", credentials={}, texts=["hello", "world"], user="abc-123")
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
-
-
-def test_get_num_tokens():
- model = SageMakerEmbeddingModel()
-
- num_tokens = model.get_num_tokens(model="bge-m3-embedding", credentials={}, texts=[])
-
- assert num_tokens == 0
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/__init__.py b/api/tests/integration_tests/model_runtime/siliconflow/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/test_llm.py b/api/tests/integration_tests/model_runtime/siliconflow/test_llm.py
deleted file mode 100644
index f47c9c558808af..00000000000000
--- a/api/tests/integration_tests/model_runtime/siliconflow/test_llm.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.siliconflow.llm.llm import SiliconflowLargeLanguageModel
-
-
-def test_validate_credentials():
- model = SiliconflowLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="deepseek-ai/DeepSeek-V2-Chat", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="deepseek-ai/DeepSeek-V2-Chat", credentials={"api_key": os.environ.get("API_KEY")})
-
-
-def test_invoke_model():
- model = SiliconflowLargeLanguageModel()
-
- response = model.invoke(
- model="deepseek-ai/DeepSeek-V2-Chat",
- credentials={"api_key": os.environ.get("API_KEY")},
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = SiliconflowLargeLanguageModel()
-
- response = model.invoke(
- model="deepseek-ai/DeepSeek-V2-Chat",
- credentials={"api_key": os.environ.get("API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = SiliconflowLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="deepseek-ai/DeepSeek-V2-Chat",
- credentials={"api_key": os.environ.get("API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 12
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/test_provider.py b/api/tests/integration_tests/model_runtime/siliconflow/test_provider.py
deleted file mode 100644
index 8f70210b7a2ace..00000000000000
--- a/api/tests/integration_tests/model_runtime/siliconflow/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.siliconflow.siliconflow import SiliconflowProvider
-
-
-def test_validate_provider_credentials():
- provider = SiliconflowProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/test_rerank.py b/api/tests/integration_tests/model_runtime/siliconflow/test_rerank.py
deleted file mode 100644
index ad794613f91013..00000000000000
--- a/api/tests/integration_tests/model_runtime/siliconflow/test_rerank.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.siliconflow.rerank.rerank import SiliconflowRerankModel
-
-
-def test_validate_credentials():
- model = SiliconflowRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="BAAI/bge-reranker-v2-m3",
- credentials={"api_key": "invalid_key"},
- )
-
- model.validate_credentials(
- model="BAAI/bge-reranker-v2-m3",
- credentials={
- "api_key": os.environ.get("API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = SiliconflowRerankModel()
-
- result = model.invoke(
- model="BAAI/bge-reranker-v2-m3",
- credentials={
- "api_key": os.environ.get("API_KEY"),
- },
- query="Who is Kasumi?",
- docs=[
- 'Kasumi is a girl\'s name of Japanese origin meaning "mist".',
- "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music ",
- "and she leads a team named PopiParty.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/test_speech2text.py b/api/tests/integration_tests/model_runtime/siliconflow/test_speech2text.py
deleted file mode 100644
index 0502ba5ab404bc..00000000000000
--- a/api/tests/integration_tests/model_runtime/siliconflow/test_speech2text.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.siliconflow.speech2text.speech2text import SiliconflowSpeech2TextModel
-
-
-def test_validate_credentials():
- model = SiliconflowSpeech2TextModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="iic/SenseVoiceSmall",
- credentials={"api_key": "invalid_key"},
- )
-
- model.validate_credentials(
- model="iic/SenseVoiceSmall",
- credentials={"api_key": os.environ.get("API_KEY")},
- )
-
-
-def test_invoke_model():
- model = SiliconflowSpeech2TextModel()
-
- # Get the directory of the current file
- current_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Get assets directory
- assets_dir = os.path.join(os.path.dirname(current_dir), "assets")
-
- # Construct the path to the audio file
- audio_file_path = os.path.join(assets_dir, "audio.mp3")
-
- # Open the file and get the file object
- with open(audio_file_path, "rb") as audio_file:
- file = audio_file
-
- result = model.invoke(
- model="iic/SenseVoiceSmall", credentials={"api_key": os.environ.get("API_KEY")}, file=file
- )
-
- assert isinstance(result, str)
- assert result == "1,2,3,4,5,6,7,8,9,10."
diff --git a/api/tests/integration_tests/model_runtime/siliconflow/test_text_embedding.py b/api/tests/integration_tests/model_runtime/siliconflow/test_text_embedding.py
deleted file mode 100644
index ab143c10613a88..00000000000000
--- a/api/tests/integration_tests/model_runtime/siliconflow/test_text_embedding.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.siliconflow.text_embedding.text_embedding import (
- SiliconflowTextEmbeddingModel,
-)
-
-
-def test_validate_credentials():
- model = SiliconflowTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="BAAI/bge-large-zh-v1.5",
- credentials={"api_key": "invalid_key"},
- )
-
- model.validate_credentials(
- model="BAAI/bge-large-zh-v1.5",
- credentials={
- "api_key": os.environ.get("API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = SiliconflowTextEmbeddingModel()
-
- result = model.invoke(
- model="BAAI/bge-large-zh-v1.5",
- credentials={
- "api_key": os.environ.get("API_KEY"),
- },
- texts=[
- "hello",
- "world",
- ],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 6
-
-
-def test_get_num_tokens():
- model = SiliconflowTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="BAAI/bge-large-zh-v1.5",
- credentials={
- "api_key": os.environ.get("API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/spark/__init__.py b/api/tests/integration_tests/model_runtime/spark/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/spark/test_llm.py b/api/tests/integration_tests/model_runtime/spark/test_llm.py
deleted file mode 100644
index 4fe2fd8c0a3eac..00000000000000
--- a/api/tests/integration_tests/model_runtime/spark/test_llm.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.spark.llm.llm import SparkLargeLanguageModel
-
-
-def test_validate_credentials():
- model = SparkLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="spark-1.5", credentials={"app_id": "invalid_key"})
-
- model.validate_credentials(
- model="spark-1.5",
- credentials={
- "app_id": os.environ.get("SPARK_APP_ID"),
- "api_secret": os.environ.get("SPARK_API_SECRET"),
- "api_key": os.environ.get("SPARK_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = SparkLargeLanguageModel()
-
- response = model.invoke(
- model="spark-1.5",
- credentials={
- "app_id": os.environ.get("SPARK_APP_ID"),
- "api_secret": os.environ.get("SPARK_API_SECRET"),
- "api_key": os.environ.get("SPARK_API_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = SparkLargeLanguageModel()
-
- response = model.invoke(
- model="spark-1.5",
- credentials={
- "app_id": os.environ.get("SPARK_APP_ID"),
- "api_secret": os.environ.get("SPARK_API_SECRET"),
- "api_key": os.environ.get("SPARK_API_KEY"),
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.5, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = SparkLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="spark-1.5",
- credentials={
- "app_id": os.environ.get("SPARK_APP_ID"),
- "api_secret": os.environ.get("SPARK_API_SECRET"),
- "api_key": os.environ.get("SPARK_API_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 14
diff --git a/api/tests/integration_tests/model_runtime/spark/test_provider.py b/api/tests/integration_tests/model_runtime/spark/test_provider.py
deleted file mode 100644
index 9da0df6bb3d556..00000000000000
--- a/api/tests/integration_tests/model_runtime/spark/test_provider.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.spark.spark import SparkProvider
-
-
-def test_validate_provider_credentials():
- provider = SparkProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={
- "app_id": os.environ.get("SPARK_APP_ID"),
- "api_secret": os.environ.get("SPARK_API_SECRET"),
- "api_key": os.environ.get("SPARK_API_KEY"),
- }
- )
diff --git a/api/tests/integration_tests/model_runtime/stepfun/__init__.py b/api/tests/integration_tests/model_runtime/stepfun/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/stepfun/test_llm.py b/api/tests/integration_tests/model_runtime/stepfun/test_llm.py
deleted file mode 100644
index f9afca6f5945b5..00000000000000
--- a/api/tests/integration_tests/model_runtime/stepfun/test_llm.py
+++ /dev/null
@@ -1,123 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.stepfun.llm.llm import StepfunLargeLanguageModel
-
-
-def test_validate_credentials():
- model = StepfunLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="step-1-8k", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="step-1-8k", credentials={"api_key": os.environ.get("STEPFUN_API_KEY")})
-
-
-def test_invoke_model():
- model = StepfunLargeLanguageModel()
-
- response = model.invoke(
- model="step-1-8k",
- credentials={"api_key": os.environ.get("STEPFUN_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.9, "top_p": 0.7},
- stop=["Hi"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = StepfunLargeLanguageModel()
-
- response = model.invoke(
- model="step-1-8k",
- credentials={"api_key": os.environ.get("STEPFUN_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.9, "top_p": 0.7},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_customizable_model_schema():
- model = StepfunLargeLanguageModel()
-
- schema = model.get_customizable_model_schema(
- model="step-1-8k", credentials={"api_key": os.environ.get("STEPFUN_API_KEY")}
- )
- assert isinstance(schema, AIModelEntity)
-
-
-def test_invoke_chat_model_with_tools():
- model = StepfunLargeLanguageModel()
-
- result = model.invoke(
- model="step-1-8k",
- credentials={"api_key": os.environ.get("STEPFUN_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in Shanghai?",
- ),
- ],
- model_parameters={"temperature": 0.9, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
diff --git a/api/tests/integration_tests/model_runtime/test_model_provider_factory.py b/api/tests/integration_tests/model_runtime/test_model_provider_factory.py
deleted file mode 100644
index 0ec4b0b7243176..00000000000000
--- a/api/tests/integration_tests/model_runtime/test_model_provider_factory.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import logging
-import os
-
-from core.model_runtime.entities.model_entities import ModelType
-from core.model_runtime.entities.provider_entities import ProviderConfig, ProviderEntity, SimpleProviderEntity
-from core.model_runtime.model_providers.model_provider_factory import ModelProviderExtension, ModelProviderFactory
-
-logger = logging.getLogger(__name__)
-
-
-def test_get_providers():
- factory = ModelProviderFactory()
- providers = factory.get_providers()
-
- for provider in providers:
- logger.debug(provider)
-
- assert len(providers) >= 1
- assert isinstance(providers[0], ProviderEntity)
-
-
-def test_get_models():
- factory = ModelProviderFactory()
- providers = factory.get_models(
- model_type=ModelType.LLM,
- provider_configs=[
- ProviderConfig(provider="openai", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
- ],
- )
-
- logger.debug(providers)
-
- assert len(providers) >= 1
- assert isinstance(providers[0], SimpleProviderEntity)
-
- # all provider models type equals to ModelType.LLM
- for provider in providers:
- for provider_model in provider.models:
- assert provider_model.model_type == ModelType.LLM
-
- providers = factory.get_models(
- provider="openai",
- provider_configs=[
- ProviderConfig(provider="openai", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")})
- ],
- )
-
- assert len(providers) == 1
- assert isinstance(providers[0], SimpleProviderEntity)
- assert providers[0].provider == "openai"
-
-
-def test_provider_credentials_validate():
- factory = ModelProviderFactory()
- factory.provider_credentials_validate(
- provider="openai", credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- )
-
-
-def test__get_model_provider_map():
- factory = ModelProviderFactory()
- model_providers = factory._get_model_provider_map()
-
- for name, model_provider in model_providers.items():
- logger.debug(name)
- logger.debug(model_provider.provider_instance)
-
- assert len(model_providers) >= 1
- assert isinstance(model_providers["openai"], ModelProviderExtension)
diff --git a/api/tests/integration_tests/model_runtime/togetherai/__init__.py b/api/tests/integration_tests/model_runtime/togetherai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/togetherai/test_llm.py b/api/tests/integration_tests/model_runtime/togetherai/test_llm.py
deleted file mode 100644
index 5787e1bf6a8d99..00000000000000
--- a/api/tests/integration_tests/model_runtime/togetherai/test_llm.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.togetherai.llm.llm import TogetherAILargeLanguageModel
-
-
-def test_validate_credentials():
- model = TogetherAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1", credentials={"api_key": "invalid_key", "mode": "chat"}
- )
-
- model.validate_credentials(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "chat"},
- )
-
-
-def test_invoke_model():
- model = TogetherAILargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "completion"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = TogetherAILargeLanguageModel()
-
- response = model.invoke(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={"api_key": os.environ.get("TOGETHER_API_KEY"), "mode": "chat"},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_get_num_tokens():
- model = TogetherAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="mistralai/Mixtral-8x7B-Instruct-v0.1",
- credentials={
- "api_key": os.environ.get("TOGETHER_API_KEY"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/tongyi/__init__.py b/api/tests/integration_tests/model_runtime/tongyi/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/tongyi/test_llm.py b/api/tests/integration_tests/model_runtime/tongyi/test_llm.py
deleted file mode 100644
index 61650735f2ad3f..00000000000000
--- a/api/tests/integration_tests/model_runtime/tongyi/test_llm.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.tongyi.llm.llm import TongyiLargeLanguageModel
-
-
-def test_validate_credentials():
- model = TongyiLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="qwen-turbo", credentials={"dashscope_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="qwen-turbo", credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = TongyiLargeLanguageModel()
-
- response = model.invoke(
- model="qwen-turbo",
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = TongyiLargeLanguageModel()
-
- response = model.invoke(
- model="qwen-turbo",
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = TongyiLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="qwen-turbo",
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 12
diff --git a/api/tests/integration_tests/model_runtime/tongyi/test_provider.py b/api/tests/integration_tests/model_runtime/tongyi/test_provider.py
deleted file mode 100644
index 0bc96c84e73195..00000000000000
--- a/api/tests/integration_tests/model_runtime/tongyi/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.tongyi.tongyi import TongyiProvider
-
-
-def test_validate_provider_credentials():
- provider = TongyiProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")}
- )
diff --git a/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py b/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py
deleted file mode 100644
index d37fcf897fc3a8..00000000000000
--- a/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import os
-
-import dashscope # type: ignore
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.tongyi.rerank.rerank import GTERerankModel
-
-
-def test_validate_credentials():
- model = GTERerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="get-rank", credentials={"dashscope_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="get-rank", credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")}
- )
-
-
-def test_invoke_model():
- model = GTERerankModel()
-
- result = model.invoke(
- model=dashscope.TextReRank.Models.gte_rerank,
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")},
- query="什么是文本排序模型",
- docs=[
- "文本排序模型广泛用于搜索引擎和推荐系统中,它们根据文本相关性对候选文本进行排序",
- "量子计算是计算科学的一个前沿领域",
- "预训练语言模型的发展给文本排序模型带来了新的进展",
- ],
- score_threshold=0.7,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.7
diff --git a/api/tests/integration_tests/model_runtime/tongyi/test_response_format.py b/api/tests/integration_tests/model_runtime/tongyi/test_response_format.py
deleted file mode 100644
index 905e7907fde5a8..00000000000000
--- a/api/tests/integration_tests/model_runtime/tongyi/test_response_format.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import json
-import os
-from collections.abc import Generator
-
-from core.model_runtime.entities.llm_entities import LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, UserPromptMessage
-from core.model_runtime.model_providers.tongyi.llm.llm import TongyiLargeLanguageModel
-
-
-def test_invoke_model_with_json_response():
- """
- Test the invocation of a model with JSON response.
- """
- model_list = [
- "qwen-max-0403",
- "qwen-max-1201",
- "qwen-max-longcontext",
- "qwen-max",
- "qwen-plus-chat",
- "qwen-plus",
- "qwen-turbo-chat",
- "qwen-turbo",
- ]
- for model_name in model_list:
- print("testing model: ", model_name)
- invoke_model_with_json_response(model_name)
-
-
-def invoke_model_with_json_response(model_name="qwen-max-0403"):
- """
- Method to invoke the model with JSON response format.
- Args:
- model_name (str): The name of the model to invoke. Defaults to "qwen-max-0403".
-
- Returns:
- None
- """
- model = TongyiLargeLanguageModel()
-
- response = model.invoke(
- model=model_name,
- credentials={"dashscope_api_key": os.environ.get("TONGYI_DASHSCOPE_API_KEY")},
- prompt_messages=[
- UserPromptMessage(content='output json data with format `{"data": "test", "code": 200, "msg": "success"}')
- ],
- model_parameters={
- "temperature": 0.5,
- "max_tokens": 50,
- "response_format": "JSON",
- },
- stream=True,
- user="abc-123",
- )
- print("=====================================")
- print(response)
- assert isinstance(response, Generator)
- output = ""
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- output += chunk.delta.message.content
- assert is_json(output)
-
-
-def is_json(s):
- """
- Check if a string is a valid JSON.
-
- Args:
- s (str): The string to check.
-
- Returns:
- bool: True if the string is a valid JSON, False otherwise.
- """
- try:
- json.loads(s)
- except ValueError:
- return False
- return True
diff --git a/api/tests/integration_tests/model_runtime/upstage/__init__.py b/api/tests/integration_tests/model_runtime/upstage/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/upstage/test_llm.py b/api/tests/integration_tests/model_runtime/upstage/test_llm.py
deleted file mode 100644
index 0f39e902f33803..00000000000000
--- a/api/tests/integration_tests/model_runtime/upstage/test_llm.py
+++ /dev/null
@@ -1,185 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.upstage.llm.llm import UpstageLargeLanguageModel
-
-"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def test_predefined_models():
- model = UpstageLargeLanguageModel()
- model_schemas = model.predefined_models()
-
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = UpstageLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- # model name to gpt-3.5-turbo because of mocking
- model.validate_credentials(model="gpt-3.5-turbo", credentials={"upstage_api_key": "invalid_key"})
-
- model.validate_credentials(
- model="solar-1-mini-chat", credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model(setup_openai_mock):
- model = UpstageLargeLanguageModel()
-
- result = model.invoke(
- model="solar-1-mini-chat",
- credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_tools(setup_openai_mock):
- model = UpstageLargeLanguageModel()
-
- result = model.invoke(
- model="solar-1-mini-chat",
- credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
- assert len(result.message.tool_calls) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock):
- model = UpstageLargeLanguageModel()
-
- result = model.invoke(
- model="solar-1-mini-chat",
- credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = UpstageLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="solar-1-mini-chat",
- credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 13
-
- num_tokens = model.get_num_tokens(
- model="solar-1-mini-chat",
- credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- ],
- )
-
- assert num_tokens == 106
diff --git a/api/tests/integration_tests/model_runtime/upstage/test_provider.py b/api/tests/integration_tests/model_runtime/upstage/test_provider.py
deleted file mode 100644
index 9d83779aa00a49..00000000000000
--- a/api/tests/integration_tests/model_runtime/upstage/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.upstage.upstage import UpstageProvider
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_provider_credentials(setup_openai_mock):
- provider = UpstageProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/upstage/test_text_embedding.py b/api/tests/integration_tests/model_runtime/upstage/test_text_embedding.py
deleted file mode 100644
index 8c83172fa3ff7e..00000000000000
--- a/api/tests/integration_tests/model_runtime/upstage/test_text_embedding.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.upstage.text_embedding.text_embedding import UpstageTextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_validate_credentials(setup_openai_mock):
- model = UpstageTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="solar-embedding-1-large-passage", credentials={"upstage_api_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="solar-embedding-1-large-passage", credentials={"upstage_api_key": os.environ.get("UPSTAGE_API_KEY")}
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["text_embedding"]], indirect=True)
-def test_invoke_model(setup_openai_mock):
- model = UpstageTextEmbeddingModel()
-
- result = model.invoke(
- model="solar-embedding-1-large-passage",
- credentials={
- "upstage_api_key": os.environ.get("UPSTAGE_API_KEY"),
- },
- texts=["hello", "world", " ".join(["long_text"] * 100), " ".join(["another_long_text"] * 100)],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 4
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = UpstageTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="solar-embedding-1-large-passage",
- credentials={
- "upstage_api_key": os.environ.get("UPSTAGE_API_KEY"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 5
diff --git a/api/tests/integration_tests/model_runtime/vessl_ai/__init__.py b/api/tests/integration_tests/model_runtime/vessl_ai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/vessl_ai/test_llm.py b/api/tests/integration_tests/model_runtime/vessl_ai/test_llm.py
deleted file mode 100644
index 7797d0f8e46a87..00000000000000
--- a/api/tests/integration_tests/model_runtime/vessl_ai/test_llm.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.vessl_ai.llm.llm import VesslAILargeLanguageModel
-
-
-def test_validate_credentials():
- model = VesslAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": "invalid_key",
- "endpoint_url": os.environ.get("VESSL_AI_ENDPOINT_URL"),
- "mode": "chat",
- },
- )
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": os.environ.get("VESSL_AI_API_KEY"),
- "endpoint_url": "http://invalid_url",
- "mode": "chat",
- },
- )
-
- model.validate_credentials(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": os.environ.get("VESSL_AI_API_KEY"),
- "endpoint_url": os.environ.get("VESSL_AI_ENDPOINT_URL"),
- "mode": "chat",
- },
- )
-
-
-def test_invoke_model():
- model = VesslAILargeLanguageModel()
-
- response = model.invoke(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": os.environ.get("VESSL_AI_API_KEY"),
- "endpoint_url": os.environ.get("VESSL_AI_ENDPOINT_URL"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = VesslAILargeLanguageModel()
-
- response = model.invoke(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": os.environ.get("VESSL_AI_API_KEY"),
- "endpoint_url": os.environ.get("VESSL_AI_ENDPOINT_URL"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Who are you?"),
- ],
- model_parameters={
- "temperature": 1.0,
- "top_k": 2,
- "top_p": 0.5,
- },
- stop=["How"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
-
-
-def test_get_num_tokens():
- model = VesslAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model=os.environ.get("VESSL_AI_MODEL_NAME"),
- credentials={
- "api_key": os.environ.get("VESSL_AI_API_KEY"),
- "endpoint_url": os.environ.get("VESSL_AI_ENDPOINT_URL"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/volcengine_maas/__init__.py b/api/tests/integration_tests/model_runtime/volcengine_maas/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/volcengine_maas/test_embedding.py b/api/tests/integration_tests/model_runtime/volcengine_maas/test_embedding.py
deleted file mode 100644
index f831c063a42630..00000000000000
--- a/api/tests/integration_tests/model_runtime/volcengine_maas/test_embedding.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.volcengine_maas.text_embedding.text_embedding import (
- VolcengineMaaSTextEmbeddingModel,
-)
-
-
-def test_validate_credentials():
- model = VolcengineMaaSTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": "INVALID",
- "volc_secret_access_key": "INVALID",
- "endpoint_id": "INVALID",
- "base_model_name": "Doubao-embedding",
- },
- )
-
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_EMBEDDING_ENDPOINT_ID"),
- "base_model_name": "Doubao-embedding",
- },
- )
-
-
-def test_invoke_model():
- model = VolcengineMaaSTextEmbeddingModel()
-
- result = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_EMBEDDING_ENDPOINT_ID"),
- "base_model_name": "Doubao-embedding",
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens > 0
-
-
-def test_get_num_tokens():
- model = VolcengineMaaSTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_EMBEDDING_ENDPOINT_ID"),
- "base_model_name": "Doubao-embedding",
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/volcengine_maas/test_llm.py b/api/tests/integration_tests/model_runtime/volcengine_maas/test_llm.py
deleted file mode 100644
index 8ff9c414046e7d..00000000000000
--- a/api/tests/integration_tests/model_runtime/volcengine_maas/test_llm.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.volcengine_maas.llm.llm import VolcengineMaaSLargeLanguageModel
-
-
-def test_validate_credentials_for_chat_model():
- model = VolcengineMaaSLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": "INVALID",
- "volc_secret_access_key": "INVALID",
- "endpoint_id": "INVALID",
- },
- )
-
- model.validate_credentials(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_MODEL_ENDPOINT_ID"),
- },
- )
-
-
-def test_invoke_model():
- model = VolcengineMaaSLargeLanguageModel()
-
- response = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_MODEL_ENDPOINT_ID"),
- "base_model_name": "Skylark2-pro-4k",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_model():
- model = VolcengineMaaSLargeLanguageModel()
-
- response = model.invoke(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_MODEL_ENDPOINT_ID"),
- "base_model_name": "Skylark2-pro-4k",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "top_k": 1,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = VolcengineMaaSLargeLanguageModel()
-
- response = model.get_num_tokens(
- model="NOT IMPORTANT",
- credentials={
- "api_endpoint_host": "maas-api.ml-platform-cn-beijing.volces.com",
- "volc_region": "cn-beijing",
- "volc_access_key_id": os.environ.get("VOLC_API_KEY"),
- "volc_secret_access_key": os.environ.get("VOLC_SECRET_KEY"),
- "endpoint_id": os.environ.get("VOLC_MODEL_ENDPOINT_ID"),
- "base_model_name": "Skylark2-pro-4k",
- },
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- tools=[],
- )
-
- assert isinstance(response, int)
- assert response == 6
diff --git a/api/tests/integration_tests/model_runtime/voyage/__init__.py b/api/tests/integration_tests/model_runtime/voyage/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/voyage/test_provider.py b/api/tests/integration_tests/model_runtime/voyage/test_provider.py
deleted file mode 100644
index 08978c88a961e7..00000000000000
--- a/api/tests/integration_tests/model_runtime/voyage/test_provider.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.voyage.voyage import VoyageProvider
-
-
-def test_validate_provider_credentials():
- provider = VoyageProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "hahahaha"})
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "object": "list",
- "data": [{"object": "embedding", "embedding": [0.23333 for _ in range(1024)], "index": 0}],
- "model": "voyage-3",
- "usage": {"total_tokens": 1},
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("VOYAGE_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/voyage/test_rerank.py b/api/tests/integration_tests/model_runtime/voyage/test_rerank.py
deleted file mode 100644
index e97a9e4c811c82..00000000000000
--- a/api/tests/integration_tests/model_runtime/voyage/test_rerank.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.voyage.rerank.rerank import VoyageRerankModel
-
-
-def test_validate_credentials():
- model = VoyageRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="rerank-lite-1",
- credentials={"api_key": "invalid_key"},
- )
- with patch("httpx.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "object": "list",
- "data": [
- {
- "relevance_score": 0.546875,
- "index": 0,
- "document": "Carson City is the capital city of the American state of Nevada. At the 2010 United "
- "States Census, Carson City had a population of 55,274.",
- },
- {
- "relevance_score": 0.4765625,
- "index": 1,
- "document": "The Commonwealth of the Northern Mariana Islands is a group of islands in the "
- "Pacific Ocean that are a political division controlled by the United States. Its "
- "capital is Saipan.",
- },
- ],
- "model": "rerank-lite-1",
- "usage": {"total_tokens": 96},
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- model.validate_credentials(
- model="rerank-lite-1",
- credentials={
- "api_key": os.environ.get("VOYAGE_API_KEY"),
- },
- )
-
-
-def test_invoke_model():
- model = VoyageRerankModel()
- with patch("httpx.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "object": "list",
- "data": [
- {
- "relevance_score": 0.84375,
- "index": 0,
- "document": "Kasumi is a girl name of Japanese origin meaning mist.",
- },
- {
- "relevance_score": 0.4765625,
- "index": 1,
- "document": "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music and she "
- "leads a team named PopiParty.",
- },
- ],
- "model": "rerank-lite-1",
- "usage": {"total_tokens": 59},
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- result = model.invoke(
- model="rerank-lite-1",
- credentials={
- "api_key": os.environ.get("VOYAGE_API_KEY"),
- },
- query="Who is Kasumi?",
- docs=[
- "Kasumi is a girl name of Japanese origin meaning mist.",
- "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music and she leads a team named "
- "PopiParty.",
- ],
- score_threshold=0.5,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.5
diff --git a/api/tests/integration_tests/model_runtime/voyage/test_text_embedding.py b/api/tests/integration_tests/model_runtime/voyage/test_text_embedding.py
deleted file mode 100644
index 75719672a9ecc9..00000000000000
--- a/api/tests/integration_tests/model_runtime/voyage/test_text_embedding.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import os
-from unittest.mock import Mock, patch
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.voyage.text_embedding.text_embedding import VoyageTextEmbeddingModel
-
-
-def test_validate_credentials():
- model = VoyageTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="voyage-3", credentials={"api_key": "invalid_key"})
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "object": "list",
- "data": [{"object": "embedding", "embedding": [0.23333 for _ in range(1024)], "index": 0}],
- "model": "voyage-3",
- "usage": {"total_tokens": 1},
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- model.validate_credentials(model="voyage-3", credentials={"api_key": os.environ.get("VOYAGE_API_KEY")})
-
-
-def test_invoke_model():
- model = VoyageTextEmbeddingModel()
-
- with patch("requests.post") as mock_post:
- mock_response = Mock()
- mock_response.json.return_value = {
- "object": "list",
- "data": [
- {"object": "embedding", "embedding": [0.23333 for _ in range(1024)], "index": 0},
- {"object": "embedding", "embedding": [0.23333 for _ in range(1024)], "index": 1},
- ],
- "model": "voyage-3",
- "usage": {"total_tokens": 2},
- }
- mock_response.status_code = 200
- mock_post.return_value = mock_response
- result = model.invoke(
- model="voyage-3",
- credentials={
- "api_key": os.environ.get("VOYAGE_API_KEY"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens == 2
-
-
-def test_get_num_tokens():
- model = VoyageTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="voyage-3",
- credentials={
- "api_key": os.environ.get("VOYAGE_API_KEY"),
- },
- texts=["ping"],
- )
-
- assert num_tokens == 1
diff --git a/api/tests/integration_tests/model_runtime/wenxin/__init__.py b/api/tests/integration_tests/model_runtime/wenxin/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/wenxin/test_embedding.py b/api/tests/integration_tests/model_runtime/wenxin/test_embedding.py
deleted file mode 100644
index ac38340aecf7d2..00000000000000
--- a/api/tests/integration_tests/model_runtime/wenxin/test_embedding.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-from time import sleep
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.model_providers.wenxin.text_embedding.text_embedding import WenxinTextEmbeddingModel
-
-
-def test_invoke_embedding_v1():
- sleep(3)
- model = WenxinTextEmbeddingModel()
-
- response = model.invoke(
- model="embedding-v1",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- texts=["hello", "你好", "xxxxx"],
- user="abc-123",
- )
-
- assert isinstance(response, TextEmbeddingResult)
- assert len(response.embeddings) == 3
- assert isinstance(response.embeddings[0], list)
-
-
-def test_invoke_embedding_bge_large_en():
- sleep(3)
- model = WenxinTextEmbeddingModel()
-
- response = model.invoke(
- model="bge-large-en",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- texts=["hello", "你好", "xxxxx"],
- user="abc-123",
- )
-
- assert isinstance(response, TextEmbeddingResult)
- assert len(response.embeddings) == 3
- assert isinstance(response.embeddings[0], list)
-
-
-def test_invoke_embedding_bge_large_zh():
- sleep(3)
- model = WenxinTextEmbeddingModel()
-
- response = model.invoke(
- model="bge-large-zh",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- texts=["hello", "你好", "xxxxx"],
- user="abc-123",
- )
-
- assert isinstance(response, TextEmbeddingResult)
- assert len(response.embeddings) == 3
- assert isinstance(response.embeddings[0], list)
-
-
-def test_invoke_embedding_tao_8k():
- sleep(3)
- model = WenxinTextEmbeddingModel()
-
- response = model.invoke(
- model="tao-8k",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- texts=["hello", "你好", "xxxxx"],
- user="abc-123",
- )
-
- assert isinstance(response, TextEmbeddingResult)
- assert len(response.embeddings) == 3
- assert isinstance(response.embeddings[0], list)
diff --git a/api/tests/integration_tests/model_runtime/wenxin/test_llm.py b/api/tests/integration_tests/model_runtime/wenxin/test_llm.py
deleted file mode 100644
index e2e58f15e025d8..00000000000000
--- a/api/tests/integration_tests/model_runtime/wenxin/test_llm.py
+++ /dev/null
@@ -1,214 +0,0 @@
-import os
-from collections.abc import Generator
-from time import sleep
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.wenxin.llm.llm import ErnieBotLargeLanguageModel
-
-
-def test_predefined_models():
- model = ErnieBotLargeLanguageModel()
- model_schemas = model.predefined_models()
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-def test_validate_credentials_for_chat_model():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="ernie-bot", credentials={"api_key": "invalid_key", "secret_key": "invalid_key"}
- )
-
- model.validate_credentials(
- model="ernie-bot",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- )
-
-
-def test_invoke_model_ernie_bot():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_model_ernie_bot_turbo():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot-turbo",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_model_ernie_8k():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot-8k",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_model_ernie_bot_4():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot-4",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-def test_invoke_stream_model():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-3.5-8k",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_invoke_model_with_system():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[SystemPromptMessage(content="你是Kasumi"), UserPromptMessage(content="你是谁?")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert "kasumi" in response.message.content.lower()
-
-
-def test_invoke_with_search():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.invoke(
- model="ernie-bot",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="北京今天的天气怎么样")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- "disable_search": True,
- },
- stop=[],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- total_message = ""
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- total_message += chunk.delta.message.content
- print(chunk.delta.message.content)
- assert len(chunk.delta.message.content) > 0 if not chunk.delta.finish_reason else True
-
- # there should be 对不起、我不能、不支持……
- assert "不" in total_message or "抱歉" in total_message or "无法" in total_message
-
-
-def test_get_num_tokens():
- sleep(3)
- model = ErnieBotLargeLanguageModel()
-
- response = model.get_num_tokens(
- model="ernie-bot",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- tools=[],
- )
-
- assert isinstance(response, int)
- assert response == 10
diff --git a/api/tests/integration_tests/model_runtime/wenxin/test_provider.py b/api/tests/integration_tests/model_runtime/wenxin/test_provider.py
deleted file mode 100644
index 337c3d2a8010dd..00000000000000
--- a/api/tests/integration_tests/model_runtime/wenxin/test_provider.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.wenxin.wenxin import WenxinProvider
-
-
-def test_validate_provider_credentials():
- provider = WenxinProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={"api_key": "hahahaha", "secret_key": "hahahaha"})
-
- provider.validate_provider_credentials(
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")}
- )
diff --git a/api/tests/integration_tests/model_runtime/wenxin/test_rerank.py b/api/tests/integration_tests/model_runtime/wenxin/test_rerank.py
deleted file mode 100644
index 33c803e8e1964e..00000000000000
--- a/api/tests/integration_tests/model_runtime/wenxin/test_rerank.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-from time import sleep
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.model_providers.wenxin.rerank.rerank import WenxinRerankModel
-
-
-def test_invoke_bce_reranker_base_v1():
- sleep(3)
- model = WenxinRerankModel()
-
- response = model.invoke(
- model="bce-reranker-base_v1",
- credentials={"api_key": os.environ.get("WENXIN_API_KEY"), "secret_key": os.environ.get("WENXIN_SECRET_KEY")},
- query="What is Deep Learning?",
- docs=["Deep Learning is ...", "My Book is ..."],
- user="abc-123",
- )
-
- assert isinstance(response, RerankResult)
- assert len(response.docs) == 2
diff --git a/api/tests/integration_tests/model_runtime/x/__init__.py b/api/tests/integration_tests/model_runtime/x/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/x/test_llm.py b/api/tests/integration_tests/model_runtime/x/test_llm.py
deleted file mode 100644
index 647a2f648075e5..00000000000000
--- a/api/tests/integration_tests/model_runtime/x/test_llm.py
+++ /dev/null
@@ -1,204 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.entities.model_entities import AIModelEntity
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.x.llm.llm import XAILargeLanguageModel
-
-"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def test_predefined_models():
- model = XAILargeLanguageModel()
- model_schemas = model.predefined_models()
-
- assert len(model_schemas) >= 1
- assert isinstance(model_schemas[0], AIModelEntity)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock):
- model = XAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- # model name to gpt-3.5-turbo because of mocking
- model.validate_credentials(
- model="gpt-3.5-turbo",
- credentials={"api_key": "invalid_key", "endpoint_url": os.environ.get("XAI_API_BASE"), "mode": "chat"},
- )
-
- model.validate_credentials(
- model="grok-beta",
- credentials={
- "api_key": os.environ.get("XAI_API_KEY"),
- "endpoint_url": os.environ.get("XAI_API_BASE"),
- "mode": "chat",
- },
- )
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model(setup_openai_mock):
- model = XAILargeLanguageModel()
-
- result = model.invoke(
- model="grok-beta",
- credentials={
- "api_key": os.environ.get("XAI_API_KEY"),
- "endpoint_url": os.environ.get("XAI_API_BASE"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.0,
- "top_p": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "max_tokens": 10,
- },
- stop=["How"],
- stream=False,
- user="foo",
- )
-
- assert isinstance(result, LLMResult)
- assert len(result.message.content) > 0
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_chat_model_with_tools(setup_openai_mock):
- model = XAILargeLanguageModel()
-
- result = model.invoke(
- model="grok-beta",
- credentials={
- "api_key": os.environ.get("XAI_API_KEY"),
- "endpoint_url": os.environ.get("XAI_API_BASE"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(
- content="what's the weather today in London?",
- ),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- PromptMessageTool(
- name="get_stock_price",
- description="Get the current stock price",
- parameters={
- "type": "object",
- "properties": {"symbol": {"type": "string", "description": "The stock symbol"}},
- "required": ["symbol"],
- },
- ),
- ],
- stream=False,
- user="foo",
- )
-
- assert isinstance(result, LLMResult)
- assert isinstance(result.message, AssistantPromptMessage)
-
-
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock):
- model = XAILargeLanguageModel()
-
- result = model.invoke(
- model="grok-beta",
- credentials={
- "api_key": os.environ.get("XAI_API_KEY"),
- "endpoint_url": os.environ.get("XAI_API_BASE"),
- "mode": "chat",
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={"temperature": 0.0, "max_tokens": 100},
- stream=True,
- user="foo",
- )
-
- assert isinstance(result, Generator)
-
- for chunk in result:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
- if chunk.delta.finish_reason is not None:
- assert chunk.delta.usage is not None
- assert chunk.delta.usage.completion_tokens > 0
-
-
-def test_get_num_tokens():
- model = XAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="grok-beta",
- credentials={"api_key": os.environ.get("XAI_API_KEY"), "endpoint_url": os.environ.get("XAI_API_BASE")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- )
-
- assert num_tokens == 10
-
- num_tokens = model.get_num_tokens(
- model="grok-beta",
- credentials={"api_key": os.environ.get("XAI_API_KEY"), "endpoint_url": os.environ.get("XAI_API_BASE")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_weather",
- description="Determine weather in my location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- ),
- ],
- )
-
- assert num_tokens == 77
diff --git a/api/tests/integration_tests/model_runtime/xinference/__init__.py b/api/tests/integration_tests/model_runtime/xinference/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/xinference/test_embeddings.py b/api/tests/integration_tests/model_runtime/xinference/test_embeddings.py
deleted file mode 100644
index 8e778d005a4bc3..00000000000000
--- a/api/tests/integration_tests/model_runtime/xinference/test_embeddings.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.xinference.text_embedding.text_embedding import XinferenceTextEmbeddingModel
-from tests.integration_tests.model_runtime.__mock.xinference import MOCK, setup_xinference_mock
-
-
-@pytest.mark.parametrize("setup_xinference_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_xinference_mock):
- model = XinferenceTextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-base-en",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": "www " + os.environ.get("XINFERENCE_EMBEDDINGS_MODEL_UID"),
- },
- )
-
- model.validate_credentials(
- model="bge-base-en",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_EMBEDDINGS_MODEL_UID"),
- },
- )
-
-
-@pytest.mark.parametrize("setup_xinference_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_xinference_mock):
- model = XinferenceTextEmbeddingModel()
-
- result = model.invoke(
- model="bge-base-en",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_EMBEDDINGS_MODEL_UID"),
- },
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens > 0
-
-
-def test_get_num_tokens():
- model = XinferenceTextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="bge-base-en",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_EMBEDDINGS_MODEL_UID"),
- },
- texts=["hello", "world"],
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/model_runtime/xinference/test_llm.py b/api/tests/integration_tests/model_runtime/xinference/test_llm.py
deleted file mode 100644
index 5e4cde36389b5b..00000000000000
--- a/api/tests/integration_tests/model_runtime/xinference/test_llm.py
+++ /dev/null
@@ -1,364 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.xinference.llm.llm import XinferenceAILargeLanguageModel
-
-"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-from tests.integration_tests.model_runtime.__mock.xinference import setup_xinference_mock
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("chat", "none")], indirect=True)
-def test_validate_credentials_for_chat_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": "www " + os.environ.get("XINFERENCE_CHAT_MODEL_UID"),
- },
- )
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="aaaaa", credentials={"server_url": "", "model_uid": ""})
-
- model.validate_credentials(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_CHAT_MODEL_UID"),
- },
- )
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("chat", "none")], indirect=True)
-def test_invoke_chat_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- response = model.invoke(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_CHAT_MODEL_UID"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("chat", "none")], indirect=True)
-def test_invoke_stream_chat_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- response = model.invoke(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_CHAT_MODEL_UID"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-"""
- Function calling of xinference does not support stream mode currently
-"""
-# def test_invoke_stream_chat_model_with_functions():
-# model = XinferenceAILargeLanguageModel()
-
-# response = model.invoke(
-# model='ChatGLM3-6b',
-# credentials={
-# 'server_url': os.environ.get('XINFERENCE_SERVER_URL'),
-# 'model_type': 'text-generation',
-# 'model_name': 'ChatGLM3',
-# 'model_uid': os.environ.get('XINFERENCE_CHAT_MODEL_UID')
-# },
-# prompt_messages=[
-# SystemPromptMessage(
-# content='你是一个天气机器人,可以通过调用函数来获取天气信息',
-# ),
-# UserPromptMessage(
-# content='波士顿天气如何?'
-# )
-# ],
-# model_parameters={
-# 'temperature': 0,
-# 'top_p': 1.0,
-# },
-# stop=['you'],
-# user='abc-123',
-# stream=True,
-# tools=[
-# PromptMessageTool(
-# name='get_current_weather',
-# description='Get the current weather in a given location',
-# parameters={
-# "type": "object",
-# "properties": {
-# "location": {
-# "type": "string",
-# "description": "The city and state e.g. San Francisco, CA"
-# },
-# "unit": {
-# "type": "string",
-# "enum": ["celsius", "fahrenheit"]
-# }
-# },
-# "required": [
-# "location"
-# ]
-# }
-# )
-# ]
-# )
-
-# assert isinstance(response, Generator)
-
-# call: LLMResultChunk = None
-# chunks = []
-
-# for chunk in response:
-# chunks.append(chunk)
-# assert isinstance(chunk, LLMResultChunk)
-# assert isinstance(chunk.delta, LLMResultChunkDelta)
-# assert isinstance(chunk.delta.message, AssistantPromptMessage)
-# assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-# if chunk.delta.message.tool_calls and len(chunk.delta.message.tool_calls) > 0:
-# call = chunk
-# break
-
-# assert call is not None
-# assert call.delta.message.tool_calls[0].function.name == 'get_current_weather'
-
-# def test_invoke_chat_model_with_functions():
-# model = XinferenceAILargeLanguageModel()
-
-# response = model.invoke(
-# model='ChatGLM3-6b',
-# credentials={
-# 'server_url': os.environ.get('XINFERENCE_SERVER_URL'),
-# 'model_type': 'text-generation',
-# 'model_name': 'ChatGLM3',
-# 'model_uid': os.environ.get('XINFERENCE_CHAT_MODEL_UID')
-# },
-# prompt_messages=[
-# UserPromptMessage(
-# content='What is the weather like in San Francisco?'
-# )
-# ],
-# model_parameters={
-# 'temperature': 0.7,
-# 'top_p': 1.0,
-# },
-# stop=['you'],
-# user='abc-123',
-# stream=False,
-# tools=[
-# PromptMessageTool(
-# name='get_current_weather',
-# description='Get the current weather in a given location',
-# parameters={
-# "type": "object",
-# "properties": {
-# "location": {
-# "type": "string",
-# "description": "The city and state e.g. San Francisco, CA"
-# },
-# "unit": {
-# "type": "string",
-# "enum": [
-# "c",
-# "f"
-# ]
-# }
-# },
-# "required": [
-# "location"
-# ]
-# }
-# )
-# ]
-# )
-
-# assert isinstance(response, LLMResult)
-# assert len(response.message.content) > 0
-# assert response.usage.total_tokens > 0
-# assert response.message.tool_calls[0].function.name == 'get_current_weather'
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("completion", "none")], indirect=True)
-def test_validate_credentials_for_generation_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="alapaca",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": "www " + os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- )
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="alapaca", credentials={"server_url": "", "model_uid": ""})
-
- model.validate_credentials(
- model="alapaca",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- )
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("completion", "none")], indirect=True)
-def test_invoke_generation_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- response = model.invoke(
- model="alapaca",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- prompt_messages=[UserPromptMessage(content="the United States is")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- user="abc-123",
- stream=False,
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
- assert response.usage.total_tokens > 0
-
-
-@pytest.mark.parametrize(("setup_openai_mock", "setup_xinference_mock"), [("completion", "none")], indirect=True)
-def test_invoke_stream_generation_model(setup_openai_mock, setup_xinference_mock):
- model = XinferenceAILargeLanguageModel()
-
- response = model.invoke(
- model="alapaca",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- prompt_messages=[UserPromptMessage(content="the United States is")],
- model_parameters={
- "temperature": 0.7,
- "top_p": 1.0,
- },
- stop=["you"],
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = XinferenceAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 77
-
- num_tokens = model.get_num_tokens(
- model="ChatGLM3",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_GENERATION_MODEL_UID"),
- },
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert isinstance(num_tokens, int)
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/xinference/test_rerank.py b/api/tests/integration_tests/model_runtime/xinference/test_rerank.py
deleted file mode 100644
index 71ac4eef7c22be..00000000000000
--- a/api/tests/integration_tests/model_runtime/xinference/test_rerank.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.rerank_entities import RerankResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.xinference.rerank.rerank import XinferenceRerankModel
-from tests.integration_tests.model_runtime.__mock.xinference import MOCK, setup_xinference_mock
-
-
-@pytest.mark.parametrize("setup_xinference_mock", [["none"]], indirect=True)
-def test_validate_credentials(setup_xinference_mock):
- model = XinferenceRerankModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(
- model="bge-reranker-base",
- credentials={"server_url": "awdawdaw", "model_uid": os.environ.get("XINFERENCE_RERANK_MODEL_UID")},
- )
-
- model.validate_credentials(
- model="bge-reranker-base",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_RERANK_MODEL_UID"),
- },
- )
-
-
-@pytest.mark.parametrize("setup_xinference_mock", [["none"]], indirect=True)
-def test_invoke_model(setup_xinference_mock):
- model = XinferenceRerankModel()
-
- result = model.invoke(
- model="bge-reranker-base",
- credentials={
- "server_url": os.environ.get("XINFERENCE_SERVER_URL"),
- "model_uid": os.environ.get("XINFERENCE_RERANK_MODEL_UID"),
- },
- query="Who is Kasumi?",
- docs=[
- 'Kasumi is a girl\'s name of Japanese origin meaning "mist".',
- "Her music is a kawaii bass, a mix of future bass, pop, and kawaii music ",
- "and she leads a team named PopiParty.",
- ],
- score_threshold=0.8,
- )
-
- assert isinstance(result, RerankResult)
- assert len(result.docs) == 1
- assert result.docs[0].index == 0
- assert result.docs[0].score >= 0.8
diff --git a/api/tests/integration_tests/model_runtime/zhinao/__init__.py b/api/tests/integration_tests/model_runtime/zhinao/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/zhinao/test_llm.py b/api/tests/integration_tests/model_runtime/zhinao/test_llm.py
deleted file mode 100644
index 4ca1b864764818..00000000000000
--- a/api/tests/integration_tests/model_runtime/zhinao/test_llm.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import AssistantPromptMessage, SystemPromptMessage, UserPromptMessage
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.zhinao.llm.llm import ZhinaoLargeLanguageModel
-
-
-def test_validate_credentials():
- model = ZhinaoLargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="360gpt2-pro", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="360gpt2-pro", credentials={"api_key": os.environ.get("ZHINAO_API_KEY")})
-
-
-def test_invoke_model():
- model = ZhinaoLargeLanguageModel()
-
- response = model.invoke(
- model="360gpt2-pro",
- credentials={"api_key": os.environ.get("ZHINAO_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 0.5, "max_tokens": 10},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = ZhinaoLargeLanguageModel()
-
- response = model.invoke(
- model="360gpt2-pro",
- credentials={"api_key": os.environ.get("ZHINAO_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.5, "max_tokens": 100, "seed": 1234},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = ZhinaoLargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="360gpt2-pro",
- credentials={"api_key": os.environ.get("ZHINAO_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 21
diff --git a/api/tests/integration_tests/model_runtime/zhinao/test_provider.py b/api/tests/integration_tests/model_runtime/zhinao/test_provider.py
deleted file mode 100644
index c22f797919597c..00000000000000
--- a/api/tests/integration_tests/model_runtime/zhinao/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.zhinao.zhinao import ZhinaoProvider
-
-
-def test_validate_provider_credentials():
- provider = ZhinaoProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("ZHINAO_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/zhipuai/__init__.py b/api/tests/integration_tests/model_runtime/zhipuai/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/integration_tests/model_runtime/zhipuai/test_llm.py b/api/tests/integration_tests/model_runtime/zhipuai/test_llm.py
deleted file mode 100644
index 20380513eaa789..00000000000000
--- a/api/tests/integration_tests/model_runtime/zhipuai/test_llm.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import os
-from collections.abc import Generator
-
-import pytest
-
-from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta
-from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
- PromptMessageTool,
- SystemPromptMessage,
- UserPromptMessage,
-)
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.zhipuai.llm.llm import ZhipuAILargeLanguageModel
-
-
-def test_validate_credentials():
- model = ZhipuAILargeLanguageModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="chatglm_turbo", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="chatglm_turbo", credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")})
-
-
-def test_invoke_model():
- model = ZhipuAILargeLanguageModel()
-
- response = model.invoke(
- model="chatglm_turbo",
- credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Who are you?")],
- model_parameters={"temperature": 0.9, "top_p": 0.7},
- stop=["How"],
- stream=False,
- user="abc-123",
- )
-
- assert isinstance(response, LLMResult)
- assert len(response.message.content) > 0
-
-
-def test_invoke_stream_model():
- model = ZhipuAILargeLanguageModel()
-
- response = model.invoke(
- model="chatglm_turbo",
- credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")},
- prompt_messages=[UserPromptMessage(content="Hello World!")],
- model_parameters={"temperature": 0.9, "top_p": 0.7},
- stream=True,
- user="abc-123",
- )
-
- assert isinstance(response, Generator)
-
- for chunk in response:
- assert isinstance(chunk, LLMResultChunk)
- assert isinstance(chunk.delta, LLMResultChunkDelta)
- assert isinstance(chunk.delta.message, AssistantPromptMessage)
- assert len(chunk.delta.message.content) > 0 if chunk.delta.finish_reason is None else True
-
-
-def test_get_num_tokens():
- model = ZhipuAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="chatglm_turbo",
- credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")},
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 14
-
-
-def test_get_tools_num_tokens():
- model = ZhipuAILargeLanguageModel()
-
- num_tokens = model.get_num_tokens(
- model="tools",
- credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")},
- tools=[
- PromptMessageTool(
- name="get_current_weather",
- description="Get the current weather in a given location",
- parameters={
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
- "unit": {"type": "string", "enum": ["c", "f"]},
- },
- "required": ["location"],
- },
- )
- ],
- prompt_messages=[
- SystemPromptMessage(
- content="You are a helpful AI assistant.",
- ),
- UserPromptMessage(content="Hello World!"),
- ],
- )
-
- assert num_tokens == 88
diff --git a/api/tests/integration_tests/model_runtime/zhipuai/test_provider.py b/api/tests/integration_tests/model_runtime/zhipuai/test_provider.py
deleted file mode 100644
index cb5bc0b20aafc1..00000000000000
--- a/api/tests/integration_tests/model_runtime/zhipuai/test_provider.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.zhipuai.zhipuai import ZhipuaiProvider
-
-
-def test_validate_provider_credentials():
- provider = ZhipuaiProvider()
-
- with pytest.raises(CredentialsValidateFailedError):
- provider.validate_provider_credentials(credentials={})
-
- provider.validate_provider_credentials(credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")})
diff --git a/api/tests/integration_tests/model_runtime/zhipuai/test_text_embedding.py b/api/tests/integration_tests/model_runtime/zhipuai/test_text_embedding.py
deleted file mode 100644
index 9c97c91ecbdd94..00000000000000
--- a/api/tests/integration_tests/model_runtime/zhipuai/test_text_embedding.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import os
-
-import pytest
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.errors.validate import CredentialsValidateFailedError
-from core.model_runtime.model_providers.zhipuai.text_embedding.text_embedding import ZhipuAITextEmbeddingModel
-
-
-def test_validate_credentials():
- model = ZhipuAITextEmbeddingModel()
-
- with pytest.raises(CredentialsValidateFailedError):
- model.validate_credentials(model="text_embedding", credentials={"api_key": "invalid_key"})
-
- model.validate_credentials(model="text_embedding", credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")})
-
-
-def test_invoke_model():
- model = ZhipuAITextEmbeddingModel()
-
- result = model.invoke(
- model="text_embedding",
- credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")},
- texts=["hello", "world"],
- user="abc-123",
- )
-
- assert isinstance(result, TextEmbeddingResult)
- assert len(result.embeddings) == 2
- assert result.usage.total_tokens > 0
-
-
-def test_get_num_tokens():
- model = ZhipuAITextEmbeddingModel()
-
- num_tokens = model.get_num_tokens(
- model="text_embedding", credentials={"api_key": os.environ.get("ZHIPUAI_API_KEY")}, texts=["hello", "world"]
- )
-
- assert num_tokens == 2
diff --git a/api/tests/integration_tests/plugin/__mock/http.py b/api/tests/integration_tests/plugin/__mock/http.py
new file mode 100644
index 00000000000000..25177274c6c049
--- /dev/null
+++ b/api/tests/integration_tests/plugin/__mock/http.py
@@ -0,0 +1,66 @@
+import os
+from typing import Literal
+
+import pytest
+import requests
+from _pytest.monkeypatch import MonkeyPatch
+
+from core.plugin.entities.plugin_daemon import PluginDaemonBasicResponse
+from core.tools.entities.common_entities import I18nObject
+from core.tools.entities.tool_entities import ToolProviderEntity, ToolProviderIdentity
+
+
+class MockedHttp:
+ @classmethod
+ def list_tools(cls) -> list[ToolProviderEntity]:
+ return [
+ ToolProviderEntity(
+ identity=ToolProviderIdentity(
+ author="Yeuoly",
+ name="Yeuoly",
+ description=I18nObject(en_US="Yeuoly"),
+ icon="ssss.svg",
+ label=I18nObject(en_US="Yeuoly"),
+ )
+ )
+ ]
+
+ @classmethod
+ def requests_request(
+ cls, method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"], url: str, **kwargs
+ ) -> requests.Response:
+ """
+ Mocked requests.request
+ """
+ request = requests.PreparedRequest()
+ request.method = method
+ request.url = url
+ if url.endswith("/tools"):
+ content = PluginDaemonBasicResponse[list[ToolProviderEntity]](
+ code=0, message="success", data=cls.list_tools()
+ ).model_dump_json()
+ else:
+ raise ValueError("")
+
+ response = requests.Response()
+ response.status_code = 200
+ response.request = request
+ response._content = content.encode("utf-8")
+ return response
+
+
+MOCK_SWITCH = os.getenv("MOCK_SWITCH", "false").lower() == "true"
+
+
+@pytest.fixture
+def setup_http_mock(request, monkeypatch: MonkeyPatch):
+ if MOCK_SWITCH:
+ monkeypatch.setattr(requests, "request", MockedHttp.requests_request)
+
+ def unpatch():
+ monkeypatch.undo()
+
+ yield
+
+ if MOCK_SWITCH:
+ unpatch()
diff --git a/api/tests/integration_tests/plugin/tools/test_fetch_all_tools.py b/api/tests/integration_tests/plugin/tools/test_fetch_all_tools.py
new file mode 100644
index 00000000000000..c6d836ed6dd965
--- /dev/null
+++ b/api/tests/integration_tests/plugin/tools/test_fetch_all_tools.py
@@ -0,0 +1,8 @@
+from core.plugin.manager.tool import PluginToolManager
+from tests.integration_tests.plugin.__mock.http import setup_http_mock
+
+
+def test_fetch_all_plugin_tools(setup_http_mock):
+ manager = PluginToolManager()
+ tools = manager.fetch_tool_providers(tenant_id="test-tenant")
+ assert len(tools) >= 1
diff --git a/api/tests/integration_tests/tools/api_tool/test_api_tool.py b/api/tests/integration_tests/tools/api_tool/test_api_tool.py
index 1bd75b91f745d6..9acc94e1108778 100644
--- a/api/tests/integration_tests/tools/api_tool/test_api_tool.py
+++ b/api/tests/integration_tests/tools/api_tool/test_api_tool.py
@@ -1,5 +1,8 @@
-from core.tools.tool.api_tool import ApiTool
-from core.tools.tool.tool import Tool
+from core.tools.__base.tool_runtime import ToolRuntime
+from core.tools.custom_tool.tool import ApiTool
+from core.tools.entities.common_entities import I18nObject
+from core.tools.entities.tool_bundle import ApiToolBundle
+from core.tools.entities.tool_entities import ToolEntity, ToolIdentity
from tests.integration_tests.tools.__mock.http import setup_http_mock
tool_bundle = {
@@ -29,7 +32,13 @@
def test_api_tool(setup_http_mock):
- tool = ApiTool(api_bundle=tool_bundle, runtime=Tool.Runtime(credentials={"auth_type": "none"}))
+ tool = ApiTool(
+ entity=ToolEntity(
+ identity=ToolIdentity(provider="", author="", name="", label=I18nObject()),
+ ),
+ api_bundle=ApiToolBundle(**tool_bundle),
+ runtime=ToolRuntime(tenant_id="", credentials={"auth_type": "none"}),
+ )
headers = tool.assembling_request(parameters)
response = tool.do_http_request(tool.api_bundle.server_url, tool.api_bundle.method, headers, parameters)
diff --git a/api/tests/integration_tests/tools/test_all_provider.py b/api/tests/integration_tests/tools/test_all_provider.py
deleted file mode 100644
index 2dfce749b3e16f..00000000000000
--- a/api/tests/integration_tests/tools/test_all_provider.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import pytest
-
-from core.tools.tool_manager import ToolManager
-
-provider_generator = ToolManager.list_builtin_providers()
-provider_names = [provider.identity.name for provider in provider_generator]
-ToolManager.clear_builtin_providers_cache()
-provider_generator = ToolManager.list_builtin_providers()
-
-
-@pytest.mark.parametrize("name", provider_names)
-def test_tool_providers(benchmark, name):
- """
- Test that all tool providers can be loaded
- """
-
- def test(generator):
- try:
- return next(generator)
- except StopIteration:
- return None
-
- benchmark.pedantic(test, args=(provider_generator,), iterations=1, rounds=1)
diff --git a/api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py b/api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
new file mode 100644
index 00000000000000..294a168310e732
--- /dev/null
+++ b/api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
@@ -0,0 +1,59 @@
+import time
+
+import pymysql
+
+
+def check_tiflash_ready() -> bool:
+ try:
+ connection = pymysql.connect(
+ host="localhost",
+ port=4000,
+ user="root",
+ password="",
+ )
+
+ with connection.cursor() as cursor:
+ # Doc reference:
+ # https://docs.pingcap.com/zh/tidb/stable/information-schema-cluster-hardware
+ select_tiflash_query = """
+ SELECT * FROM information_schema.cluster_hardware
+ WHERE TYPE='tiflash'
+ LIMIT 1;
+ """
+ cursor.execute(select_tiflash_query)
+ result = cursor.fetchall()
+ return result is not None and len(result) > 0
+ except Exception as e:
+ print(f"TiFlash is not ready. Exception: {e}")
+ return False
+ finally:
+ if connection:
+ connection.close()
+
+
+def main():
+ max_attempts = 30
+ retry_interval_seconds = 2
+ is_tiflash_ready = False
+ for attempt in range(max_attempts):
+ try:
+ is_tiflash_ready = check_tiflash_ready()
+ except Exception as e:
+ print(f"TiFlash is not ready. Exception: {e}")
+ is_tiflash_ready = False
+
+ if is_tiflash_ready:
+ break
+ else:
+ print(f"Attempt {attempt + 1} failed,retry in {retry_interval_seconds} seconds...")
+ time.sleep(retry_interval_seconds)
+
+ if is_tiflash_ready:
+ print("TiFlash is ready in TiDB.")
+ else:
+ print(f"TiFlash is not ready in TiDB after {max_attempts} attempting checks.")
+ exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/api/tests/integration_tests/workflow/nodes/__mock/model.py b/api/tests/integration_tests/workflow/nodes/__mock/model.py
new file mode 100644
index 00000000000000..7c48d84d69ee1f
--- /dev/null
+++ b/api/tests/integration_tests/workflow/nodes/__mock/model.py
@@ -0,0 +1,50 @@
+from unittest.mock import MagicMock
+
+from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
+from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
+from core.entities.provider_entities import CustomConfiguration, CustomProviderConfiguration, SystemConfiguration
+from core.model_manager import ModelInstance
+from core.model_runtime.entities.model_entities import ModelType
+from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
+from models.provider import ProviderType
+
+
+def get_mocked_fetch_model_config(
+ provider: str,
+ model: str,
+ mode: str,
+ credentials: dict,
+):
+ model_provider_factory = ModelProviderFactory(tenant_id="test_tenant")
+ model_type_instance = model_provider_factory.get_model_type_instance(provider, ModelType.LLM)
+ provider_model_bundle = ProviderModelBundle(
+ configuration=ProviderConfiguration(
+ tenant_id="1",
+ provider=model_provider_factory.get_provider_schema(provider),
+ preferred_provider_type=ProviderType.CUSTOM,
+ using_provider_type=ProviderType.CUSTOM,
+ system_configuration=SystemConfiguration(enabled=False),
+ custom_configuration=CustomConfiguration(provider=CustomProviderConfiguration(credentials=credentials)),
+ model_settings=[],
+ ),
+ model_type_instance=model_type_instance,
+ )
+ model_instance = ModelInstance(provider_model_bundle=provider_model_bundle, model=model)
+ model_schema = model_provider_factory.get_model_schema(
+ provider=provider,
+ model_type=model_type_instance.model_type,
+ model=model,
+ credentials=credentials,
+ )
+ assert model_schema is not None
+ model_config = ModelConfigWithCredentialsEntity(
+ model=model,
+ provider=provider,
+ mode=mode,
+ credentials=credentials,
+ parameters={},
+ model_schema=model_schema,
+ provider_model_bundle=provider_model_bundle,
+ )
+
+ return MagicMock(return_value=(model_instance, model_config))
diff --git a/api/tests/integration_tests/workflow/nodes/test_llm.py b/api/tests/integration_tests/workflow/nodes/test_llm.py
index 9a23949b38939e..22354df196e12f 100644
--- a/api/tests/integration_tests/workflow/nodes/test_llm.py
+++ b/api/tests/integration_tests/workflow/nodes/test_llm.py
@@ -7,12 +7,7 @@
import pytest
-from core.app.entities.app_invoke_entities import InvokeFrom, ModelConfigWithCredentialsEntity
-from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
-from core.entities.provider_entities import CustomConfiguration, CustomProviderConfiguration, SystemConfiguration
-from core.model_manager import ModelInstance
-from core.model_runtime.entities.model_entities import ModelType
-from core.model_runtime.model_providers import ModelProviderFactory
+from core.app.entities.app_invoke_entities import InvokeFrom
from core.workflow.entities.variable_pool import VariablePool
from core.workflow.enums import SystemVariableKey
from core.workflow.graph_engine.entities.graph import Graph
@@ -22,11 +17,11 @@
from core.workflow.nodes.llm.node import LLMNode
from extensions.ext_database import db
from models.enums import UserFrom
-from models.provider import ProviderType
from models.workflow import WorkflowNodeExecutionStatus, WorkflowType
+from tests.integration_tests.workflow.nodes.__mock.model import get_mocked_fetch_model_config
"""FOR MOCK FIXTURES, DO NOT REMOVE"""
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
+from tests.integration_tests.model_runtime.__mock.plugin_daemon import setup_model_mock
from tests.integration_tests.workflow.nodes.__mock.code_executor import setup_code_executor_mock
@@ -81,15 +76,19 @@ def init_llm_node(config: dict) -> LLMNode:
return node
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_execute_llm(setup_openai_mock):
+def test_execute_llm(setup_model_mock):
node = init_llm_node(
config={
"id": "llm",
"data": {
"title": "123",
"type": "llm",
- "model": {"provider": "openai", "name": "gpt-3.5-turbo", "mode": "chat", "completion_params": {}},
+ "model": {
+ "provider": "langgenius/openai/openai",
+ "name": "gpt-3.5-turbo",
+ "mode": "chat",
+ "completion_params": {},
+ },
"prompt_template": [
{"role": "system", "text": "you are a helpful assistant.\ntoday's weather is {{#abc.output#}}."},
{"role": "user", "text": "{{#sys.query#}}"},
@@ -103,39 +102,16 @@ def test_execute_llm(setup_openai_mock):
credentials = {"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- provider_instance = ModelProviderFactory().get_provider_instance("openai")
- model_type_instance = provider_instance.get_model_instance(ModelType.LLM)
- provider_model_bundle = ProviderModelBundle(
- configuration=ProviderConfiguration(
- tenant_id="1",
- provider=provider_instance.get_provider_schema(),
- preferred_provider_type=ProviderType.CUSTOM,
- using_provider_type=ProviderType.CUSTOM,
- system_configuration=SystemConfiguration(enabled=False),
- custom_configuration=CustomConfiguration(provider=CustomProviderConfiguration(credentials=credentials)),
- model_settings=[],
- ),
- provider_instance=provider_instance,
- model_type_instance=model_type_instance,
- )
- model_instance = ModelInstance(provider_model_bundle=provider_model_bundle, model="gpt-3.5-turbo")
- model_schema = model_type_instance.get_model_schema("gpt-3.5-turbo")
- assert model_schema is not None
- model_config = ModelConfigWithCredentialsEntity(
+ # Mock db.session.close()
+ db.session.close = MagicMock()
+
+ node._fetch_model_config = get_mocked_fetch_model_config(
+ provider="langgenius/openai/openai",
model="gpt-3.5-turbo",
- provider="openai",
mode="chat",
credentials=credentials,
- parameters={},
- model_schema=model_schema,
- provider_model_bundle=provider_model_bundle,
)
- # Mock db.session.close()
- db.session.close = MagicMock()
-
- node._fetch_model_config = MagicMock(return_value=(model_instance, model_config))
-
# execute node
result = node._run()
assert isinstance(result, Generator)
@@ -150,8 +126,7 @@ def test_execute_llm(setup_openai_mock):
@pytest.mark.parametrize("setup_code_executor_mock", [["none"]], indirect=True)
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_execute_llm_with_jinja2(setup_code_executor_mock, setup_openai_mock):
+def test_execute_llm_with_jinja2(setup_code_executor_mock, setup_model_mock):
"""
Test execute LLM node with jinja2
"""
@@ -191,40 +166,16 @@ def test_execute_llm_with_jinja2(setup_code_executor_mock, setup_openai_mock):
credentials = {"openai_api_key": os.environ.get("OPENAI_API_KEY")}
- provider_instance = ModelProviderFactory().get_provider_instance("openai")
- model_type_instance = provider_instance.get_model_instance(ModelType.LLM)
- provider_model_bundle = ProviderModelBundle(
- configuration=ProviderConfiguration(
- tenant_id="1",
- provider=provider_instance.get_provider_schema(),
- preferred_provider_type=ProviderType.CUSTOM,
- using_provider_type=ProviderType.CUSTOM,
- system_configuration=SystemConfiguration(enabled=False),
- custom_configuration=CustomConfiguration(provider=CustomProviderConfiguration(credentials=credentials)),
- model_settings=[],
- ),
- provider_instance=provider_instance,
- model_type_instance=model_type_instance,
- )
+ # Mock db.session.close()
+ db.session.close = MagicMock()
- model_instance = ModelInstance(provider_model_bundle=provider_model_bundle, model="gpt-3.5-turbo")
- model_schema = model_type_instance.get_model_schema("gpt-3.5-turbo")
- assert model_schema is not None
- model_config = ModelConfigWithCredentialsEntity(
+ node._fetch_model_config = get_mocked_fetch_model_config(
+ provider="langgenius/openai/openai",
model="gpt-3.5-turbo",
- provider="openai",
mode="chat",
credentials=credentials,
- parameters={},
- model_schema=model_schema,
- provider_model_bundle=provider_model_bundle,
)
- # Mock db.session.close()
- db.session.close = MagicMock()
-
- node._fetch_model_config = MagicMock(return_value=(model_instance, model_config))
-
# execute node
result = node._run()
diff --git a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py
index 42a058d29bae8d..ca055f5cc5f49d 100644
--- a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py
+++ b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py
@@ -4,14 +4,7 @@
from typing import Optional
from unittest.mock import MagicMock
-import pytest
-
-from core.app.entities.app_invoke_entities import InvokeFrom, ModelConfigWithCredentialsEntity
-from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
-from core.entities.provider_entities import CustomConfiguration, CustomProviderConfiguration, SystemConfiguration
-from core.model_manager import ModelInstance
-from core.model_runtime.entities.model_entities import ModelType
-from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
+from core.app.entities.app_invoke_entities import InvokeFrom
from core.workflow.entities.variable_pool import VariablePool
from core.workflow.enums import SystemVariableKey
from core.workflow.graph_engine.entities.graph import Graph
@@ -20,49 +13,11 @@
from core.workflow.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode
from extensions.ext_database import db
from models.enums import UserFrom
-from models.provider import ProviderType
+from tests.integration_tests.workflow.nodes.__mock.model import get_mocked_fetch_model_config
"""FOR MOCK FIXTURES, DO NOT REMOVE"""
from models.workflow import WorkflowNodeExecutionStatus, WorkflowType
-from tests.integration_tests.model_runtime.__mock.anthropic import setup_anthropic_mock
-from tests.integration_tests.model_runtime.__mock.openai import setup_openai_mock
-
-
-def get_mocked_fetch_model_config(
- provider: str,
- model: str,
- mode: str,
- credentials: dict,
-):
- provider_instance = ModelProviderFactory().get_provider_instance(provider)
- model_type_instance = provider_instance.get_model_instance(ModelType.LLM)
- provider_model_bundle = ProviderModelBundle(
- configuration=ProviderConfiguration(
- tenant_id="1",
- provider=provider_instance.get_provider_schema(),
- preferred_provider_type=ProviderType.CUSTOM,
- using_provider_type=ProviderType.CUSTOM,
- system_configuration=SystemConfiguration(enabled=False),
- custom_configuration=CustomConfiguration(provider=CustomProviderConfiguration(credentials=credentials)),
- model_settings=[],
- ),
- provider_instance=provider_instance,
- model_type_instance=model_type_instance,
- )
- model_instance = ModelInstance(provider_model_bundle=provider_model_bundle, model=model)
- model_schema = model_type_instance.get_model_schema(model)
- assert model_schema is not None
- model_config = ModelConfigWithCredentialsEntity(
- model=model,
- provider=provider,
- mode=mode,
- credentials=credentials,
- parameters={},
- model_schema=model_schema,
- provider_model_bundle=provider_model_bundle,
- )
-
- return MagicMock(return_value=(model_instance, model_config))
+from tests.integration_tests.model_runtime.__mock.plugin_daemon import setup_model_mock
def get_mocked_fetch_memory(memory_text: str):
@@ -129,8 +84,7 @@ def init_parameter_extractor_node(config: dict):
)
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_function_calling_parameter_extractor(setup_openai_mock):
+def test_function_calling_parameter_extractor(setup_model_mock):
"""
Test function calling for parameter extractor.
"""
@@ -140,7 +94,12 @@ def test_function_calling_parameter_extractor(setup_openai_mock):
"data": {
"title": "123",
"type": "parameter-extractor",
- "model": {"provider": "openai", "name": "gpt-3.5-turbo", "mode": "chat", "completion_params": {}},
+ "model": {
+ "provider": "langgenius/openai/openai",
+ "name": "gpt-3.5-turbo",
+ "mode": "chat",
+ "completion_params": {},
+ },
"query": ["sys", "query"],
"parameters": [{"name": "location", "type": "string", "description": "location", "required": True}],
"instruction": "",
@@ -151,25 +110,13 @@ def test_function_calling_parameter_extractor(setup_openai_mock):
)
node._fetch_model_config = get_mocked_fetch_model_config(
- provider="openai",
+ provider="langgenius/openai/openai",
model="gpt-3.5-turbo",
mode="chat",
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
)
db.session.close = MagicMock()
- # construct variable pool
- pool = VariablePool(
- system_variables={
- SystemVariableKey.QUERY: "what's the weather in SF",
- SystemVariableKey.FILES: [],
- SystemVariableKey.CONVERSATION_ID: "abababa",
- SystemVariableKey.USER_ID: "aaa",
- },
- user_inputs={},
- environment_variables=[],
- )
-
result = node._run()
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
@@ -178,8 +125,7 @@ def test_function_calling_parameter_extractor(setup_openai_mock):
assert result.outputs.get("__reason") == None
-@pytest.mark.parametrize("setup_openai_mock", [["chat"]], indirect=True)
-def test_instructions(setup_openai_mock):
+def test_instructions(setup_model_mock):
"""
Test chat parameter extractor.
"""
@@ -189,7 +135,12 @@ def test_instructions(setup_openai_mock):
"data": {
"title": "123",
"type": "parameter-extractor",
- "model": {"provider": "openai", "name": "gpt-3.5-turbo", "mode": "chat", "completion_params": {}},
+ "model": {
+ "provider": "langgenius/openai/openai",
+ "name": "gpt-3.5-turbo",
+ "mode": "chat",
+ "completion_params": {},
+ },
"query": ["sys", "query"],
"parameters": [{"name": "location", "type": "string", "description": "location", "required": True}],
"reasoning_mode": "function_call",
@@ -200,7 +151,7 @@ def test_instructions(setup_openai_mock):
)
node._fetch_model_config = get_mocked_fetch_model_config(
- provider="openai",
+ provider="langgenius/openai/openai",
model="gpt-3.5-turbo",
mode="chat",
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
@@ -224,8 +175,7 @@ def test_instructions(setup_openai_mock):
assert "what's the weather in SF" in prompt.get("text")
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_chat_parameter_extractor(setup_anthropic_mock):
+def test_chat_parameter_extractor(setup_model_mock):
"""
Test chat parameter extractor.
"""
@@ -235,7 +185,12 @@ def test_chat_parameter_extractor(setup_anthropic_mock):
"data": {
"title": "123",
"type": "parameter-extractor",
- "model": {"provider": "anthropic", "name": "claude-2", "mode": "chat", "completion_params": {}},
+ "model": {
+ "provider": "langgenius/openai/openai",
+ "name": "gpt-3.5-turbo",
+ "mode": "chat",
+ "completion_params": {},
+ },
"query": ["sys", "query"],
"parameters": [{"name": "location", "type": "string", "description": "location", "required": True}],
"reasoning_mode": "prompt",
@@ -246,10 +201,10 @@ def test_chat_parameter_extractor(setup_anthropic_mock):
)
node._fetch_model_config = get_mocked_fetch_model_config(
- provider="anthropic",
- model="claude-2",
+ provider="langgenius/openai/openai",
+ model="gpt-3.5-turbo",
mode="chat",
- credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")},
+ credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
)
db.session.close = MagicMock()
@@ -271,8 +226,7 @@ def test_chat_parameter_extractor(setup_anthropic_mock):
assert '\n{"type": "object"' in prompt.get("text")
-@pytest.mark.parametrize("setup_openai_mock", [["completion"]], indirect=True)
-def test_completion_parameter_extractor(setup_openai_mock):
+def test_completion_parameter_extractor(setup_model_mock):
"""
Test completion parameter extractor.
"""
@@ -283,7 +237,7 @@ def test_completion_parameter_extractor(setup_openai_mock):
"title": "123",
"type": "parameter-extractor",
"model": {
- "provider": "openai",
+ "provider": "langgenius/openai/openai",
"name": "gpt-3.5-turbo-instruct",
"mode": "completion",
"completion_params": {},
@@ -298,7 +252,7 @@ def test_completion_parameter_extractor(setup_openai_mock):
)
node._fetch_model_config = get_mocked_fetch_model_config(
- provider="openai",
+ provider="langgenius/openai/openai",
model="gpt-3.5-turbo-instruct",
mode="completion",
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
@@ -331,7 +285,7 @@ def test_extract_json_response():
"title": "123",
"type": "parameter-extractor",
"model": {
- "provider": "openai",
+ "provider": "langgenius/openai/openai",
"name": "gpt-3.5-turbo-instruct",
"mode": "completion",
"completion_params": {},
@@ -357,8 +311,7 @@ def test_extract_json_response():
assert result["location"] == "kawaii"
-@pytest.mark.parametrize("setup_anthropic_mock", [["none"]], indirect=True)
-def test_chat_parameter_extractor_with_memory(setup_anthropic_mock):
+def test_chat_parameter_extractor_with_memory(setup_model_mock):
"""
Test chat parameter extractor with memory.
"""
@@ -368,7 +321,12 @@ def test_chat_parameter_extractor_with_memory(setup_anthropic_mock):
"data": {
"title": "123",
"type": "parameter-extractor",
- "model": {"provider": "anthropic", "name": "claude-2", "mode": "chat", "completion_params": {}},
+ "model": {
+ "provider": "langgenius/openai/openai",
+ "name": "gpt-3.5-turbo",
+ "mode": "chat",
+ "completion_params": {},
+ },
"query": ["sys", "query"],
"parameters": [{"name": "location", "type": "string", "description": "location", "required": True}],
"reasoning_mode": "prompt",
@@ -379,10 +337,10 @@ def test_chat_parameter_extractor_with_memory(setup_anthropic_mock):
)
node._fetch_model_config = get_mocked_fetch_model_config(
- provider="anthropic",
- model="claude-2",
+ provider="langgenius/openai/openai",
+ model="gpt-3.5-turbo",
mode="chat",
- credentials={"anthropic_api_key": os.environ.get("ANTHROPIC_API_KEY")},
+ credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
)
node._fetch_memory = get_mocked_fetch_memory("customized memory")
db.session.close = MagicMock()
diff --git a/api/tests/integration_tests/workflow/nodes/test_tool.py b/api/tests/integration_tests/workflow/nodes/test_tool.py
index 4068e796b787ef..5a569a5983558e 100644
--- a/api/tests/integration_tests/workflow/nodes/test_tool.py
+++ b/api/tests/integration_tests/workflow/nodes/test_tool.py
@@ -1,13 +1,15 @@
import time
import uuid
+from unittest.mock import MagicMock
from core.app.entities.app_invoke_entities import InvokeFrom
-from core.workflow.entities.node_entities import NodeRunResult
+from core.tools.utils.configuration import ToolParameterConfigurationManager
from core.workflow.entities.variable_pool import VariablePool
from core.workflow.enums import SystemVariableKey
from core.workflow.graph_engine.entities.graph import Graph
from core.workflow.graph_engine.entities.graph_init_params import GraphInitParams
from core.workflow.graph_engine.entities.graph_runtime_state import GraphRuntimeState
+from core.workflow.nodes.event.event import RunCompletedEvent
from core.workflow.nodes.tool.tool_node import ToolNode
from models.enums import UserFrom
from models.workflow import WorkflowNodeExecutionStatus, WorkflowType
@@ -63,31 +65,28 @@ def test_tool_variable_invoke():
"data": {
"title": "a",
"desc": "a",
- "provider_id": "maths",
+ "provider_id": "time",
"provider_type": "builtin",
- "provider_name": "maths",
- "tool_name": "eval_expression",
- "tool_label": "eval_expression",
+ "provider_name": "time",
+ "tool_name": "current_time",
+ "tool_label": "current_time",
"tool_configurations": {},
- "tool_parameters": {
- "expression": {
- "type": "variable",
- "value": ["1", "123", "args1"],
- }
- },
+ "tool_parameters": {},
},
}
)
+ ToolParameterConfigurationManager.decrypt_tool_parameters = MagicMock(return_value={"format": "%Y-%m-%d %H:%M:%S"})
+
node.graph_runtime_state.variable_pool.add(["1", "123", "args1"], "1+1")
# execute node
result = node._run()
- assert isinstance(result, NodeRunResult)
- assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
- assert result.outputs is not None
- assert "2" in result.outputs["text"]
- assert result.outputs["files"] == []
+ for item in result:
+ if isinstance(item, RunCompletedEvent):
+ assert item.run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
+ assert item.run_result.outputs is not None
+ assert item.run_result.outputs.get("text") is not None
def test_tool_mixed_invoke():
@@ -97,28 +96,25 @@ def test_tool_mixed_invoke():
"data": {
"title": "a",
"desc": "a",
- "provider_id": "maths",
+ "provider_id": "time",
"provider_type": "builtin",
- "provider_name": "maths",
- "tool_name": "eval_expression",
- "tool_label": "eval_expression",
- "tool_configurations": {},
- "tool_parameters": {
- "expression": {
- "type": "mixed",
- "value": "{{#1.args1#}}",
- }
+ "provider_name": "time",
+ "tool_name": "current_time",
+ "tool_label": "current_time",
+ "tool_configurations": {
+ "format": "%Y-%m-%d %H:%M:%S",
},
+ "tool_parameters": {},
},
}
)
- node.graph_runtime_state.variable_pool.add(["1", "args1"], "1+1")
+ ToolParameterConfigurationManager.decrypt_tool_parameters = MagicMock(return_value={"format": "%Y-%m-%d %H:%M:%S"})
# execute node
result = node._run()
- assert isinstance(result, NodeRunResult)
- assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
- assert result.outputs is not None
- assert "2" in result.outputs["text"]
- assert result.outputs["files"] == []
+ for item in result:
+ if isinstance(item, RunCompletedEvent):
+ assert item.run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
+ assert item.run_result.outputs is not None
+ assert item.run_result.outputs.get("text") is not None
diff --git a/api/tests/unit_tests/core/helper/test_marketplace.py b/api/tests/unit_tests/core/helper/test_marketplace.py
new file mode 100644
index 00000000000000..6ccce7ac9ff73c
--- /dev/null
+++ b/api/tests/unit_tests/core/helper/test_marketplace.py
@@ -0,0 +1,7 @@
+from core.helper.marketplace import download_plugin_pkg
+
+
+def test_download_plugin_pkg():
+ pkg = download_plugin_pkg("langgenius/bing:0.0.1@e58735424d2104f208c2bd683c5142e0332045b425927067acf432b26f3d970b")
+ assert pkg is not None
+ assert len(pkg) > 0
diff --git a/api/tests/unit_tests/core/model_runtime/model_providers/__init__.py b/api/tests/unit_tests/core/model_runtime/model_providers/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/unit_tests/core/model_runtime/model_providers/wenxin/__init__.py b/api/tests/unit_tests/core/model_runtime/model_providers/wenxin/__init__.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/api/tests/unit_tests/core/model_runtime/model_providers/wenxin/test_text_embedding.py b/api/tests/unit_tests/core/model_runtime/model_providers/wenxin/test_text_embedding.py
deleted file mode 100644
index a301f56d75c506..00000000000000
--- a/api/tests/unit_tests/core/model_runtime/model_providers/wenxin/test_text_embedding.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import string
-
-import numpy as np
-
-from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult
-from core.model_runtime.model_providers.__base.tokenizers.gpt2_tokenzier import GPT2Tokenizer
-from core.model_runtime.model_providers.wenxin.text_embedding.text_embedding import (
- TextEmbedding,
- WenxinTextEmbeddingModel,
-)
-
-
-def test_max_chunks():
- class _MockTextEmbedding(TextEmbedding):
- def embed_documents(self, model: str, texts: list[str], user: str) -> (list[list[float]], int, int):
- embeddings = [[1.0, 2.0, 3.0] for i in range(len(texts))]
- tokens = 0
- for text in texts:
- tokens += len(text)
-
- return embeddings, tokens, tokens
-
- def _create_text_embedding(api_key: str, secret_key: str) -> TextEmbedding:
- return _MockTextEmbedding()
-
- model = "embedding-v1"
- credentials = {
- "api_key": "xxxx",
- "secret_key": "yyyy",
- }
- embedding_model = WenxinTextEmbeddingModel()
- context_size = embedding_model._get_context_size(model, credentials)
- max_chunks = embedding_model._get_max_chunks(model, credentials)
- embedding_model._create_text_embedding = _create_text_embedding
-
- texts = [string.digits for i in range(0, max_chunks * 2)]
- result: TextEmbeddingResult = embedding_model.invoke(model, credentials, texts, "test")
- assert len(result.embeddings) == max_chunks * 2
-
-
-def test_context_size():
- def get_num_tokens_by_gpt2(text: str) -> int:
- return GPT2Tokenizer.get_num_tokens(text)
-
- def mock_text(token_size: int) -> str:
- _text = "".join(["0" for i in range(token_size)])
- num_tokens = get_num_tokens_by_gpt2(_text)
- ratio = int(np.floor(len(_text) / num_tokens))
- m_text = "".join([_text for i in range(ratio)])
- return m_text
-
- model = "embedding-v1"
- credentials = {
- "api_key": "xxxx",
- "secret_key": "yyyy",
- }
- embedding_model = WenxinTextEmbeddingModel()
- context_size = embedding_model._get_context_size(model, credentials)
-
- class _MockTextEmbedding(TextEmbedding):
- def embed_documents(self, model: str, texts: list[str], user: str) -> (list[list[float]], int, int):
- embeddings = [[1.0, 2.0, 3.0] for i in range(len(texts))]
- tokens = 0
- for text in texts:
- tokens += get_num_tokens_by_gpt2(text)
- return embeddings, tokens, tokens
-
- def _create_text_embedding(api_key: str, secret_key: str) -> TextEmbedding:
- return _MockTextEmbedding()
-
- embedding_model._create_text_embedding = _create_text_embedding
- text = mock_text(context_size * 2)
- assert get_num_tokens_by_gpt2(text) == context_size * 2
-
- texts = [text]
- result: TextEmbeddingResult = embedding_model.invoke(model, credentials, texts, "test")
- assert result.usage.tokens == context_size
diff --git a/api/tests/unit_tests/core/prompt/test_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_prompt_transform.py
index 89c14463bbfb94..16896a0c6c0f13 100644
--- a/api/tests/unit_tests/core/prompt/test_prompt_transform.py
+++ b/api/tests/unit_tests/core/prompt/test_prompt_transform.py
@@ -1,52 +1,52 @@
-from unittest.mock import MagicMock
-
-from core.app.app_config.entities import ModelConfigEntity
-from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
-from core.model_runtime.entities.message_entities import UserPromptMessage
-from core.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey, ParameterRule
-from core.model_runtime.entities.provider_entities import ProviderEntity
-from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
-from core.prompt.prompt_transform import PromptTransform
-
-
-def test__calculate_rest_token():
- model_schema_mock = MagicMock(spec=AIModelEntity)
- parameter_rule_mock = MagicMock(spec=ParameterRule)
- parameter_rule_mock.name = "max_tokens"
- model_schema_mock.parameter_rules = [parameter_rule_mock]
- model_schema_mock.model_properties = {ModelPropertyKey.CONTEXT_SIZE: 62}
-
- large_language_model_mock = MagicMock(spec=LargeLanguageModel)
- large_language_model_mock.get_num_tokens.return_value = 6
-
- provider_mock = MagicMock(spec=ProviderEntity)
- provider_mock.provider = "openai"
-
- provider_configuration_mock = MagicMock(spec=ProviderConfiguration)
- provider_configuration_mock.provider = provider_mock
- provider_configuration_mock.model_settings = None
-
- provider_model_bundle_mock = MagicMock(spec=ProviderModelBundle)
- provider_model_bundle_mock.model_type_instance = large_language_model_mock
- provider_model_bundle_mock.configuration = provider_configuration_mock
-
- model_config_mock = MagicMock(spec=ModelConfigEntity)
- model_config_mock.model = "gpt-4"
- model_config_mock.credentials = {}
- model_config_mock.parameters = {"max_tokens": 50}
- model_config_mock.model_schema = model_schema_mock
- model_config_mock.provider_model_bundle = provider_model_bundle_mock
-
- prompt_transform = PromptTransform()
-
- prompt_messages = [UserPromptMessage(content="Hello, how are you?")]
- rest_tokens = prompt_transform._calculate_rest_token(prompt_messages, model_config_mock)
-
- # Validate based on the mock configuration and expected logic
- expected_rest_tokens = (
- model_schema_mock.model_properties[ModelPropertyKey.CONTEXT_SIZE]
- - model_config_mock.parameters["max_tokens"]
- - large_language_model_mock.get_num_tokens.return_value
- )
- assert rest_tokens == expected_rest_tokens
- assert rest_tokens == 6
+# from unittest.mock import MagicMock
+
+# from core.app.app_config.entities import ModelConfigEntity
+# from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
+# from core.model_runtime.entities.message_entities import UserPromptMessage
+# from core.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey, ParameterRule
+# from core.model_runtime.entities.provider_entities import ProviderEntity
+# from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
+# from core.prompt.prompt_transform import PromptTransform
+
+
+# def test__calculate_rest_token():
+# model_schema_mock = MagicMock(spec=AIModelEntity)
+# parameter_rule_mock = MagicMock(spec=ParameterRule)
+# parameter_rule_mock.name = "max_tokens"
+# model_schema_mock.parameter_rules = [parameter_rule_mock]
+# model_schema_mock.model_properties = {ModelPropertyKey.CONTEXT_SIZE: 62}
+
+# large_language_model_mock = MagicMock(spec=LargeLanguageModel)
+# large_language_model_mock.get_num_tokens.return_value = 6
+
+# provider_mock = MagicMock(spec=ProviderEntity)
+# provider_mock.provider = "openai"
+
+# provider_configuration_mock = MagicMock(spec=ProviderConfiguration)
+# provider_configuration_mock.provider = provider_mock
+# provider_configuration_mock.model_settings = None
+
+# provider_model_bundle_mock = MagicMock(spec=ProviderModelBundle)
+# provider_model_bundle_mock.model_type_instance = large_language_model_mock
+# provider_model_bundle_mock.configuration = provider_configuration_mock
+
+# model_config_mock = MagicMock(spec=ModelConfigEntity)
+# model_config_mock.model = "gpt-4"
+# model_config_mock.credentials = {}
+# model_config_mock.parameters = {"max_tokens": 50}
+# model_config_mock.model_schema = model_schema_mock
+# model_config_mock.provider_model_bundle = provider_model_bundle_mock
+
+# prompt_transform = PromptTransform()
+
+# prompt_messages = [UserPromptMessage(content="Hello, how are you?")]
+# rest_tokens = prompt_transform._calculate_rest_token(prompt_messages, model_config_mock)
+
+# # Validate based on the mock configuration and expected logic
+# expected_rest_tokens = (
+# model_schema_mock.model_properties[ModelPropertyKey.CONTEXT_SIZE]
+# - model_config_mock.parameters["max_tokens"]
+# - large_language_model_mock.get_num_tokens.return_value
+# )
+# assert rest_tokens == expected_rest_tokens
+# assert rest_tokens == 6
diff --git a/api/tests/unit_tests/core/test_provider_manager.py b/api/tests/unit_tests/core/test_provider_manager.py
index 2f4214a5801de1..90d5a6f15b4e8e 100644
--- a/api/tests/unit_tests/core/test_provider_manager.py
+++ b/api/tests/unit_tests/core/test_provider_manager.py
@@ -1,183 +1,190 @@
-from core.entities.provider_entities import ModelSettings
-from core.model_runtime.entities.model_entities import ModelType
-from core.model_runtime.model_providers import model_provider_factory
-from core.provider_manager import ProviderManager
-from models.provider import LoadBalancingModelConfig, ProviderModelSetting
-
-
-def test__to_model_settings(mocker):
- # Get all provider entities
- provider_entities = model_provider_factory.get_providers()
-
- provider_entity = None
- for provider in provider_entities:
- if provider.provider == "openai":
- provider_entity = provider
-
- # Mocking the inputs
- provider_model_settings = [
- ProviderModelSetting(
- id="id",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- enabled=True,
- load_balancing_enabled=True,
- )
- ]
- load_balancing_model_configs = [
- LoadBalancingModelConfig(
- id="id1",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- name="__inherit__",
- encrypted_config=None,
- enabled=True,
- ),
- LoadBalancingModelConfig(
- id="id2",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- name="first",
- encrypted_config='{"openai_api_key": "fake_key"}',
- enabled=True,
- ),
- ]
-
- mocker.patch(
- "core.helper.model_provider_cache.ProviderCredentialsCache.get", return_value={"openai_api_key": "fake_key"}
- )
-
- provider_manager = ProviderManager()
-
- # Running the method
- result = provider_manager._to_model_settings(provider_entity, provider_model_settings, load_balancing_model_configs)
-
- # Asserting that the result is as expected
- assert len(result) == 1
- assert isinstance(result[0], ModelSettings)
- assert result[0].model == "gpt-4"
- assert result[0].model_type == ModelType.LLM
- assert result[0].enabled is True
- assert len(result[0].load_balancing_configs) == 2
- assert result[0].load_balancing_configs[0].name == "__inherit__"
- assert result[0].load_balancing_configs[1].name == "first"
-
-
-def test__to_model_settings_only_one_lb(mocker):
- # Get all provider entities
- provider_entities = model_provider_factory.get_providers()
-
- provider_entity = None
- for provider in provider_entities:
- if provider.provider == "openai":
- provider_entity = provider
-
- # Mocking the inputs
- provider_model_settings = [
- ProviderModelSetting(
- id="id",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- enabled=True,
- load_balancing_enabled=True,
- )
- ]
- load_balancing_model_configs = [
- LoadBalancingModelConfig(
- id="id1",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- name="__inherit__",
- encrypted_config=None,
- enabled=True,
- )
- ]
-
- mocker.patch(
- "core.helper.model_provider_cache.ProviderCredentialsCache.get", return_value={"openai_api_key": "fake_key"}
- )
-
- provider_manager = ProviderManager()
-
- # Running the method
- result = provider_manager._to_model_settings(provider_entity, provider_model_settings, load_balancing_model_configs)
-
- # Asserting that the result is as expected
- assert len(result) == 1
- assert isinstance(result[0], ModelSettings)
- assert result[0].model == "gpt-4"
- assert result[0].model_type == ModelType.LLM
- assert result[0].enabled is True
- assert len(result[0].load_balancing_configs) == 0
-
-
-def test__to_model_settings_lb_disabled(mocker):
- # Get all provider entities
- provider_entities = model_provider_factory.get_providers()
-
- provider_entity = None
- for provider in provider_entities:
- if provider.provider == "openai":
- provider_entity = provider
-
- # Mocking the inputs
- provider_model_settings = [
- ProviderModelSetting(
- id="id",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- enabled=True,
- load_balancing_enabled=False,
- )
- ]
- load_balancing_model_configs = [
- LoadBalancingModelConfig(
- id="id1",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- name="__inherit__",
- encrypted_config=None,
- enabled=True,
- ),
- LoadBalancingModelConfig(
- id="id2",
- tenant_id="tenant_id",
- provider_name="openai",
- model_name="gpt-4",
- model_type="text-generation",
- name="first",
- encrypted_config='{"openai_api_key": "fake_key"}',
- enabled=True,
- ),
- ]
-
- mocker.patch(
- "core.helper.model_provider_cache.ProviderCredentialsCache.get", return_value={"openai_api_key": "fake_key"}
- )
-
- provider_manager = ProviderManager()
-
- # Running the method
- result = provider_manager._to_model_settings(provider_entity, provider_model_settings, load_balancing_model_configs)
-
- # Asserting that the result is as expected
- assert len(result) == 1
- assert isinstance(result[0], ModelSettings)
- assert result[0].model == "gpt-4"
- assert result[0].model_type == ModelType.LLM
- assert result[0].enabled is True
- assert len(result[0].load_balancing_configs) == 0
+# from core.entities.provider_entities import ModelSettings
+# from core.model_runtime.entities.model_entities import ModelType
+# from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
+# from core.provider_manager import ProviderManager
+# from models.provider import LoadBalancingModelConfig, ProviderModelSetting
+
+
+# def test__to_model_settings(mocker):
+# # Get all provider entities
+# model_provider_factory = ModelProviderFactory("test_tenant")
+# provider_entities = model_provider_factory.get_providers()
+
+# provider_entity = None
+# for provider in provider_entities:
+# if provider.provider == "openai":
+# provider_entity = provider
+
+# # Mocking the inputs
+# provider_model_settings = [
+# ProviderModelSetting(
+# id="id",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# enabled=True,
+# load_balancing_enabled=True,
+# )
+# ]
+# load_balancing_model_configs = [
+# LoadBalancingModelConfig(
+# id="id1",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# name="__inherit__",
+# encrypted_config=None,
+# enabled=True,
+# ),
+# LoadBalancingModelConfig(
+# id="id2",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# name="first",
+# encrypted_config='{"openai_api_key": "fake_key"}',
+# enabled=True,
+# ),
+# ]
+
+# mocker.patch(
+# "core.helper.model_provider_cache.ProviderCredentialsCache.get", return_value={"openai_api_key": "fake_key"}
+# )
+
+# provider_manager = ProviderManager()
+
+# # Running the method
+# result = provider_manager._to_model_settings(provider_entity,
+# provider_model_settings, load_balancing_model_configs)
+
+# # Asserting that the result is as expected
+# assert len(result) == 1
+# assert isinstance(result[0], ModelSettings)
+# assert result[0].model == "gpt-4"
+# assert result[0].model_type == ModelType.LLM
+# assert result[0].enabled is True
+# assert len(result[0].load_balancing_configs) == 2
+# assert result[0].load_balancing_configs[0].name == "__inherit__"
+# assert result[0].load_balancing_configs[1].name == "first"
+
+
+# def test__to_model_settings_only_one_lb(mocker):
+# # Get all provider entities
+# model_provider_factory = ModelProviderFactory("test_tenant")
+# provider_entities = model_provider_factory.get_providers()
+
+# provider_entity = None
+# for provider in provider_entities:
+# if provider.provider == "openai":
+# provider_entity = provider
+
+# # Mocking the inputs
+# provider_model_settings = [
+# ProviderModelSetting(
+# id="id",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# enabled=True,
+# load_balancing_enabled=True,
+# )
+# ]
+# load_balancing_model_configs = [
+# LoadBalancingModelConfig(
+# id="id1",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# name="__inherit__",
+# encrypted_config=None,
+# enabled=True,
+# )
+# ]
+
+# mocker.patch(
+# "core.helper.model_provider_cache.ProviderCredentialsCache.get", return_value={"openai_api_key": "fake_key"}
+# )
+
+# provider_manager = ProviderManager()
+
+# # Running the method
+# result = provider_manager._to_model_settings(
+# provider_entity, provider_model_settings, load_balancing_model_configs)
+
+# # Asserting that the result is as expected
+# assert len(result) == 1
+# assert isinstance(result[0], ModelSettings)
+# assert result[0].model == "gpt-4"
+# assert result[0].model_type == ModelType.LLM
+# assert result[0].enabled is True
+# assert len(result[0].load_balancing_configs) == 0
+
+
+# def test__to_model_settings_lb_disabled(mocker):
+# # Get all provider entities
+# model_provider_factory = ModelProviderFactory("test_tenant")
+# provider_entities = model_provider_factory.get_providers()
+
+# provider_entity = None
+# for provider in provider_entities:
+# if provider.provider == "openai":
+# provider_entity = provider
+
+# # Mocking the inputs
+# provider_model_settings = [
+# ProviderModelSetting(
+# id="id",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# enabled=True,
+# load_balancing_enabled=False,
+# )
+# ]
+# load_balancing_model_configs = [
+# LoadBalancingModelConfig(
+# id="id1",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# name="__inherit__",
+# encrypted_config=None,
+# enabled=True,
+# ),
+# LoadBalancingModelConfig(
+# id="id2",
+# tenant_id="tenant_id",
+# provider_name="openai",
+# model_name="gpt-4",
+# model_type="text-generation",
+# name="first",
+# encrypted_config='{"openai_api_key": "fake_key"}',
+# enabled=True,
+# ),
+# ]
+
+# mocker.patch(
+# "core.helper.model_provider_cache.ProviderCredentialsCache.get",
+# return_value={"openai_api_key": "fake_key"}
+# )
+
+# provider_manager = ProviderManager()
+
+# # Running the method
+# result = provider_manager._to_model_settings(provider_entity,
+# provider_model_settings, load_balancing_model_configs)
+
+# # Asserting that the result is as expected
+# assert len(result) == 1
+# assert isinstance(result[0], ModelSettings)
+# assert result[0].model == "gpt-4"
+# assert result[0].model_type == ModelType.LLM
+# assert result[0].enabled is True
+# assert len(result[0].load_balancing_configs) == 0
diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py
index 74af5eb56b5038..5c3e5540c44c88 100644
--- a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py
+++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py
@@ -3,24 +3,20 @@
import pytest
-from configs import dify_config
from core.app.entities.app_invoke_entities import InvokeFrom, ModelConfigWithCredentialsEntity
from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
from core.entities.provider_entities import CustomConfiguration, SystemConfiguration
from core.file import File, FileTransferMethod, FileType
from core.model_runtime.entities.common_entities import I18nObject
from core.model_runtime.entities.message_entities import (
- AssistantPromptMessage,
ImagePromptMessageContent,
PromptMessage,
PromptMessageRole,
- SystemPromptMessage,
TextPromptMessageContent,
UserPromptMessage,
)
-from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelFeature, ModelType
+from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType
from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
-from core.prompt.entities.advanced_prompt_entities import MemoryConfig
from core.variables import ArrayAnySegment, ArrayFileSegment, NoneSegment
from core.workflow.entities.variable_pool import VariablePool
from core.workflow.graph_engine import Graph, GraphInitParams, GraphRuntimeState
@@ -38,7 +34,6 @@
from models.enums import UserFrom
from models.provider import ProviderType
from models.workflow import WorkflowType
-from tests.unit_tests.core.workflow.nodes.llm.test_scenarios import LLMNodeTestScenario
class MockTokenBufferMemory:
@@ -112,22 +107,21 @@ def llm_node():
@pytest.fixture
def model_config():
# Create actual provider and model type instances
- model_provider_factory = ModelProviderFactory()
- provider_instance = model_provider_factory.get_provider_instance("openai")
- model_type_instance = provider_instance.get_model_instance(ModelType.LLM)
+ model_provider_factory = ModelProviderFactory(tenant_id="test")
+ provider_instance = model_provider_factory.get_plugin_model_provider("openai")
+ model_type_instance = model_provider_factory.get_model_type_instance("openai", ModelType.LLM)
# Create a ProviderModelBundle
provider_model_bundle = ProviderModelBundle(
configuration=ProviderConfiguration(
tenant_id="1",
- provider=provider_instance.get_provider_schema(),
+ provider=provider_instance,
preferred_provider_type=ProviderType.CUSTOM,
using_provider_type=ProviderType.CUSTOM,
system_configuration=SystemConfiguration(enabled=False),
custom_configuration=CustomConfiguration(provider=None),
model_settings=[],
),
- provider_instance=provider_instance,
model_type_instance=model_type_instance,
)
@@ -211,236 +205,240 @@ def test_fetch_files_with_non_existent_variable(llm_node):
assert result == []
-def test_fetch_prompt_messages__vison_disabled(faker, llm_node, model_config):
- prompt_template = []
- llm_node.node_data.prompt_template = prompt_template
-
- fake_vision_detail = faker.random_element(
- [ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
- )
- fake_remote_url = faker.url()
- files = [
- File(
- id="1",
- tenant_id="test",
- type=FileType.IMAGE,
- filename="test1.jpg",
- transfer_method=FileTransferMethod.REMOTE_URL,
- remote_url=fake_remote_url,
- storage_key="",
- )
- ]
-
- fake_query = faker.sentence()
-
- prompt_messages, _ = llm_node._fetch_prompt_messages(
- sys_query=fake_query,
- sys_files=files,
- context=None,
- memory=None,
- model_config=model_config,
- prompt_template=prompt_template,
- memory_config=None,
- vision_enabled=False,
- vision_detail=fake_vision_detail,
- variable_pool=llm_node.graph_runtime_state.variable_pool,
- jinja2_variables=[],
- )
-
- assert prompt_messages == [UserPromptMessage(content=fake_query)]
-
-
-def test_fetch_prompt_messages__basic(faker, llm_node, model_config):
- # Setup dify config
- dify_config.MULTIMODAL_SEND_FORMAT = "url"
-
- # Generate fake values for prompt template
- fake_assistant_prompt = faker.sentence()
- fake_query = faker.sentence()
- fake_context = faker.sentence()
- fake_window_size = faker.random_int(min=1, max=3)
- fake_vision_detail = faker.random_element(
- [ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
- )
- fake_remote_url = faker.url()
-
- # Setup mock memory with history messages
- mock_history = [
- UserPromptMessage(content=faker.sentence()),
- AssistantPromptMessage(content=faker.sentence()),
- UserPromptMessage(content=faker.sentence()),
- AssistantPromptMessage(content=faker.sentence()),
- UserPromptMessage(content=faker.sentence()),
- AssistantPromptMessage(content=faker.sentence()),
- ]
-
- # Setup memory configuration
- memory_config = MemoryConfig(
- role_prefix=MemoryConfig.RolePrefix(user="Human", assistant="Assistant"),
- window=MemoryConfig.WindowConfig(enabled=True, size=fake_window_size),
- query_prompt_template=None,
- )
-
- memory = MockTokenBufferMemory(history_messages=mock_history)
-
- # Test scenarios covering different file input combinations
- test_scenarios = [
- LLMNodeTestScenario(
- description="No files",
- sys_query=fake_query,
- sys_files=[],
- features=[],
- vision_enabled=False,
- vision_detail=None,
- window_size=fake_window_size,
- prompt_template=[
- LLMNodeChatModelMessage(
- text=fake_context,
- role=PromptMessageRole.SYSTEM,
- edition_type="basic",
- ),
- LLMNodeChatModelMessage(
- text="{#context#}",
- role=PromptMessageRole.USER,
- edition_type="basic",
- ),
- LLMNodeChatModelMessage(
- text=fake_assistant_prompt,
- role=PromptMessageRole.ASSISTANT,
- edition_type="basic",
- ),
- ],
- expected_messages=[
- SystemPromptMessage(content=fake_context),
- UserPromptMessage(content=fake_context),
- AssistantPromptMessage(content=fake_assistant_prompt),
- ]
- + mock_history[fake_window_size * -2 :]
- + [
- UserPromptMessage(content=fake_query),
- ],
- ),
- LLMNodeTestScenario(
- description="User files",
- sys_query=fake_query,
- sys_files=[
- File(
- tenant_id="test",
- type=FileType.IMAGE,
- filename="test1.jpg",
- transfer_method=FileTransferMethod.REMOTE_URL,
- remote_url=fake_remote_url,
- extension=".jpg",
- mime_type="image/jpg",
- storage_key="",
- )
- ],
- vision_enabled=True,
- vision_detail=fake_vision_detail,
- features=[ModelFeature.VISION],
- window_size=fake_window_size,
- prompt_template=[
- LLMNodeChatModelMessage(
- text=fake_context,
- role=PromptMessageRole.SYSTEM,
- edition_type="basic",
- ),
- LLMNodeChatModelMessage(
- text="{#context#}",
- role=PromptMessageRole.USER,
- edition_type="basic",
- ),
- LLMNodeChatModelMessage(
- text=fake_assistant_prompt,
- role=PromptMessageRole.ASSISTANT,
- edition_type="basic",
- ),
- ],
- expected_messages=[
- SystemPromptMessage(content=fake_context),
- UserPromptMessage(content=fake_context),
- AssistantPromptMessage(content=fake_assistant_prompt),
- ]
- + mock_history[fake_window_size * -2 :]
- + [
- UserPromptMessage(
- content=[
- TextPromptMessageContent(data=fake_query),
- ImagePromptMessageContent(
- url=fake_remote_url, mime_type="image/jpg", format="jpg", detail=fake_vision_detail
- ),
- ]
- ),
- ],
- ),
- LLMNodeTestScenario(
- description="Prompt template with variable selector of File",
- sys_query=fake_query,
- sys_files=[],
- vision_enabled=False,
- vision_detail=fake_vision_detail,
- features=[ModelFeature.VISION],
- window_size=fake_window_size,
- prompt_template=[
- LLMNodeChatModelMessage(
- text="{{#input.image#}}",
- role=PromptMessageRole.USER,
- edition_type="basic",
- ),
- ],
- expected_messages=[
- UserPromptMessage(
- content=[
- ImagePromptMessageContent(
- url=fake_remote_url, mime_type="image/jpg", format="jpg", detail=fake_vision_detail
- ),
- ]
- ),
- ]
- + mock_history[fake_window_size * -2 :]
- + [UserPromptMessage(content=fake_query)],
- file_variables={
- "input.image": File(
- tenant_id="test",
- type=FileType.IMAGE,
- filename="test1.jpg",
- transfer_method=FileTransferMethod.REMOTE_URL,
- remote_url=fake_remote_url,
- extension=".jpg",
- mime_type="image/jpg",
- storage_key="",
- )
- },
- ),
- ]
-
- for scenario in test_scenarios:
- model_config.model_schema.features = scenario.features
-
- for k, v in scenario.file_variables.items():
- selector = k.split(".")
- llm_node.graph_runtime_state.variable_pool.add(selector, v)
-
- # Call the method under test
- prompt_messages, _ = llm_node._fetch_prompt_messages(
- sys_query=scenario.sys_query,
- sys_files=scenario.sys_files,
- context=fake_context,
- memory=memory,
- model_config=model_config,
- prompt_template=scenario.prompt_template,
- memory_config=memory_config,
- vision_enabled=scenario.vision_enabled,
- vision_detail=scenario.vision_detail,
- variable_pool=llm_node.graph_runtime_state.variable_pool,
- jinja2_variables=[],
- )
-
- # Verify the result
- assert len(prompt_messages) == len(scenario.expected_messages), f"Scenario failed: {scenario.description}"
- assert prompt_messages == scenario.expected_messages, (
- f"Message content mismatch in scenario: {scenario.description}"
- )
+# def test_fetch_prompt_messages__vison_disabled(faker, llm_node, model_config):
+# TODO: Add test
+# pass
+# prompt_template = []
+# llm_node.node_data.prompt_template = prompt_template
+
+# fake_vision_detail = faker.random_element(
+# [ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
+# )
+# fake_remote_url = faker.url()
+# files = [
+# File(
+# id="1",
+# tenant_id="test",
+# type=FileType.IMAGE,
+# filename="test1.jpg",
+# transfer_method=FileTransferMethod.REMOTE_URL,
+# remote_url=fake_remote_url,
+# storage_key="",
+# )
+# ]
+
+# fake_query = faker.sentence()
+
+# prompt_messages, _ = llm_node._fetch_prompt_messages(
+# sys_query=fake_query,
+# sys_files=files,
+# context=None,
+# memory=None,
+# model_config=model_config,
+# prompt_template=prompt_template,
+# memory_config=None,
+# vision_enabled=False,
+# vision_detail=fake_vision_detail,
+# variable_pool=llm_node.graph_runtime_state.variable_pool,
+# jinja2_variables=[],
+# )
+
+# assert prompt_messages == [UserPromptMessage(content=fake_query)]
+
+
+# def test_fetch_prompt_messages__basic(faker, llm_node, model_config):
+# TODO: Add test
+# pass
+# Setup dify config
+# dify_config.MULTIMODAL_SEND_FORMAT = "url"
+
+# # Generate fake values for prompt template
+# fake_assistant_prompt = faker.sentence()
+# fake_query = faker.sentence()
+# fake_context = faker.sentence()
+# fake_window_size = faker.random_int(min=1, max=3)
+# fake_vision_detail = faker.random_element(
+# [ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
+# )
+# fake_remote_url = faker.url()
+
+# # Setup mock memory with history messages
+# mock_history = [
+# UserPromptMessage(content=faker.sentence()),
+# AssistantPromptMessage(content=faker.sentence()),
+# UserPromptMessage(content=faker.sentence()),
+# AssistantPromptMessage(content=faker.sentence()),
+# UserPromptMessage(content=faker.sentence()),
+# AssistantPromptMessage(content=faker.sentence()),
+# ]
+
+# # Setup memory configuration
+# memory_config = MemoryConfig(
+# role_prefix=MemoryConfig.RolePrefix(user="Human", assistant="Assistant"),
+# window=MemoryConfig.WindowConfig(enabled=True, size=fake_window_size),
+# query_prompt_template=None,
+# )
+
+# memory = MockTokenBufferMemory(history_messages=mock_history)
+
+# # Test scenarios covering different file input combinations
+# test_scenarios = [
+# LLMNodeTestScenario(
+# description="No files",
+# sys_query=fake_query,
+# sys_files=[],
+# features=[],
+# vision_enabled=False,
+# vision_detail=None,
+# window_size=fake_window_size,
+# prompt_template=[
+# LLMNodeChatModelMessage(
+# text=fake_context,
+# role=PromptMessageRole.SYSTEM,
+# edition_type="basic",
+# ),
+# LLMNodeChatModelMessage(
+# text="{#context#}",
+# role=PromptMessageRole.USER,
+# edition_type="basic",
+# ),
+# LLMNodeChatModelMessage(
+# text=fake_assistant_prompt,
+# role=PromptMessageRole.ASSISTANT,
+# edition_type="basic",
+# ),
+# ],
+# expected_messages=[
+# SystemPromptMessage(content=fake_context),
+# UserPromptMessage(content=fake_context),
+# AssistantPromptMessage(content=fake_assistant_prompt),
+# ]
+# + mock_history[fake_window_size * -2 :]
+# + [
+# UserPromptMessage(content=fake_query),
+# ],
+# ),
+# LLMNodeTestScenario(
+# description="User files",
+# sys_query=fake_query,
+# sys_files=[
+# File(
+# tenant_id="test",
+# type=FileType.IMAGE,
+# filename="test1.jpg",
+# transfer_method=FileTransferMethod.REMOTE_URL,
+# remote_url=fake_remote_url,
+# extension=".jpg",
+# mime_type="image/jpg",
+# storage_key="",
+# )
+# ],
+# vision_enabled=True,
+# vision_detail=fake_vision_detail,
+# features=[ModelFeature.VISION],
+# window_size=fake_window_size,
+# prompt_template=[
+# LLMNodeChatModelMessage(
+# text=fake_context,
+# role=PromptMessageRole.SYSTEM,
+# edition_type="basic",
+# ),
+# LLMNodeChatModelMessage(
+# text="{#context#}",
+# role=PromptMessageRole.USER,
+# edition_type="basic",
+# ),
+# LLMNodeChatModelMessage(
+# text=fake_assistant_prompt,
+# role=PromptMessageRole.ASSISTANT,
+# edition_type="basic",
+# ),
+# ],
+# expected_messages=[
+# SystemPromptMessage(content=fake_context),
+# UserPromptMessage(content=fake_context),
+# AssistantPromptMessage(content=fake_assistant_prompt),
+# ]
+# + mock_history[fake_window_size * -2 :]
+# + [
+# UserPromptMessage(
+# content=[
+# TextPromptMessageContent(data=fake_query),
+# ImagePromptMessageContent(
+# url=fake_remote_url, mime_type="image/jpg", format="jpg", detail=fake_vision_detail
+# ),
+# ]
+# ),
+# ],
+# ),
+# LLMNodeTestScenario(
+# description="Prompt template with variable selector of File",
+# sys_query=fake_query,
+# sys_files=[],
+# vision_enabled=False,
+# vision_detail=fake_vision_detail,
+# features=[ModelFeature.VISION],
+# window_size=fake_window_size,
+# prompt_template=[
+# LLMNodeChatModelMessage(
+# text="{{#input.image#}}",
+# role=PromptMessageRole.USER,
+# edition_type="basic",
+# ),
+# ],
+# expected_messages=[
+# UserPromptMessage(
+# content=[
+# ImagePromptMessageContent(
+# url=fake_remote_url, mime_type="image/jpg", format="jpg", detail=fake_vision_detail
+# ),
+# ]
+# ),
+# ]
+# + mock_history[fake_window_size * -2 :]
+# + [UserPromptMessage(content=fake_query)],
+# file_variables={
+# "input.image": File(
+# tenant_id="test",
+# type=FileType.IMAGE,
+# filename="test1.jpg",
+# transfer_method=FileTransferMethod.REMOTE_URL,
+# remote_url=fake_remote_url,
+# extension=".jpg",
+# mime_type="image/jpg",
+# storage_key="",
+# )
+# },
+# ),
+# ]
+
+# for scenario in test_scenarios:
+# model_config.model_schema.features = scenario.features
+
+# for k, v in scenario.file_variables.items():
+# selector = k.split(".")
+# llm_node.graph_runtime_state.variable_pool.add(selector, v)
+
+# # Call the method under test
+# prompt_messages, _ = llm_node._fetch_prompt_messages(
+# sys_query=scenario.sys_query,
+# sys_files=scenario.sys_files,
+# context=fake_context,
+# memory=memory,
+# model_config=model_config,
+# prompt_template=scenario.prompt_template,
+# memory_config=memory_config,
+# vision_enabled=scenario.vision_enabled,
+# vision_detail=scenario.vision_detail,
+# variable_pool=llm_node.graph_runtime_state.variable_pool,
+# jinja2_variables=[],
+# )
+
+# # Verify the result
+# assert len(prompt_messages) == len(scenario.expected_messages), f"Scenario failed: {scenario.description}"
+# assert prompt_messages == scenario.expected_messages, (
+# f"Message content mismatch in scenario: {scenario.description}"
+# )
def test_handle_list_messages_basic(llm_node):
diff --git a/api/tests/unit_tests/core/workflow/nodes/test_continue_on_error.py b/api/tests/unit_tests/core/workflow/nodes/test_continue_on_error.py
index 2d74be9da9a96c..ed35d8a32acf33 100644
--- a/api/tests/unit_tests/core/workflow/nodes/test_continue_on_error.py
+++ b/api/tests/unit_tests/core/workflow/nodes/test_continue_on_error.py
@@ -126,7 +126,7 @@ def get_tool_node(error_strategy: str = "fail-branch", default_value: dict | Non
},
}
if default_value:
- node["data"]["default_value"] = default_value
+ node.node_data.default_value = default_value
return node
@staticmethod
@@ -331,55 +331,55 @@ def test_http_node_fail_branch_continue_on_error():
assert sum(1 for e in events if isinstance(e, NodeRunStreamChunkEvent)) == 1
-def test_tool_node_default_value_continue_on_error():
- """Test tool node with default value error strategy"""
- graph_config = {
- "edges": DEFAULT_VALUE_EDGE,
- "nodes": [
- {"data": {"title": "start", "type": "start", "variables": []}, "id": "start"},
- {"data": {"title": "answer", "type": "answer", "answer": "{{#node.result#}}"}, "id": "answer"},
- ContinueOnErrorTestHelper.get_tool_node(
- "default-value", [{"key": "result", "type": "string", "value": "default tool result"}]
- ),
- ],
- }
-
- graph_engine = ContinueOnErrorTestHelper.create_test_graph_engine(graph_config)
- events = list(graph_engine.run())
-
- assert any(isinstance(e, NodeRunExceptionEvent) for e in events)
- assert any(
- isinstance(e, GraphRunPartialSucceededEvent) and e.outputs == {"answer": "default tool result"} for e in events
- )
- assert sum(1 for e in events if isinstance(e, NodeRunStreamChunkEvent)) == 1
-
-
-def test_tool_node_fail_branch_continue_on_error():
- """Test HTTP node with fail-branch error strategy"""
- graph_config = {
- "edges": FAIL_BRANCH_EDGES,
- "nodes": [
- {"data": {"title": "Start", "type": "start", "variables": []}, "id": "start"},
- {
- "data": {"title": "success", "type": "answer", "answer": "tool execute successful"},
- "id": "success",
- },
- {
- "data": {"title": "error", "type": "answer", "answer": "tool execute failed"},
- "id": "error",
- },
- ContinueOnErrorTestHelper.get_tool_node(),
- ],
- }
-
- graph_engine = ContinueOnErrorTestHelper.create_test_graph_engine(graph_config)
- events = list(graph_engine.run())
-
- assert any(isinstance(e, NodeRunExceptionEvent) for e in events)
- assert any(
- isinstance(e, GraphRunPartialSucceededEvent) and e.outputs == {"answer": "tool execute failed"} for e in events
- )
- assert sum(1 for e in events if isinstance(e, NodeRunStreamChunkEvent)) == 1
+# def test_tool_node_default_value_continue_on_error():
+# """Test tool node with default value error strategy"""
+# graph_config = {
+# "edges": DEFAULT_VALUE_EDGE,
+# "nodes": [
+# {"data": {"title": "start", "type": "start", "variables": []}, "id": "start"},
+# {"data": {"title": "answer", "type": "answer", "answer": "{{#node.result#}}"}, "id": "answer"},
+# ContinueOnErrorTestHelper.get_tool_node(
+# "default-value", [{"key": "result", "type": "string", "value": "default tool result"}]
+# ),
+# ],
+# }
+
+# graph_engine = ContinueOnErrorTestHelper.create_test_graph_engine(graph_config)
+# events = list(graph_engine.run())
+
+# assert any(isinstance(e, NodeRunExceptionEvent) for e in events)
+# assert any(
+# isinstance(e, GraphRunPartialSucceededEvent) and e.outputs == {"answer": "default tool result"} for e in events # noqa: E501
+# )
+# assert sum(1 for e in events if isinstance(e, NodeRunStreamChunkEvent)) == 1
+
+
+# def test_tool_node_fail_branch_continue_on_error():
+# """Test HTTP node with fail-branch error strategy"""
+# graph_config = {
+# "edges": FAIL_BRANCH_EDGES,
+# "nodes": [
+# {"data": {"title": "Start", "type": "start", "variables": []}, "id": "start"},
+# {
+# "data": {"title": "success", "type": "answer", "answer": "tool execute successful"},
+# "id": "success",
+# },
+# {
+# "data": {"title": "error", "type": "answer", "answer": "tool execute failed"},
+# "id": "error",
+# },
+# ContinueOnErrorTestHelper.get_tool_node(),
+# ],
+# }
+
+# graph_engine = ContinueOnErrorTestHelper.create_test_graph_engine(graph_config)
+# events = list(graph_engine.run())
+
+# assert any(isinstance(e, NodeRunExceptionEvent) for e in events)
+# assert any(
+# isinstance(e, GraphRunPartialSucceededEvent) and e.outputs == {"answer": "tool execute failed"} for e in events # noqa: E501
+# )
+# assert sum(1 for e in events if isinstance(e, NodeRunStreamChunkEvent)) == 1
def test_llm_node_default_value_continue_on_error():
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 1a550ec5309aa3..5dfdfc0ebdac2f 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
@@ -8,7 +8,7 @@
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.document_extractor import DocumentExtractorNode, DocumentExtractorNodeData
from core.workflow.nodes.document_extractor.node import (
- _extract_text_from_doc,
+ _extract_text_from_docx,
_extract_text_from_pdf,
_extract_text_from_plain_text,
)
@@ -120,7 +120,7 @@ def test_run_extract_text(
monkeypatch.setattr("core.workflow.nodes.document_extractor.node._extract_text_from_pdf", mock_pdf_extract)
elif mime_type.startswith("application/vnd.openxmlformats"):
mock_docx_extract = Mock(return_value=expected_text[0])
- monkeypatch.setattr("core.workflow.nodes.document_extractor.node._extract_text_from_doc", mock_docx_extract)
+ monkeypatch.setattr("core.workflow.nodes.document_extractor.node._extract_text_from_docx", mock_docx_extract)
result = document_extractor_node._run()
@@ -163,14 +163,14 @@ def test_extract_text_from_pdf(mock_pdf_document):
@patch("docx.Document")
-def test_extract_text_from_doc(mock_document):
+def test_extract_text_from_docx(mock_document):
mock_paragraph1 = Mock()
mock_paragraph1.text = "Paragraph 1"
mock_paragraph2 = Mock()
mock_paragraph2.text = "Paragraph 2"
mock_document.return_value.paragraphs = [mock_paragraph1, mock_paragraph2]
- text = _extract_text_from_doc(b"PK\x03\x04")
+ text = _extract_text_from_docx(b"PK\x03\x04")
assert text == "Paragraph 1\nParagraph 2"
diff --git a/dev/pytest/pytest_config_tests.py b/dev/pytest/pytest_config_tests.py
index 08adc9ebe999b1..63d0cbaf3a84ad 100644
--- a/dev/pytest/pytest_config_tests.py
+++ b/dev/pytest/pytest_config_tests.py
@@ -10,6 +10,8 @@
"HTTP_REQUEST_MAX_CONNECT_TIMEOUT",
"HTTP_REQUEST_MAX_READ_TIMEOUT",
"HTTP_REQUEST_MAX_WRITE_TIMEOUT",
+ "INNER_API_KEY",
+ "INNER_API_KEY_FOR_PLUGIN",
"KEYWORD_DATA_SOURCE_TYPE",
"LOGIN_LOCKOUT_DURATION",
"LOG_FORMAT",
@@ -18,6 +20,10 @@
"OCI_ENDPOINT",
"OCI_REGION",
"OCI_SECRET_KEY",
+ "PLUGIN_DAEMON_KEY",
+ "PLUGIN_DAEMON_URL",
+ "PLUGIN_REMOTE_INSTALL_HOST",
+ "PLUGIN_REMOTE_INSTALL_PORT",
"REDIS_DB",
"RESEND_API_URL",
"RESPECT_XFORWARD_HEADERS_ENABLED",
@@ -40,6 +46,8 @@
"HTTP_REQUEST_MAX_CONNECT_TIMEOUT",
"HTTP_REQUEST_MAX_READ_TIMEOUT",
"HTTP_REQUEST_MAX_WRITE_TIMEOUT",
+ "INNER_API_KEY",
+ "INNER_API_KEY_FOR_PLUGIN",
"KEYWORD_DATA_SOURCE_TYPE",
"LOGIN_LOCKOUT_DURATION",
"LOG_FORMAT",
@@ -58,6 +66,10 @@
"PGVECTO_RS_PASSWORD",
"PGVECTO_RS_PORT",
"PGVECTO_RS_USER",
+ "PLUGIN_DAEMON_KEY",
+ "PLUGIN_DAEMON_URL",
+ "PLUGIN_REMOTE_INSTALL_HOST",
+ "PLUGIN_REMOTE_INSTALL_PORT",
"RESPECT_XFORWARD_HEADERS_ENABLED",
"SCARF_NO_ANALYTICS",
"SSRF_DEFAULT_CONNECT_TIME_OUT",
diff --git a/docker-legacy/docker-compose.chroma.yaml b/docker-legacy/docker-compose.chroma.yaml
deleted file mode 100644
index 63354305deff40..00000000000000
--- a/docker-legacy/docker-compose.chroma.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-services:
- # Chroma vector store.
- chroma:
- image: ghcr.io/chroma-core/chroma:0.5.20
- restart: always
- volumes:
- - ./volumes/chroma:/chroma/chroma
- environment:
- CHROMA_SERVER_AUTHN_CREDENTIALS: difyai123456
- CHROMA_SERVER_AUTHN_PROVIDER: chromadb.auth.token_authn.TokenAuthenticationServerProvider
- IS_PERSISTENT: TRUE
- ports:
- - "8000:8000"
diff --git a/docker-legacy/docker-compose.middleware.yaml b/docker-legacy/docker-compose.middleware.yaml
deleted file mode 100644
index da54fe33fdd1a7..00000000000000
--- a/docker-legacy/docker-compose.middleware.yaml
+++ /dev/null
@@ -1,109 +0,0 @@
-version: '3'
-services:
- # The postgres database.
- db:
- image: postgres:15-alpine
- restart: always
- environment:
- # The password for the default postgres user.
- POSTGRES_PASSWORD: difyai123456
- # The name of the default postgres database.
- POSTGRES_DB: dify
- # postgres data directory
- PGDATA: /var/lib/postgresql/data/pgdata
- volumes:
- - ./volumes/db/data:/var/lib/postgresql/data
- ports:
- - "5432:5432"
-
- # The redis cache.
- redis:
- image: redis:6-alpine
- restart: always
- volumes:
- # Mount the redis data directory to the container.
- - ./volumes/redis/data:/data
- # Set the redis password when startup redis server.
- command: redis-server --requirepass difyai123456
- ports:
- - "6379:6379"
-
- # The Weaviate vector store.
- weaviate:
- image: semitechnologies/weaviate:1.19.0
- restart: always
- volumes:
- # Mount the Weaviate data directory to the container.
- - ./volumes/weaviate:/var/lib/weaviate
- environment:
- # The Weaviate configurations
- # You can refer to the [Weaviate](https://weaviate.io/developers/weaviate/config-refs/env-vars) documentation for more information.
- QUERY_DEFAULTS_LIMIT: 25
- AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false'
- PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
- DEFAULT_VECTORIZER_MODULE: 'none'
- CLUSTER_HOSTNAME: 'node1'
- AUTHENTICATION_APIKEY_ENABLED: 'true'
- AUTHENTICATION_APIKEY_ALLOWED_KEYS: 'WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih'
- AUTHENTICATION_APIKEY_USERS: 'hello@dify.ai'
- AUTHORIZATION_ADMINLIST_ENABLED: 'true'
- AUTHORIZATION_ADMINLIST_USERS: 'hello@dify.ai'
- ports:
- - "8080:8080"
-
- # The DifySandbox
- sandbox:
- image: langgenius/dify-sandbox:0.2.1
- restart: always
- environment:
- # The DifySandbox configurations
- # Make sure you are changing this key for your deployment with a strong key.
- # You can generate a strong key using `openssl rand -base64 42`.
- API_KEY: dify-sandbox
- GIN_MODE: 'release'
- WORKER_TIMEOUT: 15
- ENABLE_NETWORK: 'true'
- HTTP_PROXY: 'http://ssrf_proxy:3128'
- HTTPS_PROXY: 'http://ssrf_proxy:3128'
- SANDBOX_PORT: 8194
- volumes:
- - ./volumes/sandbox/dependencies:/dependencies
- networks:
- - ssrf_proxy_network
-
- # ssrf_proxy server
- # for more information, please refer to
- # https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
- ssrf_proxy:
- image: ubuntu/squid:latest
- restart: always
- ports:
- - "3128:3128"
- - "8194:8194"
- volumes:
- # pls clearly modify the squid.conf file to fit your network environment.
- - ./volumes/ssrf_proxy/squid.conf:/etc/squid/squid.conf
- networks:
- - ssrf_proxy_network
- - default
- # Qdrant vector store.
- # uncomment to use qdrant as vector store.
- # (if uncommented, you need to comment out the weaviate service above,
- # and set VECTOR_STORE to qdrant in the api & worker service.)
- # qdrant:
- # image: qdrant/qdrant:1.7.3
- # restart: always
- # volumes:
- # - ./volumes/qdrant:/qdrant/storage
- # environment:
- # QDRANT_API_KEY: 'difyai123456'
- # ports:
- # - "6333:6333"
- # - "6334:6334"
-
-
-networks:
- # create a network between sandbox, api and ssrf_proxy, and can not access outside.
- ssrf_proxy_network:
- driver: bridge
- internal: true
diff --git a/docker-legacy/docker-compose.milvus.yaml b/docker-legacy/docker-compose.milvus.yaml
deleted file mode 100644
index f4a7afa3a10101..00000000000000
--- a/docker-legacy/docker-compose.milvus.yaml
+++ /dev/null
@@ -1,64 +0,0 @@
-version: '3.5'
-
-services:
- etcd:
- container_name: milvus-etcd
- image: quay.io/coreos/etcd:v3.5.5
- environment:
- - ETCD_AUTO_COMPACTION_MODE=revision
- - ETCD_AUTO_COMPACTION_RETENTION=1000
- - ETCD_QUOTA_BACKEND_BYTES=4294967296
- - ETCD_SNAPSHOT_COUNT=50000
- volumes:
- - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
- command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
- healthcheck:
- test: ["CMD", "etcdctl", "endpoint", "health"]
- interval: 30s
- timeout: 20s
- retries: 3
-
- minio:
- container_name: milvus-minio
- image: minio/minio:RELEASE.2023-03-20T20-16-18Z
- environment:
- MINIO_ACCESS_KEY: minioadmin
- MINIO_SECRET_KEY: minioadmin
- ports:
- - "9001:9001"
- - "9000:9000"
- volumes:
- - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
- command: minio server /minio_data --console-address ":9001"
- healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
- interval: 30s
- timeout: 20s
- retries: 3
-
- milvus-standalone:
- container_name: milvus-standalone
- image: milvusdb/milvus:v2.4.6
- command: ["milvus", "run", "standalone"]
- environment:
- ETCD_ENDPOINTS: etcd:2379
- MINIO_ADDRESS: minio:9000
- common.security.authorizationEnabled: true
- volumes:
- - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
- healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
- interval: 30s
- start_period: 90s
- timeout: 20s
- retries: 3
- ports:
- - "19530:19530"
- - "9091:9091"
- depends_on:
- - "etcd"
- - "minio"
-
-networks:
- default:
- name: milvus
diff --git a/docker-legacy/docker-compose.opensearch.yml b/docker-legacy/docker-compose.opensearch.yml
deleted file mode 100644
index ce7203318070aa..00000000000000
--- a/docker-legacy/docker-compose.opensearch.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-services:
- opensearch: # This is also the hostname of the container within the Docker network (i.e. https://opensearch/)
- image: opensearchproject/opensearch:latest # Specifying the latest available image - modify if you want a specific version
- container_name: opensearch
- environment:
- - discovery.type=single-node
- - bootstrap.memory_lock=true # Disable JVM heap memory swapping
- - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx1024m" # Set min and max JVM heap sizes to at least 50% of system RAM
- - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Qazwsxedc!@#123 # Sets the demo admin user password when using demo configuration, required for OpenSearch 2.12 and later
- ulimits:
- memlock:
- soft: -1 # Set memlock to unlimited (no soft or hard limit)
- hard: -1
- nofile:
- soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536
- hard: 65536
- volumes:
- - ./volumes/opensearch/data:/usr/share/opensearch/data # Creates volume called opensearch-data1 and mounts it to the container
- ports:
- - 9200:9200 # REST API
- - 9600:9600 # Performance Analyzer
- networks:
- - opensearch-net # All of the containers will join the same Docker bridge network
- opensearch-dashboards:
- image: opensearchproject/opensearch-dashboards:latest # Make sure the version of opensearch-dashboards matches the version of opensearch installed on other nodes
- container_name: opensearch-dashboards
- ports:
- - 5601:5601 # Map host port 5601 to container port 5601
- expose:
- - "5601" # Expose port 5601 for web access to OpenSearch Dashboards
- environment:
- OPENSEARCH_HOSTS: '["https://opensearch:9200"]' # Define the OpenSearch nodes that OpenSearch Dashboards will query
- volumes:
- - ./volumes/opensearch/opensearch_dashboards.yml:/usr/share/opensearch-dashboards/config/opensearch_dashboards.yml
- networks:
- - opensearch-net
-
-networks:
- opensearch-net:
- driver: bridge
diff --git a/docker-legacy/docker-compose.oracle.yaml b/docker-legacy/docker-compose.oracle.yaml
deleted file mode 100644
index a10d2556b33ad4..00000000000000
--- a/docker-legacy/docker-compose.oracle.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- # oracle 23 ai vector store.
- oracle:
- image: container-registry.oracle.com/database/free:latest
- restart: always
- ports:
- - 1521:1521
- volumes:
- - type: volume
- source: oradata_vector
- target: /opt/oracle/oradata
- - ./startupscripts:/opt/oracle/scripts/startup
- environment:
- - ORACLE_PWD=Dify123456
- - ORACLE_CHARACTERSET=AL32UTF8
-volumes:
- oradata_vector:
diff --git a/docker-legacy/docker-compose.pgvecto-rs.yaml b/docker-legacy/docker-compose.pgvecto-rs.yaml
deleted file mode 100644
index e383b75a838dac..00000000000000
--- a/docker-legacy/docker-compose.pgvecto-rs.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-services:
- # The pgvecto—rs database.
- pgvecto-rs:
- image: tensorchord/pgvecto-rs:pg16-v0.2.0
- restart: always
- environment:
- PGUSER: postgres
- # The password for the default postgres user.
- POSTGRES_PASSWORD: difyai123456
- # The name of the default postgres database.
- POSTGRES_DB: dify
- # postgres data directory
- PGDATA: /var/lib/postgresql/data/pgdata
- volumes:
- - ./volumes/pgvectors/data:/var/lib/postgresql/data
- # uncomment to expose db(postgresql) port to host
- ports:
- - "5431:5432"
- healthcheck:
- test: [ "CMD", "pg_isready" ]
- interval: 1s
- timeout: 3s
- retries: 30
diff --git a/docker-legacy/docker-compose.pgvector.yaml b/docker-legacy/docker-compose.pgvector.yaml
deleted file mode 100644
index fce1cf904381ec..00000000000000
--- a/docker-legacy/docker-compose.pgvector.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-services:
- # Qdrant vector store.
- pgvector:
- image: pgvector/pgvector:pg16
- restart: always
- environment:
- PGUSER: postgres
- # The password for the default postgres user.
- POSTGRES_PASSWORD: difyai123456
- # The name of the default postgres database.
- POSTGRES_DB: dify
- # postgres data directory
- PGDATA: /var/lib/postgresql/data/pgdata
- volumes:
- - ./volumes/pgvector/data:/var/lib/postgresql/data
- # uncomment to expose db(postgresql) port to host
- ports:
- - "5433:5432"
- healthcheck:
- test: [ "CMD", "pg_isready" ]
- interval: 1s
- timeout: 3s
- retries: 30
diff --git a/docker-legacy/docker-compose.png b/docker-legacy/docker-compose.png
deleted file mode 100644
index bdac113086d870..00000000000000
Binary files a/docker-legacy/docker-compose.png and /dev/null differ
diff --git a/docker-legacy/docker-compose.qdrant.yaml b/docker-legacy/docker-compose.qdrant.yaml
deleted file mode 100644
index 8e59287b2835c5..00000000000000
--- a/docker-legacy/docker-compose.qdrant.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-services:
- # Qdrant vector store.
- qdrant:
- image: langgenius/qdrant:v1.7.3
- restart: always
- volumes:
- - ./volumes/qdrant:/qdrant/storage
- environment:
- QDRANT_API_KEY: 'difyai123456'
- ports:
- - "6333:6333"
- - "6334:6334"
diff --git a/docker-legacy/docker-compose.yaml b/docker-legacy/docker-compose.yaml
deleted file mode 100644
index 0a071e80b360f5..00000000000000
--- a/docker-legacy/docker-compose.yaml
+++ /dev/null
@@ -1,597 +0,0 @@
-version: '3'
-services:
- # API service
- api:
- image: langgenius/dify-api:0.15.3
- restart: always
- environment:
- # Startup mode, 'api' starts the API server.
- MODE: api
- # The log level for the application. Supported values are `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
- LOG_LEVEL: INFO
- # enable DEBUG mode to output more logs
- # DEBUG : true
- # A secret key that is used for securely signing the session cookie and encrypting sensitive information on the database. You can generate a strong key using `openssl rand -base64 42`.
- SECRET_KEY: sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U
- # The base URL of console application web frontend, refers to the Console base URL of WEB service if console domain is
- # different from api or web app domain.
- # example: http://cloud.dify.ai
- CONSOLE_WEB_URL: ''
- # Password for admin user initialization.
- # If left unset, admin user will not be prompted for a password when creating the initial admin account.
- INIT_PASSWORD: ''
- # The base URL of console application api server, refers to the Console base URL of WEB service if console domain is
- # different from api or web app domain.
- # example: http://cloud.dify.ai
- CONSOLE_API_URL: ''
- # The URL prefix for Service API endpoints, refers to the base URL of the current API service if api domain is
- # different from console domain.
- # example: http://api.dify.ai
- SERVICE_API_URL: ''
- # The URL prefix for Web APP frontend, refers to the Web App base URL of WEB service if web app domain is different from
- # console or api domain.
- # example: http://udify.app
- APP_WEB_URL: ''
- # File preview or download Url prefix.
- # used to display File preview or download Url to the front-end or as Multi-model inputs;
- # Url is signed and has expiration time.
- FILES_URL: ''
- # File Access Time specifies a time interval in seconds for the file to be accessed.
- # The default value is 300 seconds.
- FILES_ACCESS_TIMEOUT: 300
- # The maximum number of active requests for the application, where 0 means unlimited, should be a non-negative integer.
- APP_MAX_ACTIVE_REQUESTS: 0
- # When enabled, migrations will be executed prior to application startup and the application will start after the migrations have completed.
- MIGRATION_ENABLED: 'true'
- # The configurations of postgres database connection.
- # It is consistent with the configuration in the 'db' service below.
- DB_USERNAME: postgres
- DB_PASSWORD: difyai123456
- DB_HOST: db
- DB_PORT: 5432
- DB_DATABASE: dify
- # The configurations of redis connection.
- # It is consistent with the configuration in the 'redis' service below.
- REDIS_HOST: redis
- REDIS_PORT: 6379
- REDIS_USERNAME: ''
- REDIS_PASSWORD: difyai123456
- REDIS_USE_SSL: 'false'
- # use redis db 0 for redis cache
- REDIS_DB: 0
- # The configurations of celery broker.
- # Use redis as the broker, and redis db 1 for celery broker.
- CELERY_BROKER_URL: redis://:difyai123456@redis:6379/1
- # Specifies the allowed origins for cross-origin requests to the Web API, e.g. https://dify.app or * for all origins.
- WEB_API_CORS_ALLOW_ORIGINS: '*'
- # Specifies the allowed origins for cross-origin requests to the console API, e.g. https://cloud.dify.ai or * for all origins.
- CONSOLE_CORS_ALLOW_ORIGINS: '*'
- # CSRF Cookie settings
- # Controls whether a cookie is sent with cross-site requests,
- # providing some protection against cross-site request forgery attacks
- #
- # Default: `SameSite=Lax, Secure=false, HttpOnly=true`
- # This default configuration supports same-origin requests using either HTTP or HTTPS,
- # but does not support cross-origin requests. It is suitable for local debugging purposes.
- #
- # If you want to enable cross-origin support,
- # you must use the HTTPS protocol and set the configuration to `SameSite=None, Secure=true, HttpOnly=true`.
- #
- # The type of storage to use for storing user files. Supported values are `local` and `s3` and `azure-blob` and `google-storage`, Default: `local`
- STORAGE_TYPE: local
- # The path to the local storage directory, the directory relative the root path of API service codes or absolute path. Default: `storage` or `/home/john/storage`.
- # only available when STORAGE_TYPE is `local`.
- STORAGE_LOCAL_PATH: storage
- # The S3 storage configurations, only available when STORAGE_TYPE is `s3`.
- S3_USE_AWS_MANAGED_IAM: 'false'
- S3_ENDPOINT: 'https://xxx.r2.cloudflarestorage.com'
- S3_BUCKET_NAME: 'difyai'
- S3_ACCESS_KEY: 'ak-difyai'
- S3_SECRET_KEY: 'sk-difyai'
- S3_REGION: 'us-east-1'
- # The Azure Blob storage configurations, only available when STORAGE_TYPE is `azure-blob`.
- AZURE_BLOB_ACCOUNT_NAME: 'difyai'
- AZURE_BLOB_ACCOUNT_KEY: 'difyai'
- AZURE_BLOB_CONTAINER_NAME: 'difyai-container'
- AZURE_BLOB_ACCOUNT_URL: 'https://.blob.core.windows.net'
- # The Google storage configurations, only available when STORAGE_TYPE is `google-storage`.
- GOOGLE_STORAGE_BUCKET_NAME: 'yout-bucket-name'
- # if you want to use Application Default Credentials, you can leave GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64 empty.
- GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64: 'your-google-service-account-json-base64-string'
- # The Alibaba Cloud OSS configurations, only available when STORAGE_TYPE is `aliyun-oss`
- ALIYUN_OSS_BUCKET_NAME: 'your-bucket-name'
- ALIYUN_OSS_ACCESS_KEY: 'your-access-key'
- ALIYUN_OSS_SECRET_KEY: 'your-secret-key'
- ALIYUN_OSS_ENDPOINT: 'https://oss-ap-southeast-1-internal.aliyuncs.com'
- ALIYUN_OSS_REGION: 'ap-southeast-1'
- ALIYUN_OSS_AUTH_VERSION: 'v4'
- # The Tencent COS storage configurations, only available when STORAGE_TYPE is `tencent-cos`.
- TENCENT_COS_BUCKET_NAME: 'your-bucket-name'
- TENCENT_COS_SECRET_KEY: 'your-secret-key'
- TENCENT_COS_SECRET_ID: 'your-secret-id'
- TENCENT_COS_REGION: 'your-region'
- TENCENT_COS_SCHEME: 'your-scheme'
- # The type of vector store to use. Supported values are `weaviate`, `qdrant`, `milvus`, `relyt`,`pgvector`, `chroma`, 'opensearch', 'tidb_vector'.
- VECTOR_STORE: weaviate
- # The Weaviate endpoint URL. Only available when VECTOR_STORE is `weaviate`.
- WEAVIATE_ENDPOINT: http://weaviate:8080
- # The Weaviate API key.
- WEAVIATE_API_KEY: WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih
- # The Qdrant endpoint URL. Only available when VECTOR_STORE is `qdrant`.
- QDRANT_URL: http://qdrant:6333
- # The Qdrant API key.
- QDRANT_API_KEY: difyai123456
- # The Qdrant client timeout setting.
- QDRANT_CLIENT_TIMEOUT: 20
- # The Qdrant client enable gRPC mode.
- QDRANT_GRPC_ENABLED: 'false'
- # The Qdrant server gRPC mode PORT.
- QDRANT_GRPC_PORT: 6334
- # Milvus configuration Only available when VECTOR_STORE is `milvus`.
- # The milvus uri.
- MILVUS_URI: http://127.0.0.1:19530
- # The milvus token.
- MILVUS_TOKEN: ''
- # The milvus username.
- MILVUS_USER: root
- # The milvus password.
- MILVUS_PASSWORD: Milvus
- # relyt configurations
- RELYT_HOST: db
- RELYT_PORT: 5432
- RELYT_USER: postgres
- RELYT_PASSWORD: difyai123456
- RELYT_DATABASE: postgres
- # pgvector configurations
- PGVECTOR_HOST: pgvector
- PGVECTOR_PORT: 5432
- PGVECTOR_USER: postgres
- PGVECTOR_PASSWORD: difyai123456
- PGVECTOR_DATABASE: dify
- # tidb vector configurations
- TIDB_VECTOR_HOST: tidb
- TIDB_VECTOR_PORT: 4000
- TIDB_VECTOR_USER: xxx.root
- TIDB_VECTOR_PASSWORD: xxxxxx
- TIDB_VECTOR_DATABASE: dify
- # oracle configurations
- ORACLE_HOST: oracle
- ORACLE_PORT: 1521
- ORACLE_USER: dify
- ORACLE_PASSWORD: dify
- ORACLE_DATABASE: FREEPDB1
- # Chroma configuration
- CHROMA_HOST: 127.0.0.1
- CHROMA_PORT: 8000
- CHROMA_TENANT: default_tenant
- CHROMA_DATABASE: default_database
- CHROMA_AUTH_PROVIDER: chromadb.auth.token_authn.TokenAuthClientProvider
- CHROMA_AUTH_CREDENTIALS: xxxxxx
- # ElasticSearch Config
- ELASTICSEARCH_HOST: 127.0.0.1
- ELASTICSEARCH_PORT: 9200
- ELASTICSEARCH_USERNAME: elastic
- ELASTICSEARCH_PASSWORD: elastic
- # Mail configuration, support: resend, smtp
- MAIL_TYPE: ''
- # default send from email address, if not specified
- MAIL_DEFAULT_SEND_FROM: 'YOUR EMAIL FROM (eg: no-reply )'
- SMTP_SERVER: ''
- SMTP_PORT: 465
- SMTP_USERNAME: ''
- SMTP_PASSWORD: ''
- SMTP_USE_TLS: 'true'
- SMTP_OPPORTUNISTIC_TLS: 'false'
- # the api-key for resend (https://resend.com)
- RESEND_API_KEY: ''
- RESEND_API_URL: https://api.resend.com
- # The DSN for Sentry error reporting. If not set, Sentry error reporting will be disabled.
- SENTRY_DSN: ''
- # The sample rate for Sentry events. Default: `1.0`
- SENTRY_TRACES_SAMPLE_RATE: 1.0
- # The sample rate for Sentry profiles. Default: `1.0`
- SENTRY_PROFILES_SAMPLE_RATE: 1.0
- # Notion import configuration, support public and internal
- NOTION_INTEGRATION_TYPE: public
- NOTION_CLIENT_SECRET: you-client-secret
- NOTION_CLIENT_ID: you-client-id
- NOTION_INTERNAL_SECRET: you-internal-secret
- # The sandbox service endpoint.
- CODE_EXECUTION_ENDPOINT: "http://sandbox:8194"
- CODE_EXECUTION_API_KEY: dify-sandbox
- CODE_MAX_NUMBER: 9223372036854775807
- CODE_MIN_NUMBER: -9223372036854775808
- CODE_MAX_STRING_LENGTH: 80000
- TEMPLATE_TRANSFORM_MAX_LENGTH: 80000
- CODE_MAX_STRING_ARRAY_LENGTH: 30
- CODE_MAX_OBJECT_ARRAY_LENGTH: 30
- CODE_MAX_NUMBER_ARRAY_LENGTH: 1000
- # SSRF Proxy server
- SSRF_PROXY_HTTP_URL: 'http://ssrf_proxy:3128'
- SSRF_PROXY_HTTPS_URL: 'http://ssrf_proxy:3128'
- # Indexing configuration
- INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: 4000
- depends_on:
- - db
- - redis
- volumes:
- # Mount the storage directory to the container, for storing user files.
- - ./volumes/app/storage:/app/api/storage
- # uncomment to expose dify-api port to host
- # ports:
- # - "5001:5001"
- networks:
- - ssrf_proxy_network
- - default
-
- # worker service
- # The Celery worker for processing the queue.
- worker:
- image: langgenius/dify-api:0.15.3
- restart: always
- environment:
- CONSOLE_WEB_URL: ''
- # Startup mode, 'worker' starts the Celery worker for processing the queue.
- MODE: worker
-
- # --- All the configurations below are the same as those in the 'api' service. ---
-
- # The log level for the application. Supported values are `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
- LOG_LEVEL: INFO
- # A secret key that is used for securely signing the session cookie and encrypting sensitive information on the database. You can generate a strong key using `openssl rand -base64 42`.
- # same as the API service
- SECRET_KEY: sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U
- # The configurations of postgres database connection.
- # It is consistent with the configuration in the 'db' service below.
- DB_USERNAME: postgres
- DB_PASSWORD: difyai123456
- DB_HOST: db
- DB_PORT: 5432
- DB_DATABASE: dify
- # The configurations of redis cache connection.
- REDIS_HOST: redis
- REDIS_PORT: 6379
- REDIS_USERNAME: ''
- REDIS_PASSWORD: difyai123456
- REDIS_DB: 0
- REDIS_USE_SSL: 'false'
- # The configurations of celery broker.
- CELERY_BROKER_URL: redis://:difyai123456@redis:6379/1
- # The type of storage to use for storing user files. Supported values are `local` and `s3` and `azure-blob` and `google-storage`, Default: `local`
- STORAGE_TYPE: local
- STORAGE_LOCAL_PATH: storage
- # The S3 storage configurations, only available when STORAGE_TYPE is `s3`.
- S3_USE_AWS_MANAGED_IAM: 'false'
- S3_ENDPOINT: 'https://xxx.r2.cloudflarestorage.com'
- S3_BUCKET_NAME: 'difyai'
- S3_ACCESS_KEY: 'ak-difyai'
- S3_SECRET_KEY: 'sk-difyai'
- S3_REGION: 'us-east-1'
- # The Azure Blob storage configurations, only available when STORAGE_TYPE is `azure-blob`.
- AZURE_BLOB_ACCOUNT_NAME: 'difyai'
- AZURE_BLOB_ACCOUNT_KEY: 'difyai'
- AZURE_BLOB_CONTAINER_NAME: 'difyai-container'
- AZURE_BLOB_ACCOUNT_URL: 'https://.blob.core.windows.net'
- # The Google storage configurations, only available when STORAGE_TYPE is `google-storage`.
- GOOGLE_STORAGE_BUCKET_NAME: 'yout-bucket-name'
- # if you want to use Application Default Credentials, you can leave GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64 empty.
- GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64: 'your-google-service-account-json-base64-string'
- # The Alibaba Cloud OSS configurations, only available when STORAGE_TYPE is `aliyun-oss`
- ALIYUN_OSS_BUCKET_NAME: 'your-bucket-name'
- ALIYUN_OSS_ACCESS_KEY: 'your-access-key'
- ALIYUN_OSS_SECRET_KEY: 'your-secret-key'
- ALIYUN_OSS_ENDPOINT: 'https://oss-ap-southeast-1-internal.aliyuncs.com'
- ALIYUN_OSS_REGION: 'ap-southeast-1'
- ALIYUN_OSS_AUTH_VERSION: 'v4'
- # The Tencent COS storage configurations, only available when STORAGE_TYPE is `tencent-cos`.
- TENCENT_COS_BUCKET_NAME: 'your-bucket-name'
- TENCENT_COS_SECRET_KEY: 'your-secret-key'
- TENCENT_COS_SECRET_ID: 'your-secret-id'
- TENCENT_COS_REGION: 'your-region'
- TENCENT_COS_SCHEME: 'your-scheme'
- # The type of vector store to use. Supported values are `weaviate`, `qdrant`, `milvus`, `relyt`, `pgvector`, `chroma`, 'opensearch', 'tidb_vector'.
- VECTOR_STORE: weaviate
- # The Weaviate endpoint URL. Only available when VECTOR_STORE is `weaviate`.
- WEAVIATE_ENDPOINT: http://weaviate:8080
- # The Weaviate API key.
- WEAVIATE_API_KEY: WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih
- # The Qdrant endpoint URL. Only available when VECTOR_STORE is `qdrant`.
- QDRANT_URL: http://qdrant:6333
- # The Qdrant API key.
- QDRANT_API_KEY: difyai123456
- # The Qdrant client timeout setting.
- QDRANT_CLIENT_TIMEOUT: 20
- # The Qdrant client enable gRPC mode.
- QDRANT_GRPC_ENABLED: 'false'
- # The Qdrant server gRPC mode PORT.
- QDRANT_GRPC_PORT: 6334
- # Milvus configuration Only available when VECTOR_STORE is `milvus`.
- # The milvus uri.
- MILVUS_URI: http://127.0.0.1:19530
- # The milvus token.
- MILVUS_PORT: ''
- # The milvus username.
- MILVUS_USER: root
- # The milvus password.
- MILVUS_PASSWORD: Milvus
- # Mail configuration, support: resend
- MAIL_TYPE: ''
- # default send from email address, if not specified
- MAIL_DEFAULT_SEND_FROM: 'YOUR EMAIL FROM (eg: no-reply )'
- SMTP_SERVER: ''
- SMTP_PORT: 465
- SMTP_USERNAME: ''
- SMTP_PASSWORD: ''
- SMTP_USE_TLS: 'true'
- SMTP_OPPORTUNISTIC_TLS: 'false'
- # the api-key for resend (https://resend.com)
- RESEND_API_KEY: ''
- RESEND_API_URL: https://api.resend.com
- # relyt configurations
- RELYT_HOST: db
- RELYT_PORT: 5432
- RELYT_USER: postgres
- RELYT_PASSWORD: difyai123456
- RELYT_DATABASE: postgres
- # tencent configurations
- TENCENT_VECTOR_DB_URL: http://127.0.0.1
- TENCENT_VECTOR_DB_API_KEY: dify
- TENCENT_VECTOR_DB_TIMEOUT: 30
- TENCENT_VECTOR_DB_USERNAME: dify
- TENCENT_VECTOR_DB_DATABASE: dify
- TENCENT_VECTOR_DB_SHARD: 1
- TENCENT_VECTOR_DB_REPLICAS: 2
- # OpenSearch configuration
- OPENSEARCH_HOST: 127.0.0.1
- OPENSEARCH_PORT: 9200
- OPENSEARCH_USER: admin
- OPENSEARCH_PASSWORD: admin
- OPENSEARCH_SECURE: 'true'
- # pgvector configurations
- PGVECTOR_HOST: pgvector
- PGVECTOR_PORT: 5432
- PGVECTOR_USER: postgres
- PGVECTOR_PASSWORD: difyai123456
- PGVECTOR_DATABASE: dify
- # tidb vector configurations
- TIDB_VECTOR_HOST: tidb
- TIDB_VECTOR_PORT: 4000
- TIDB_VECTOR_USER: xxx.root
- TIDB_VECTOR_PASSWORD: xxxxxx
- TIDB_VECTOR_DATABASE: dify
- # oracle configurations
- ORACLE_HOST: oracle
- ORACLE_PORT: 1521
- ORACLE_USER: dify
- ORACLE_PASSWORD: dify
- ORACLE_DATABASE: FREEPDB1
- # Chroma configuration
- CHROMA_HOST: 127.0.0.1
- CHROMA_PORT: 8000
- CHROMA_TENANT: default_tenant
- CHROMA_DATABASE: default_database
- CHROMA_AUTH_PROVIDER: chromadb.auth.token_authn.TokenAuthClientProvider
- CHROMA_AUTH_CREDENTIALS: xxxxxx
- # ElasticSearch Config
- ELASTICSEARCH_HOST: 127.0.0.1
- ELASTICSEARCH_PORT: 9200
- ELASTICSEARCH_USERNAME: elastic
- ELASTICSEARCH_PASSWORD: elastic
- # Notion import configuration, support public and internal
- NOTION_INTEGRATION_TYPE: public
- NOTION_CLIENT_SECRET: you-client-secret
- NOTION_CLIENT_ID: you-client-id
- NOTION_INTERNAL_SECRET: you-internal-secret
- # Indexing configuration
- INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: 1000
- CREATE_TIDB_SERVICE_JOB_ENABLED: false
- depends_on:
- - db
- - redis
- volumes:
- # Mount the storage directory to the container, for storing user files.
- - ./volumes/app/storage:/app/api/storage
- networks:
- - ssrf_proxy_network
- - default
-
- # Frontend web application.
- web:
- image: langgenius/dify-web:0.15.3
- restart: always
- environment:
- # The base URL of console application api server, refers to the Console base URL of WEB service if console domain is
- # different from api or web app domain.
- # example: http://cloud.dify.ai
- CONSOLE_API_URL: ''
- # The URL for Web APP api server, refers to the Web App base URL of WEB service if web app domain is different from
- # console or api domain.
- # example: http://udify.app
- APP_API_URL: ''
- # The DSN for Sentry error reporting. If not set, Sentry error reporting will be disabled.
- SENTRY_DSN: ''
- # uncomment to expose dify-web port to host
- # ports:
- # - "3000:3000"
-
- # The postgres database.
- db:
- image: postgres:15-alpine
- restart: always
- environment:
- PGUSER: postgres
- # The password for the default postgres user.
- POSTGRES_PASSWORD: difyai123456
- # The name of the default postgres database.
- POSTGRES_DB: dify
- # postgres data directory
- PGDATA: /var/lib/postgresql/data/pgdata
- volumes:
- - ./volumes/db/data:/var/lib/postgresql/data
- # notice!: if you use windows-wsl2, postgres may not work properly due to the ntfs issue.you can use volumes to mount the data directory to the host.
- # if you use the following config, you need to uncomment the volumes configuration below at the end of the file.
- # - postgres:/var/lib/postgresql/data
- # uncomment to expose db(postgresql) port to host
- # ports:
- # - "5432:5432"
- healthcheck:
- test: [ "CMD", "pg_isready" ]
- interval: 1s
- timeout: 3s
- retries: 30
-
- # The redis cache.
- redis:
- image: redis:6-alpine
- restart: always
- volumes:
- # Mount the redis data directory to the container.
- - ./volumes/redis/data:/data
- # Set the redis password when startup redis server.
- command: redis-server --requirepass difyai123456
- healthcheck:
- test: [ "CMD", "redis-cli", "ping" ]
- # uncomment to expose redis port to host
- # ports:
- # - "6379:6379"
-
- # The Weaviate vector store.
- weaviate:
- image: semitechnologies/weaviate:1.19.0
- restart: always
- volumes:
- # Mount the Weaviate data directory to the container.
- - ./volumes/weaviate:/var/lib/weaviate
- environment:
- # The Weaviate configurations
- # You can refer to the [Weaviate](https://weaviate.io/developers/weaviate/config-refs/env-vars) documentation for more information.
- QUERY_DEFAULTS_LIMIT: 25
- AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false'
- PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
- DEFAULT_VECTORIZER_MODULE: 'none'
- CLUSTER_HOSTNAME: 'node1'
- AUTHENTICATION_APIKEY_ENABLED: 'true'
- AUTHENTICATION_APIKEY_ALLOWED_KEYS: 'WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih'
- AUTHENTICATION_APIKEY_USERS: 'hello@dify.ai'
- AUTHORIZATION_ADMINLIST_ENABLED: 'true'
- AUTHORIZATION_ADMINLIST_USERS: 'hello@dify.ai'
- # uncomment to expose weaviate port to host
- # ports:
- # - "8080:8080"
-
- # The DifySandbox
- sandbox:
- image: langgenius/dify-sandbox:0.2.1
- restart: always
- environment:
- # The DifySandbox configurations
- # Make sure you are changing this key for your deployment with a strong key.
- # You can generate a strong key using `openssl rand -base64 42`.
- API_KEY: dify-sandbox
- GIN_MODE: 'release'
- WORKER_TIMEOUT: 15
- ENABLE_NETWORK: 'true'
- HTTP_PROXY: 'http://ssrf_proxy:3128'
- HTTPS_PROXY: 'http://ssrf_proxy:3128'
- SANDBOX_PORT: 8194
- volumes:
- - ./volumes/sandbox/dependencies:/dependencies
- networks:
- - ssrf_proxy_network
-
- # ssrf_proxy server
- # for more information, please refer to
- # https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
- ssrf_proxy:
- image: ubuntu/squid:latest
- restart: always
- volumes:
- # pls clearly modify the squid.conf file to fit your network environment.
- - ./volumes/ssrf_proxy/squid.conf:/etc/squid/squid.conf
- networks:
- - ssrf_proxy_network
- - default
- # Qdrant vector store.
- # uncomment to use qdrant as vector store.
- # (if uncommented, you need to comment out the weaviate service above,
- # and set VECTOR_STORE to qdrant in the api & worker service.)
- # qdrant:
- # image: langgenius/qdrant:v1.7.3
- # restart: always
- # volumes:
- # - ./volumes/qdrant:/qdrant/storage
- # environment:
- # QDRANT_API_KEY: 'difyai123456'
- # # uncomment to expose qdrant port to host
- # # ports:
- # # - "6333:6333"
- # # - "6334:6334"
-
- # The pgvector vector database.
- # Uncomment to use qdrant as vector store.
- # pgvector:
- # image: pgvector/pgvector:pg16
- # restart: always
- # environment:
- # PGUSER: postgres
- # # The password for the default postgres user.
- # POSTGRES_PASSWORD: difyai123456
- # # The name of the default postgres database.
- # POSTGRES_DB: dify
- # # postgres data directory
- # PGDATA: /var/lib/postgresql/data/pgdata
- # volumes:
- # - ./volumes/pgvector/data:/var/lib/postgresql/data
- # # uncomment to expose db(postgresql) port to host
- # # ports:
- # # - "5433:5432"
- # healthcheck:
- # test: [ "CMD", "pg_isready" ]
- # interval: 1s
- # timeout: 3s
- # retries: 30
-
- # The oracle vector database.
- # Uncomment to use oracle23ai as vector store. Also need to Uncomment volumes block
- # oracle:
- # image: container-registry.oracle.com/database/free:latest
- # restart: always
- # ports:
- # - 1521:1521
- # volumes:
- # - type: volume
- # source: oradata
- # target: /opt/oracle/oradata
- # - ./startupscripts:/opt/oracle/scripts/startup
- # environment:
- # - ORACLE_PWD=Dify123456
- # - ORACLE_CHARACTERSET=AL32UTF8
-
-
- # The nginx reverse proxy.
- # used for reverse proxying the API service and Web service.
- nginx:
- image: nginx:latest
- restart: always
- volumes:
- - ./nginx/nginx.conf:/etc/nginx/nginx.conf
- - ./nginx/proxy.conf:/etc/nginx/proxy.conf
- - ./nginx/conf.d:/etc/nginx/conf.d
- #- ./nginx/ssl:/etc/ssl
- depends_on:
- - api
- - web
- ports:
- - "80:80"
- #- "443:443"
-# notice: if you use windows-wsl2, postgres may not work properly due to the ntfs issue.you can use volumes to mount the data directory to the host.
-# volumes:
-# postgres:
-networks:
- # create a network between sandbox, api and ssrf_proxy, and can not access outside.
- ssrf_proxy_network:
- driver: bridge
- internal: true
-
-#volumes:
-# oradata:
diff --git a/docker-legacy/nginx/conf.d/default.conf b/docker-legacy/nginx/conf.d/default.conf
deleted file mode 100644
index d6ee302b789a99..00000000000000
--- a/docker-legacy/nginx/conf.d/default.conf
+++ /dev/null
@@ -1,38 +0,0 @@
-server {
- listen 80;
- server_name _;
-
- location /console/api {
- proxy_pass http://api:5001;
- include proxy.conf;
- }
-
- location /api {
- proxy_pass http://api:5001;
- include proxy.conf;
- }
-
- location /v1 {
- proxy_pass http://api:5001;
- include proxy.conf;
- }
-
- location /files {
- proxy_pass http://api:5001;
- include proxy.conf;
- }
-
- location / {
- proxy_pass http://web:3000;
- include proxy.conf;
- }
-
- # If you want to support HTTPS, please uncomment the code snippet below
- #listen 443 ssl;
- #ssl_certificate ./../ssl/your_cert_file.cer;
- #ssl_certificate_key ./../ssl/your_cert_key.key;
- #ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
- #ssl_prefer_server_ciphers on;
- #ssl_session_cache shared:SSL:10m;
- #ssl_session_timeout 10m;
-}
diff --git a/docker-legacy/nginx/nginx.conf b/docker-legacy/nginx/nginx.conf
deleted file mode 100644
index d2b52963e8451e..00000000000000
--- a/docker-legacy/nginx/nginx.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-user nginx;
-worker_processes auto;
-
-error_log /var/log/nginx/error.log notice;
-pid /var/run/nginx.pid;
-
-
-events {
- worker_connections 1024;
-}
-
-
-http {
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
-
- access_log /var/log/nginx/access.log main;
-
- sendfile on;
- #tcp_nopush on;
-
- keepalive_timeout 65;
-
- #gzip on;
- client_max_body_size 15M;
-
- include /etc/nginx/conf.d/*.conf;
-}
\ No newline at end of file
diff --git a/docker-legacy/nginx/proxy.conf b/docker-legacy/nginx/proxy.conf
deleted file mode 100644
index 254f6259612e99..00000000000000
--- a/docker-legacy/nginx/proxy.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-proxy_set_header Host $host;
-proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-proxy_set_header X-Forwarded-Proto $scheme;
-proxy_http_version 1.1;
-proxy_set_header Connection "";
-proxy_buffering off;
-proxy_read_timeout 3600s;
-proxy_send_timeout 3600s;
\ No newline at end of file
diff --git a/docker-legacy/nginx/ssl/.gitkeep b/docker-legacy/nginx/ssl/.gitkeep
deleted file mode 100644
index 8b137891791fe9..00000000000000
--- a/docker-legacy/nginx/ssl/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docker-legacy/startupscripts/create_user.sql b/docker-legacy/startupscripts/create_user.sql
deleted file mode 100755
index b80e19c3b05a06..00000000000000
--- a/docker-legacy/startupscripts/create_user.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-show pdbs;
-ALTER SYSTEM SET PROCESSES=500 SCOPE=SPFILE;
-alter session set container= freepdb1;
-create user dify identified by dify DEFAULT TABLESPACE users quota unlimited on users;
-grant DB_DEVELOPER_ROLE to dify;
diff --git a/docker-legacy/volumes/opensearch/opensearch_dashboards.yml b/docker-legacy/volumes/opensearch/opensearch_dashboards.yml
deleted file mode 100644
index f50d63bbb9f425..00000000000000
--- a/docker-legacy/volumes/opensearch/opensearch_dashboards.yml
+++ /dev/null
@@ -1,222 +0,0 @@
----
-# Copyright OpenSearch Contributors
-# SPDX-License-Identifier: Apache-2.0
-
-# Description:
-# Default configuration for OpenSearch Dashboards
-
-# OpenSearch Dashboards is served by a back end server. This setting specifies the port to use.
-# server.port: 5601
-
-# Specifies the address to which the OpenSearch Dashboards server will bind. IP addresses and host names are both valid values.
-# The default is 'localhost', which usually means remote machines will not be able to connect.
-# To allow connections from remote users, set this parameter to a non-loopback address.
-# server.host: "localhost"
-
-# Enables you to specify a path to mount OpenSearch Dashboards at if you are running behind a proxy.
-# Use the `server.rewriteBasePath` setting to tell OpenSearch Dashboards if it should remove the basePath
-# from requests it receives, and to prevent a deprecation warning at startup.
-# This setting cannot end in a slash.
-# server.basePath: ""
-
-# Specifies whether OpenSearch Dashboards should rewrite requests that are prefixed with
-# `server.basePath` or require that they are rewritten by your reverse proxy.
-# server.rewriteBasePath: false
-
-# The maximum payload size in bytes for incoming server requests.
-# server.maxPayloadBytes: 1048576
-
-# The OpenSearch Dashboards server's name. This is used for display purposes.
-# server.name: "your-hostname"
-
-# The URLs of the OpenSearch instances to use for all your queries.
-# opensearch.hosts: ["http://localhost:9200"]
-
-# OpenSearch Dashboards uses an index in OpenSearch to store saved searches, visualizations and
-# dashboards. OpenSearch Dashboards creates a new index if the index doesn't already exist.
-# opensearchDashboards.index: ".opensearch_dashboards"
-
-# The default application to load.
-# opensearchDashboards.defaultAppId: "home"
-
-# Setting for an optimized healthcheck that only uses the local OpenSearch node to do Dashboards healthcheck.
-# This settings should be used for large clusters or for clusters with ingest heavy nodes.
-# It allows Dashboards to only healthcheck using the local OpenSearch node rather than fan out requests across all nodes.
-#
-# It requires the user to create an OpenSearch node attribute with the same name as the value used in the setting
-# This node attribute should assign all nodes of the same cluster an integer value that increments with each new cluster that is spun up
-# e.g. in opensearch.yml file you would set the value to a setting using node.attr.cluster_id:
-# Should only be enabled if there is a corresponding node attribute created in your OpenSearch config that matches the value here
-# opensearch.optimizedHealthcheckId: "cluster_id"
-
-# If your OpenSearch is protected with basic authentication, these settings provide
-# the username and password that the OpenSearch Dashboards server uses to perform maintenance on the OpenSearch Dashboards
-# index at startup. Your OpenSearch Dashboards users still need to authenticate with OpenSearch, which
-# is proxied through the OpenSearch Dashboards server.
-# opensearch.username: "opensearch_dashboards_system"
-# opensearch.password: "pass"
-
-# Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively.
-# These settings enable SSL for outgoing requests from the OpenSearch Dashboards server to the browser.
-# server.ssl.enabled: false
-# server.ssl.certificate: /path/to/your/server.crt
-# server.ssl.key: /path/to/your/server.key
-
-# Optional settings that provide the paths to the PEM-format SSL certificate and key files.
-# These files are used to verify the identity of OpenSearch Dashboards to OpenSearch and are required when
-# xpack.security.http.ssl.client_authentication in OpenSearch is set to required.
-# opensearch.ssl.certificate: /path/to/your/client.crt
-# opensearch.ssl.key: /path/to/your/client.key
-
-# Optional setting that enables you to specify a path to the PEM file for the certificate
-# authority for your OpenSearch instance.
-# opensearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ]
-
-# To disregard the validity of SSL certificates, change this setting's value to 'none'.
-# opensearch.ssl.verificationMode: full
-
-# Time in milliseconds to wait for OpenSearch to respond to pings. Defaults to the value of
-# the opensearch.requestTimeout setting.
-# opensearch.pingTimeout: 1500
-
-# Time in milliseconds to wait for responses from the back end or OpenSearch. This value
-# must be a positive integer.
-# opensearch.requestTimeout: 30000
-
-# List of OpenSearch Dashboards client-side headers to send to OpenSearch. To send *no* client-side
-# headers, set this value to [] (an empty list).
-# opensearch.requestHeadersWhitelist: [ authorization ]
-
-# Header names and values that are sent to OpenSearch. Any custom headers cannot be overwritten
-# by client-side headers, regardless of the opensearch.requestHeadersWhitelist configuration.
-# opensearch.customHeaders: {}
-
-# Time in milliseconds for OpenSearch to wait for responses from shards. Set to 0 to disable.
-# opensearch.shardTimeout: 30000
-
-# Logs queries sent to OpenSearch. Requires logging.verbose set to true.
-# opensearch.logQueries: false
-
-# Specifies the path where OpenSearch Dashboards creates the process ID file.
-# pid.file: /var/run/opensearchDashboards.pid
-
-# Enables you to specify a file where OpenSearch Dashboards stores log output.
-# logging.dest: stdout
-
-# Set the value of this setting to true to suppress all logging output.
-# logging.silent: false
-
-# Set the value of this setting to true to suppress all logging output other than error messages.
-# logging.quiet: false
-
-# Set the value of this setting to true to log all events, including system usage information
-# and all requests.
-# logging.verbose: false
-
-# Set the interval in milliseconds to sample system and process performance
-# metrics. Minimum is 100ms. Defaults to 5000.
-# ops.interval: 5000
-
-# Specifies locale to be used for all localizable strings, dates and number formats.
-# Supported languages are the following: English - en , by default , Chinese - zh-CN .
-# i18n.locale: "en"
-
-# Set the allowlist to check input graphite Url. Allowlist is the default check list.
-# vis_type_timeline.graphiteAllowedUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite']
-
-# Set the blocklist to check input graphite Url. Blocklist is an IP list.
-# Below is an example for reference
-# vis_type_timeline.graphiteBlockedIPs: [
-# //Loopback
-# '127.0.0.0/8',
-# '::1/128',
-# //Link-local Address for IPv6
-# 'fe80::/10',
-# //Private IP address for IPv4
-# '10.0.0.0/8',
-# '172.16.0.0/12',
-# '192.168.0.0/16',
-# //Unique local address (ULA)
-# 'fc00::/7',
-# //Reserved IP address
-# '0.0.0.0/8',
-# '100.64.0.0/10',
-# '192.0.0.0/24',
-# '192.0.2.0/24',
-# '198.18.0.0/15',
-# '192.88.99.0/24',
-# '198.51.100.0/24',
-# '203.0.113.0/24',
-# '224.0.0.0/4',
-# '240.0.0.0/4',
-# '255.255.255.255/32',
-# '::/128',
-# '2001:db8::/32',
-# 'ff00::/8',
-# ]
-# vis_type_timeline.graphiteBlockedIPs: []
-
-# opensearchDashboards.branding:
-# logo:
-# defaultUrl: ""
-# darkModeUrl: ""
-# mark:
-# defaultUrl: ""
-# darkModeUrl: ""
-# loadingLogo:
-# defaultUrl: ""
-# darkModeUrl: ""
-# faviconUrl: ""
-# applicationTitle: ""
-
-# Set the value of this setting to true to capture region blocked warnings and errors
-# for your map rendering services.
-# map.showRegionBlockedWarning: false%
-
-# Set the value of this setting to false to suppress search usage telemetry
-# for reducing the load of OpenSearch cluster.
-# data.search.usageTelemetry.enabled: false
-
-# 2.4 renames 'wizard.enabled: false' to 'vis_builder.enabled: false'
-# Set the value of this setting to false to disable VisBuilder
-# functionality in Visualization.
-# vis_builder.enabled: false
-
-# 2.4 New Experimental Feature
-# Set the value of this setting to true to enable the experimental multiple data source
-# support feature. Use with caution.
-# data_source.enabled: false
-# Set the value of these settings to customize crypto materials to encryption saved credentials
-# in data sources.
-# data_source.encryption.wrappingKeyName: 'changeme'
-# data_source.encryption.wrappingKeyNamespace: 'changeme'
-# data_source.encryption.wrappingKey: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
-
-# 2.6 New ML Commons Dashboards Feature
-# Set the value of this setting to true to enable the ml commons dashboards
-# ml_commons_dashboards.enabled: false
-
-# 2.12 New experimental Assistant Dashboards Feature
-# Set the value of this setting to true to enable the assistant dashboards
-# assistant.chat.enabled: false
-
-# 2.13 New Query Assistant Feature
-# Set the value of this setting to false to disable the query assistant
-# observability.query_assist.enabled: false
-
-# 2.14 Enable Ui Metric Collectors in Usage Collector
-# Set the value of this setting to true to enable UI Metric collections
-# usageCollection.uiMetric.enabled: false
-
-opensearch.hosts: [https://localhost:9200]
-opensearch.ssl.verificationMode: none
-opensearch.username: admin
-opensearch.password: 'Qazwsxedc!@#123'
-opensearch.requestHeadersWhitelist: [authorization, securitytenant]
-
-opensearch_security.multitenancy.enabled: true
-opensearch_security.multitenancy.tenants.preferred: [Private, Global]
-opensearch_security.readonly_mode.roles: [kibana_read_only]
-# Use this setting if you are running opensearch-dashboards without https
-opensearch_security.cookie.secure: false
-server.host: '0.0.0.0'
diff --git a/docker-legacy/volumes/sandbox/dependencies/python-requirements.txt b/docker-legacy/volumes/sandbox/dependencies/python-requirements.txt
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/docker-legacy/volumes/ssrf_proxy/squid.conf b/docker-legacy/volumes/ssrf_proxy/squid.conf
deleted file mode 100644
index 06bedb8aaf928f..00000000000000
--- a/docker-legacy/volumes/ssrf_proxy/squid.conf
+++ /dev/null
@@ -1,49 +0,0 @@
-acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
-acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
-acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
-acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
-acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
-acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
-acl localnet src fc00::/7 # RFC 4193 local private network range
-acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
-acl SSL_ports port 443
-acl Safe_ports port 80 # http
-acl Safe_ports port 21 # ftp
-acl Safe_ports port 443 # https
-acl Safe_ports port 70 # gopher
-acl Safe_ports port 210 # wais
-acl Safe_ports port 1025-65535 # unregistered ports
-acl Safe_ports port 280 # http-mgmt
-acl Safe_ports port 488 # gss-http
-acl Safe_ports port 591 # filemaker
-acl Safe_ports port 777 # multiling http
-acl CONNECT method CONNECT
-http_access deny !Safe_ports
-http_access deny CONNECT !SSL_ports
-http_access allow localhost manager
-http_access deny manager
-http_access allow localhost
-include /etc/squid/conf.d/*.conf
-http_access deny all
-
-################################## Proxy Server ################################
-http_port 3128
-coredump_dir /var/spool/squid
-refresh_pattern ^ftp: 1440 20% 10080
-refresh_pattern ^gopher: 1440 0% 1440
-refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
-refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
-refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
-refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
-refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
-refresh_pattern . 0 20% 4320
-
-# upstream proxy, set to your own upstream proxy IP to avoid SSRF attacks
-# cache_peer 172.1.1.1 parent 3128 0 no-query no-digest no-netdb-exchange default
-
-
-################################## Reverse Proxy To Sandbox ################################
-http_port 8194 accel vhost
-cache_peer sandbox parent 8194 0 no-query originserver
-acl src_all src all
-http_access allow src_all
diff --git a/docker/.env.example b/docker/.env.example
index b21bdc70853483..afd2caf5014fbc 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -887,6 +887,10 @@ SSRF_HTTP_PORT=3128
SSRF_COREDUMP_DIR=/var/spool/squid
SSRF_REVERSE_PROXY_PORT=8194
SSRF_SANDBOX_HOST=sandbox
+SSRF_DEFAULT_TIME_OUT=5
+SSRF_DEFAULT_CONNECT_TIME_OUT=5
+SSRF_DEFAULT_READ_TIME_OUT=5
+SSRF_DEFAULT_WRITE_TIME_OUT=5
# ------------------------------
# docker env var for specifying vector db type at startup
@@ -932,3 +936,30 @@ MAX_SUBMIT_COUNT=100
# The maximum number of top-k value for RAG.
TOP_K_MAX_VALUE=10
+
+# ------------------------------
+# Plugin Daemon Configuration
+# ------------------------------
+
+DB_PLUGIN_DATABASE=dify_plugin
+EXPOSE_PLUGIN_DAEMON_PORT=5002
+PLUGIN_DAEMON_PORT=5002
+PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
+PLUGIN_DAEMON_URL=http://plugin_daemon:5002
+PLUGIN_MAX_PACKAGE_SIZE=52428800
+PLUGIN_PPROF_ENABLED=false
+
+PLUGIN_DEBUGGING_HOST=0.0.0.0
+PLUGIN_DEBUGGING_PORT=5003
+EXPOSE_PLUGIN_DEBUGGING_HOST=localhost
+EXPOSE_PLUGIN_DEBUGGING_PORT=5003
+
+PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
+PLUGIN_DIFY_INNER_API_URL=http://api:5001
+
+ENDPOINT_URL_TEMPLATE=http://localhost/e/{hook_id}
+
+MARKETPLACE_ENABLED=true
+MARKETPLACE_API_URL=https://marketplace.dify.ai
+
+FORCE_VERIFYING_SIGNATURE=true
diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml
index c10c4d80d85300..f284449f95307f 100644
--- a/docker/docker-compose-template.yaml
+++ b/docker/docker-compose-template.yaml
@@ -12,6 +12,8 @@ services:
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
+ PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
depends_on:
- db
- redis
@@ -35,6 +37,8 @@ services:
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
+ PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
depends_on:
- db
- redis
@@ -56,8 +60,11 @@ services:
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
CSP_WHITELIST: ${CSP_WHITELIST:-}
+ MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
+ MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
+ PM2_INSTANCES: ${PM2_INSTANCES:-2}
# The postgres database.
db:
@@ -81,6 +88,8 @@ services:
interval: 1s
timeout: 3s
retries: 30
+ ports:
+ - '${EXPOSE_DB_PORT:-5432}:5432'
# The redis cache.
redis:
@@ -118,6 +127,30 @@ services:
networks:
- ssrf_proxy_network
+ # plugin daemon
+ plugin_daemon:
+ image: langgenius/dify-plugin-daemon:0.0.2-local
+ restart: always
+ environment:
+ # Use the shared environment variables.
+ <<: *shared-api-worker-env
+ DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin}
+ SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002}
+ SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}
+ MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false}
+ DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}
+ DIFY_INNER_API_KEY: ${INNER_API_KEY_FOR_PLUGIN:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
+ PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_REMOTE_INSTALL_HOST:-0.0.0.0}
+ PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_REMOTE_INSTALL_PORT:-5003}
+ PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd}
+ FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true}
+ ports:
+ - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}"
+ volumes:
+ - ./volumes/plugin_daemon:/app/storage
+
+
# ssrf_proxy server
# for more information, please refer to
# https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
@@ -199,16 +232,6 @@ services:
- '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}'
- '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}'
- # The TiDB vector store.
- # For production use, please refer to https://github.com/pingcap/tidb-docker-compose
- tidb:
- image: pingcap/tidb:v8.4.0
- profiles:
- - tidb
- command:
- - --store=unistore
- restart: always
-
# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.19.0
diff --git a/docker/docker-compose.middleware.yaml b/docker/docker-compose.middleware.yaml
index 11f53021972e6e..961f953b669e4b 100644
--- a/docker/docker-compose.middleware.yaml
+++ b/docker/docker-compose.middleware.yaml
@@ -64,6 +64,36 @@ services:
networks:
- ssrf_proxy_network
+ # plugin daemon
+ plugin_daemon:
+ image: langgenius/dify-plugin-daemon:0.0.2-local
+ restart: always
+ environment:
+ # Use the shared environment variables.
+ DB_HOST: ${DB_HOST:-db}
+ DB_PORT: ${DB_PORT:-5432}
+ DB_USERNAME: ${DB_USER:-postgres}
+ DB_PASSWORD: ${DB_PASSWORD:-difyai123456}
+ DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin}
+ REDIS_HOST: ${REDIS_HOST:-redis}
+ REDIS_PORT: ${REDIS_PORT:-6379}
+ REDIS_PASSWORD: ${REDIS_PASSWORD:-difyai123456}
+ SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002}
+ SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}
+ MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false}
+ DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://host.docker.internal:5001}
+ DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
+ PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0}
+ PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003}
+ PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd}
+ FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true}
+ ports:
+ - "${EXPOSE_PLUGIN_DAEMON_PORT:-5002}:${PLUGIN_DAEMON_PORT:-5002}"
+ - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}"
+ volumes:
+ - ./volumes/plugin_daemon:/app/storage
+
# ssrf_proxy server
# for more information, please refer to
# https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index 67207fd4668618..b1eb3d826483ff 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -377,6 +377,10 @@ x-shared-env: &shared-api-worker-env
SSRF_COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid}
SSRF_REVERSE_PROXY_PORT: ${SSRF_REVERSE_PROXY_PORT:-8194}
SSRF_SANDBOX_HOST: ${SSRF_SANDBOX_HOST:-sandbox}
+ SSRF_DEFAULT_TIME_OUT: ${SSRF_DEFAULT_TIME_OUT:-5}
+ SSRF_DEFAULT_CONNECT_TIME_OUT: ${SSRF_DEFAULT_CONNECT_TIME_OUT:-5}
+ SSRF_DEFAULT_READ_TIME_OUT: ${SSRF_DEFAULT_READ_TIME_OUT:-5}
+ SSRF_DEFAULT_WRITE_TIME_OUT: ${SSRF_DEFAULT_WRITE_TIME_OUT:-5}
EXPOSE_NGINX_PORT: ${EXPOSE_NGINX_PORT:-80}
EXPOSE_NGINX_SSL_PORT: ${EXPOSE_NGINX_SSL_PORT:-443}
POSITION_TOOL_PINS: ${POSITION_TOOL_PINS:-}
@@ -389,6 +393,23 @@ x-shared-env: &shared-api-worker-env
CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false}
MAX_SUBMIT_COUNT: ${MAX_SUBMIT_COUNT:-100}
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-10}
+ DB_PLUGIN_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin}
+ EXPOSE_PLUGIN_DAEMON_PORT: ${EXPOSE_PLUGIN_DAEMON_PORT:-5002}
+ PLUGIN_DAEMON_PORT: ${PLUGIN_DAEMON_PORT:-5002}
+ PLUGIN_DAEMON_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}
+ PLUGIN_DAEMON_URL: ${PLUGIN_DAEMON_URL:-http://plugin_daemon:5002}
+ PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ PLUGIN_PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false}
+ PLUGIN_DEBUGGING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0}
+ PLUGIN_DEBUGGING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003}
+ EXPOSE_PLUGIN_DEBUGGING_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost}
+ EXPOSE_PLUGIN_DEBUGGING_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}
+ PLUGIN_DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
+ PLUGIN_DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}
+ ENDPOINT_URL_TEMPLATE: ${ENDPOINT_URL_TEMPLATE:-http://localhost/e/{hook_id}}
+ MARKETPLACE_ENABLED: ${MARKETPLACE_ENABLED:-true}
+ MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
+ FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true}
services:
# API service
@@ -403,6 +424,8 @@ services:
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
+ PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
depends_on:
- db
- redis
@@ -426,6 +449,8 @@ services:
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
+ PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
depends_on:
- db
- redis
@@ -447,8 +472,11 @@ services:
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
CSP_WHITELIST: ${CSP_WHITELIST:-}
+ MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
+ MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
+ PM2_INSTANCES: ${PM2_INSTANCES:-2}
# The postgres database.
db:
@@ -472,6 +500,8 @@ services:
interval: 1s
timeout: 3s
retries: 30
+ ports:
+ - '${EXPOSE_DB_PORT:-5432}:5432'
# The redis cache.
redis:
@@ -509,6 +539,30 @@ services:
networks:
- ssrf_proxy_network
+ # plugin daemon
+ plugin_daemon:
+ image: langgenius/dify-plugin-daemon:0.0.2-local
+ restart: always
+ environment:
+ # Use the shared environment variables.
+ <<: *shared-api-worker-env
+ DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin}
+ SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002}
+ SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}
+ MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
+ PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false}
+ DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}
+ DIFY_INNER_API_KEY: ${INNER_API_KEY_FOR_PLUGIN:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}
+ PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_REMOTE_INSTALL_HOST:-0.0.0.0}
+ PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_REMOTE_INSTALL_PORT:-5003}
+ PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd}
+ FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true}
+ ports:
+ - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}"
+ volumes:
+ - ./volumes/plugin_daemon:/app/storage
+
+
# ssrf_proxy server
# for more information, please refer to
# https://docs.dify.ai/learn-more/faq/install-faq#id-18.-why-is-ssrf_proxy-needed
@@ -590,16 +644,6 @@ services:
- '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}'
- '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}'
- # The TiDB vector store.
- # For production use, please refer to https://github.com/pingcap/tidb-docker-compose
- tidb:
- image: pingcap/tidb:v8.4.0
- profiles:
- - tidb
- command:
- - --store=unistore
- restart: always
-
# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.19.0
diff --git a/docker/middleware.env.example b/docker/middleware.env.example
index c4ce9f0114ebad..aaecad72b5ad89 100644
--- a/docker/middleware.env.example
+++ b/docker/middleware.env.example
@@ -87,3 +87,31 @@ EXPOSE_REDIS_PORT=6379
EXPOSE_SANDBOX_PORT=8194
EXPOSE_SSRF_PROXY_PORT=3128
EXPOSE_WEAVIATE_PORT=8080
+
+# ------------------------------
+# Plugin Daemon Configuration
+# ------------------------------
+
+DB_PLUGIN_DATABASE=dify_plugin
+EXPOSE_PLUGIN_DAEMON_PORT=5002
+PLUGIN_DAEMON_PORT=5002
+PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
+PLUGIN_DAEMON_URL=http://host.docker.internal:5002
+PLUGIN_MAX_PACKAGE_SIZE=52428800
+PLUGIN_PPROF_ENABLED=false
+PLUGIN_WORKING_PATH=/app/storage/cwd
+
+ENDPOINT_URL_TEMPLATE=http://localhost:5002/e/{hook_id}
+
+PLUGIN_DEBUGGING_PORT=5003
+PLUGIN_DEBUGGING_HOST=0.0.0.0
+EXPOSE_PLUGIN_DEBUGGING_HOST=localhost
+EXPOSE_PLUGIN_DEBUGGING_PORT=5003
+
+PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
+PLUGIN_DIFY_INNER_API_URL=http://api:5001
+
+MARKETPLACE_ENABLED=true
+MARKETPLACE_API_URL=https://marketplace.dify.ai
+
+FORCE_VERIFYING_SIGNATURE=true
\ No newline at end of file
diff --git a/docker/nginx/conf.d/default.conf.template b/docker/nginx/conf.d/default.conf.template
index 9691122ceafac3..a24219ed40fe9d 100644
--- a/docker/nginx/conf.d/default.conf.template
+++ b/docker/nginx/conf.d/default.conf.template
@@ -24,6 +24,17 @@ server {
include proxy.conf;
}
+ location /explore {
+ proxy_pass http://web:3000;
+ include proxy.conf;
+ }
+
+ location /e {
+ proxy_pass http://plugin_daemon:5002;
+ proxy_set_header Dify-Hook-Url $scheme://$host$request_uri;
+ include proxy.conf;
+ }
+
location / {
proxy_pass http://web:3000;
include proxy.conf;
diff --git a/docker/nginx/proxy.conf.template b/docker/nginx/proxy.conf.template
index 6b52d23512a60b..117f8061464e06 100644
--- a/docker/nginx/proxy.conf.template
+++ b/docker/nginx/proxy.conf.template
@@ -3,6 +3,7 @@
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
+proxy_set_header X-Forwarded-Port $server_port;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
diff --git a/docker/ssrf_proxy/squid.conf.template b/docker/ssrf_proxy/squid.conf.template
index a0875a8826e033..676fe7379c6e7b 100644
--- a/docker/ssrf_proxy/squid.conf.template
+++ b/docker/ssrf_proxy/squid.conf.template
@@ -7,6 +7,7 @@ acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
+# acl SSL_ports port 1025-65535 # Enable the configuration to resolve this issue: https://github.com/langgenius/dify/issues/12792
acl Safe_ports port 80 # http
acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
diff --git a/docker/tidb/config/pd.toml b/docker/tidb/config/pd.toml
new file mode 100644
index 00000000000000..042b251e464baa
--- /dev/null
+++ b/docker/tidb/config/pd.toml
@@ -0,0 +1,4 @@
+# PD Configuration File reference:
+# https://docs.pingcap.com/tidb/stable/pd-configuration-file#pd-configuration-file
+[replication]
+max-replicas = 1
\ No newline at end of file
diff --git a/docker/tidb/config/tiflash-learner.toml b/docker/tidb/config/tiflash-learner.toml
new file mode 100644
index 00000000000000..5098829aaa6dbb
--- /dev/null
+++ b/docker/tidb/config/tiflash-learner.toml
@@ -0,0 +1,13 @@
+# TiFlash tiflash-learner.toml Configuration File reference:
+# https://docs.pingcap.com/tidb/stable/tiflash-configuration#configure-the-tiflash-learnertoml-file
+
+log-file = "/logs/tiflash_tikv.log"
+
+[server]
+engine-addr = "tiflash:4030"
+addr = "0.0.0.0:20280"
+advertise-addr = "tiflash:20280"
+status-addr = "tiflash:20292"
+
+[storage]
+data-dir = "/data/flash"
diff --git a/docker/tidb/config/tiflash.toml b/docker/tidb/config/tiflash.toml
new file mode 100644
index 00000000000000..30ac13efcbdd15
--- /dev/null
+++ b/docker/tidb/config/tiflash.toml
@@ -0,0 +1,19 @@
+# TiFlash tiflash.toml Configuration File reference:
+# https://docs.pingcap.com/tidb/stable/tiflash-configuration#configure-the-tiflashtoml-file
+
+listen_host = "0.0.0.0"
+path = "/data"
+
+[flash]
+tidb_status_addr = "tidb:10080"
+service_addr = "tiflash:4030"
+
+[flash.proxy]
+config = "/tiflash-learner.toml"
+
+[logger]
+errorlog = "/logs/tiflash_error.log"
+log = "/logs/tiflash.log"
+
+[raft]
+pd_addr = "pd0:2379"
diff --git a/docker/tidb/docker-compose.yaml b/docker/tidb/docker-compose.yaml
new file mode 100644
index 00000000000000..fa157701753978
--- /dev/null
+++ b/docker/tidb/docker-compose.yaml
@@ -0,0 +1,62 @@
+services:
+ pd0:
+ image: pingcap/pd:v8.5.1
+ # ports:
+ # - "2379"
+ volumes:
+ - ./config/pd.toml:/pd.toml:ro
+ - ./volumes/data:/data
+ - ./volumes/logs:/logs
+ command:
+ - --name=pd0
+ - --client-urls=http://0.0.0.0:2379
+ - --peer-urls=http://0.0.0.0:2380
+ - --advertise-client-urls=http://pd0:2379
+ - --advertise-peer-urls=http://pd0:2380
+ - --initial-cluster=pd0=http://pd0:2380
+ - --data-dir=/data/pd
+ - --config=/pd.toml
+ - --log-file=/logs/pd.log
+ restart: on-failure
+ tikv:
+ image: pingcap/tikv:v8.5.1
+ volumes:
+ - ./volumes/data:/data
+ - ./volumes/logs:/logs
+ command:
+ - --addr=0.0.0.0:20160
+ - --advertise-addr=tikv:20160
+ - --status-addr=tikv:20180
+ - --data-dir=/data/tikv
+ - --pd=pd0:2379
+ - --log-file=/logs/tikv.log
+ depends_on:
+ - "pd0"
+ restart: on-failure
+ tidb:
+ image: pingcap/tidb:v8.5.1
+ # ports:
+ # - "4000:4000"
+ volumes:
+ - ./volumes/logs:/logs
+ command:
+ - --advertise-address=tidb
+ - --store=tikv
+ - --path=pd0:2379
+ - --log-file=/logs/tidb.log
+ depends_on:
+ - "tikv"
+ restart: on-failure
+ tiflash:
+ image: pingcap/tiflash:v8.5.1
+ volumes:
+ - ./config/tiflash.toml:/tiflash.toml:ro
+ - ./config/tiflash-learner.toml:/tiflash-learner.toml:ro
+ - ./volumes/data:/data
+ - ./volumes/logs:/logs
+ command:
+ - --config=/tiflash.toml
+ depends_on:
+ - "tikv"
+ - "tidb"
+ restart: on-failure
diff --git a/sdks/nodejs-client/babel.config.json b/sdks/nodejs-client/babel.config.json
deleted file mode 100644
index 0639bf76432a98..00000000000000
--- a/sdks/nodejs-client/babel.config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "presets": [
- "@babel/preset-env"
- ]
-}
\ No newline at end of file
diff --git a/web/.env.example b/web/.env.example
index e2117ddfd8702d..800dbb633e4c45 100644
--- a/web/.env.example
+++ b/web/.env.example
@@ -10,6 +10,10 @@ NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
+# The API PREFIX for MARKETPLACE
+NEXT_PUBLIC_MARKETPLACE_API_PREFIX=https://marketplace.dify.ai/api/v1
+# The URL for MARKETPLACE
+NEXT_PUBLIC_MARKETPLACE_URL_PREFIX=https://marketplace.dify.ai
# SENTRY
NEXT_PUBLIC_SENTRY_DSN=
@@ -26,6 +30,8 @@ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=60000
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
NEXT_PUBLIC_CSP_WHITELIST=
+# Github Access Token, used for invoking Github API
+NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
# The maximum number of top-k value for RAG.
NEXT_PUBLIC_TOP_K_MAX_VALUE=10
diff --git a/web/.eslintignore b/web/.eslintignore
deleted file mode 100644
index 8a8bc38d805242..00000000000000
--- a/web/.eslintignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/**/node_modules/*
-node_modules/
-
-dist/
-build/
-out/
-.next/
\ No newline at end of file
diff --git a/web/.eslintrc.json b/web/.eslintrc.json
deleted file mode 100644
index 18b6bc6016ff56..00000000000000
--- a/web/.eslintrc.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "extends": [
- "next",
- "@antfu",
- "plugin:storybook/recommended"
- ],
- "rules": {
- "@typescript-eslint/consistent-type-definitions": [
- "error",
- "type"
- ],
- "@typescript-eslint/no-var-requires": "off",
- "no-console": "off",
- "indent": "off",
- "@typescript-eslint/indent": [
- "error",
- 2,
- {
- "SwitchCase": 1,
- "flatTernaryExpressions": false,
- "ignoredNodes": [
- "PropertyDefinition[decorators]",
- "TSUnionType",
- "FunctionExpression[params]:has(Identifier[decorators])"
- ]
- }
- ],
- "react-hooks/exhaustive-deps": "warn",
- "react/display-name": "warn"
- }
-}
diff --git a/web/.gitignore b/web/.gitignore
index cb8fbe77ac0c0f..048c5f64853ffe 100644
--- a/web/.gitignore
+++ b/web/.gitignore
@@ -44,12 +44,11 @@ package-lock.json
.pnp.cjs
.pnp.loader.mjs
.yarn/
-.yarnrc.yml
-
-# pmpm
-pnpm-lock.yaml
.favorites.json
+
+# storybook
+/storybook-static
*storybook.log
# mise
diff --git a/web/.husky/pre-commit b/web/.husky/pre-commit
index d9290e1853e457..cca8abe27aa28f 100755
--- a/web/.husky/pre-commit
+++ b/web/.husky/pre-commit
@@ -1,6 +1,3 @@
-#!/usr/bin/env bash
-. "$(dirname -- "$0")/_/husky.sh"
-
# get the list of modified files
files=$(git diff --cached --name-only)
@@ -50,7 +47,7 @@ fi
if $web_modified; then
echo "Running ESLint on web module"
cd ./web || exit 1
- npx lint-staged
+ lint-staged
echo "Running unit tests check"
modified_files=$(git diff --cached --name-only -- utils | grep -v '\.spec\.ts$' || true)
@@ -63,7 +60,7 @@ if $web_modified; then
# check if the test file exists
if [ -f "../$test_file" ]; then
echo "Detected changes in $file, running corresponding unit tests..."
- npm run test "../$test_file"
+ pnpm run test "../$test_file"
if [ $? -ne 0 ]; then
echo "Unit tests failed. Please fix the errors before committing."
diff --git a/web/.storybook/main.ts b/web/.storybook/main.ts
index 74e95821de5714..fecf774e98b784 100644
--- a/web/.storybook/main.ts
+++ b/web/.storybook/main.ts
@@ -1,19 +1,19 @@
import type { StorybookConfig } from '@storybook/nextjs'
const config: StorybookConfig = {
- // stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- stories: ['../app/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: [
- '@storybook/addon-onboarding',
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@chromatic-com/storybook',
- '@storybook/addon-interactions',
- ],
- framework: {
- name: '@storybook/nextjs',
- options: {},
- },
- staticDirs: ['../public'],
+ // stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ stories: ['../app/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ addons: [
+ '@storybook/addon-onboarding',
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@chromatic-com/storybook',
+ '@storybook/addon-interactions',
+ ],
+ framework: {
+ name: '@storybook/nextjs',
+ options: {},
+ },
+ staticDirs: ['../public'],
}
export default config
diff --git a/web/.storybook/preview.tsx b/web/.storybook/preview.tsx
index 49cd24e97483b9..55328602f9e414 100644
--- a/web/.storybook/preview.tsx
+++ b/web/.storybook/preview.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import type { Preview } from '@storybook/react'
-import { withThemeByDataAttribute } from '@storybook/addon-themes';
+import { withThemeByDataAttribute } from '@storybook/addon-themes'
import I18nServer from '../app/components/i18n-server'
import '../app/styles/globals.css'
@@ -8,30 +8,30 @@ import '../app/styles/markdown.scss'
import './storybook.css'
export const decorators = [
- withThemeByDataAttribute({
- themes: {
- light: 'light',
- dark: 'dark',
- },
- defaultTheme: 'light',
- attributeName: 'data-theme',
- }),
- Story => {
- return
-
-
- }
- ];
+ withThemeByDataAttribute({
+ themes: {
+ light: 'light',
+ dark: 'dark',
+ },
+ defaultTheme: 'light',
+ attributeName: 'data-theme',
+ }),
+ (Story) => {
+ return
+
+
+ },
+]
const preview: Preview = {
parameters: {
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/i,
- },
- },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
},
+ },
}
export default preview
diff --git a/web/.vscode/settings.example.json b/web/.vscode/settings.example.json
index a2dfe7c6694559..ce5c9d6b0113c9 100644
--- a/web/.vscode/settings.example.json
+++ b/web/.vscode/settings.example.json
@@ -21,5 +21,6 @@
"editor.defaultFormatter": "vscode.json-language-features"
},
"typescript.tsdk": "node_modules/typescript/lib",
- "typescript.enablePromptUseWorkspaceTsdk": true
+ "typescript.enablePromptUseWorkspaceTsdk": true,
+ "npm.packageManager": "pnpm"
}
diff --git a/web/Dockerfile b/web/Dockerfile
index 6c341fac4e23a3..51c88e7fc92ed4 100644
--- a/web/Dockerfile
+++ b/web/Dockerfile
@@ -6,6 +6,9 @@ LABEL maintainer="takatost@gmail.com"
# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache tzdata
+RUN npm install -g pnpm@9.12.2
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
# install packages
@@ -14,12 +17,12 @@ FROM base AS packages
WORKDIR /app/web
COPY package.json .
-COPY yarn.lock .
+COPY pnpm-lock.yaml .
# if you located in China, you can use taobao registry to speed up
-# RUN yarn install --frozen-lockfile --registry https://registry.npmmirror.com/
+# RUN pnpm install --frozen-lockfile --registry https://registry.npmmirror.com/
-RUN yarn install --frozen-lockfile
+RUN pnpm install --frozen-lockfile
# build resources
FROM base AS builder
@@ -27,7 +30,8 @@ WORKDIR /app/web
COPY --from=packages /app/web/ .
COPY . .
-RUN yarn build
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+RUN pnpm build
# production stage
@@ -38,8 +42,11 @@ ENV EDITION=SELF_HOSTED
ENV DEPLOY_ENV=PRODUCTION
ENV CONSOLE_API_URL=http://127.0.0.1:5001
ENV APP_API_URL=http://127.0.0.1:5001
+ENV MARKETPLACE_API_URL=http://127.0.0.1:5001
+ENV MARKETPLACE_URL=http://127.0.0.1:5001
ENV PORT=3000
ENV NEXT_TELEMETRY_DISABLED=1
+ENV PM2_INSTANCES=2
# set timezone
ENV TZ=UTC
@@ -52,13 +59,11 @@ COPY --from=builder /app/web/public ./public
COPY --from=builder /app/web/.next/standalone ./
COPY --from=builder /app/web/.next/static ./.next/static
-COPY docker/pm2.json ./pm2.json
COPY docker/entrypoint.sh ./entrypoint.sh
# global runtime packages
-RUN yarn global add pm2 \
- && yarn cache clean \
+RUN pnpm add -g pm2 \
&& mkdir /.pm2 \
&& chown -R 1001:0 /.pm2 /app/web \
&& chmod -R g=u /.pm2 /app/web
diff --git a/web/README.md b/web/README.md
index ce5239e57ffc35..900924f348d133 100644
--- a/web/README.md
+++ b/web/README.md
@@ -6,14 +6,12 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next
### Run by source code
-To start the web frontend service, you will need [Node.js v18.x (LTS)](https://nodejs.org/en) and [NPM version 8.x.x](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/).
+To start the web frontend service, you will need [Node.js v18.x (LTS)](https://nodejs.org/en) and [pnpm version 9.12.2](https://pnpm.io).
First, install the dependencies:
```bash
-npm install
-# or
-yarn install --frozen-lockfile
+pnpm install
```
Then, configure the environment variables. Create a file named `.env.local` in the current directory and copy the contents from `.env.example`. Modify the values of these environment variables according to your requirements:
@@ -43,9 +41,7 @@ NEXT_PUBLIC_SENTRY_DSN=
Finally, run the development server:
```bash
-npm run dev
-# or
-yarn dev
+pnpm run dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
@@ -59,21 +55,23 @@ You can start editing the file under folder `app`. The page auto-updates as you
First, build the app for production:
```bash
-npm run build
+pnpm run build
```
Then, start the server:
```bash
-npm run start
+pnpm run start
```
If you want to customize the host and port:
```bash
-npm run start --port=3001 --host=0.0.0.0
+pnpm run start --port=3001 --host=0.0.0.0
```
+If you want to customize the number of instances launched by PM2, you can configure `PM2_INSTANCES` in `docker-compose.yaml` or `Dockerfile`.
+
## Storybook
This project uses [Storybook](https://storybook.js.org/) for UI component development.
@@ -81,7 +79,7 @@ This project uses [Storybook](https://storybook.js.org/) for UI component develo
To start the storybook server, run:
```bash
-yarn storybook
+pnpm storybook
```
Open [http://localhost:6006](http://localhost:6006) with your browser to see the result.
@@ -99,7 +97,7 @@ You can create a test file with a suffix of `.spec` beside the file that to be t
Run test:
```bash
-npm run test
+pnpm run test
```
If you are not familiar with writing tests, here is some code to refer to:
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx
index 41011207032beb..a4ee3922d91766 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import { type Locale } from '@/i18n'
+import type { Locale } from '@/i18n'
import DevelopMain from '@/app/components/develop'
export type IDevelopProps = {
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
index 91b305dc8caadf..69a423ab21c005 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
@@ -106,6 +106,7 @@ const AppDetailLayout: FC = (props) => {
// if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow'))
// setAppSiderbarExpand('collapse')
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [appDetail, isMobile])
useEffect(() => {
@@ -146,7 +147,8 @@ const AppDetailLayout: FC = (props) => {
})
}
}
- }, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail, systemFeatures.enable_web_sso_switch_component])
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, router, setAppDetail, systemFeatures.enable_web_sso_switch_component])
useUnmount(() => {
setAppDetail()
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx
index bb1e4fd95bd10e..5bb826012ac03c 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx
@@ -46,14 +46,14 @@ export default function ChartView({ appId }: IChartViewProps) {
return (
-
+
{t('appOverview.analysis.title')}
({ value: k, name: t(`appLog.filter.period.${v.name}`) }))}
className='mt-0 !w-40'
onSelect={(item) => {
const id = item.value
- const value = TIME_PERIOD_MAPPING[id]?.value || '-1'
+ const value = TIME_PERIOD_MAPPING[id]?.value ?? '-1'
const name = item.name || t('appLog.filter.period.allTime')
onSelect({ value, name })
}}
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
index 137c2c36ee750f..47dd36eb812fb6 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
@@ -12,7 +12,7 @@ const Overview = async ({
params: { appId },
}: IDevelopProps) => {
return (
-