From 1c3213184e1b3784a25c520d4b23cb6c7f28c36e Mon Sep 17 00:00:00 2001 From: takatost Date: Sun, 29 Sep 2024 00:13:53 +0800 Subject: [PATCH] feat: move model request to plugin daemon --- .../console/workspace/model_providers.py | 2 +- .../model_config/converter.py | 13 +- .../easy_ui_based_app/model_config/manager.py | 3 +- api/core/entities/provider_configuration.py | 192 +- api/core/helper/moderation.py | 3 + .../entities/provider_entities.py | 6 +- .../model_providers/__base/ai_model.py | 35 +- .../__base/large_language_model.py | 469 +- .../__base/text_embedding_model.py | 14 +- .../model_runtime/model_providers/__init__.py | 3 - .../model_providers/anthropic/__init__.py | 0 .../anthropic/_assets/icon_l_en.svg | 78 - .../anthropic/_assets/icon_s_en.svg | 4 - .../model_providers/anthropic/anthropic.py | 28 - .../model_providers/anthropic/anthropic.yaml | 39 - .../model_providers/anthropic/llm/__init__.py | 0 .../anthropic/llm/_position.yaml | 8 - .../anthropic/llm/claude-2.1.yaml | 36 - .../anthropic/llm/claude-2.yaml | 37 - .../llm/claude-3-5-sonnet-20240620.yaml | 39 - .../llm/claude-3-haiku-20240307.yaml | 39 - .../anthropic/llm/claude-3-opus-20240229.yaml | 39 - .../llm/claude-3-sonnet-20240229.yaml | 39 - .../anthropic/llm/claude-instant-1.2.yaml | 36 - .../anthropic/llm/claude-instant-1.yaml | 36 - .../model_providers/anthropic/llm/llm.py | 624 - .../azure_ai_studio/__init__.py | 0 .../azure_ai_studio/_assets/icon_l_en.png | Bin 21236 -> 0 bytes .../azure_ai_studio/_assets/icon_s_en.png | Bin 10541 -> 0 bytes .../azure_ai_studio/azure_ai_studio.py | 17 - .../azure_ai_studio/azure_ai_studio.yaml | 65 - .../azure_ai_studio/llm/__init__.py | 0 .../azure_ai_studio/llm/llm.py | 334 - .../azure_ai_studio/rerank/__init__.py | 0 .../azure_ai_studio/rerank/rerank.py | 164 - .../model_providers/azure_openai/__init__.py | 0 .../azure_openai/_assets/icon_l_en.png | Bin 5007 -> 0 bytes .../azure_openai/_assets/icon_s_en.svg | 8 - .../model_providers/azure_openai/_common.py | 42 - .../model_providers/azure_openai/_constant.py | 1262 - .../azure_openai/azure_openai.py | 10 - .../azure_openai/azure_openai.yaml | 227 - .../azure_openai/llm/__init__.py | 0 .../model_providers/azure_openai/llm/llm.py | 665 - .../azure_openai/speech2text/__init__.py | 0 .../azure_openai/speech2text/speech2text.py | 79 - .../azure_openai/text_embedding/__init__.py | 0 .../azure_openai/tts/__init__.py | 0 .../model_providers/azure_openai/tts/tts.py | 128 - .../model_providers/baichuan/__init__.py | 0 .../baichuan/_assets/icon_l_en.svg | 19 - .../baichuan/_assets/icon_s_en.svg | 11 - .../model_providers/baichuan/baichuan.py | 28 - .../model_providers/baichuan/baichuan.yaml | 29 - .../model_providers/baichuan/llm/__init__.py | 0 .../baichuan/llm/baichuan2-53b.yaml | 46 - .../baichuan/llm/baichuan2-turbo-192k.yaml | 46 - .../baichuan/llm/baichuan2-turbo.yaml | 41 - .../baichuan/llm/baichuan3-turbo-128k.yaml | 53 - .../baichuan/llm/baichuan3-turbo.yaml | 53 - .../baichuan/llm/baichuan4.yaml | 53 - .../baichuan/llm/baichuan_tokenizer.py | 21 - .../baichuan/llm/baichuan_turbo.py | 144 - .../baichuan/llm/baichuan_turbo_errors.py | 22 - .../model_providers/baichuan/llm/llm.py | 296 - .../baichuan/text_embedding/__init__.py | 0 .../baichuan-text-embedding.yaml | 5 - .../model_providers/bedrock/__init__.py | 0 .../bedrock/_assets/icon_l_en.svg | 14 - .../bedrock/_assets/icon_s_en.svg | 15 - .../model_providers/bedrock/bedrock.py | 29 - .../model_providers/bedrock/bedrock.yaml | 89 - .../model_providers/bedrock/llm/__init__.py | 0 .../bedrock/llm/_position.yaml | 24 - .../bedrock/llm/ai21.j2-mid-v1.yaml | 47 - .../bedrock/llm/ai21.j2-ultra-v1.yaml | 47 - .../llm/amazon.titan-text-express-v1.yaml | 23 - .../llm/amazon.titan-text-lite-v1.yaml | 23 - .../llm/anthropic.claude-3-haiku-v1.yaml | 61 - .../llm/anthropic.claude-3-opus-v1.yaml | 61 - .../llm/anthropic.claude-3-sonnet-v1.5.yaml | 60 - .../llm/anthropic.claude-3-sonnet-v1.yaml | 60 - .../llm/anthropic.claude-instant-v1.yaml | 52 - .../bedrock/llm/anthropic.claude-v1.yaml | 53 - .../bedrock/llm/anthropic.claude-v2.1.yaml | 54 - .../bedrock/llm/anthropic.claude-v2.yaml | 54 - .../llm/cohere.command-light-text-v14.yaml | 35 - .../llm/cohere.command-r-plus-v1.0.yaml | 44 - .../bedrock/llm/cohere.command-r-v1.0.yaml | 43 - .../bedrock/llm/cohere.command-text-v14.yaml | 32 - .../llm/eu.anthropic.claude-3-haiku-v1.yaml | 59 - .../eu.anthropic.claude-3-sonnet-v1.5.yaml | 58 - .../llm/eu.anthropic.claude-3-sonnet-v1.yaml | 58 - .../model_providers/bedrock/llm/llm.py | 903 - .../bedrock/llm/meta.llama2-13b-chat-v1.yaml | 23 - .../bedrock/llm/meta.llama2-70b-chat-v1.yaml | 23 - .../llm/meta.llama3-1-405b-instruct-v1.0.yaml | 25 - .../llm/meta.llama3-1-70b-instruct-v1.0.yaml | 25 - .../llm/meta.llama3-1-8b-instruct-v1.0.yaml | 25 - .../llm/meta.llama3-70b-instruct-v1.yaml | 23 - .../llm/meta.llama3-8b-instruct-v1.yaml | 23 - .../llm/mistral.mistral-7b-instruct-v0.2.yaml | 39 - .../llm/mistral.mistral-large-2402-v1.0.yaml | 30 - .../llm/mistral.mistral-large-2407-v1.0.yaml | 29 - .../llm/mistral.mistral-small-2402-v1.0.yaml | 29 - .../mistral.mixtral-8x7b-instruct-v0.1.yaml | 39 - .../llm/us.anthropic.claude-3-haiku-v1.yaml | 59 - .../llm/us.anthropic.claude-3-opus-v1.yaml | 59 - .../us.anthropic.claude-3-sonnet-v1.5.yaml | 58 - .../llm/us.anthropic.claude-3-sonnet-v1.yaml | 58 - .../bedrock/text_embedding/__init__.py | 0 .../bedrock/text_embedding/_position.yaml | 4 - .../amazon.titan-embed-text-v1.yaml | 8 - .../amazon.titan-embed-text-v2.yaml | 8 - .../cohere.embed-english-v3.yaml | 8 - .../cohere.embed-multilingual-v3.yaml | 8 - .../model_providers/chatglm/__init__.py | 0 .../chatglm/_assets/icon_l_en.svg | 1 - .../chatglm/_assets/icon_s_en.svg | 9 - .../model_providers/chatglm/chatglm.py | 28 - .../model_providers/chatglm/chatglm.yaml | 28 - .../model_providers/chatglm/llm/__init__.py | 0 .../chatglm/llm/chatglm2-6b-32k.yaml | 21 - .../chatglm/llm/chatglm2-6b.yaml | 21 - .../chatglm/llm/chatglm3-6b-32k.yaml | 22 - .../chatglm/llm/chatglm3-6b.yaml | 22 - .../model_providers/chatglm/llm/llm.py | 507 - .../model_providers/cohere/__init__.py | 0 .../cohere/_assets/icon_l_en.svg | 11 - .../cohere/_assets/icon_s_en.svg | 16 - .../model_providers/cohere/cohere.py | 28 - .../model_providers/cohere/cohere.yaml | 90 - .../model_providers/cohere/llm/__init__.py | 0 .../model_providers/cohere/llm/_position.yaml | 10 - .../cohere/llm/command-chat.yaml | 62 - .../cohere/llm/command-light-chat.yaml | 62 - .../llm/command-light-nightly-chat.yaml | 62 - .../cohere/llm/command-light-nightly.yaml | 44 - .../cohere/llm/command-light.yaml | 44 - .../cohere/llm/command-nightly-chat.yaml | 62 - .../cohere/llm/command-nightly.yaml | 44 - .../cohere/llm/command-r-plus.yaml | 45 - .../model_providers/cohere/llm/command-r.yaml | 45 - .../model_providers/cohere/llm/command.yaml | 44 - .../model_providers/cohere/llm/llm.py | 733 - .../model_providers/cohere/rerank/__init__.py | 0 .../cohere/rerank/_position.yaml | 4 - .../cohere/rerank/rerank-english-v2.0.yaml | 4 - .../cohere/rerank/rerank-english-v3.0.yaml | 4 - .../rerank/rerank-multilingual-v2.0.yaml | 4 - .../rerank/rerank-multilingual-v3.0.yaml | 4 - .../model_providers/cohere/rerank/rerank.py | 125 - .../cohere/text_embedding/__init__.py | 0 .../cohere/text_embedding/_position.yaml | 7 - .../embed-english-light-v2.0.yaml | 9 - .../embed-english-light-v3.0.yaml | 9 - .../text_embedding/embed-english-v2.0.yaml | 9 - .../text_embedding/embed-english-v3.0.yaml | 9 - .../embed-multilingual-light-v3.0.yaml | 9 - .../embed-multilingual-v2.0.yaml | 9 - .../embed-multilingual-v3.0.yaml | 9 - .../model_providers/deepseek/__init__.py | 0 .../deepseek/_assets/icon_l_en.svg | 22 - .../deepseek/_assets/icon_s_en.svg | 3 - .../model_providers/deepseek/deepseek.py | 28 - .../model_providers/deepseek/deepseek.yaml | 41 - .../model_providers/deepseek/llm/__init__.py | 0 .../deepseek/llm/_position.yaml | 2 - .../deepseek/llm/deepseek-chat.yaml | 78 - .../deepseek/llm/deepseek-coder.yaml | 28 - .../model_providers/deepseek/llm/llm.py | 116 - .../model_providers/fireworks/__init__.py | 0 .../fireworks/_assets/icon_l_en.svg | 3 - .../fireworks/_assets/icon_s_en.svg | 5 - .../model_providers/fireworks/_common.py | 52 - .../model_providers/fireworks/fireworks.py | 27 - .../model_providers/fireworks/llm/__init__.py | 0 .../fireworks/llm/_position.yaml | 16 - .../fireworks/llm/firefunction-v1.yaml | 46 - .../fireworks/llm/firefunction-v2.yaml | 46 - .../fireworks/llm/gemma2-9b-it.yaml | 45 - .../llm/llama-v3-70b-instruct-hf.yaml | 46 - .../fireworks/llm/llama-v3-70b-instruct.yaml | 46 - .../llm/llama-v3-8b-instruct-hf.yaml | 46 - .../fireworks/llm/llama-v3-8b-instruct.yaml | 46 - .../llm/llama-v3p1-405b-instruct.yaml | 46 - .../llm/llama-v3p1-70b-instruct.yaml | 46 - .../fireworks/llm/llama-v3p1-8b-instruct.yaml | 46 - .../model_providers/fireworks/llm/llm.py | 610 - .../fireworks/llm/mixtral-8x22b-instruct.yaml | 46 - .../llm/mixtral-8x7b-instruct-hf.yaml | 46 - .../fireworks/llm/mixtral-8x7b-instruct.yaml | 46 - .../fireworks/llm/mythomax-l2-13b.yaml | 46 - .../llm/phi-3-vision-128k-instruct.yaml | 46 - .../fireworks/llm/yi-large.yaml | 45 - .../model_providers/fishaudio/__init__.py | 0 .../fishaudio/_assets/fishaudio_l_en.svg | 1 - .../fishaudio/_assets/fishaudio_s_en.svg | 1 - .../model_providers/fishaudio/fishaudio.py | 26 - .../model_providers/fishaudio/tts/__init__.py | 0 .../model_providers/fishaudio/tts/tts.py | 158 - .../model_providers/fishaudio/tts/tts.yaml | 5 - .../model_providers/google/__init__.py | 0 .../google/_assets/icon_l_en.svg | 15 - .../google/_assets/icon_s_en.svg | 11 - .../model_providers/google/google.py | 28 - .../model_providers/google/google.yaml | 31 - .../model_providers/google/llm/__init__.py | 0 .../llm/gemini-1.5-flash-8b-exp-0827.yaml | 48 - .../google/llm/gemini-1.5-flash-exp-0827.yaml | 48 - .../google/llm/gemini-1.5-flash-latest.yaml | 48 - .../google/llm/gemini-1.5-pro-exp-0801.yaml | 48 - .../google/llm/gemini-1.5-pro-exp-0827.yaml | 48 - .../google/llm/gemini-1.5-pro-latest.yaml | 48 - .../google/llm/gemini-pro-vision.yaml | 43 - .../google/llm/gemini-pro.yaml | 47 - .../model_providers/google/llm/llm.py | 443 - .../groq/_assets/icon_l_en.svg | 11 - .../groq/_assets/icon_s_en.svg | 4 - .../model_providers/groq/groq.py | 26 - .../model_providers/groq/groq.yaml | 32 - .../model_providers/groq/llm/_position.yaml | 7 - .../groq/llm/llama-3.1-405b-reasoning.yaml | 25 - .../groq/llm/llama-3.1-70b-versatile.yaml | 25 - .../groq/llm/llama-3.1-8b-instant.yaml | 25 - .../groq/llm/llama2-70b-4096.yaml | 25 - .../groq/llm/llama3-70b-8192.yaml | 25 - .../groq/llm/llama3-8b-8192.yaml | 25 - .../model_providers/groq/llm/llm.py | 31 - .../groq/llm/mixtral-8x7b-instruct-v0.1.yaml | 25 - .../huggingface_hub/__init__.py | 0 .../huggingface_hub/_assets/icon_l_en.svg | 42 - .../huggingface_hub/_assets/icon_s_en.svg | 19 - .../huggingface_hub/_common.py | 9 - .../huggingface_hub/huggingface_hub.py | 10 - .../huggingface_hub/huggingface_hub.yaml | 102 - .../huggingface_hub/llm/__init__.py | 0 .../huggingface_hub/llm/llm.py | 313 - .../text_embedding/__init__.py | 0 .../huggingface_tei/__init__.py | 0 .../huggingface_tei/huggingface_tei.py | 10 - .../huggingface_tei/huggingface_tei.yaml | 36 - .../huggingface_tei/rerank/__init__.py | 0 .../huggingface_tei/rerank/rerank.py | 136 - .../huggingface_tei/tei_helper.py | 182 - .../text_embedding/__init__.py | 0 .../model_providers/hunyuan/__init__.py | 0 .../hunyuan/_assets/icon_l_en.png | Bin 68638 -> 0 bytes .../hunyuan/_assets/icon_s_en.png | Bin 60824 -> 0 bytes .../model_providers/hunyuan/hunyuan.py | 27 - .../model_providers/hunyuan/hunyuan.yaml | 41 - .../model_providers/hunyuan/llm/__init__.py | 0 .../hunyuan/llm/_position.yaml | 6 - .../hunyuan/llm/hunyuan-lite.yaml | 28 - .../hunyuan/llm/hunyuan-pro.yaml | 38 - .../hunyuan/llm/hunyuan-standard-256k.yaml | 38 - .../hunyuan/llm/hunyuan-standard.yaml | 38 - .../hunyuan/llm/hunyuan-turbo.yaml | 38 - .../hunyuan/llm/hunyuan-vision.yaml | 39 - .../model_providers/hunyuan/llm/llm.py | 348 - .../hunyuan/text_embedding/__init__.py | 0 .../hunyuan-text-embedding.yaml | 5 - .../jina/_assets/icon_l_en.svg | 12 - .../jina/_assets/icon_s_en.svg | 4 - .../model_providers/jina/jina.py | 28 - .../model_providers/jina/rerank/__init__.py | 0 .../jina/rerank/_position.yaml | 5 - .../jina/rerank/jina-colbert-v1-en.yaml | 4 - .../jina/rerank/jina-reranker-v1-base-en.yaml | 4 - .../jina/rerank/jina-reranker-v1-tiny-en.yaml | 4 - .../rerank/jina-reranker-v1-turbo-en.yaml | 4 - .../jina-reranker-v2-base-multilingual.yaml | 4 - .../model_providers/jina/rerank/rerank.py | 125 - .../jina/text_embedding/jina-clip-v1.yaml | 9 - .../jina-embeddings-v2-base-de.yaml | 9 - .../jina-embeddings-v2-base-en.yaml | 9 - .../jina-embeddings-v2-base-zh.yaml | 9 - .../jina-embeddings-v2-small-en.yaml | 9 - .../text_embedding/jina-embeddings-v3.yaml | 9 - .../jina/text_embedding/jina_tokenizer.py | 32 - .../text_embedding/tokenizer/tokenizer.json | 30678 ---------------- .../tokenizer/tokenizer_config.json | 15 - .../leptonai/_assets/icon_l_en.png | Bin 528134 -> 0 bytes .../leptonai/_assets/icon_s_en.png | Bin 12101 -> 0 bytes .../model_providers/leptonai/leptonai.py | 26 - .../model_providers/leptonai/leptonai.yaml | 29 - .../leptonai/llm/_position.yaml | 6 - .../leptonai/llm/gemma-7b.yaml | 20 - .../leptonai/llm/llama2-13b.yaml | 20 - .../leptonai/llm/llama2-7b.yaml | 20 - .../leptonai/llm/llama3-70b.yaml | 20 - .../model_providers/leptonai/llm/llm.py | 40 - .../leptonai/llm/mistral-7b.yaml | 20 - .../leptonai/llm/mixtral-8x7b.yaml | 20 - .../model_providers/localai/__init__.py | 0 .../localai/_assets/icon_l_en.svg | 22 - .../localai/_assets/icon_s_en.svg | 15 - .../model_providers/localai/llm/__init__.py | 0 .../model_providers/localai/llm/llm.py | 674 - .../model_providers/localai/localai.py | 10 - .../model_providers/localai/localai.yaml | 72 - .../localai/rerank/__init__.py | 0 .../model_providers/localai/rerank/rerank.py | 134 - .../localai/speech2text/__init__.py | 0 .../localai/speech2text/speech2text.py | 89 - .../localai/text_embedding/__init__.py | 0 .../model_providers/minimax/__init__.py | 0 .../minimax/_assets/icon_l_en.png | Bin 5767 -> 0 bytes .../minimax/_assets/icon_s_en.png | Bin 2007 -> 0 bytes .../model_providers/minimax/llm/__init__.py | 0 .../minimax/llm/abab5-chat.yaml | 38 - .../minimax/llm/abab5.5-chat.yaml | 53 - .../minimax/llm/abab5.5s-chat.yaml | 44 - .../minimax/llm/abab6-chat.yaml | 46 - .../minimax/llm/abab6.5-chat.yaml | 46 - .../minimax/llm/abab6.5s-chat.yaml | 46 - .../minimax/llm/chat_completion.py | 166 - .../minimax/llm/chat_completion_pro.py | 191 - .../model_providers/minimax/llm/errors.py | 22 - .../model_providers/minimax/llm/llm.py | 271 - .../model_providers/minimax/llm/types.py | 30 - .../model_providers/minimax/minimax.py | 28 - .../model_providers/minimax/minimax.yaml | 37 - .../minimax/text_embedding/__init__.py | 0 .../minimax/text_embedding/embo-01.yaml | 9 - .../model_providers/mistralai/__init__.py | 0 .../mistralai/_assets/icon_l_en.png | Bin 7064 -> 0 bytes .../mistralai/_assets/icon_s_en.png | Bin 7418 -> 0 bytes .../mistralai/llm/_position.yaml | 11 - .../mistralai/llm/codestral-latest.yaml | 51 - .../model_providers/mistralai/llm/llm.py | 36 - .../mistralai/llm/mistral-embed.yaml | 51 - .../mistralai/llm/mistral-large-latest.yaml | 51 - .../mistralai/llm/mistral-medium-latest.yaml | 51 - .../mistralai/llm/mistral-small-latest.yaml | 51 - .../mistralai/llm/open-codestral-mamba.yaml | 51 - .../mistralai/llm/open-mistral-7b.yaml | 51 - .../mistralai/llm/open-mistral-nemo.yaml | 51 - .../mistralai/llm/open-mixtral-8x22b.yaml | 51 - .../mistralai/llm/open-mixtral-8x7b.yaml | 51 - .../mistralai/llm/pixtral-12b-2409.yaml | 51 - .../model_providers/mistralai/mistralai.py | 26 - .../model_providers/mistralai/mistralai.yaml | 31 - .../model_providers/mixedbread/__init__.py | 0 .../mixedbread/_assets/icon_l_en.png | Bin 123637 -> 0 bytes .../mixedbread/_assets/icon_s_en.png | Bin 37303 -> 0 bytes .../model_providers/mixedbread/mixedbread.py | 27 - .../mixedbread/mixedbread.yaml | 31 - .../mixedbread/rerank/__init__.py | 0 .../rerank/mxbai-rerank-large-v1-en.yaml | 4 - .../mixedbread/rerank/rerank.py | 125 - .../mixedbread/text_embedding/__init__.py | 0 .../mxbai-embed-2d-large-v1-en.yaml | 8 - .../mxbai-embed-large-v1-en.yaml | 8 - .../model_providers/model_provider_factory.py | 337 +- .../model_providers/moonshot/__init__.py | 0 .../moonshot/_assets/icon_l_en.png | Bin 13654 -> 0 bytes .../moonshot/_assets/icon_s_en.png | Bin 7419 -> 0 bytes .../model_providers/moonshot/llm/__init__.py | 0 .../moonshot/llm/_position.yaml | 3 - .../model_providers/moonshot/llm/llm.py | 327 - .../moonshot/llm/moonshot-v1-128k.yaml | 40 - .../moonshot/llm/moonshot-v1-32k.yaml | 40 - .../moonshot/llm/moonshot-v1-8k.yaml | 40 - .../model_providers/moonshot/moonshot.py | 26 - .../model_providers/moonshot/moonshot.yaml | 89 - .../model_providers/nomic/__init__.py | 0 .../nomic/_assets/icon_l_en.svg | 13 - .../nomic/_assets/icon_s_en.png | Bin 25814 -> 0 bytes .../model_providers/nomic/_common.py | 28 - .../model_providers/nomic/nomic.py | 26 - .../model_providers/nomic/nomic.yaml | 29 - .../nomic/text_embedding/__init__.py | 0 .../text_embedding/nomic-embed-text-v1.5.yaml | 8 - .../text_embedding/nomic-embed-text-v1.yaml | 8 - .../novita/_assets/icon_l_en.svg | 19 - .../novita/_assets/icon_s_en.svg | 10 - .../llm/Nous-Hermes-2-Mixtral-8x7B-DPO.yaml | 41 - .../novita/llm/airoboros-l2-70b.yaml | 41 - .../novita/llm/dolphin-mixtral-8x22b.yaml | 41 - .../novita/llm/gemma-2-9b-it.yaml | 41 - .../novita/llm/hermes-2-pro-llama-3-8b.yaml | 41 - .../novita/llm/l3-70b-euryale-v2.1.yaml | 41 - .../novita/llm/llama-3-70b-instruct.yaml | 41 - .../novita/llm/llama-3-8b-instruct.yaml | 41 - .../novita/llm/llama-3.1-405b-instruct.yaml | 41 - .../novita/llm/llama-3.1-70b-instruct.yaml | 41 - .../novita/llm/llama-3.1-8b-instruct.yaml | 41 - .../model_providers/novita/llm/llm.py | 69 - .../model_providers/novita/llm/lzlv_70b.yaml | 41 - .../novita/llm/midnight-rose-70b.yaml | 41 - .../novita/llm/mistral-7b-instruct.yaml | 41 - .../novita/llm/mythomax-l2-13b.yaml | 41 - .../novita/llm/nous-hermes-llama2-13b.yaml | 41 - .../novita/llm/openhermes-2.5-mistral-7b.yaml | 41 - .../novita/llm/wizardlm-2-8x22b.yaml | 41 - .../model_providers/novita/novita.py | 28 - .../model_providers/novita/novita.yaml | 31 - .../model_providers/nvidia/__init__.py | 0 .../nvidia/_assets/icon_l_en.png | Bin 112528 -> 0 bytes .../nvidia/_assets/icon_s_en.svg | 3 - .../model_providers/nvidia/llm/_position.yaml | 17 - .../model_providers/nvidia/llm/arctic.yaml | 36 - .../nvidia/llm/codegemma-7b.yaml | 36 - .../model_providers/nvidia/llm/fuyu-8b.yaml | 27 - .../model_providers/nvidia/llm/gemma-7b.yaml | 36 - .../nvidia/llm/llama-3.1-405b.yaml | 36 - .../nvidia/llm/llama-3.1-70b.yaml | 36 - .../nvidia/llm/llama-3.1-8b.yaml | 36 - .../nvidia/llm/llama2-70b.yaml | 36 - .../nvidia/llm/llama3-70b.yaml | 36 - .../model_providers/nvidia/llm/llama3-8b.yaml | 36 - .../model_providers/nvidia/llm/llm.py | 247 - .../nvidia/llm/mistral-large.yaml | 36 - .../mistralai_mixtral-8x7b-instruct-v0.1.yaml | 36 - .../llm/mixtral-8x22b-instruct-v0.1.yaml | 36 - .../nvidia/llm/nemotron-4-340b-instruct.yaml | 36 - .../llm/phi-3-medium-128k-instruct.yaml | 36 - .../nvidia/llm/phi-3-mini-128k-instruct.yaml | 36 - .../nvidia/llm/recurrentgemma-2b.yaml | 37 - .../model_providers/nvidia/nvidia.py | 26 - .../model_providers/nvidia/nvidia.yaml | 33 - .../model_providers/nvidia/rerank/__init__.py | 0 .../nvidia/rerank/rerank-qa-mistral-4b.yaml | 4 - .../model_providers/nvidia/rerank/rerank.py | 121 - .../nvidia/text_embedding/__init__.py | 0 .../nvidia/text_embedding/embed-qa-4.yaml | 5 - .../model_providers/nvidia_nim/__init__.py | 0 .../nvidia_nim/_assets/icon_l_en.png | Bin 112528 -> 0 bytes .../nvidia_nim/_assets/icon_s_en.svg | 3 - .../nvidia_nim/llm/__init__.py | 0 .../model_providers/nvidia_nim/llm/llm.py | 13 - .../model_providers/nvidia_nim/nvidia_nim.py | 10 - .../nvidia_nim/nvidia_nim.yaml | 79 - .../model_providers/oci/__init__.py | 0 .../model_providers/oci/_assets/icon_l_en.svg | 1 - .../model_providers/oci/_assets/icon_s_en.svg | 1 - .../oci/llm/cohere.command-r-16k.yaml | 52 - .../oci/llm/cohere.command-r-plus.yaml | 52 - .../model_providers/oci/llm/llm.py | 469 - .../oci/llm/meta.llama-3-70b-instruct.yaml | 51 - .../model_runtime/model_providers/oci/oci.py | 28 - .../model_providers/oci/oci.yaml | 42 - .../oci/text_embedding/__init__.py | 0 .../oci/text_embedding/_position.yaml | 5 - .../cohere.embed-english-light-v2.0.yaml | 9 - .../cohere.embed-english-light-v3.0.yaml | 9 - .../cohere.embed-english-v3.0.yaml | 9 - .../cohere.embed-multilingual-light-v3.0.yaml | 9 - .../cohere.embed-multilingual-v3.0.yaml | 9 - .../model_providers/ollama/__init__.py | 0 .../ollama/_assets/icon_l_en.svg | 15 - .../ollama/_assets/icon_s_en.svg | 15 - .../model_providers/ollama/llm/__init__.py | 0 .../model_providers/ollama/llm/llm.py | 726 - .../model_providers/ollama/ollama.py | 16 - .../model_providers/ollama/ollama.yaml | 98 - .../ollama/text_embedding/__init__.py | 0 .../model_providers/openai/__init__.py | 0 .../openai/_assets/icon_l_en.svg | 11 - .../openai/_assets/icon_s_en.svg | 4 - .../model_providers/openai/_common.py | 60 - .../model_providers/openai/llm/__init__.py | 0 .../model_providers/openai/llm/_position.yaml | 26 - .../openai/llm/chatgpt-4o-latest.yaml | 44 - .../openai/llm/gpt-3.5-turbo-0125.yaml | 43 - .../openai/llm/gpt-3.5-turbo-0613.yaml | 34 - .../openai/llm/gpt-3.5-turbo-1106.yaml | 43 - .../openai/llm/gpt-3.5-turbo-16k-0613.yaml | 34 - .../openai/llm/gpt-3.5-turbo-16k.yaml | 33 - .../openai/llm/gpt-3.5-turbo-instruct.yaml | 30 - .../openai/llm/gpt-3.5-turbo.yaml | 43 - .../openai/llm/gpt-4-0125-preview.yaml | 56 - .../openai/llm/gpt-4-1106-preview.yaml | 56 - .../model_providers/openai/llm/gpt-4-32k.yaml | 56 - .../openai/llm/gpt-4-turbo-2024-04-09.yaml | 57 - .../openai/llm/gpt-4-turbo-preview.yaml | 56 - .../openai/llm/gpt-4-turbo.yaml | 57 - .../openai/llm/gpt-4-vision-preview.yaml | 54 - .../model_providers/openai/llm/gpt-4.yaml | 56 - .../openai/llm/gpt-4o-2024-05-13.yaml | 44 - .../openai/llm/gpt-4o-2024-08-06.yaml | 47 - .../openai/llm/gpt-4o-mini-2024-07-18.yaml | 47 - .../openai/llm/gpt-4o-mini.yaml | 47 - .../model_providers/openai/llm/gpt-4o.yaml | 44 - .../model_providers/openai/llm/llm.py | 1182 - .../openai/llm/o1-mini-2024-09-12.yaml | 33 - .../model_providers/openai/llm/o1-mini.yaml | 33 - .../openai/llm/o1-preview-2024-09-12.yaml | 33 - .../openai/llm/o1-preview.yaml | 33 - .../openai/llm/text-davinci-003.yaml | 29 - .../openai/moderation/moderation.py | 59 +- .../moderation/text-moderation-stable.yaml | 5 - .../model_providers/openai/openai.py | 29 - .../model_providers/openai/openai.yaml | 89 - .../openai/speech2text/__init__.py | 0 .../openai/speech2text/speech2text.py | 60 - .../openai/speech2text/whisper-1.yaml | 5 - .../openai/text_embedding/__init__.py | 0 .../text-embedding-3-large.yaml | 9 - .../text-embedding-3-small.yaml | 9 - .../text-embedding-ada-002.yaml | 9 - .../model_providers/openai/tts/__init__.py | 0 .../model_providers/openai/tts/tts-1-hd.yaml | 31 - .../model_providers/openai/tts/tts-1.yaml | 31 - .../model_providers/openai/tts/tts.py | 118 - .../openai_api_compatible/__init__.py | 0 .../openai_api_compatible/_common.py | 43 - .../openai_api_compatible/llm/__init__.py | 0 .../openai_api_compatible/llm/llm.py | 828 - .../openai_api_compatible.py | 10 - .../openai_api_compatible.yaml | 162 - .../speech2text/__init__.py | 0 .../speech2text/speech2text.py | 61 - .../text_embedding/__init__.py | 0 .../model_providers/openllm/__init__.py | 0 .../openllm/_assets/icon_l_en.svg | 19 - .../openllm/_assets/icon_s_en.svg | 12 - .../model_providers/openllm/llm/__init__.py | 0 .../model_providers/openllm/llm/llm.py | 264 - .../openllm/llm/openllm_generate.py | 198 - .../openllm/llm/openllm_generate_errors.py | 22 - .../model_providers/openllm/openllm.py | 10 - .../model_providers/openllm/openllm.yaml | 37 - .../openllm/text_embedding/__init__.py | 0 .../model_providers/openrouter/__init__.py | 0 .../openrouter/_assets/openrouter.svg | 11 - .../openrouter/_assets/openrouter_square.svg | 10 - .../openrouter/llm/__init__.py | 0 .../openrouter/llm/_position.yaml | 27 - .../openrouter/llm/claude-3-5-sonnet.yaml | 39 - .../openrouter/llm/claude-3-haiku.yaml | 39 - .../openrouter/llm/claude-3-opus.yaml | 39 - .../openrouter/llm/claude-3-sonnet.yaml | 39 - .../openrouter/llm/command-r-plus.yaml | 45 - .../openrouter/llm/command-r.yaml | 45 - .../openrouter/llm/deepseek-chat.yaml | 50 - .../openrouter/llm/deepseek-coder.yaml | 30 - .../openrouter/llm/gemini-1.5-flash.yaml | 39 - .../openrouter/llm/gemini-1.5-pro.yaml | 39 - .../openrouter/llm/gemini-pro.yaml | 38 - .../openrouter/llm/gpt-3.5-turbo.yaml | 42 - .../openrouter/llm/gpt-4-32k.yaml | 57 - .../model_providers/openrouter/llm/gpt-4.yaml | 57 - .../openrouter/llm/gpt-4o-2024-08-06.yaml | 44 - .../openrouter/llm/gpt-4o-mini.yaml | 43 - .../openrouter/llm/gpt-4o.yaml | 43 - .../openrouter/llm/llama-3-70b-instruct.yaml | 23 - .../openrouter/llm/llama-3-8b-instruct.yaml | 23 - .../llm/llama-3.1-405b-instruct.yaml | 23 - .../llm/llama-3.1-70b-instruct.yaml | 23 - .../openrouter/llm/llama-3.1-8b-instruct.yaml | 23 - .../model_providers/openrouter/llm/llm.py | 106 - .../openrouter/llm/mistral-7b-instruct.yaml | 30 - .../llm/mixtral-8x22b-instruct.yaml | 30 - .../openrouter/llm/mixtral-8x7b-instruct.yaml | 31 - .../openrouter/llm/o1-mini.yaml | 40 - .../openrouter/llm/o1-preview.yaml | 40 - .../openrouter/llm/qwen2-72b-instruct.yaml | 30 - .../model_providers/openrouter/openrouter.py | 20 - .../openrouter/openrouter.yaml | 105 - .../model_providers/perfxcloud/__init__.py | 0 .../perfxcloud/_assets/icon_l_en.svg | 8 - .../perfxcloud/_assets/icon_s_en.svg | 8 - .../perfxcloud/llm/Llama3-Chinese_v2.yaml | 62 - .../Meta-Llama-3-70B-Instruct-GPTQ-Int4.yaml | 62 - .../llm/Meta-Llama-3-8B-Instruct.yaml | 62 - ...Meta-Llama-3.1-405B-Instruct-AWQ-INT4.yaml | 62 - .../llm/Meta-Llama-3.1-8B-Instruct.yaml | 61 - .../perfxcloud/llm/Qwen-14B-Chat-Int4.yaml | 62 - .../llm/Qwen1.5-110B-Chat-GPTQ-Int4.yaml | 62 - .../llm/Qwen1.5-72B-Chat-GPTQ-Int4.yaml | 62 - .../perfxcloud/llm/Qwen1.5-7B.yaml | 62 - .../llm/Qwen2-72B-Instruct-AWQ-int4.yaml | 61 - .../llm/Qwen2-72B-Instruct-GPTQ-Int4.yaml | 64 - .../perfxcloud/llm/Qwen2-72B-Instruct.yaml | 61 - .../perfxcloud/llm/Qwen2-7B-Instruct.yaml | 63 - .../perfxcloud/llm/Qwen2-7B.yaml | 64 - .../perfxcloud/llm/Qwen2.5-72B-Instruct.yaml | 61 - .../perfxcloud/llm/Qwen2.5-7B-Instruct.yaml | 61 - .../llm/Reflection-Llama-3.1-70B.yaml | 61 - .../perfxcloud/llm/Yi-1_5-9B-Chat-16K.yaml | 61 - .../perfxcloud/llm/Yi-Coder-1.5B-Chat.yaml | 61 - .../perfxcloud/llm/Yi-Coder-9B-Chat.yaml | 61 - .../perfxcloud/llm/__init__.py | 0 .../perfxcloud/llm/_position.yaml | 24 - .../perfxcloud/llm/chatglm3-6b.yaml | 62 - .../perfxcloud/llm/deepseek-v2-chat.yaml | 62 - .../perfxcloud/llm/deepseek-v2-lite-chat.yaml | 62 - .../model_providers/perfxcloud/llm/llm.py | 116 - .../model_providers/perfxcloud/perfxcloud.py | 10 - .../perfxcloud/perfxcloud.yaml | 42 - .../BAAI-bge-large-en-v1.5.yaml | 4 - .../BAAI-bge-large-zh-v1.5.yaml | 4 - .../text_embedding/BAAI-bge-m3.yaml | 4 - .../perfxcloud/text_embedding/__init__.py | 0 .../text_embedding/gte-Qwen2-7B-instruct.yaml | 4 - .../model_providers/replicate/__init__.py | 0 .../replicate/_assets/icon_l_en.svg | 13 - .../replicate/_assets/icon_s_en.svg | 4 - .../model_providers/replicate/_common.py | 9 - .../model_providers/replicate/llm/__init__.py | 0 .../model_providers/replicate/llm/llm.py | 305 - .../model_providers/replicate/replicate.py | 10 - .../model_providers/replicate/replicate.yaml | 41 - .../replicate/text_embedding/__init__.py | 0 .../model_providers/sagemaker/__init__.py | 0 .../sagemaker/_assets/icon_l_en.png | Bin 9395 -> 0 bytes .../sagemaker/_assets/icon_s_en.png | Bin 9720 -> 0 bytes .../model_providers/sagemaker/llm/__init__.py | 0 .../sagemaker/rerank/__init__.py | 0 .../sagemaker/rerank/rerank.py | 173 - .../model_providers/sagemaker/sagemaker.py | 41 - .../model_providers/sagemaker/sagemaker.yaml | 193 - .../sagemaker/speech2text/__init__.py | 0 .../sagemaker/speech2text/speech2text.py | 125 - .../sagemaker/text_embedding/__init__.py | 0 .../model_providers/sagemaker/tts/__init__.py | 0 .../model_providers/sagemaker/tts/tts.py | 275 - .../siliconflow/_assets/siliconflow.svg | 1 - .../_assets/siliconflow_square.svg | 1 - .../llm/deepdeek-coder-v2-instruct.yaml | 30 - .../siliconflow/llm/deepseek-v2-chat.yaml | 30 - .../siliconflow/llm/deepseek-v2.5.yaml | 30 - .../siliconflow/llm/gemma-2-27b-it.yaml | 30 - .../siliconflow/llm/gemma-2-9b-it.yaml | 30 - .../siliconflow/llm/glm4-9b-chat.yaml | 30 - .../siliconflow/llm/internlm2_5-7b-chat.yaml | 30 - .../model_providers/siliconflow/llm/llm.py | 31 - .../llm/meta-mlama-3-70b-instruct.yaml | 30 - .../llm/meta-mlama-3-8b-instruct.yaml | 30 - .../llm/meta-mlama-3.1-405b-instruct.yaml | 30 - .../llm/meta-mlama-3.1-70b-instruct.yaml | 30 - .../llm/meta-mlama-3.1-8b-instruct.yaml | 30 - .../llm/mistral-7b-instruct-v0.2.yaml | 31 - .../llm/mistral-8x7b-instruct-v0.1.yaml | 31 - .../siliconflow/llm/qwen2-1.5b-instruct.yaml | 30 - .../llm/qwen2-57b-a14b-instruct.yaml | 30 - .../siliconflow/llm/qwen2-72b-instruct.yaml | 30 - .../siliconflow/llm/qwen2-7b-instruct.yaml | 30 - .../siliconflow/llm/qwen2.5-14b-instruct.yaml | 30 - .../siliconflow/llm/qwen2.5-32b-instruct.yaml | 30 - .../siliconflow/llm/qwen2.5-72b-instruct.yaml | 30 - .../siliconflow/llm/qwen2.5-7b-instruct.yaml | 30 - .../siliconflow/llm/yi-1.5-34b-chat.yaml | 30 - .../siliconflow/llm/yi-1.5-6b-chat.yaml | 30 - .../siliconflow/llm/yi-1.5-9b-chat.yaml | 30 - .../siliconflow/rerank/__init__.py | 0 .../rerank/bce-reranker-base_v1.yaml | 4 - .../rerank/bge-reranker-v2-m3.yaml | 4 - .../siliconflow/rerank/rerank.py | 85 - .../siliconflow/siliconflow.py | 26 - .../siliconflow/siliconflow.yaml | 32 - .../siliconflow/speech2text/__init__.py | 0 .../speech2text/sense-voice-small.yaml | 5 - .../siliconflow/speech2text/speech2text.py | 30 - .../text_embedding/bce-embedding-base-v1.yaml | 5 - .../text_embedding/bge-large-en-v1.5.yaml | 5 - .../text_embedding/bge-large-zh-v1.5.yaml | 5 - .../siliconflow/text_embedding/bge-m3.yaml | 5 - .../model_providers/spark/__init__.py | 0 .../spark/_assets/icon_l_en.svg | 24 - .../spark/_assets/icon_l_zh.svg | 11 - .../spark/_assets/icon_s_en.svg | 5 - .../model_providers/spark/llm/__init__.py | 0 .../model_providers/spark/llm/_client.py | 163 - .../model_providers/spark/llm/_position.yaml | 11 - .../model_providers/spark/llm/spark-1.5.yaml | 34 - .../model_providers/spark/llm/spark-2.yaml | 34 - .../model_providers/spark/llm/spark-3.5.yaml | 34 - .../model_providers/spark/llm/spark-3.yaml | 34 - .../spark/llm/spark-4.0-ultra.yaml | 42 - .../model_providers/spark/llm/spark-4.yaml | 34 - .../model_providers/spark/llm/spark-lite.yaml | 33 - .../spark/llm/spark-max-32k.yaml | 33 - .../model_providers/spark/llm/spark-max.yaml | 33 - .../spark/llm/spark-pro-128k.yaml | 33 - .../model_providers/spark/llm/spark-pro.yaml | 33 - .../model_providers/spark/spark.py | 18 - .../model_providers/spark/spark.yaml | 46 - .../model_providers/stepfun/__init__.py | 0 .../stepfun/_assets/icon_l_en.png | Bin 9176 -> 0 bytes .../stepfun/_assets/icon_s_en.png | Bin 1991 -> 0 bytes .../model_providers/stepfun/llm/__init__.py | 0 .../stepfun/llm/_position.yaml | 8 - .../model_providers/stepfun/llm/llm.py | 328 - .../stepfun/llm/step-1-128k.yaml | 25 - .../stepfun/llm/step-1-256k.yaml | 25 - .../stepfun/llm/step-1-32k.yaml | 28 - .../stepfun/llm/step-1-8k.yaml | 28 - .../stepfun/llm/step-1-flash.yaml | 25 - .../stepfun/llm/step-1v-32k.yaml | 28 - .../stepfun/llm/step-1v-8k.yaml | 28 - .../stepfun/llm/step-2-16k.yaml | 28 - .../model_providers/stepfun/stepfun.py | 26 - .../model_providers/stepfun/stepfun.yaml | 81 - .../model_providers/tencent/__init__.py | 0 .../tencent/_assets/icon_l_en.svg | 13 - .../tencent/_assets/icon_l_zh.svg | 13 - .../tencent/_assets/icon_s_en.svg | 11 - .../tencent/speech2text/__init__.py | 0 .../tencent/speech2text/flash_recognizer.py | 164 - .../tencent/speech2text/speech2text.py | 86 - .../tencent/speech2text/tencent.yaml | 5 - .../model_providers/tencent/tencent.py | 26 - .../model_providers/tencent/tencent.yaml | 49 - .../model_providers/togetherai/__init__.py | 0 .../togetherai/_assets/togetherai.svg | 13 - .../togetherai/_assets/togetherai_square.svg | 19 - .../togetherai/llm/__init__.py | 0 .../model_providers/togetherai/llm/llm.py | 170 - .../model_providers/togetherai/togetherai.py | 10 - .../togetherai/togetherai.yaml | 75 - .../model_providers/tongyi/__init__.py | 0 .../tongyi/_assets/icon_l_en.png | Bin 4741 -> 0 bytes .../tongyi/_assets/icon_l_zh.png | Bin 7052 -> 0 bytes .../tongyi/_assets/icon_s_en.png | Bin 2835 -> 0 bytes .../model_providers/tongyi/_common.py | 55 - .../model_providers/tongyi/llm/__init__.py | 0 .../model_providers/tongyi/llm/_position.yaml | 51 - .../tongyi/llm/farui-plus.yaml | 77 - .../model_providers/tongyi/llm/llm.py | 593 - .../tongyi/llm/qwen-coder-turbo-0919.yaml | 75 - .../tongyi/llm/qwen-coder-turbo-latest.yaml | 75 - .../tongyi/llm/qwen-coder-turbo.yaml | 75 - .../model_providers/tongyi/llm/qwen-long.yaml | 77 - .../tongyi/llm/qwen-math-plus-0816.yaml | 75 - .../tongyi/llm/qwen-math-plus-0919.yaml | 75 - .../tongyi/llm/qwen-math-plus-latest.yaml | 75 - .../tongyi/llm/qwen-math-plus.yaml | 75 - .../tongyi/llm/qwen-math-turbo-0919.yaml | 75 - .../tongyi/llm/qwen-math-turbo-latest.yaml | 75 - .../tongyi/llm/qwen-math-turbo.yaml | 75 - .../tongyi/llm/qwen-max-0107.yaml | 78 - .../tongyi/llm/qwen-max-0403.yaml | 78 - .../tongyi/llm/qwen-max-0428.yaml | 78 - .../tongyi/llm/qwen-max-0919.yaml | 78 - .../tongyi/llm/qwen-max-1201.yaml | 78 - .../tongyi/llm/qwen-max-latest.yaml | 78 - .../tongyi/llm/qwen-max-longcontext.yaml | 78 - .../model_providers/tongyi/llm/qwen-max.yaml | 87 - .../tongyi/llm/qwen-plus-0206.yaml | 76 - .../tongyi/llm/qwen-plus-0624.yaml | 76 - .../tongyi/llm/qwen-plus-0723.yaml | 76 - .../tongyi/llm/qwen-plus-0806.yaml | 76 - .../tongyi/llm/qwen-plus-0919.yaml | 76 - .../tongyi/llm/qwen-plus-chat.yaml | 79 - .../tongyi/llm/qwen-plus-latest.yaml | 76 - .../model_providers/tongyi/llm/qwen-plus.yaml | 87 - .../tongyi/llm/qwen-turbo-0206.yaml | 77 - .../tongyi/llm/qwen-turbo-0624.yaml | 76 - .../tongyi/llm/qwen-turbo-0919.yaml | 76 - .../tongyi/llm/qwen-turbo-chat.yaml | 79 - .../tongyi/llm/qwen-turbo-latest.yaml | 76 - .../tongyi/llm/qwen-turbo.yaml | 87 - .../tongyi/llm/qwen-vl-max-0201.yaml | 49 - .../tongyi/llm/qwen-vl-max-0809.yaml | 79 - .../tongyi/llm/qwen-vl-max.yaml | 79 - .../tongyi/llm/qwen-vl-plus-0201.yaml | 79 - .../tongyi/llm/qwen-vl-plus-0809.yaml | 79 - .../tongyi/llm/qwen-vl-plus.yaml | 79 - .../tongyi/llm/qwen2-math-1.5b-instruct.yaml | 75 - .../tongyi/llm/qwen2-math-72b-instruct.yaml | 75 - .../tongyi/llm/qwen2-math-7b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-0.5b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-1.5b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-14b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-32b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-3b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-72b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-7b-instruct.yaml | 75 - .../tongyi/llm/qwen2.5-coder-7b-instruct.yaml | 75 - .../tongyi/text_embedding/__init__.py | 0 .../text_embedding/text-embedding-v1.yaml | 10 - .../text_embedding/text-embedding-v2.yaml | 10 - .../text_embedding/text-embedding-v3.yaml | 10 - .../model_providers/tongyi/tongyi.py | 28 - .../model_providers/tongyi/tongyi.yaml | 87 - .../model_providers/tongyi/tts/__init__.py | 0 .../model_providers/tongyi/tts/tts-1.yaml | 139 - .../model_providers/tongyi/tts/tts.py | 152 - .../triton_inference_server/__init__.py | 0 .../_assets/icon_l_en.png | Bin 79751 -> 0 bytes .../_assets/icon_s_en.svg | 3 - .../triton_inference_server/llm/__init__.py | 0 .../triton_inference_server/llm/llm.py | 280 - .../triton_inference_server.py | 10 - .../triton_inference_server.yaml | 84 - .../model_providers/upstage/__init__.py | 0 .../upstage/_assets/icon_l_en.svg | 14 - .../upstage/_assets/icon_s_en.svg | 3 - .../model_providers/upstage/_common.py | 54 - .../model_providers/upstage/llm/__init__.py | 0 .../upstage/llm/_position.yaml | 1 - .../model_providers/upstage/llm/llm.py | 603 - .../upstage/llm/solar-1-mini-chat.yaml | 43 - .../upstage/text_embedding/__init__.py | 0 .../solar-embedding-1-large-passage.yaml | 9 - .../solar-embedding-1-large-query.yaml | 9 - .../model_providers/upstage/upstage.py | 27 - .../model_providers/upstage/upstage.yaml | 49 - .../model_providers/vertex_ai/__init__.py | 0 .../vertex_ai/_assets/icon_l_en.png | Bin 18078 -> 0 bytes .../vertex_ai/_assets/icon_s_en.svg | 1 - .../model_providers/vertex_ai/_common.py | 15 - .../model_providers/vertex_ai/llm/__init__.py | 0 .../llm/anthropic.claude-3-haiku.yaml | 56 - .../llm/anthropic.claude-3-opus.yaml | 56 - .../llm/anthropic.claude-3-sonnet.yaml | 55 - .../llm/anthropic.claude-3.5-sonnet.yaml | 55 - .../vertex_ai/llm/gemini-1.0-pro-vision.yaml | 37 - .../vertex_ai/llm/gemini-1.0-pro.yaml | 36 - .../vertex_ai/text_embedding/__init__.py | 0 .../text_embedding/text-embedding-004.yaml | 8 - .../text-multilingual-embedding-002.yaml | 8 - .../model_providers/vertex_ai/vertex_ai.py | 28 - .../model_providers/vertex_ai/vertex_ai.yaml | 43 - .../volcengine_maas/__init__.py | 0 .../volcengine_maas/_assets/icon_l_en.svg | 23 - .../volcengine_maas/_assets/icon_l_zh.svg | 39 - .../volcengine_maas/_assets/icon_s_en.svg | 8 - .../model_providers/volcengine_maas/client.py | 216 - .../volcengine_maas/legacy/__init__.py | 0 .../volcengine_maas/legacy/client.py | 123 - .../volcengine_maas/legacy/errors.py | 156 - .../legacy/volc_sdk/__init__.py | 4 - .../legacy/volc_sdk/base/__init__.py | 1 - .../legacy/volc_sdk/base/auth.py | 159 - .../legacy/volc_sdk/base/service.py | 216 - .../legacy/volc_sdk/base/util.py | 44 - .../volcengine_maas/legacy/volc_sdk/common.py | 77 - .../volcengine_maas/legacy/volc_sdk/maas.py | 198 - .../volcengine_maas/llm/__init__.py | 0 .../volcengine_maas/llm/llm.py | 388 - .../volcengine_maas/llm/models.py | 142 - .../text_embedding/__init__.py | 0 .../volcengine_maas/text_embedding/models.py | 28 - .../volcengine_maas/volcengine_maas.py | 10 - .../volcengine_maas/volcengine_maas.yaml | 266 - .../model_providers/wenxin/__init__.py | 0 .../wenxin/_assets/icon_l_en.png | Bin 6615 -> 0 bytes .../wenxin/_assets/icon_l_zh.png | Bin 7967 -> 0 bytes .../wenxin/_assets/icon_s_en.png | Bin 3350 -> 0 bytes .../model_providers/wenxin/_common.py | 194 - .../model_providers/wenxin/llm/__init__.py | 0 .../wenxin/llm/ernie-3.5-128k.yaml | 37 - .../wenxin/llm/ernie-3.5-4k-0205.yaml | 38 - .../wenxin/llm/ernie-3.5-8k-0205.yaml | 38 - .../wenxin/llm/ernie-3.5-8k-1222.yaml | 38 - .../wenxin/llm/ernie-3.5-8k.yaml | 40 - .../wenxin/llm/ernie-4.0-8k-latest.yaml | 40 - .../wenxin/llm/ernie-4.0-8k.yaml | 40 - .../llm/ernie-4.0-turbo-8k-preview.yaml | 40 - .../wenxin/llm/ernie-4.0-turbo-8k.yaml | 40 - .../wenxin/llm/ernie-bot-4.yaml | 39 - .../wenxin/llm/ernie-bot-8k.yaml | 39 - .../wenxin/llm/ernie-bot-turbo.yaml | 30 - .../model_providers/wenxin/llm/ernie-bot.yaml | 39 - .../wenxin/llm/ernie-character-8k-0321.yaml | 31 - .../wenxin/llm/ernie-character-8k.yaml | 30 - .../wenxin/llm/ernie-lite-8k-0308.yaml | 31 - .../wenxin/llm/ernie-lite-8k-0922.yaml | 31 - .../wenxin/llm/ernie-speed-128k.yaml | 30 - .../wenxin/llm/ernie-speed-8k.yaml | 30 - .../wenxin/llm/ernie-speed-appbuilder.yaml | 25 - .../model_providers/wenxin/llm/ernie_bot.py | 245 - .../model_providers/wenxin/llm/llm.py | 316 - .../wenxin/llm/yi_34b_chat.yaml | 30 - .../wenxin/text_embedding/__init__.py | 0 .../wenxin/text_embedding/bge-large-en.yaml | 9 - .../wenxin/text_embedding/bge-large-zh.yaml | 9 - .../wenxin/text_embedding/embedding-v1.yaml | 9 - .../wenxin/text_embedding/tao-8k.yaml | 9 - .../model_providers/wenxin/wenxin.py | 28 - .../model_providers/wenxin/wenxin.yaml | 40 - .../model_providers/wenxin/wenxin_errors.py | 54 - .../model_providers/xinference/__init__.py | 0 .../xinference/_assets/icon_l_en.svg | 42 - .../xinference/_assets/icon_s_en.svg | 24 - .../xinference/llm/__init__.py | 0 .../model_providers/xinference/llm/llm.py | 816 - .../xinference/rerank/__init__.py | 0 .../xinference/rerank/rerank.py | 189 - .../xinference/speech2text/__init__.py | 0 .../xinference/speech2text/speech2text.py | 144 - .../xinference/text_embedding/__init__.py | 0 .../xinference/tts/__init__.py | 0 .../model_providers/xinference/tts/tts.py | 228 - .../model_providers/xinference/xinference.py | 10 - .../xinference/xinference.yaml | 58 - .../xinference/xinference_helper.py | 134 - .../model_providers/yi/__init__.py | 0 .../model_providers/yi/_assets/icon_l_en.svg | 12 - .../model_providers/yi/_assets/icon_s_en.svg | 8 - .../model_providers/yi/llm/__init__.py | 0 .../model_providers/yi/llm/_position.yaml | 9 - .../model_providers/yi/llm/llm.py | 127 - .../yi/llm/yi-34b-chat-0205.yaml | 43 - .../yi/llm/yi-34b-chat-200k.yaml | 43 - .../yi/llm/yi-large-turbo.yaml | 43 - .../model_providers/yi/llm/yi-large.yaml | 43 - .../yi/llm/yi-medium-200k.yaml | 43 - .../model_providers/yi/llm/yi-medium.yaml | 43 - .../model_providers/yi/llm/yi-spark.yaml | 43 - .../model_providers/yi/llm/yi-vision.yaml | 44 - .../model_providers/yi/llm/yi-vl-plus.yaml | 43 - .../model_runtime/model_providers/yi/yi.py | 28 - .../model_runtime/model_providers/yi/yi.yaml | 41 - .../model_providers/zhinao/__init__.py | 0 .../zhinao/_assets/icon_l_en.svg | 8 - .../zhinao/_assets/icon_s_en.svg | 8 - .../llm/360gpt-turbo-responsibility-8k.yaml | 36 - .../zhinao/llm/360gpt-turbo.yaml | 36 - .../zhinao/llm/360gpt2-pro.yaml | 36 - .../model_providers/zhinao/llm/__init__.py | 0 .../model_providers/zhinao/llm/_position.yaml | 3 - .../model_providers/zhinao/llm/llm.py | 31 - .../model_providers/zhinao/zhinao.py | 28 - .../model_providers/zhinao/zhinao.yaml | 32 - .../model_providers/zhipuai/__init__.py | 0 .../zhipuai/_assets/icon_l_en.svg | 6 - .../zhipuai/_assets/icon_l_zh.svg | 8 - .../zhipuai/_assets/icon_s_en.svg | 8 - .../model_providers/zhipuai/_common.py | 41 - .../model_providers/zhipuai/llm/__init__.py | 0 .../zhipuai/llm/chatglm_lite.yaml | 22 - .../zhipuai/llm/chatglm_lite_32k.yaml | 22 - .../zhipuai/llm/chatglm_pro.yaml | 22 - .../zhipuai/llm/chatglm_std.yaml | 22 - .../zhipuai/llm/chatglm_turbo.yaml | 51 - .../zhipuai/llm/glm-4-0520.yaml | 62 - .../zhipuai/llm/glm-4-air.yaml | 62 - .../zhipuai/llm/glm-4-airx.yaml | 62 - .../zhipuai/llm/glm-4-flash.yaml | 62 - .../zhipuai/llm/glm_3_turbo.yaml | 62 - .../model_providers/zhipuai/llm/glm_4.yaml | 62 - .../zhipuai/llm/glm_4_long.yaml | 65 - .../zhipuai/llm/glm_4_plus.yaml | 62 - .../model_providers/zhipuai/llm/glm_4v.yaml | 60 - .../zhipuai/llm/glm_4v_plus.yaml | 60 - .../model_providers/zhipuai/llm/llm.py | 486 - .../zhipuai/text_embedding/__init__.py | 0 .../zhipuai/text_embedding/embedding-2.yaml | 8 - .../zhipuai/text_embedding/embedding-3.yaml | 8 - .../text_embedding/text_embedding.yaml | 4 - .../model_providers/zhipuai/zhipuai.py | 27 - .../model_providers/zhipuai/zhipuai.yaml | 31 - .../zhipuai/zhipuai_sdk/__init__.py | 15 - .../zhipuai/zhipuai_sdk/__version__.py | 1 - .../zhipuai/zhipuai_sdk/_client.py | 82 - .../zhipuai_sdk/api_resource/__init__.py | 34 - .../api_resource/assistant/__init__.py | 3 - .../api_resource/assistant/assistant.py | 122 - .../zhipuai_sdk/api_resource/batches.py | 146 - .../zhipuai_sdk/api_resource/chat/__init__.py | 5 - .../api_resource/chat/async_completions.py | 115 - .../zhipuai_sdk/api_resource/chat/chat.py | 18 - .../api_resource/chat/completions.py | 108 - .../zhipuai_sdk/api_resource/embeddings.py | 50 - .../zhipuai/zhipuai_sdk/api_resource/files.py | 194 - .../api_resource/fine_tuning/__init__.py | 5 - .../api_resource/fine_tuning/fine_tuning.py | 18 - .../api_resource/fine_tuning/jobs/__init__.py | 3 - .../api_resource/fine_tuning/jobs/jobs.py | 152 - .../fine_tuning/models/__init__.py | 3 - .../fine_tuning/models/fine_tuned_models.py | 41 - .../zhipuai_sdk/api_resource/images.py | 59 - .../api_resource/knowledge/__init__.py | 3 - .../knowledge/document/__init__.py | 3 - .../knowledge/document/document.py | 217 - .../api_resource/knowledge/knowledge.py | 173 - .../api_resource/tools/__init__.py | 3 - .../zhipuai_sdk/api_resource/tools/tools.py | 65 - .../api_resource/videos/__init__.py | 7 - .../zhipuai_sdk/api_resource/videos/videos.py | 77 - .../zhipuai/zhipuai_sdk/core/__init__.py | 108 - .../zhipuai/zhipuai_sdk/core/_base_api.py | 19 - .../zhipuai/zhipuai_sdk/core/_base_compat.py | 209 - .../zhipuai/zhipuai_sdk/core/_base_models.py | 670 - .../zhipuai/zhipuai_sdk/core/_base_type.py | 170 - .../zhipuai/zhipuai_sdk/core/_constants.py | 12 - .../zhipuai/zhipuai_sdk/core/_errors.py | 86 - .../zhipuai/zhipuai_sdk/core/_files.py | 75 - .../zhipuai/zhipuai_sdk/core/_http_client.py | 910 - .../zhipuai/zhipuai_sdk/core/_jwt_token.py | 31 - .../core/_legacy_binary_response.py | 207 - .../zhipuai_sdk/core/_legacy_response.py | 341 - .../zhipuai/zhipuai_sdk/core/_request_opt.py | 97 - .../zhipuai/zhipuai_sdk/core/_response.py | 398 - .../zhipuai/zhipuai_sdk/core/_sse_client.py | 206 - .../zhipuai_sdk/core/_utils/__init__.py | 52 - .../zhipuai_sdk/core/_utils/_transform.py | 383 - .../zhipuai_sdk/core/_utils/_typing.py | 122 - .../zhipuai/zhipuai_sdk/core/_utils/_utils.py | 409 - .../zhipuai/zhipuai_sdk/core/logs.py | 78 - .../zhipuai/zhipuai_sdk/core/pagination.py | 62 - .../zhipuai/zhipuai_sdk/types/__init__.py | 0 .../zhipuai_sdk/types/assistant/__init__.py | 5 - .../types/assistant/assistant_completion.py | 40 - .../assistant_conversation_params.py | 7 - .../assistant/assistant_conversation_resp.py | 29 - .../assistant/assistant_create_params.py | 32 - .../types/assistant/assistant_support_resp.py | 21 - .../types/assistant/message/__init__.py | 3 - .../assistant/message/message_content.py | 13 - .../assistant/message/text_content_block.py | 14 - .../tools/code_interpreter_delta_block.py | 27 - .../message/tools/drawing_tool_delta_block.py | 21 - .../message/tools/function_delta_block.py | 22 - .../message/tools/retrieval_delta_black.py | 41 - .../assistant/message/tools/tools_type.py | 16 - .../message/tools/web_browser_delta_block.py | 48 - .../assistant/message/tools_delta_block.py | 16 - .../zhipuai/zhipuai_sdk/types/batch.py | 82 - .../zhipuai_sdk/types/batch_create_params.py | 37 - .../zhipuai/zhipuai_sdk/types/batch_error.py | 21 - .../zhipuai_sdk/types/batch_list_params.py | 20 - .../zhipuai_sdk/types/batch_request_counts.py | 14 - .../zhipuai_sdk/types/chat/__init__.py | 0 .../types/chat/async_chat_completion.py | 22 - .../zhipuai_sdk/types/chat/chat_completion.py | 43 - .../types/chat/chat_completion_chunk.py | 57 - .../chat/chat_completions_create_param.py | 8 - .../types/chat/code_geex/code_geex_params.py | 146 - .../zhipuai/zhipuai_sdk/types/embeddings.py | 21 - .../zhipuai_sdk/types/files/__init__.py | 5 - .../types/files/file_create_params.py | 38 - .../zhipuai_sdk/types/files/file_deleted.py | 13 - .../zhipuai_sdk/types/files/file_object.py | 22 - .../zhipuai_sdk/types/files/upload_detail.py | 13 - .../zhipuai_sdk/types/fine_tuning/__init__.py | 4 - .../types/fine_tuning/fine_tuning_job.py | 51 - .../fine_tuning/fine_tuning_job_event.py | 35 - .../types/fine_tuning/job_create_params.py | 15 - .../types/fine_tuning/models/__init__.py | 1 - .../fine_tuning/models/fine_tuned_models.py | 13 - .../zhipuai/zhipuai_sdk/types/image.py | 18 - .../zhipuai_sdk/types/knowledge/__init__.py | 8 - .../types/knowledge/document/__init__.py | 8 - .../types/knowledge/document/document.py | 51 - .../document/document_edit_params.py | 29 - .../document/document_list_params.py | 26 - .../knowledge/document/document_list_resp.py | 11 - .../zhipuai_sdk/types/knowledge/knowledge.py | 21 - .../knowledge/knowledge_create_params.py | 30 - .../types/knowledge/knowledge_list_params.py | 15 - .../types/knowledge/knowledge_list_resp.py | 11 - .../types/knowledge/knowledge_used.py | 21 - .../types/sensitive_word_check/__init__.py | 3 - .../sensitive_word_check.py | 14 - .../zhipuai_sdk/types/tools/__init__.py | 9 - .../types/tools/tools_web_search_params.py | 35 - .../zhipuai_sdk/types/tools/web_search.py | 71 - .../types/tools/web_search_chunk.py | 33 - .../zhipuai_sdk/types/video/__init__.py | 3 - .../types/video/video_create_params.py | 27 - .../zhipuai_sdk/types/video/video_object.py | 30 - api/core/plugin/entities/plugin_daemon.py | 48 +- api/core/plugin/entities/request.py | 6 +- api/core/plugin/manager/asset.py | 4 +- api/core/plugin/manager/base.py | 15 +- api/core/plugin/manager/model.py | 518 +- api/core/provider_manager.py | 11 +- .../tools/utils/model_invocation_utils.py | 4 +- api/services/model_load_balancing_service.py | 3 +- api/services/model_provider_service.py | 70 +- .../test_model_provider_factory.py | 18 +- .../workflow/nodes/test_llm.py | 2 - .../nodes/test_parameter_extractor.py | 14 +- .../unit_tests/core/test_provider_manager.py | 5 +- 1070 files changed, 1072 insertions(+), 87428 deletions(-) delete mode 100644 api/core/model_runtime/model_providers/anthropic/__init__.py delete mode 100644 api/core/model_runtime/model_providers/anthropic/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/anthropic/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/anthropic/anthropic.py delete mode 100644 api/core/model_runtime/model_providers/anthropic/anthropic.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-2.1.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-2.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-3-5-sonnet-20240620.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-3-haiku-20240307.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-3-opus-20240229.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-3-sonnet-20240229.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.2.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.yaml delete mode 100644 api/core/model_runtime/model_providers/anthropic/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/azure_openai/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/azure_openai/_common.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/_constant.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/azure_openai.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/azure_openai.yaml delete mode 100644 api/core/model_runtime/model_providers/azure_openai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/azure_openai/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/__init__.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/baichuan/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/baichuan/baichuan.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/baichuan.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan2-53b.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo-192k.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan4.yaml delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan_tokenizer.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo_errors.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/baichuan/text_embedding/baichuan-text-embedding.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/__init__.py delete mode 100644 api/core/model_runtime/model_providers/bedrock/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/bedrock/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/bedrock/bedrock.py delete mode 100644 api/core/model_runtime/model_providers/bedrock/bedrock.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-mid-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-ultra-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-express-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-lite-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-haiku-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-opus-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-instant-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/cohere.command-light-text-v14.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-plus-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/cohere.command-text-v14.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-haiku-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-13b-chat-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-70b-chat-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-405b-instruct-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-70b-instruct-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-8b-instruct-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-70b-instruct-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-8b-instruct-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-7b-instruct-v0.2.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2402-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2407-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-small-2402-v1.0.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/mistral.mixtral-8x7b-instruct-v0.1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-haiku-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-opus-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v2.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-english-v3.yaml delete mode 100644 api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-multilingual-v3.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/chatglm/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/chatglm/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/chatglm/chatglm.py delete mode 100644 api/core/model_runtime/model_providers/chatglm/chatglm.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b.yaml delete mode 100644 api/core/model_runtime/model_providers/chatglm/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/cohere/__init__.py delete mode 100644 api/core/model_runtime/model_providers/cohere/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/cohere/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/cohere/cohere.py delete mode 100644 api/core/model_runtime/model_providers/cohere/cohere.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-light-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-light-nightly-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-light-nightly.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-light.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-nightly-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-nightly.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-r-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command-r.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/command.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-light-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/deepseek/__init__.py delete mode 100644 api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/deepseek/deepseek.py delete mode 100644 api/core/model_runtime/model_providers/deepseek/deepseek.yaml delete mode 100644 api/core/model_runtime/model_providers/deepseek/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/deepseek/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/deepseek/llm/deepseek-coder.yaml delete mode 100644 api/core/model_runtime/model_providers/deepseek/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/__init__.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/fireworks/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/fireworks/_common.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/fireworks.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/firefunction-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/firefunction-v2.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/gemma2-9b-it.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct-hf.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct-hf.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-405b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x22b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct-hf.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/mythomax-l2-13b.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/phi-3-vision-128k-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/fireworks/llm/yi-large.yaml delete mode 100644 api/core/model_runtime/model_providers/fishaudio/__init__.py delete mode 100644 api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/fishaudio/fishaudio.py delete mode 100644 api/core/model_runtime/model_providers/fishaudio/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/fishaudio/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/fishaudio/tts/tts.yaml delete mode 100644 api/core/model_runtime/model_providers/google/__init__.py delete mode 100644 api/core/model_runtime/model_providers/google/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/google/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/google/google.py delete mode 100644 api/core/model_runtime/model_providers/google/google.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-pro-vision.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/gemini-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/google/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/groq/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/groq/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/groq/groq.py delete mode 100644 api/core/model_runtime/model_providers/groq/groq.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama-3.1-405b-reasoning.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama-3.1-70b-versatile.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama-3.1-8b-instant.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama2-70b-4096.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama3-70b-8192.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llama3-8b-8192.yaml delete mode 100644 api/core/model_runtime/model_providers/groq/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/groq/llm/mixtral-8x7b-instruct-v0.1.yaml delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/__init__.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/_common.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.yaml delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_hub/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/__init__.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.yaml delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/tei_helper.py delete mode 100644 api/core/model_runtime/model_providers/huggingface_tei/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/__init__.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/hunyuan/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/hunyuan/hunyuan.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/hunyuan.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-lite.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard-256k.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-vision.yaml delete mode 100644 api/core/model_runtime/model_providers/hunyuan/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/hunyuan/text_embedding/hunyuan-text-embedding.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/jina/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/jina/jina.py delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-base-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-tiny-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-turbo-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v2-base-multilingual.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-clip-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-de.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-zh.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-small-en.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v3.yaml delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer.json delete mode 100644 api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer_config.json delete mode 100644 api/core/model_runtime/model_providers/leptonai/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/leptonai/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/leptonai/leptonai.py delete mode 100644 api/core/model_runtime/model_providers/leptonai/leptonai.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/gemma-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/llama2-13b.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/llama2-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/llama3-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/mistral-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/leptonai/llm/mixtral-8x7b.yaml delete mode 100644 api/core/model_runtime/model_providers/localai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/localai/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/localai/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/localai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/localai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/localai/localai.py delete mode 100644 api/core/model_runtime/model_providers/localai/localai.yaml delete mode 100644 api/core/model_runtime/model_providers/localai/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/localai/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/localai/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/localai/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/localai/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/minimax/__init__.py delete mode 100644 api/core/model_runtime/model_providers/minimax/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/minimax/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab5-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab5.5-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab5.5s-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab6-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab6.5-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/abab6.5s-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/chat_completion.py delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/errors.py delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/minimax/llm/types.py delete mode 100644 api/core/model_runtime/model_providers/minimax/minimax.py delete mode 100644 api/core/model_runtime/model_providers/minimax/minimax.yaml delete mode 100644 api/core/model_runtime/model_providers/minimax/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/minimax/text_embedding/embo-01.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/mistralai/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/mistralai/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/codestral-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/mistral-embed.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/mistral-large-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/mistral-medium-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/mistral-small-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/open-codestral-mamba.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/open-mistral-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/open-mistral-nemo.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x22b.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x7b.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/llm/pixtral-12b-2409.yaml delete mode 100644 api/core/model_runtime/model_providers/mistralai/mistralai.py delete mode 100644 api/core/model_runtime/model_providers/mistralai/mistralai.yaml delete mode 100644 api/core/model_runtime/model_providers/mixedbread/__init__.py delete mode 100644 api/core/model_runtime/model_providers/mixedbread/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/mixedbread/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/mixedbread/mixedbread.py delete mode 100644 api/core/model_runtime/model_providers/mixedbread/mixedbread.yaml delete mode 100644 api/core/model_runtime/model_providers/mixedbread/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/mixedbread/rerank/mxbai-rerank-large-v1-en.yaml delete mode 100644 api/core/model_runtime/model_providers/mixedbread/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/mixedbread/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-2d-large-v1-en.yaml delete mode 100644 api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-large-v1-en.yaml delete mode 100644 api/core/model_runtime/model_providers/moonshot/__init__.py delete mode 100644 api/core/model_runtime/model_providers/moonshot/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/moonshot/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/moonshot/moonshot.py delete mode 100644 api/core/model_runtime/model_providers/moonshot/moonshot.yaml delete mode 100644 api/core/model_runtime/model_providers/nomic/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nomic/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/nomic/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/nomic/_common.py delete mode 100644 api/core/model_runtime/model_providers/nomic/nomic.py delete mode 100644 api/core/model_runtime/model_providers/nomic/nomic.yaml delete mode 100644 api/core/model_runtime/model_providers/nomic/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/novita/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/novita/llm/Nous-Hermes-2-Mixtral-8x7B-DPO.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/airoboros-l2-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/dolphin-mixtral-8x22b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/gemma-2-9b-it.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/hermes-2-pro-llama-3-8b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/l3-70b-euryale-v2.1.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llama-3-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llama-3-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llama-3.1-405b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llama-3.1-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llama-3.1-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/novita/llm/lzlv_70b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/midnight-rose-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/mistral-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/mythomax-l2-13b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/nous-hermes-llama2-13b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/openhermes-2.5-mistral-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/llm/wizardlm-2-8x22b.yaml delete mode 100644 api/core/model_runtime/model_providers/novita/novita.py delete mode 100644 api/core/model_runtime/model_providers/novita/novita.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/nvidia/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/arctic.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/codegemma-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/fuyu-8b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/gemma-7b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-405b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-8b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama2-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama3-70b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llama3-8b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/mistral-large.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/mistralai_mixtral-8x7b-instruct-v0.1.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/mixtral-8x22b-instruct-v0.1.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/nemotron-4-340b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/phi-3-medium-128k-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/phi-3-mini-128k-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/llm/recurrentgemma-2b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/nvidia.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/nvidia.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/rerank/rerank-qa-mistral-4b.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nvidia/text_embedding/embed-qa-4.yaml delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.py delete mode 100644 api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/__init__.py delete mode 100644 api/core/model_runtime/model_providers/oci/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/oci/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/oci/llm/cohere.command-r-16k.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/llm/cohere.command-r-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/oci/llm/meta.llama-3-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/oci.py delete mode 100644 api/core/model_runtime/model_providers/oci/oci.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v2.0.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-light-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-v3.0.yaml delete mode 100644 api/core/model_runtime/model_providers/ollama/__init__.py delete mode 100644 api/core/model_runtime/model_providers/ollama/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/ollama/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/ollama/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/ollama/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/ollama/ollama.py delete mode 100644 api/core/model_runtime/model_providers/ollama/ollama.yaml delete mode 100644 api/core/model_runtime/model_providers/ollama/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/openai/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/openai/_common.py delete mode 100644 api/core/model_runtime/model_providers/openai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/chatgpt-4o-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0125.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0613.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-1106.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k-0613.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-0125-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-1106-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-2024-04-09.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4-vision-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-05-13.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-08-06.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini-2024-07-18.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/gpt-4o.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/openai/llm/o1-mini-2024-09-12.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/o1-mini.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/o1-preview-2024-09-12.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/o1-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/llm/text-davinci-003.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/moderation/text-moderation-stable.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/openai.py delete mode 100644 api/core/model_runtime/model_providers/openai/openai.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/openai/speech2text/whisper-1.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-large.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-small.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-ada-002.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai/tts/tts-1-hd.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/tts/tts-1.yaml delete mode 100644 api/core/model_runtime/model_providers/openai/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/_common.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.yaml delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openllm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openllm/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/openllm/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/openllm/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openllm/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py delete mode 100644 api/core/model_runtime/model_providers/openllm/llm/openllm_generate_errors.py delete mode 100644 api/core/model_runtime/model_providers/openllm/openllm.py delete mode 100644 api/core/model_runtime/model_providers/openllm/openllm.yaml delete mode 100644 api/core/model_runtime/model_providers/openllm/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openrouter/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openrouter/_assets/openrouter.svg delete mode 100644 api/core/model_runtime/model_providers/openrouter/_assets/openrouter_square.svg delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/claude-3-5-sonnet.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/claude-3-haiku.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/claude-3-opus.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/claude-3-sonnet.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/command-r-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/command-r.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/deepseek-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/deepseek-coder.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-flash.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gemini-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-3.5-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-4-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-4.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-2024-08-06.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-mini.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/gpt-4o.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llama-3-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llama-3-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-405b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/mistral-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x22b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/o1-mini.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/o1-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/llm/qwen2-72b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/openrouter/openrouter.py delete mode 100644 api/core/model_runtime/model_providers/openrouter/openrouter.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/__init__.py delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Llama3-Chinese_v2.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-70B-Instruct-GPTQ-Int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-8B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-405B-Instruct-AWQ-INT4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-8B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen-14B-Chat-Int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-110B-Chat-GPTQ-Int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-72B-Chat-GPTQ-Int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-7B.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-AWQ-int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-GPTQ-Int4.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-72B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-7B-Instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Reflection-Llama-3.1-70B.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Yi-1_5-9B-Chat-16K.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-1.5B-Chat.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-9B-Chat.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/chatglm3-6b.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-lite-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/perfxcloud.py delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/perfxcloud.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-en-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-zh-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-m3.yaml delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/perfxcloud/text_embedding/gte-Qwen2-7B-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/replicate/__init__.py delete mode 100644 api/core/model_runtime/model_providers/replicate/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/replicate/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/replicate/_common.py delete mode 100644 api/core/model_runtime/model_providers/replicate/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/replicate/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/replicate/replicate.py delete mode 100644 api/core/model_runtime/model_providers/replicate/replicate.yaml delete mode 100644 api/core/model_runtime/model_providers/replicate/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/sagemaker/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/sagemaker/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/sagemaker.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/sagemaker.yaml delete mode 100644 api/core/model_runtime/model_providers/sagemaker/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/sagemaker/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow.svg delete mode 100644 api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow_square.svg delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/mistral-7b-instruct-v0.2.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/mistral-8x7b-instruct-v0.1.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2-1.5b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-34b-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-6b-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-9b-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/rerank/bce-reranker-base_v1.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/rerank/bge-reranker-v2-m3.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/siliconflow.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/siliconflow.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/siliconflow/text_embedding/bce-embedding-base-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-en-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-zh-v1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-m3.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/__init__.py delete mode 100644 api/core/model_runtime/model_providers/spark/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/spark/_assets/icon_l_zh.svg delete mode 100644 api/core/model_runtime/model_providers/spark/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/spark/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/spark/llm/_client.py delete mode 100644 api/core/model_runtime/model_providers/spark/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-1.5.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-2.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-3.5.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-3.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-4.0-ultra.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-4.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-lite.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-max-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-max.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-pro-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/llm/spark-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/spark/spark.py delete mode 100644 api/core/model_runtime/model_providers/spark/spark.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/__init__.py delete mode 100644 api/core/model_runtime/model_providers/stepfun/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/stepfun/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1-256k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1-flash.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1v-32k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-1v-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/llm/step-2-16k.yaml delete mode 100644 api/core/model_runtime/model_providers/stepfun/stepfun.py delete mode 100644 api/core/model_runtime/model_providers/stepfun/stepfun.yaml delete mode 100644 api/core/model_runtime/model_providers/tencent/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tencent/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/tencent/_assets/icon_l_zh.svg delete mode 100644 api/core/model_runtime/model_providers/tencent/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/tencent/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tencent/speech2text/flash_recognizer.py delete mode 100644 api/core/model_runtime/model_providers/tencent/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/tencent/speech2text/tencent.yaml delete mode 100644 api/core/model_runtime/model_providers/tencent/tencent.py delete mode 100644 api/core/model_runtime/model_providers/tencent/tencent.yaml delete mode 100644 api/core/model_runtime/model_providers/togetherai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/togetherai/_assets/togetherai.svg delete mode 100644 api/core/model_runtime/model_providers/togetherai/_assets/togetherai_square.svg delete mode 100644 api/core/model_runtime/model_providers/togetherai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/togetherai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/togetherai/togetherai.py delete mode 100644 api/core/model_runtime/model_providers/togetherai/togetherai.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/tongyi/_assets/icon_l_zh.png delete mode 100644 api/core/model_runtime/model_providers/tongyi/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/tongyi/_common.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/farui-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-long.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0816.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0107.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0403.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0428.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-1201.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max-longcontext.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-max.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0206.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0624.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0723.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0806.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0206.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0624.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0919.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0201.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0809.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0201.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0809.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-1.5b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-72b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-0.5b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-1.5b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-14b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-32b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-3b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-72b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-coder-7b-instruct.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v2.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v3.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/tongyi.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/tongyi.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/tongyi/tts/tts-1.yaml delete mode 100644 api/core/model_runtime/model_providers/tongyi/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/__init__.py delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.py delete mode 100644 api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.yaml delete mode 100644 api/core/model_runtime/model_providers/upstage/__init__.py delete mode 100644 api/core/model_runtime/model_providers/upstage/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/upstage/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/upstage/_common.py delete mode 100644 api/core/model_runtime/model_providers/upstage/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/upstage/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/upstage/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/upstage/llm/solar-1-mini-chat.yaml delete mode 100644 api/core/model_runtime/model_providers/upstage/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-passage.yaml delete mode 100644 api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-query.yaml delete mode 100644 api/core/model_runtime/model_providers/upstage/upstage.py delete mode 100644 api/core/model_runtime/model_providers/upstage/upstage.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/_common.py delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-opus.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-embedding-004.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-multilingual-embedding-002.yaml delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/vertex_ai.py delete mode 100644 api/core/model_runtime/model_providers/vertex_ai/vertex_ai.yaml delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_zh.svg delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/client.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/client.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/auth.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/service.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/util.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/maas.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/llm/models.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/text_embedding/models.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.py delete mode 100644 api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/__init__.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/_assets/icon_l_en.png delete mode 100644 api/core/model_runtime/model_providers/wenxin/_assets/icon_l_zh.png delete mode 100644 api/core/model_runtime/model_providers/wenxin/_assets/icon_s_en.png delete mode 100644 api/core/model_runtime/model_providers/wenxin/_common.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-4k-0205.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-0205.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-1222.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k-latest.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k-preview.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-4.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-bot.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k-0321.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0308.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0922.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-128k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-appbuilder.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/llm/yi_34b_chat.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-en.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-zh.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/text_embedding/embedding-v1.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/text_embedding/tao-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/wenxin.py delete mode 100644 api/core/model_runtime/model_providers/wenxin/wenxin.yaml delete mode 100644 api/core/model_runtime/model_providers/wenxin/wenxin_errors.py delete mode 100644 api/core/model_runtime/model_providers/xinference/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/xinference/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/xinference/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/xinference/rerank/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/rerank/rerank.py delete mode 100644 api/core/model_runtime/model_providers/xinference/speech2text/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py delete mode 100644 api/core/model_runtime/model_providers/xinference/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/tts/__init__.py delete mode 100644 api/core/model_runtime/model_providers/xinference/tts/tts.py delete mode 100644 api/core/model_runtime/model_providers/xinference/xinference.py delete mode 100644 api/core/model_runtime/model_providers/xinference/xinference.yaml delete mode 100644 api/core/model_runtime/model_providers/xinference/xinference_helper.py delete mode 100644 api/core/model_runtime/model_providers/yi/__init__.py delete mode 100644 api/core/model_runtime/model_providers/yi/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/yi/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/yi/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/yi/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-large-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-large.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-medium-200k.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-medium.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-spark.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-vision.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml delete mode 100644 api/core/model_runtime/model_providers/yi/yi.py delete mode 100644 api/core/model_runtime/model_providers/yi/yi.yaml delete mode 100644 api/core/model_runtime/model_providers/zhinao/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhinao/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/zhinao/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo-responsibility-8k.yaml delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/360gpt2-pro.yaml delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/_position.yaml delete mode 100644 api/core/model_runtime/model_providers/zhinao/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/zhinao/zhinao.py delete mode 100644 api/core/model_runtime/model_providers/zhinao/zhinao.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_en.svg delete mode 100644 api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_zh.svg delete mode 100644 api/core/model_runtime/model_providers/zhipuai/_assets/icon_s_en.svg delete mode 100644 api/core/model_runtime/model_providers/zhipuai/_common.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite_32k.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/chatglm_pro.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/chatglm_std.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/chatglm_turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm-4-0520.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm-4-air.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm-4-airx.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm-4-flash.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_3_turbo.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_4.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_4_long.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_4_plus.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_4v.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/glm_4v_plus.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/llm/llm.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/text_embedding/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-2.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-3.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai.yaml delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__version__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/_client.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/assistant.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/batches.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/async_completions.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/chat.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/completions.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/embeddings.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/files.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/fine_tuning.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/jobs.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/fine_tuned_models.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/images.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/document.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/knowledge.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/tools.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/videos.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_api.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_compat.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_models.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_type.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_constants.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_errors.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_files.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_http_client.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_jwt_token.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_binary_response.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_response.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_request_opt.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_response.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_sse_client.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_transform.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_typing.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_utils.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/logs.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/pagination.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_completion.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_resp.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_support_resp.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/message_content.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/text_content_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/code_interpreter_delta_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/drawing_tool_delta_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/function_delta_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/retrieval_delta_black.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/tools_type.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/web_browser_delta_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools_delta_block.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_error.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_list_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_request_counts.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/async_chat_completion.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion_chunk.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completions_create_param.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/code_geex/code_geex_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/embeddings.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_deleted.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_object.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/upload_detail.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job_event.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/job_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/fine_tuned_models.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/image.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_edit_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_resp.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_resp.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_used.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/sensitive_word_check.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/tools_web_search_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search_chunk.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/__init__.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_create_params.py delete mode 100644 api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_object.py diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index fe0bcf7338..03d11b5619 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -132,7 +132,7 @@ class ModelProviderIconApi(Resource): def get(self, provider: str, icon_type: str, lang: str): model_provider_service = ModelProviderService() icon, mimetype = model_provider_service.get_model_provider_icon( - provider=provider, icon_type=icon_type, lang=lang + tenant_id=current_user.current_tenant_id, provider=provider, icon_type=icon_type, lang=lang ) return send_file(io.BytesIO(icon), mimetype=mimetype) 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 a91b9f0f02..c72c838d06 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.app_config.entities import EasyUIBasedAppConfig from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.entities.model_entities import ModelStatus from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError -from core.model_runtime.entities.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 @@ -67,14 +68,14 @@ class ModelConfigConverter: stop = completion_params["stop"] del completion_params["stop"] + model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials) + # get model mode model_mode = model_config.mode if not model_mode: - 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 skip_check and 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 b5e4554181..acc1a2d35b 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 @@ -1,6 +1,6 @@ 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.provider_manager import ProviderManager @@ -50,6 +50,7 @@ class ModelConfigManager: raise ValueError("model must be of object type") # model.provider + model_provider_factory = ModelProviderFactory(tenant_id) provider_entities = model_provider_factory.get_providers() model_provider_names = [provider.provider for provider in provider_entities] if "provider" not in config["model"] or config["model"]["provider"] not in model_provider_names: diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index 807f09598c..764221dec5 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -2,7 +2,7 @@ import datetime 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 @@ -18,16 +18,15 @@ from core.entities.provider_entities import ( ) 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 extensions.ext_database import db from models.provider import ( LoadBalancingModelConfig, @@ -100,7 +99,9 @@ class ProviderConfiguration(BaseModel): restrict_models = quota_configuration.restrict_models - 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 ( @@ -137,6 +138,9 @@ class ProviderConfiguration(BaseModel): (q for q in self.system_configuration.quota_configurations if q.quota_type == current_quota_type), None ) + if not current_quota_configuration: + return SystemConfigurationStatus.UNSUPPORTED + return ( SystemConfigurationStatus.ACTIVE if current_quota_configuration.is_valid @@ -172,7 +176,7 @@ class ProviderConfiguration(BaseModel): else [], ) - def custom_credentials_validate(self, credentials: dict) -> tuple[Provider, dict]: + def custom_credentials_validate(self, credentials: dict) -> tuple[Provider | None, dict]: """ Validate custom credentials. :param credentials: provider credentials @@ -216,6 +220,7 @@ class ProviderConfiguration(BaseModel): 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 ) @@ -243,13 +248,13 @@ class ProviderConfiguration(BaseModel): provider_record.updated_at = datetime.datetime.now(datetime.timezone.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() @@ -324,7 +329,7 @@ class ProviderConfiguration(BaseModel): def custom_model_credentials_validate( self, model_type: ModelType, model: str, credentials: dict - ) -> tuple[ProviderModel, dict]: + ) -> tuple[ProviderModel | None, dict]: """ Validate custom model credentials. @@ -367,6 +372,7 @@ class ProviderConfiguration(BaseModel): 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 ) @@ -397,14 +403,13 @@ class ProviderConfiguration(BaseModel): provider_model_record.updated_at = datetime.datetime.now(datetime.timezone.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() @@ -471,13 +476,12 @@ class ProviderConfiguration(BaseModel): model_setting.updated_at = datetime.datetime.now(datetime.timezone.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() @@ -506,13 +510,12 @@ class ProviderConfiguration(BaseModel): model_setting.updated_at = datetime.datetime.now(datetime.timezone.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() @@ -573,13 +576,12 @@ class ProviderConfiguration(BaseModel): model_setting.updated_at = datetime.datetime.now(datetime.timezone.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() @@ -608,25 +610,17 @@ class ProviderConfiguration(BaseModel): model_setting.updated_at = datetime.datetime.now(datetime.timezone.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. @@ -634,11 +628,19 @@ class ProviderConfiguration(BaseModel): :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: """ @@ -665,11 +667,10 @@ class ProviderConfiguration(BaseModel): 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() @@ -734,13 +735,14 @@ class ProviderConfiguration(BaseModel): :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 = [] if model_type: model_types.append(model_type) else: - model_types = provider_instance.get_provider_schema().supported_model_types + model_types = provider_schema.supported_model_types # Group model settings by model type and model model_setting_map = defaultdict(dict) @@ -749,11 +751,11 @@ class ProviderConfiguration(BaseModel): 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: @@ -764,23 +766,26 @@ class ProviderConfiguration(BaseModel): 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 @@ -801,7 +806,7 @@ class ProviderConfiguration(BaseModel): 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 @@ -822,14 +827,20 @@ class ProviderConfiguration(BaseModel): ]: # only customizable model for restrict_model in restrict_models: - copy_credentials = self.system_configuration.credentials.copy() + 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 = provider_instance.get_model_instance( - restrict_model.model_type - ).get_customizable_model_schema_from_credentials(restrict_model.model, copy_credentials) + 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}") continue @@ -875,15 +886,15 @@ class ProviderConfiguration(BaseModel): 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: """ @@ -897,8 +908,10 @@ class ProviderConfiguration(BaseModel): 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]: @@ -930,10 +943,10 @@ class ProviderConfiguration(BaseModel): 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}") @@ -1043,7 +1056,7 @@ class ProviderConfigurations(BaseModel): return iter(self.configurations) def values(self) -> Iterator[ProviderConfiguration]: - return self.configurations.values() + return iter(self.configurations.values()) def get(self, key, default=None): return self.configurations.get(key, default) @@ -1055,7 +1068,6 @@ class ProviderModelBundle(BaseModel): """ configuration: ProviderConfiguration - provider_instance: ModelProvider model_type_instance: AIModel # pydantic configs diff --git a/api/core/helper/moderation.py b/api/core/helper/moderation.py index b880590de2..f3144039e3 100644 --- a/api/core/helper/moderation.py +++ b/api/core/helper/moderation.py @@ -23,6 +23,9 @@ def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str) if using_provider_type == ProviderType.SYSTEM and provider_name in moderation_config.providers: hosting_openai_config = hosting_configuration.provider_map["openai"] + if hosting_openai_config.credentials is None: + return False + # 2000 text per chunk length = 2000 text_chunks = [text[i : i + length] for i in range(0, len(text), length)] diff --git a/api/core/model_runtime/entities/provider_entities.py b/api/core/model_runtime/entities/provider_entities.py index bfe861a97f..a6a7b67577 100644 --- a/api/core/model_runtime/entities/provider_entities.py +++ b/api/core/model_runtime/entities/provider_entities.py @@ -5,7 +5,7 @@ from typing import Optional from pydantic import BaseModel, ConfigDict 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,7 +127,7 @@ class ProviderEntity(BaseModel): help: Optional[ProviderHelpEntity] = None supported_model_types: Sequence[ModelType] configurate_methods: list[ConfigurateMethod] - models: list[ProviderModel] = [] + models: list[AIModelEntity] = [] provider_credential_schema: Optional[ProviderCredentialSchema] = None model_credential_schema: Optional[ModelCredentialSchema] = None 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 79a1d28ebe..6b04ba2efd 100644 --- a/api/core/model_runtime/model_providers/__base/ai_model.py +++ b/api/core/model_runtime/model_providers/__base/ai_model.py @@ -1,10 +1,9 @@ import decimal import os -from abc import ABC, abstractmethod from collections.abc import Mapping from typing import Optional -from pydantic import ConfigDict +from pydantic import ConfigDict, Field from core.helper.position_helper import get_position_map, sort_by_position_map from core.model_runtime.entities.common_entities import I18nObject @@ -20,34 +19,26 @@ from core.model_runtime.entities.model_entities import ( ) from core.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError from core.model_runtime.model_providers.__base.tokenizers.gpt2_tokenzier import GPT2Tokenizer +from core.plugin.entities.plugin_daemon import PluginModelProviderEntity from core.tools.utils.yaml_utils import load_yaml_file -class AIModel(ABC): +class AIModel: """ 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: Mapping) -> 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]]]: """ Map model invoke error to unified error @@ -66,20 +57,18 @@ class AIModel(ABC): :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( 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." ) ) - return invoke_error(description=f"[{provider_name}] {invoke_error.description}, {str(error)}") + return invoke_error(description=f"[{self.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: """ 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 ba88cc1f38..33dbce37c4 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,32 +1,25 @@ import logging import os -import re import time -from abc import abstractmethod -from collections.abc import Generator, Mapping +from collections.abc import Generator from typing import Optional, Union from pydantic import ConfigDict 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__) @@ -71,8 +64,6 @@ class LargeLanguageModel(AIModel): 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 [] @@ -94,20 +85,43 @@ class LargeLanguageModel(AIModel): ) try: - if "response_format" in model_parameters: - result = self._code_block_mode_wrapper( + 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=stop, + stream=stream, + ) + + if not stream: + content = "" + content_list = [] + usage = LLMUsage.empty_usage() + system_fingerprint = None + 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) + + 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, - callbacks=callbacks, + message=AssistantPromptMessage(content=content or content_list), + usage=usage, + system_fingerprint=system_fingerprint, ) - else: - result = self._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) except Exception as e: self._trigger_invoke_error_callbacks( model=model, @@ -122,6 +136,7 @@ class LargeLanguageModel(AIModel): callbacks=callbacks, ) + # TODO raise self._transform_invoke_error(e) if stream and isinstance(result, Generator): @@ -153,244 +168,6 @@ class LargeLanguageModel(AIModel): 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[list[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 = stop or [] - 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 - piece = content - else: - yield piece - continue - new_piece: str = "" - for char in 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 - piece = content - else: - # Yield pieces without content directly - yield piece - continue - - if state == "done": - continue - - new_piece: str = "" - for char in 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 _invoke_result_generator( self, model: str, @@ -462,34 +239,6 @@ if you are not sure about the structure. callbacks=callbacks, ) - @abstractmethod - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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, @@ -506,41 +255,18 @@ if you are not sure about the structure. :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[Mapping] = 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 @@ -772,98 +498,3 @@ if you are not sure about the structure. 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/text_embedding_model.py b/api/core/model_runtime/model_providers/__base/text_embedding_model.py index a948dca20d..1a5c40ed51 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 @@ -8,6 +8,7 @@ from core.embedding.embedding_constant import EmbeddingInputType 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): @@ -66,7 +67,6 @@ class TextEmbeddingModel(AIModel): """ raise NotImplementedError - @abstractmethod def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int: """ Get number of tokens for given prompt messages @@ -76,7 +76,17 @@ class TextEmbeddingModel(AIModel): :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_type=self.model_type.value, + 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/__init__.py b/api/core/model_runtime/model_providers/__init__.py index 9d71013dbf..e69de29bb2 100644 --- a/api/core/model_runtime/model_providers/__init__.py +++ b/api/core/model_runtime/model_providers/__init__.py @@ -1,3 +0,0 @@ -from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory - -model_provider_factory = ModelProviderFactory() diff --git a/api/core/model_runtime/model_providers/anthropic/__init__.py b/api/core/model_runtime/model_providers/anthropic/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/anthropic/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/anthropic/_assets/icon_l_en.svg deleted file mode 100644 index cace17da73..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/_assets/icon_l_en.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/anthropic/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/anthropic/_assets/icon_s_en.svg deleted file mode 100644 index d852f04401..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/_assets/icon_s_en.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/api/core/model_runtime/model_providers/anthropic/anthropic.py b/api/core/model_runtime/model_providers/anthropic/anthropic.py deleted file mode 100644 index 5b12f04a3e..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/anthropic.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class AnthropicProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `claude-3-opus-20240229` model for validate, - model_instance.validate_credentials(model="claude-3-opus-20240229", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/anthropic/anthropic.yaml b/api/core/model_runtime/model_providers/anthropic/anthropic.yaml deleted file mode 100644 index cf41f544ef..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/anthropic.yaml +++ /dev/null @@ -1,39 +0,0 @@ -provider: anthropic -label: - en_US: Anthropic -description: - en_US: Anthropic’s powerful models, such as Claude 3. - zh_Hans: Anthropic 的强大模型,例如 Claude 3。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#F0F0EB" -help: - title: - en_US: Get your API Key from Anthropic - zh_Hans: 从 Anthropic 获取 API Key - url: - en_US: https://console.anthropic.com/account/keys -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: anthropic_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: anthropic_api_url - label: - en_US: API URL - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的 API URL - en_US: Enter your API URL diff --git a/api/core/model_runtime/model_providers/anthropic/llm/__init__.py b/api/core/model_runtime/model_providers/anthropic/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/anthropic/llm/_position.yaml b/api/core/model_runtime/model_providers/anthropic/llm/_position.yaml deleted file mode 100644 index 8394c4276a..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/_position.yaml +++ /dev/null @@ -1,8 +0,0 @@ -- claude-3-5-sonnet-20240620 -- claude-3-haiku-20240307 -- claude-3-opus-20240229 -- claude-3-sonnet-20240229 -- claude-2.1 -- claude-instant-1.2 -- claude-2 -- claude-instant-1 diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-2.1.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-2.1.yaml deleted file mode 100644 index 6707c34594..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-2.1.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: claude-2.1 -label: - en_US: claude-2.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '8.00' - output: '24.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-2.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-2.yaml deleted file mode 100644 index 1986947129..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-2.yaml +++ /dev/null @@ -1,37 +0,0 @@ -model: claude-2 -label: - en_US: claude-2 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '8.00' - output: '24.00' - unit: '0.000001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-5-sonnet-20240620.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-3-5-sonnet-20240620.yaml deleted file mode 100644 index e02c5517fe..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-5-sonnet-20240620.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: claude-3-5-sonnet-20240620 -label: - en_US: claude-3-5-sonnet-20240620 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format -pricing: - input: '3.00' - output: '15.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-haiku-20240307.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-3-haiku-20240307.yaml deleted file mode 100644 index cb2af1308a..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-haiku-20240307.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: claude-3-haiku-20240307 -label: - en_US: claude-3-haiku-20240307 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '0.25' - output: '1.25' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-opus-20240229.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-3-opus-20240229.yaml deleted file mode 100644 index 101f54c3f8..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-opus-20240229.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: claude-3-opus-20240229 -label: - en_US: claude-3-opus-20240229 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '15.00' - output: '75.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-sonnet-20240229.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-3-sonnet-20240229.yaml deleted file mode 100644 index daf55553f8..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-3-sonnet-20240229.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: claude-3-sonnet-20240229 -label: - en_US: claude-3-sonnet-20240229 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '3.00' - output: '15.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.2.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.2.yaml deleted file mode 100644 index ac69bbf4d2..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.2.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: claude-instant-1.2 -label: - en_US: claude-instant-1.2 -model_type: llm -features: [ ] -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '1.63' - output: '5.51' - unit: '0.000001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.yaml b/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.yaml deleted file mode 100644 index 5e76d5b1c2..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/claude-instant-1.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: claude-instant-1 -label: - en_US: claude-instant-1 -model_type: llm -features: [ ] -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '1.63' - output: '5.51' - unit: '0.000001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/anthropic/llm/llm.py b/api/core/model_runtime/model_providers/anthropic/llm/llm.py deleted file mode 100644 index 46e1b415b8..0000000000 --- a/api/core/model_runtime/model_providers/anthropic/llm/llm.py +++ /dev/null @@ -1,624 +0,0 @@ -import base64 -import io -import json -from collections.abc import Generator -from typing import Optional, Union, cast - -import anthropic -import requests -from anthropic import Anthropic, Stream -from anthropic.types import ( - ContentBlockDeltaEvent, - Message, - MessageDeltaEvent, - MessageStartEvent, - MessageStopEvent, - MessageStreamEvent, - completion_create_params, -) -from anthropic.types.beta.tools import ToolsBetaMessage -from httpx import Timeout -from PIL import Image - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -ANTHROPIC_BLOCK_MODE_PROMPT = """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 - - -class AnthropicLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # invoke model - return self._chat_generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm chat model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # transform model parameters from completion api of anthropic to chat api - if "max_tokens_to_sample" in model_parameters: - model_parameters["max_tokens"] = model_parameters.pop("max_tokens_to_sample") - - # init model client - client = Anthropic(**credentials_kwargs) - - extra_model_kwargs = {} - if stop: - extra_model_kwargs["stop_sequences"] = stop - - if user: - extra_model_kwargs["metadata"] = completion_create_params.Metadata(user_id=user) - - system, prompt_message_dicts = self._convert_prompt_messages(prompt_messages) - - if system: - extra_model_kwargs["system"] = system - - # Add the new header for claude-3-5-sonnet-20240620 model - extra_headers = {} - if model == "claude-3-5-sonnet-20240620": - if model_parameters.get("max_tokens") > 4096: - extra_headers["anthropic-beta"] = "max-tokens-3-5-sonnet-2024-07-15" - - if tools: - extra_model_kwargs["tools"] = [self._transform_tool_prompt(tool) for tool in tools] - response = client.beta.tools.messages.create( - model=model, - messages=prompt_message_dicts, - stream=stream, - extra_headers=extra_headers, - **model_parameters, - **extra_model_kwargs, - ) - else: - # chat model - response = client.messages.create( - model=model, - messages=prompt_message_dicts, - stream=stream, - extra_headers=extra_headers, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_chat_generate_response(model, credentials, response, prompt_messages) - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: list[Callback] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - if model_parameters.get("response_format"): - stop = stop or [] - # chat model - self._transform_chat_json_prompts( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - response_format=model_parameters["response_format"], - ) - model_parameters.pop("response_format") - - return self._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _transform_tool_prompt(self, tool: PromptMessageTool) -> dict: - return {"name": tool.name, "description": tool.description, "input_schema": tool.parameters} - - def _transform_chat_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts - """ - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - - # 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=ANTHROPIC_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - else: - # insert the system message - prompt_messages.insert( - 0, - SystemPromptMessage( - content=ANTHROPIC_BLOCK_MODE_PROMPT.replace( - "{{instructions}}", f"Please output a valid {response_format} object." - ).replace("{{block}}", response_format) - ), - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - prompt = self._convert_messages_to_prompt_anthropic(prompt_messages) - - client = Anthropic(api_key="") - tokens = client.count_tokens(prompt) - - tool_call_inner_prompts_tokens_map = { - "claude-3-opus-20240229": 395, - "claude-3-haiku-20240307": 264, - "claude-3-sonnet-20240229": 159, - } - - if model in tool_call_inner_prompts_tokens_map and tools: - tokens += tool_call_inner_prompts_tokens_map[model] - - return tokens - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=[ - UserPromptMessage(content="ping"), - ], - model_parameters={ - "temperature": 0, - "max_tokens": 20, - }, - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: Union[Message, ToolsBetaMessage], - prompt_messages: list[PromptMessage], - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content="", tool_calls=[]) - - for content in response.content: - if content.type == "text": - assistant_prompt_message.content += content.text - elif content.type == "tool_use": - tool_call = AssistantPromptMessage.ToolCall( - id=content.id, - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=content.name, arguments=json.dumps(content.input) - ), - ) - assistant_prompt_message.tool_calls.append(tool_call) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.input_tokens - completion_tokens = response.usage.output_tokens - else: - # calculate num tokens - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=response.model, prompt_messages=prompt_messages, message=assistant_prompt_message, usage=usage - ) - - return response - - def _handle_chat_generate_stream_response( - self, model: str, credentials: dict, response: Stream[MessageStreamEvent], prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - full_assistant_content = "" - return_model = None - input_tokens = 0 - output_tokens = 0 - finish_reason = None - index = 0 - - tool_calls: list[AssistantPromptMessage.ToolCall] = [] - - for chunk in response: - if isinstance(chunk, MessageStartEvent): - if hasattr(chunk, "content_block"): - content_block = chunk.content_block - if isinstance(content_block, dict): - if content_block.get("type") == "tool_use": - tool_call = AssistantPromptMessage.ToolCall( - id=content_block.get("id"), - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=content_block.get("name"), arguments="" - ), - ) - tool_calls.append(tool_call) - elif hasattr(chunk, "delta"): - delta = chunk.delta - if isinstance(delta, dict) and len(tool_calls) > 0: - if delta.get("type") == "input_json_delta": - tool_calls[-1].function.arguments += delta.get("partial_json", "") - elif chunk.message: - return_model = chunk.message.model - input_tokens = chunk.message.usage.input_tokens - elif isinstance(chunk, MessageDeltaEvent): - output_tokens = chunk.usage.output_tokens - finish_reason = chunk.delta.stop_reason - elif isinstance(chunk, MessageStopEvent): - # transform usage - usage = self._calc_response_usage(model, credentials, input_tokens, output_tokens) - - # transform empty tool call arguments to {} - for tool_call in tool_calls: - if not tool_call.function.arguments: - tool_call.function.arguments = "{}" - - yield LLMResultChunk( - model=return_model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index + 1, - message=AssistantPromptMessage(content="", tool_calls=tool_calls), - finish_reason=finish_reason, - usage=usage, - ), - ) - elif isinstance(chunk, ContentBlockDeltaEvent): - chunk_text = chunk.delta.text or "" - full_assistant_content += chunk_text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=chunk_text) - - index = chunk.index - - yield LLMResultChunk( - model=return_model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk.index, - message=assistant_prompt_message, - ), - ) - - def _to_credential_kwargs(self, credentials: dict) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["anthropic_api_key"], - "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), - "max_retries": 1, - } - - if credentials.get("anthropic_api_url"): - credentials["anthropic_api_url"] = credentials["anthropic_api_url"].rstrip("/") - credentials_kwargs["base_url"] = credentials["anthropic_api_url"] - - return credentials_kwargs - - def _convert_prompt_messages(self, prompt_messages: list[PromptMessage]) -> tuple[str, list[dict]]: - """ - Convert prompt messages to dict list and system - """ - system = "" - first_loop = True - for message in prompt_messages: - if isinstance(message, SystemPromptMessage): - message.content = message.content.strip() - if first_loop: - system = message.content - first_loop = False - else: - system += "\n" - system += message.content - - prompt_message_dicts = [] - for message in prompt_messages: - if not isinstance(message, SystemPromptMessage): - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - prompt_message_dicts.append(message_dict) - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - if not message_content.data.startswith("data:"): - # fetch image data from url - try: - image_content = requests.get(message_content.data).content - with Image.open(io.BytesIO(image_content)) as img: - mime_type = f"image/{img.format.lower()}" - base64_data = base64.b64encode(image_content).decode("utf-8") - except Exception as ex: - raise ValueError( - f"Failed to fetch image data from url {message_content.data}, {ex}" - ) - else: - data_split = message_content.data.split(";base64,") - mime_type = data_split[0].replace("data:", "") - base64_data = data_split[1] - - if mime_type not in {"image/jpeg", "image/png", "image/gif", "image/webp"}: - raise ValueError( - f"Unsupported image type {mime_type}, " - f"only support image/jpeg, image/png, image/gif, and image/webp" - ) - - sub_message_dict = { - "type": "image", - "source": {"type": "base64", "media_type": mime_type, "data": base64_data}, - } - sub_messages.append(sub_message_dict) - prompt_message_dicts.append({"role": "user", "content": sub_messages}) - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - content = [] - if message.tool_calls: - for tool_call in message.tool_calls: - content.append( - { - "type": "tool_use", - "id": tool_call.id, - "name": tool_call.function.name, - "input": json.loads(tool_call.function.arguments), - } - ) - if message.content: - content.append({"type": "text", "text": message.content}) - - if prompt_message_dicts[-1]["role"] == "assistant": - prompt_message_dicts[-1]["content"].extend(content) - else: - prompt_message_dicts.append({"role": "assistant", "content": content}) - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = { - "role": "user", - "content": [ - {"type": "tool_result", "tool_use_id": message.tool_call_id, "content": message.content} - ], - } - prompt_message_dicts.append(message_dict) - else: - raise ValueError(f"Got unknown type {message}") - - return system, prompt_message_dicts - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - content = message.content - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - if not isinstance(message.content, list): - message_text = f"{ai_prompt} {content}" - else: - message_text = "" - for sub_message in message.content: - if sub_message.type == PromptMessageContentType.TEXT: - message_text += f"{human_prompt} {sub_message.data}" - elif sub_message.type == PromptMessageContentType.IMAGE: - message_text += f"{human_prompt} [IMAGE]" - elif isinstance(message, AssistantPromptMessage): - if not isinstance(message.content, list): - message_text = f"{ai_prompt} {content}" - else: - message_text = "" - for sub_message in message.content: - if sub_message.type == PromptMessageContentType.TEXT: - message_text += f"{ai_prompt} {sub_message.data}" - elif sub_message.type == PromptMessageContentType.IMAGE: - message_text += f"{ai_prompt} [IMAGE]" - elif isinstance(message, SystemPromptMessage): - message_text = content - elif isinstance(message, ToolPromptMessage): - message_text = f"{human_prompt} {message.content}" - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - def _convert_messages_to_prompt_anthropic(self, messages: list[PromptMessage]) -> str: - """ - Format a list of messages into a full prompt for the Anthropic model - - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - if not messages: - return "" - - messages = messages.copy() # don't mutate the original list - if not isinstance(messages[-1], AssistantPromptMessage): - messages.append(AssistantPromptMessage(content="")) - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [anthropic.APIConnectionError, anthropic.APITimeoutError], - InvokeServerUnavailableError: [anthropic.InternalServerError], - InvokeRateLimitError: [anthropic.RateLimitError], - InvokeAuthorizationError: [anthropic.AuthenticationError, anthropic.PermissionDeniedError], - InvokeBadRequestError: [ - anthropic.BadRequestError, - anthropic.NotFoundError, - anthropic.UnprocessableEntityError, - anthropic.APIError, - ], - } diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png deleted file mode 100644 index 4b941654a78c1593450d336c2d784d45179f0e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21236 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 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png deleted file mode 100644 index ca3043dc8dcb19139d4d8c8f179f74ae397dc217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10541 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 bAwDr)k^d#K?0= None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - pass diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml b/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml deleted file mode 100644 index 9e17ba0884..0000000000 --- a/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml +++ /dev/null @@ -1,65 +0,0 @@ -provider: azure_ai_studio -label: - zh_Hans: Azure AI Studio - en_US: Azure AI Studio -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -description: - en_US: Azure AI Studio - zh_Hans: Azure AI Studio -background: "#93c5fd" -help: - title: - en_US: How to deploy customized model on Azure AI Studio - zh_Hans: 如何在Azure AI Studio上的私有化部署的模型 - url: - en_US: https://learn.microsoft.com/en-us/azure/ai-studio/how-to/deploy-models - zh_Hans: https://learn.microsoft.com/zh-cn/azure/ai-studio/how-to/deploy-models -supported_model_types: - - llm - - rerank -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: endpoint - label: - en_US: Azure AI Studio Endpoint - type: text-input - required: true - placeholder: - zh_Hans: 请输入你的Azure AI Studio推理端点 - en_US: 'Enter your API Endpoint, eg: https://example.com' - - variable: api_key - required: true - label: - en_US: API Key - zh_Hans: API Key - type: secret-input - placeholder: - en_US: Enter your Azure AI Studio API Key - zh_Hans: 在此输入您的 Azure AI Studio API Key - show_on: - - variable: __model_type - value: llm - - variable: jwt_token - required: true - label: - en_US: JWT Token - zh_Hans: JWT令牌 - type: secret-input - placeholder: - en_US: Enter your Azure AI Studio JWT Token - zh_Hans: 在此输入您的 Azure AI Studio 推理 API Key - show_on: - - variable: __model_type - value: rerank diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py b/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py deleted file mode 100644 index 516ef8b295..0000000000 --- a/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py +++ /dev/null @@ -1,334 +0,0 @@ -import logging -from collections.abc import Generator -from typing import Any, Optional, Union - -from azure.ai.inference import ChatCompletionsClient -from azure.ai.inference.models import StreamingChatCompletionsUpdate -from azure.core.credentials import AzureKeyCredential -from azure.core.exceptions import ( - ClientAuthenticationError, - DecodeError, - DeserializationError, - HttpResponseError, - ResourceExistsError, - ResourceModifiedError, - ResourceNotFoundError, - ResourceNotModifiedError, - SerializationError, - ServiceRequestError, - ServiceResponseError, -) - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import 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, - I18nObject, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) - - -class AzureAIStudioLargeLanguageModel(LargeLanguageModel): - """ - Model class for Azure AI Studio large language model. - """ - - client: Any = None - - from azure.ai.inference.models import StreamingChatCompletionsUpdate - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - - if not self.client: - endpoint = credentials.get("endpoint") - api_key = credentials.get("api_key") - self.client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(api_key)) - - messages = [{"role": msg.role.value, "content": msg.content} for msg in prompt_messages] - - payload = { - "messages": messages, - "max_tokens": model_parameters.get("max_tokens", 4096), - "temperature": model_parameters.get("temperature", 0), - "top_p": model_parameters.get("top_p", 1), - "stream": stream, - } - - if stop: - payload["stop"] = stop - - if tools: - payload["tools"] = [tool.model_dump() for tool in tools] - - try: - response = self.client.complete(**payload) - - if stream: - return self._handle_stream_response(response, model, prompt_messages) - else: - return self._handle_non_stream_response(response, model, prompt_messages, credentials) - except Exception as e: - raise self._transform_invoke_error(e) - - def _handle_stream_response(self, response, model: str, prompt_messages: list[PromptMessage]) -> Generator: - for chunk in response: - if isinstance(chunk, StreamingChatCompletionsUpdate): - if chunk.choices: - delta = chunk.choices[0].delta - if delta.content: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=delta.content, tool_calls=[]), - ), - ) - - def _handle_non_stream_response( - self, response, model: str, prompt_messages: list[PromptMessage], credentials: dict - ) -> LLMResult: - assistant_text = response.choices[0].message.content - assistant_prompt_message = AssistantPromptMessage(content=assistant_text) - usage = self._calc_response_usage( - model, credentials, response.usage.prompt_tokens, response.usage.completion_tokens - ) - result = LLMResult(model=model, prompt_messages=prompt_messages, message=assistant_prompt_message, usage=usage) - - if hasattr(response, "system_fingerprint"): - result.system_fingerprint = response.system_fingerprint - - return result - - def _invoke_result_generator( - self, - model: str, - result: Generator, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: Optional[list[Callback]] = None, - ) -> Generator: - """ - Invoke result generator - - :param result: result generator - :return: result generator - """ - callbacks = callbacks or [] - prompt_message = AssistantPromptMessage(content="") - usage = None - system_fingerprint = None - real_model = model - - try: - for chunk in result: - if isinstance(chunk, dict): - content = chunk["choices"][0]["message"]["content"] - usage = chunk["usage"] - chunk = LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=content, tool_calls=[]), - ), - system_fingerprint=chunk.get("system_fingerprint"), - ) - - yield chunk - - self._trigger_new_chunk_callbacks( - chunk=chunk, - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - callbacks=callbacks, - ) - - prompt_message.content += chunk.delta.message.content - real_model = chunk.model - if hasattr(chunk.delta, "usage"): - usage = chunk.delta.usage - - if chunk.system_fingerprint: - system_fingerprint = chunk.system_fingerprint - except Exception as e: - raise self._transform_invoke_error(e) - - self._trigger_after_invoke_callbacks( - model=model, - result=LLMResult( - model=real_model, - prompt_messages=prompt_messages, - message=prompt_message, - usage=usage or LLMUsage.empty_usage(), - system_fingerprint=system_fingerprint, - ), - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - callbacks=callbacks, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - # Implement token counting logic here - # Might need to use a tokenizer specific to the Azure AI Studio model - return 0 - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - endpoint = credentials.get("endpoint") - api_key = credentials.get("api_key") - client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(api_key)) - client.get_model_info() - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - ServiceRequestError, - ], - InvokeServerUnavailableError: [ - ServiceResponseError, - ], - InvokeAuthorizationError: [ - ClientAuthenticationError, - ], - InvokeBadRequestError: [ - HttpResponseError, - DecodeError, - ResourceExistsError, - ResourceNotFoundError, - ResourceModifiedError, - ResourceNotModifiedError, - SerializationError, - DeserializationError, - ], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - Used to define customizable model schema - """ - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - default=512, - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ] - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - features=[], - model_properties={}, - parameter_rules=rules, - ) - - return entity diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py b/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py deleted file mode 100644 index 6ed7ab277c..0000000000 --- a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py +++ /dev/null @@ -1,164 +0,0 @@ -import json -import logging -import os -import ssl -import urllib.request -from typing import Optional - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - -logger = logging.getLogger(__name__) - - -class AzureRerankModel(RerankModel): - """ - Model class for Azure AI Studio rerank model. - """ - - def _allow_self_signed_https(self, allowed): - # bypass the server certificate verification on client side - if allowed and not os.environ.get("PYTHONHTTPSVERIFY", "") and getattr(ssl, "_create_unverified_context", None): - ssl._create_default_https_context = ssl._create_unverified_context - - def _azure_rerank(self, query_input: str, docs: list[str], endpoint: str, api_key: str): - # self._allow_self_signed_https(True) # Enable if using self-signed certificate - - data = {"inputs": query_input, "docs": docs} - - body = json.dumps(data).encode("utf-8") - headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} - - req = urllib.request.Request(endpoint, body, headers) - - try: - with urllib.request.urlopen(req) as response: - result = response.read() - return json.loads(result) - except urllib.error.HTTPError as error: - logger.error(f"The request failed with status code: {error.code}") - logger.error(error.info()) - logger.error(error.read().decode("utf8", "ignore")) - raise - - 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 - """ - try: - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - endpoint = credentials.get("endpoint") - api_key = credentials.get("jwt_token") - - if not endpoint or not api_key: - raise ValueError("Azure endpoint and API key must be provided in credentials") - - result = self._azure_rerank(query, docs, endpoint, api_key) - logger.info(f"Azure rerank result: {result}") - - rerank_documents = [] - for idx, (doc, score_dict) in enumerate(zip(docs, result)): - score = score_dict["score"] - rerank_document = RerankDocument(index=idx, text=doc, score=score) - - if score_threshold is None or score >= score_threshold: - rerank_documents.append(rerank_document) - - rerank_documents.sort(key=lambda x: x.score, reverse=True) - - if top_n: - rerank_documents = rerank_documents[:top_n] - - return RerankResult(model=model, docs=rerank_documents) - - except Exception as e: - logger.exception(f"Exception in Azure rerank: {e}") - raise - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [urllib.error.URLError], - InvokeServerUnavailableError: [urllib.error.HTTPError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError, json.JSONDecodeError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.RERANK, - model_properties={}, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/azure_openai/__init__.py b/api/core/model_runtime/model_providers/azure_openai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_openai/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/azure_openai/_assets/icon_l_en.png deleted file mode 100644 index 0791a67911ec40247c40bf13a2ba2d2512e013b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5007 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 VjvA=y{3p`@T55W#70TA3{{wPWtZe`Q diff --git a/api/core/model_runtime/model_providers/azure_openai/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/azure_openai/_assets/icon_s_en.svg deleted file mode 100644 index df1f54f36e..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/azure_openai/_common.py b/api/core/model_runtime/model_providers/azure_openai/_common.py deleted file mode 100644 index 32a0269af4..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/_common.py +++ /dev/null @@ -1,42 +0,0 @@ -import openai -from httpx import Timeout - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.model_providers.azure_openai._constant import AZURE_OPENAI_API_VERSION - - -class _CommonAzureOpenAI: - @staticmethod - def _to_credential_kwargs(credentials: dict) -> dict: - api_version = credentials.get("openai_api_version", AZURE_OPENAI_API_VERSION) - credentials_kwargs = { - "api_key": credentials["openai_api_key"], - "azure_endpoint": credentials["openai_api_base"], - "api_version": api_version, - "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), - "max_retries": 1, - } - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - return { - InvokeConnectionError: [openai.APIConnectionError, openai.APITimeoutError], - InvokeServerUnavailableError: [openai.InternalServerError], - InvokeRateLimitError: [openai.RateLimitError], - InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError], - InvokeBadRequestError: [ - openai.BadRequestError, - openai.NotFoundError, - openai.UnprocessableEntityError, - openai.APIError, - ], - } diff --git a/api/core/model_runtime/model_providers/azure_openai/_constant.py b/api/core/model_runtime/model_providers/azure_openai/_constant.py deleted file mode 100644 index 0dada70cc5..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/_constant.py +++ /dev/null @@ -1,1262 +0,0 @@ -from pydantic import BaseModel - -from core.model_runtime.entities.defaults import PARAMETER_RULE_TEMPLATE -from core.model_runtime.entities.llm_entities import LLMMode -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - I18nObject, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - PriceConfig, -) - -AZURE_OPENAI_API_VERSION = "2024-02-15-preview" - -AZURE_DEFAULT_PARAM_SEED_HELP = I18nObject( - zh_Hans="如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性," - "您应该参考 system_fingerprint 响应参数来监视变化。", - en_US="If specified, model will make a best effort to sample deterministically," - " such that repeated requests with the same seed and parameters should return the same result." - " Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter" - " to monitor changes in the backend.", -) - - -def _get_max_tokens(default: int, min_val: int, max_val: int) -> ParameterRule: - rule = ParameterRule( - name="max_tokens", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.MAX_TOKENS], - ) - rule.default = default - rule.min = min_val - rule.max = max_val - return rule - - -class AzureBaseModel(BaseModel): - base_model_name: str - entity: AIModelEntity - - -LLM_BASE_MODELS = [ - AzureBaseModel( - base_model_name="gpt-35-turbo", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 16385, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.0005, - output=0.0015, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-35-turbo-16k", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 16385, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=16385), - ], - pricing=PriceConfig( - input=0.003, - output=0.004, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-35-turbo-0125", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 16385, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.0005, - output=0.0015, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 8192, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=8192), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.03, - output=0.06, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-32k", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 32768, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=32768), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.06, - output=0.12, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-0125-preview", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.01, - output=0.03, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-1106-preview", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.01, - output=0.03, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4o-mini", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=16384), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.150, - output=0.600, - unit=0.000001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4o-mini-2024-07-18", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=16384), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object", "json_schema"], - ), - ParameterRule( - name="json_schema", - label=I18nObject(en_US="JSON Schema"), - type="text", - help=I18nObject( - zh_Hans="设置返回的json schema,llm将按照它返回", - en_US="Set a response json schema will ensure LLM to adhere it.", - ), - required=False, - ), - ], - pricing=PriceConfig( - input=0.150, - output=0.600, - unit=0.000001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4o", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=5.00, - output=15.00, - unit=0.000001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4o-2024-05-13", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=5.00, - output=15.00, - unit=0.000001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4o-2024-08-06", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object", "json_schema"], - ), - ParameterRule( - name="json_schema", - label=I18nObject(en_US="JSON Schema"), - type="text", - help=I18nObject( - zh_Hans="设置返回的json schema,llm将按照它返回", - en_US="Set a response json schema will ensure LLM to adhere it.", - ), - required=False, - ), - ], - pricing=PriceConfig( - input=5.00, - output=15.00, - unit=0.000001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-turbo", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.01, - output=0.03, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-turbo-2024-04-09", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ - ModelFeature.AGENT_THOUGHT, - ModelFeature.VISION, - ModelFeature.MULTI_TOOL_CALL, - ModelFeature.STREAM_TOOL_CALL, - ], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.01, - output=0.03, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-4-vision-preview", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - features=[ModelFeature.VISION], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.CHAT.value, - ModelPropertyKey.CONTEXT_SIZE: 128000, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ParameterRule( - name="seed", - label=I18nObject(zh_Hans="种子", en_US="Seed"), - type="int", - help=AZURE_DEFAULT_PARAM_SEED_HELP, - required=False, - precision=2, - min=0, - max=1, - ), - ParameterRule( - name="response_format", - label=I18nObject(zh_Hans="回复格式", en_US="response_format"), - type="string", - help=I18nObject( - zh_Hans="指定模型必须输出的格式", en_US="specifying the format that the model must output" - ), - required=False, - options=["text", "json_object"], - ), - ], - pricing=PriceConfig( - input=0.01, - output=0.03, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="gpt-35-turbo-instruct", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.COMPLETION.value, - ModelPropertyKey.CONTEXT_SIZE: 4096, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ], - pricing=PriceConfig( - input=0.0015, - output=0.002, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="text-davinci-003", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject( - en_US="fake-deployment-name-label", - ), - model_type=ModelType.LLM, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: LLMMode.COMPLETION.value, - ModelPropertyKey.CONTEXT_SIZE: 4096, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TEMPERATURE], - ), - ParameterRule( - name="top_p", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.TOP_P], - ), - ParameterRule( - name="presence_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.PRESENCE_PENALTY], - ), - ParameterRule( - name="frequency_penalty", - **PARAMETER_RULE_TEMPLATE[DefaultParameterName.FREQUENCY_PENALTY], - ), - _get_max_tokens(default=512, min_val=1, max_val=4096), - ], - pricing=PriceConfig( - input=0.02, - output=0.02, - unit=0.001, - currency="USD", - ), - ), - ), -] - -EMBEDDING_BASE_MODELS = [ - AzureBaseModel( - base_model_name="text-embedding-ada-002", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TEXT_EMBEDDING, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: 8097, - ModelPropertyKey.MAX_CHUNKS: 32, - }, - pricing=PriceConfig( - input=0.0001, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="text-embedding-3-small", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TEXT_EMBEDDING, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: 8191, - ModelPropertyKey.MAX_CHUNKS: 32, - }, - pricing=PriceConfig( - input=0.00002, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="text-embedding-3-large", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TEXT_EMBEDDING, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: 8191, - ModelPropertyKey.MAX_CHUNKS: 32, - }, - pricing=PriceConfig( - input=0.00013, - unit=0.001, - currency="USD", - ), - ), - ), -] -SPEECH2TEXT_BASE_MODELS = [ - AzureBaseModel( - base_model_name="whisper-1", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.SPEECH2TEXT, - model_properties={ - ModelPropertyKey.FILE_UPLOAD_LIMIT: 25, - ModelPropertyKey.SUPPORTED_FILE_EXTENSIONS: "flac,mp3,mp4,mpeg,mpga,m4a,ogg,wav,webm", - }, - ), - ) -] -TTS_BASE_MODELS = [ - AzureBaseModel( - base_model_name="tts-1", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TTS, - model_properties={ - ModelPropertyKey.DEFAULT_VOICE: "alloy", - ModelPropertyKey.VOICES: [ - { - "mode": "alloy", - "name": "Alloy", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "echo", - "name": "Echo", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "fable", - "name": "Fable", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "onyx", - "name": "Onyx", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "nova", - "name": "Nova", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "shimmer", - "name": "Shimmer", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - ], - ModelPropertyKey.WORD_LIMIT: 120, - ModelPropertyKey.AUDIO_TYPE: "mp3", - ModelPropertyKey.MAX_WORKERS: 5, - }, - pricing=PriceConfig( - input=0.015, - unit=0.001, - currency="USD", - ), - ), - ), - AzureBaseModel( - base_model_name="tts-1-hd", - entity=AIModelEntity( - model="fake-deployment-name", - label=I18nObject(en_US="fake-deployment-name-label"), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TTS, - model_properties={ - ModelPropertyKey.DEFAULT_VOICE: "alloy", - ModelPropertyKey.VOICES: [ - { - "mode": "alloy", - "name": "Alloy", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "echo", - "name": "Echo", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "fable", - "name": "Fable", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "onyx", - "name": "Onyx", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "nova", - "name": "Nova", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - { - "mode": "shimmer", - "name": "Shimmer", - "language": ["zh-Hans", "en-US", "de-DE", "fr-FR", "es-ES", "it-IT", "th-TH", "id-ID"], - }, - ], - ModelPropertyKey.WORD_LIMIT: 120, - ModelPropertyKey.AUDIO_TYPE: "mp3", - ModelPropertyKey.MAX_WORKERS: 5, - }, - pricing=PriceConfig( - input=0.03, - unit=0.001, - currency="USD", - ), - ), - ), -] diff --git a/api/core/model_runtime/model_providers/azure_openai/azure_openai.py b/api/core/model_runtime/model_providers/azure_openai/azure_openai.py deleted file mode 100644 index 2e3c6aab05..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/azure_openai.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class AzureOpenAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/azure_openai/azure_openai.yaml b/api/core/model_runtime/model_providers/azure_openai/azure_openai.yaml deleted file mode 100644 index 867f9fec42..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/azure_openai.yaml +++ /dev/null @@ -1,227 +0,0 @@ -provider: azure_openai -label: - en_US: Azure OpenAI Service Model -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.png -background: "#E3F0FF" -help: - title: - en_US: Get your API key from Azure - zh_Hans: 从 Azure 获取 API Key - url: - en_US: https://azure.microsoft.com/en-us/products/ai-services/openai-service -supported_model_types: - - llm - - text-embedding - - speech2text - - tts -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Deployment Name - zh_Hans: 部署名称 - placeholder: - en_US: Enter your Deployment Name here, matching the Azure deployment name. - zh_Hans: 在此输入您的部署名称,与 Azure 部署名称匹配。 - credential_form_schemas: - - variable: openai_api_base - label: - en_US: API Endpoint URL - zh_Hans: API 域名 - type: text-input - required: true - placeholder: - zh_Hans: '在此输入您的 API 域名,如:https://example.com/xxx' - en_US: 'Enter your API Endpoint, eg: https://example.com/xxx' - - variable: openai_api_key - label: - en_US: API Key - zh_Hans: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API key here - - variable: openai_api_version - label: - zh_Hans: API 版本 - en_US: API Version - type: select - required: true - options: - - label: - en_US: 2024-08-01-preview - value: 2024-08-01-preview - - label: - en_US: 2024-07-01-preview - value: 2024-07-01-preview - - label: - en_US: 2024-05-01-preview - value: 2024-05-01-preview - - label: - en_US: 2024-04-01-preview - value: 2024-04-01-preview - - label: - en_US: 2024-03-01-preview - value: 2024-03-01-preview - - label: - en_US: 2024-02-15-preview - value: 2024-02-15-preview - - label: - en_US: 2023-12-01-preview - value: 2023-12-01-preview - - label: - en_US: '2024-02-01' - value: '2024-02-01' - - label: - en_US: '2024-06-01' - value: '2024-06-01' - placeholder: - zh_Hans: 在此选择您的 API 版本 - en_US: Select your API Version here - - variable: base_model_name - label: - en_US: Base Model - zh_Hans: 基础模型 - type: select - required: true - options: - - label: - en_US: gpt-35-turbo - value: gpt-35-turbo - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-35-turbo-0125 - value: gpt-35-turbo-0125 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-35-turbo-16k - value: gpt-35-turbo-16k - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4 - value: gpt-4 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-32k - value: gpt-4-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4o-mini - value: gpt-4o-mini - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4o-mini-2024-07-18 - value: gpt-4o-mini-2024-07-18 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4o - value: gpt-4o - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4o-2024-05-13 - value: gpt-4o-2024-05-13 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4o-2024-08-06 - value: gpt-4o-2024-08-06 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-turbo - value: gpt-4-turbo - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-turbo-2024-04-09 - value: gpt-4-turbo-2024-04-09 - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-0125-preview - value: gpt-4-0125-preview - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-1106-preview - value: gpt-4-1106-preview - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-4-vision-preview - value: gpt-4-vision-preview - show_on: - - variable: __model_type - value: llm - - label: - en_US: gpt-35-turbo-instruct - value: gpt-35-turbo-instruct - show_on: - - variable: __model_type - value: llm - - label: - en_US: text-embedding-ada-002 - value: text-embedding-ada-002 - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: text-embedding-3-small - value: text-embedding-3-small - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: text-embedding-3-large - value: text-embedding-3-large - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: whisper-1 - value: whisper-1 - show_on: - - variable: __model_type - value: speech2text - - label: - en_US: tts-1 - value: tts-1 - show_on: - - variable: __model_type - value: tts - - label: - en_US: tts-1-hd - value: tts-1-hd - show_on: - - variable: __model_type - value: tts - placeholder: - zh_Hans: 在此输入您的模型版本 - en_US: Enter your model version diff --git a/api/core/model_runtime/model_providers/azure_openai/llm/__init__.py b/api/core/model_runtime/model_providers/azure_openai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_openai/llm/llm.py b/api/core/model_runtime/model_providers/azure_openai/llm/llm.py deleted file mode 100644 index f0033ea051..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/llm/llm.py +++ /dev/null @@ -1,665 +0,0 @@ -import copy -import json -import logging -from collections.abc import Generator, Sequence -from typing import Optional, Union, cast - -import tiktoken -from openai import AzureOpenAI, Stream -from openai.types import Completion -from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall - -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageFunction, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.azure_openai._common import _CommonAzureOpenAI -from core.model_runtime.model_providers.azure_openai._constant import LLM_BASE_MODELS -from core.model_runtime.utils import helper - -logger = logging.getLogger(__name__) - - -class AzureOpenAILargeLanguageModel(_CommonAzureOpenAI, LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - base_model_name = credentials.get("base_model_name") - if not base_model_name: - raise ValueError("Base Model Name is required") - ai_model_entity = self._get_ai_model_entity(base_model_name=base_model_name, model=model) - - if ai_model_entity and ai_model_entity.entity.model_properties.get(ModelPropertyKey.MODE) == LLMMode.CHAT.value: - # chat model - return self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - else: - # text completion model - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - base_model_name = credentials.get("base_model_name") - if not base_model_name: - raise ValueError("Base Model Name is required") - model_entity = self._get_ai_model_entity(base_model_name=base_model_name, model=model) - if not model_entity: - raise ValueError(f"Base Model Name {base_model_name} is invalid") - model_mode = model_entity.entity.model_properties.get(ModelPropertyKey.MODE) - - if model_mode == LLMMode.CHAT.value: - # chat model - return self._num_tokens_from_messages(credentials, prompt_messages, tools) - else: - # text completion model, do not support tool calling - content = prompt_messages[0].content - assert isinstance(content, str) - return self._num_tokens_from_string(credentials, content) - - def validate_credentials(self, model: str, credentials: dict) -> None: - if "openai_api_base" not in credentials: - raise CredentialsValidateFailedError("Azure OpenAI API Base Endpoint is required") - - if "openai_api_key" not in credentials: - raise CredentialsValidateFailedError("Azure OpenAI API key is required") - - if "base_model_name" not in credentials: - raise CredentialsValidateFailedError("Base Model Name is required") - - base_model_name = credentials.get("base_model_name") - if not base_model_name: - raise CredentialsValidateFailedError("Base Model Name is required") - ai_model_entity = self._get_ai_model_entity(base_model_name=base_model_name, model=model) - - if not ai_model_entity: - raise CredentialsValidateFailedError(f'Base Model Name {credentials["base_model_name"]} is invalid') - - try: - client = AzureOpenAI(**self._to_credential_kwargs(credentials)) - - if ai_model_entity.entity.model_properties.get(ModelPropertyKey.MODE) == LLMMode.CHAT.value: - # chat model - client.chat.completions.create( - messages=[{"role": "user", "content": "ping"}], - model=model, - temperature=0, - max_tokens=20, - stream=False, - ) - else: - # text completion model - client.completions.create( - prompt="ping", - model=model, - temperature=0, - max_tokens=20, - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: - base_model_name = credentials.get("base_model_name") - if not base_model_name: - raise ValueError("Base Model Name is required") - ai_model_entity = self._get_ai_model_entity(base_model_name=base_model_name, model=model) - return ai_model_entity.entity if ai_model_entity else None - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - client = AzureOpenAI(**self._to_credential_kwargs(credentials)) - - extra_model_kwargs = {} - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - # text completion model - response = client.completions.create( - prompt=prompt_messages[0].content, model=model, stream=stream, **model_parameters, **extra_model_kwargs - ) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: Completion, prompt_messages: list[PromptMessage] - ): - assistant_text = response.choices[0].text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_text) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - content = prompt_messages[0].content - assert isinstance(content, str) - prompt_tokens = self._num_tokens_from_string(credentials, content) - completion_tokens = self._num_tokens_from_string(credentials, assistant_text) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=response.model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return result - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: Stream[Completion], prompt_messages: list[PromptMessage] - ) -> Generator: - full_text = "" - for chunk in response: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.text is None or delta.text == ""): - continue - - # transform assistant message to prompt message - text = delta.text or "" - assistant_prompt_message = AssistantPromptMessage(content=text) - - full_text += text - - if delta.finish_reason is not None: - # calculate num tokens - if chunk.usage: - # transform usage - prompt_tokens = chunk.usage.prompt_tokens - completion_tokens = chunk.usage.completion_tokens - else: - # calculate num tokens - content = prompt_messages[0].content - assert isinstance(content, str) - prompt_tokens = self._num_tokens_from_string(credentials, content) - completion_tokens = self._num_tokens_from_string(credentials, full_text) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - client = AzureOpenAI(**self._to_credential_kwargs(credentials)) - - response_format = model_parameters.get("response_format") - if response_format: - if response_format == "json_schema": - json_schema = model_parameters.get("json_schema") - if not json_schema: - raise ValueError("Must define JSON Schema when the response format is json_schema") - try: - schema = json.loads(json_schema) - except: - raise ValueError(f"not correct json_schema format: {json_schema}") - model_parameters.pop("json_schema") - model_parameters["response_format"] = {"type": "json_schema", "json_schema": schema} - else: - model_parameters["response_format"] = {"type": response_format} - - extra_model_kwargs = {} - - if tools: - extra_model_kwargs["tools"] = [helper.dump_model(PromptMessageFunction(function=tool)) for tool in tools] - # extra_model_kwargs['functions'] = [{ - # "name": tool.name, - # "description": tool.description, - # "parameters": tool.parameters - # } for tool in tools] - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - # chat model - messages = [self._convert_prompt_message_to_dict(m) for m in prompt_messages] - response = client.chat.completions.create( - messages=messages, - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages, tools) - - return self._handle_chat_generate_response(model, credentials, response, prompt_messages, tools) - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: ChatCompletion, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ): - assistant_message = response.choices[0].message - assistant_message_tool_calls = assistant_message.tool_calls - - # extract tool calls from response - tool_calls = [] - self._update_tool_calls(tool_calls=tool_calls, tool_calls_response=assistant_message_tool_calls) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(credentials, prompt_messages, tools) - completion_tokens = self._num_tokens_from_messages(credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=response.model or model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return result - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Stream[ChatCompletionChunk], - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ): - index = 0 - full_assistant_content = "" - real_model = model - system_fingerprint = None - completion = "" - tool_calls = [] - for chunk in response: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - # NOTE: For fix https://github.com/langgenius/dify/issues/5790 - if delta.delta is None: - continue - - # extract tool calls from response - self._update_tool_calls(tool_calls=tool_calls, tool_calls_response=delta.delta.tool_calls) - - # Handling exceptions when content filters' streaming mode is set to asynchronous modified filter - if delta.finish_reason is None and not delta.delta.content: - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.delta.content or "", tool_calls=tool_calls) - - full_assistant_content += delta.delta.content or "" - - real_model = chunk.model - system_fingerprint = chunk.system_fingerprint - completion += delta.delta.content or "" - - yield LLMResultChunk( - model=real_model, - prompt_messages=prompt_messages, - system_fingerprint=system_fingerprint, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - ), - ) - - index += 1 - - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(credentials, prompt_messages, tools) - - full_assistant_prompt_message = AssistantPromptMessage(content=completion) - completion_tokens = self._num_tokens_from_messages(credentials, [full_assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=real_model, - prompt_messages=prompt_messages, - system_fingerprint=system_fingerprint, - delta=LLMResultChunkDelta( - index=index, message=AssistantPromptMessage(content=""), finish_reason="stop", usage=usage - ), - ) - - @staticmethod - def _update_tool_calls( - tool_calls: list[AssistantPromptMessage.ToolCall], - tool_calls_response: Optional[Sequence[ChatCompletionMessageToolCall | ChoiceDeltaToolCall]], - ) -> None: - if tool_calls_response: - for response_tool_call in tool_calls_response: - if isinstance(response_tool_call, ChatCompletionMessageToolCall): - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - elif isinstance(response_tool_call, ChoiceDeltaToolCall): - index = response_tool_call.index - if index < len(tool_calls): - tool_calls[index].id = response_tool_call.id or tool_calls[index].id - tool_calls[index].type = response_tool_call.type or tool_calls[index].type - if response_tool_call.function: - tool_calls[index].function.name = ( - response_tool_call.function.name or tool_calls[index].function.name - ) - tool_calls[index].function.arguments += response_tool_call.function.arguments or "" - else: - assert response_tool_call.id is not None - assert response_tool_call.type is not None - assert response_tool_call.function is not None - assert response_tool_call.function.name is not None - assert response_tool_call.function.arguments is not None - - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - - @staticmethod - def _convert_prompt_message_to_dict(message: PromptMessage): - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - assert message.content is not None - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - # message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - message_dict["tool_calls"] = [helper.dump_model(tool_call) for tool_call in message.tool_calls] - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = { - "role": "tool", - "name": message.name, - "content": message.content, - "tool_call_id": message.tool_call_id, - } - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _num_tokens_from_string( - self, credentials: dict, text: str, tools: Optional[list[PromptMessageTool]] = None - ) -> int: - try: - encoding = tiktoken.encoding_for_model(credentials["base_model_name"]) - except KeyError: - encoding = tiktoken.get_encoding("cl100k_base") - - num_tokens = len(encoding.encode(text)) - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - def _num_tokens_from_messages( - self, credentials: dict, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. - - Official documentation: https://github.com/openai/openai-cookbook/blob/ - main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - model = credentials["base_model_name"] - try: - encoding = tiktoken.encoding_for_model(model) - except KeyError: - logger.warning("Warning: model not found. Using cl100k_base encoding.") - model = "cl100k_base" - encoding = tiktoken.get_encoding(model) - - if model.startswith("gpt-35-turbo-0301"): - # every message follows {role/name}\n{content}\n - tokens_per_message = 4 - # if there's a name, the role is omitted - tokens_per_name = -1 - elif model.startswith("gpt-35-turbo") or model.startswith("gpt-4"): - tokens_per_message = 3 - tokens_per_name = 1 - else: - raise NotImplementedError( - f"get_num_tokens_from_messages() is not presently implemented " - f"for model {model}." - "See https://github.com/openai/openai-python/blob/main/chatml.md for " - "information on how messages are converted to tokens." - ) - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - assert isinstance(tool_call, dict) - for t_key, t_value in tool_call.items(): - num_tokens += len(encoding.encode(t_key)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(encoding.encode(f_key)) - num_tokens += len(encoding.encode(f_value)) - else: - num_tokens += len(encoding.encode(t_key)) - num_tokens += len(encoding.encode(t_value)) - else: - num_tokens += len(encoding.encode(str(value))) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - @staticmethod - def _num_tokens_for_tools(encoding: tiktoken.Encoding, tools: list[PromptMessageTool]) -> int: - num_tokens = 0 - for tool in tools: - num_tokens += len(encoding.encode("type")) - num_tokens += len(encoding.encode("function")) - - # calculate num tokens for function object - num_tokens += len(encoding.encode("name")) - num_tokens += len(encoding.encode(tool.name)) - num_tokens += len(encoding.encode("description")) - num_tokens += len(encoding.encode(tool.description)) - parameters = tool.parameters - num_tokens += len(encoding.encode("parameters")) - if "title" in parameters: - num_tokens += len(encoding.encode("title")) - num_tokens += len(encoding.encode(parameters["title"])) - num_tokens += len(encoding.encode("type")) - num_tokens += len(encoding.encode(parameters["type"])) - if "properties" in parameters: - num_tokens += len(encoding.encode("properties")) - for key, value in parameters["properties"].items(): - num_tokens += len(encoding.encode(key)) - for field_key, field_value in value.items(): - num_tokens += len(encoding.encode(field_key)) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += len(encoding.encode(enum_field)) - else: - num_tokens += len(encoding.encode(field_key)) - num_tokens += len(encoding.encode(str(field_value))) - if "required" in parameters: - num_tokens += len(encoding.encode("required")) - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += len(encoding.encode(required_field)) - - return num_tokens - - @staticmethod - def _get_ai_model_entity(base_model_name: str, model: str): - for ai_model_entity in LLM_BASE_MODELS: - if ai_model_entity.base_model_name == base_model_name: - ai_model_entity_copy = copy.deepcopy(ai_model_entity) - ai_model_entity_copy.entity.model = model - ai_model_entity_copy.entity.label.en_US = model - ai_model_entity_copy.entity.label.zh_Hans = model - return ai_model_entity_copy diff --git a/api/core/model_runtime/model_providers/azure_openai/speech2text/__init__.py b/api/core/model_runtime/model_providers/azure_openai/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py b/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py deleted file mode 100644 index a2b14cf3db..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py +++ /dev/null @@ -1,79 +0,0 @@ -import copy -from typing import IO, Optional - -from openai import AzureOpenAI - -from core.model_runtime.entities.model_entities import AIModelEntity -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from core.model_runtime.model_providers.azure_openai._common import _CommonAzureOpenAI -from core.model_runtime.model_providers.azure_openai._constant import SPEECH2TEXT_BASE_MODELS, AzureBaseModel - - -class AzureOpenAISpeech2TextModel(_CommonAzureOpenAI, Speech2TextModel): - """ - Model class for OpenAI Speech to text model. - """ - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - return self._speech2text_invoke(model, credentials, file) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self._speech2text_invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _speech2text_invoke(self, model: str, credentials: dict, file: IO[bytes]) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :return: text for given audio file - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # init model client - client = AzureOpenAI(**credentials_kwargs) - - response = client.audio.transcriptions.create(model=model, file=file) - - return response.text - - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: - ai_model_entity = self._get_ai_model_entity(credentials["base_model_name"], model) - return ai_model_entity.entity - - @staticmethod - def _get_ai_model_entity(base_model_name: str, model: str) -> AzureBaseModel: - for ai_model_entity in SPEECH2TEXT_BASE_MODELS: - if ai_model_entity.base_model_name == base_model_name: - ai_model_entity_copy = copy.deepcopy(ai_model_entity) - ai_model_entity_copy.entity.model = model - ai_model_entity_copy.entity.label.en_US = model - ai_model_entity_copy.entity.label.zh_Hans = model - return ai_model_entity_copy - - return None diff --git a/api/core/model_runtime/model_providers/azure_openai/text_embedding/__init__.py b/api/core/model_runtime/model_providers/azure_openai/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_openai/tts/__init__.py b/api/core/model_runtime/model_providers/azure_openai/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/azure_openai/tts/tts.py b/api/core/model_runtime/model_providers/azure_openai/tts/tts.py deleted file mode 100644 index af178703a0..0000000000 --- a/api/core/model_runtime/model_providers/azure_openai/tts/tts.py +++ /dev/null @@ -1,128 +0,0 @@ -import concurrent.futures -import copy -from typing import Optional - -from openai import AzureOpenAI - -from core.model_runtime.entities.model_entities import AIModelEntity -from core.model_runtime.errors.invoke import InvokeBadRequestError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.tts_model import TTSModel -from core.model_runtime.model_providers.azure_openai._common import _CommonAzureOpenAI -from core.model_runtime.model_providers.azure_openai._constant import TTS_BASE_MODELS, AzureBaseModel - - -class AzureOpenAIText2SpeechModel(_CommonAzureOpenAI, TTSModel): - """ - Model class for OpenAI Speech to text model. - """ - - def _invoke( - self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ) -> any: - """ - _invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :param user: unique user id - :return: text translated to audio file - """ - if not voice or voice not in [ - d["value"] for d in self.get_tts_model_voices(model=model, credentials=credentials) - ]: - voice = self._get_model_default_voice(model, credentials) - - return self._tts_invoke_streaming(model=model, credentials=credentials, content_text=content_text, voice=voice) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - validate credentials text2speech model - - :param model: model name - :param credentials: model credentials - :return: text translated to audio file - """ - try: - self._tts_invoke_streaming( - model=model, - credentials=credentials, - content_text="Hello Dify!", - voice=self._get_model_default_voice(model, credentials), - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: - """ - _tts_invoke_streaming text2speech model - :param model: model name - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :return: text translated to audio file - """ - try: - # doc: https://platform.openai.com/docs/guides/text-to-speech - credentials_kwargs = self._to_credential_kwargs(credentials) - client = AzureOpenAI(**credentials_kwargs) - # max length is 4096 characters, there is 3500 limit for each request - max_length = 3500 - if len(content_text) > max_length: - sentences = self._split_text_into_sentences(content_text, max_length=max_length) - executor = concurrent.futures.ThreadPoolExecutor(max_workers=min(3, len(sentences))) - futures = [ - executor.submit( - client.audio.speech.with_streaming_response.create, - model=model, - response_format="mp3", - input=sentences[i], - voice=voice, - ) - for i in range(len(sentences)) - ] - for future in futures: - yield from future.result().__enter__().iter_bytes(1024) # noqa:PLC2801 - - else: - response = client.audio.speech.with_streaming_response.create( - model=model, voice=voice, response_format="mp3", input=content_text.strip() - ) - - yield from response.__enter__().iter_bytes(1024) # noqa:PLC2801 - except Exception as ex: - raise InvokeBadRequestError(str(ex)) - - def _process_sentence(self, sentence: str, model: str, voice, credentials: dict): - """ - _tts_invoke openai text2speech model api - - :param model: model name - :param credentials: model credentials - :param voice: model timbre - :param sentence: text content to be translated - :return: text translated to audio file - """ - credentials_kwargs = self._to_credential_kwargs(credentials) - client = AzureOpenAI(**credentials_kwargs) - response = client.audio.speech.create(model=model, voice=voice, input=sentence.strip()) - if isinstance(response.read(), bytes): - return response.read() - - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: - ai_model_entity = self._get_ai_model_entity(credentials["base_model_name"], model) - return ai_model_entity.entity - - @staticmethod - def _get_ai_model_entity(base_model_name: str, model: str) -> AzureBaseModel | None: - for ai_model_entity in TTS_BASE_MODELS: - if ai_model_entity.base_model_name == base_model_name: - ai_model_entity_copy = copy.deepcopy(ai_model_entity) - ai_model_entity_copy.entity.model = model - ai_model_entity_copy.entity.label.en_US = model - ai_model_entity_copy.entity.label.zh_Hans = model - return ai_model_entity_copy - return None diff --git a/api/core/model_runtime/model_providers/baichuan/__init__.py b/api/core/model_runtime/model_providers/baichuan/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/baichuan/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/baichuan/_assets/icon_l_en.svg deleted file mode 100644 index 7ff6b5a67a..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/_assets/icon_l_en.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/baichuan/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/baichuan/_assets/icon_s_en.svg deleted file mode 100644 index 4ddcd26726..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/_assets/icon_s_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/baichuan/baichuan.py b/api/core/model_runtime/model_providers/baichuan/baichuan.py deleted file mode 100644 index 626fc811cf..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/baichuan.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class BaichuanProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `baichuan2-turbo` model for validate, - model_instance.validate_credentials(model="baichuan2-turbo", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/baichuan/baichuan.yaml b/api/core/model_runtime/model_providers/baichuan/baichuan.yaml deleted file mode 100644 index 81e6e36215..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/baichuan.yaml +++ /dev/null @@ -1,29 +0,0 @@ -provider: baichuan -label: - en_US: Baichuan -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FFF6F2" -help: - title: - en_US: Get your API Key from BAICHUAN AI - zh_Hans: 从百川智能获取您的 API Key - url: - en_US: https://www.baichuan-ai.com -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/baichuan/llm/__init__.py b/api/core/model_runtime/model_providers/baichuan/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-53b.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-53b.yaml deleted file mode 100644 index 8360dd5faf..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-53b.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: baichuan2-53b -label: - en_US: Baichuan2-53B -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 1000 - min: 1 - max: 4000 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - default: 1 - min: 1 - max: 2 - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo-192k.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo-192k.yaml deleted file mode 100644 index 0ce0265cfe..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo-192k.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: baichuan2-turbo-192k -label: - en_US: Baichuan2-Turbo-192K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 192000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 8000 - min: 1 - max: 192000 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - default: 1 - min: 1 - max: 2 - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo.yaml deleted file mode 100644 index ccb4ee8b92..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan2-turbo.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: baichuan2-turbo -label: - en_US: Baichuan2-Turbo -model_type: llm -features: - - agent-thought - - multi-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.3 - - name: top_p - use_template: top_p - default: 0.85 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - min: 0 - max: 20 - default: 5 - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - default: 2048 - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo-128k.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo-128k.yaml deleted file mode 100644 index d9cd086e82..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo-128k.yaml +++ /dev/null @@ -1,53 +0,0 @@ -model: baichuan3-turbo-128k -label: - en_US: Baichuan3-Turbo-128k -model_type: llm -features: - - agent-thought - - multi-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.3 - - name: top_p - use_template: top_p - default: 0.85 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - min: 0 - max: 20 - default: 5 - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - default: 2048 - - name: res_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo.yaml deleted file mode 100644 index 58f9b39a43..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan3-turbo.yaml +++ /dev/null @@ -1,53 +0,0 @@ -model: baichuan3-turbo -label: - en_US: Baichuan3-Turbo -model_type: llm -features: - - agent-thought - - multi-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.3 - - name: top_p - use_template: top_p - default: 0.85 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - min: 0 - max: 20 - default: 5 - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - default: 2048 - - name: res_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan4.yaml b/api/core/model_runtime/model_providers/baichuan/llm/baichuan4.yaml deleted file mode 100644 index 6a1135e165..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan4.yaml +++ /dev/null @@ -1,53 +0,0 @@ -model: baichuan4 -label: - en_US: Baichuan4 -model_type: llm -features: - - agent-thought - - multi-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.3 - - name: top_p - use_template: top_p - default: 0.85 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - min: 0 - max: 20 - default: 5 - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - default: 2048 - - name: res_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - name: with_search_enhance - label: - zh_Hans: 搜索增强 - en_US: Search Enhance - type: boolean - help: - zh_Hans: 允许模型自行进行外部搜索,以增强生成结果。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_tokenizer.py b/api/core/model_runtime/model_providers/baichuan/llm/baichuan_tokenizer.py deleted file mode 100644 index a7ca28d49d..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_tokenizer.py +++ /dev/null @@ -1,21 +0,0 @@ -import re - - -class BaichuanTokenizer: - @classmethod - def count_chinese_characters(cls, text: str) -> int: - return len(re.findall(r"[\u4e00-\u9fa5]", text)) - - @classmethod - def count_english_vocabularies(cls, text: str) -> int: - # remove all non-alphanumeric characters but keep spaces and other symbols like !, ., etc. - text = re.sub(r"[^a-zA-Z0-9\s]", "", text) - # count the number of words not characters - return len(text.split()) - - @classmethod - def _get_num_tokens(cls, text: str) -> int: - # tokens = number of Chinese characters + number of English words * 1.3 - # (for estimation only, subject to actual return) - # https://platform.baichuan-ai.com/docs/text-Embedding - return int(cls.count_chinese_characters(text) + cls.count_english_vocabularies(text) * 1.3) diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo.py b/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo.py deleted file mode 100644 index d5fda73009..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo.py +++ /dev/null @@ -1,144 +0,0 @@ -import json -from collections.abc import Iterator -from typing import Any, Optional, Union - -from requests import post - -from core.model_runtime.entities.message_entities import PromptMessageTool -from core.model_runtime.model_providers.baichuan.llm.baichuan_turbo_errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) - - -class BaichuanModel: - api_key: str - - def __init__(self, api_key: str) -> None: - self.api_key = api_key - - @property - def _model_mapping(self) -> dict: - return { - "baichuan2-turbo": "Baichuan2-Turbo", - "baichuan3-turbo": "Baichuan3-Turbo", - "baichuan3-turbo-128k": "Baichuan3-Turbo-128k", - "baichuan4": "Baichuan4", - } - - @property - def request_headers(self) -> dict[str, Any]: - return { - "Content-Type": "application/json", - "Authorization": "Bearer " + self.api_key, - } - - def _build_parameters( - self, - model: str, - stream: bool, - messages: list[dict], - parameters: dict[str, Any], - tools: Optional[list[PromptMessageTool]] = None, - ) -> dict[str, Any]: - if model in self._model_mapping: - # the LargeLanguageModel._code_block_mode_wrapper() method will remove the response_format of parameters. - # we need to rename it to res_format to get its value - if parameters.get("res_format") == "json_object": - parameters["response_format"] = {"type": "json_object"} - - if tools or parameters.get("with_search_enhance") is True: - parameters["tools"] = [] - - # with_search_enhance is deprecated, use web_search instead - if parameters.get("with_search_enhance") is True: - parameters["tools"].append( - { - "type": "web_search", - "web_search": {"enable": True}, - } - ) - if tools: - for tool in tools: - parameters["tools"].append( - { - "type": "function", - "function": { - "name": tool.name, - "description": tool.description, - "parameters": tool.parameters, - }, - } - ) - - # turbo api accepts flat parameters - return { - "model": self._model_mapping.get(model), - "stream": stream, - "messages": messages, - **parameters, - } - else: - raise BadRequestError(f"Unknown model: {model}") - - def generate( - self, - model: str, - stream: bool, - messages: list[dict], - parameters: dict[str, Any], - timeout: int, - tools: Optional[list[PromptMessageTool]] = None, - ) -> Union[Iterator, dict]: - if model in self._model_mapping: - api_base = "https://api.baichuan-ai.com/v1/chat/completions" - else: - raise BadRequestError(f"Unknown model: {model}") - - data = self._build_parameters(model, stream, messages, parameters, tools) - - try: - response = post( - url=api_base, - headers=self.request_headers, - data=json.dumps(data), - timeout=timeout, - stream=stream, - ) - except Exception as e: - raise InternalServerError(f"Failed to invoke model: {e}") - - if response.status_code != 200: - try: - resp = response.json() - # try to parse error message - err = resp["error"]["type"] - msg = resp["error"]["message"] - except Exception as e: - raise InternalServerError(f"Failed to convert response to json: {e} with text: {response.text}") - - if err == "invalid_api_key": - raise InvalidAPIKeyError(msg) - elif err == "insufficient_quota": - raise InsufficientAccountBalanceError(msg) - elif err == "invalid_authentication": - raise InvalidAuthenticationError(msg) - elif err == "invalid_request_error": - raise BadRequestError(msg) - elif "rate" in err: - raise RateLimitReachedError(msg) - elif "internal" in err: - raise InternalServerError(msg) - elif err == "api_key_empty": - raise InvalidAPIKeyError(msg) - else: - raise InternalServerError(f"Unknown error: {err} with message: {msg}") - - if stream: - return response.iter_lines() - else: - return response.json() diff --git a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo_errors.py b/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo_errors.py deleted file mode 100644 index 309b5cf413..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/baichuan_turbo_errors.py +++ /dev/null @@ -1,22 +0,0 @@ -class InvalidAuthenticationError(Exception): - pass - - -class InvalidAPIKeyError(Exception): - pass - - -class RateLimitReachedError(Exception): - pass - - -class InsufficientAccountBalanceError(Exception): - pass - - -class InternalServerError(Exception): - pass - - -class BadRequestError(Exception): - pass diff --git a/api/core/model_runtime/model_providers/baichuan/llm/llm.py b/api/core/model_runtime/model_providers/baichuan/llm/llm.py deleted file mode 100644 index 91a14bf100..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/llm/llm.py +++ /dev/null @@ -1,296 +0,0 @@ -import json -from collections.abc import Generator, Iterator -from typing import cast - -from core.model_runtime.entities.llm_entities import ( - LLMResult, - LLMResultChunk, - LLMResultChunkDelta, -) -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.baichuan.llm.baichuan_tokenizer import BaichuanTokenizer -from core.model_runtime.model_providers.baichuan.llm.baichuan_turbo import BaichuanModel -from core.model_runtime.model_providers.baichuan.llm.baichuan_turbo_errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) - - -class BaichuanLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stream=stream, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - return self._num_tokens_from_messages(prompt_messages) - - def _num_tokens_from_messages( - self, - messages: list[PromptMessage], - ) -> int: - """Calculate num tokens for baichuan model""" - - def tokens(text: str): - return BaichuanTokenizer._get_num_tokens(text) - - tokens_per_message = 3 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - num_tokens += tokens(str(value)) - num_tokens += 3 - - return num_tokens - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for Baichuan - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - raise ValueError("User message content must be str") - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - message_dict["tool_calls"] = [tool_call.dict() for tool_call in message.tool_calls] - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = {"role": "tool", "content": message.content, "tool_call_id": message.tool_call_id} - else: - raise ValueError(f"Unknown message type {type(message)}") - - return message_dict - - def validate_credentials(self, model: str, credentials: dict) -> None: - # ping - instance = BaichuanModel(api_key=credentials["api_key"]) - - try: - instance.generate( - model=model, - stream=False, - messages=[{"content": "ping", "role": "user"}], - parameters={ - "max_tokens": 1, - }, - timeout=60, - ) - except Exception as e: - raise CredentialsValidateFailedError(f"Invalid API key: {e}") - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stream: bool = True, - ) -> LLMResult | Generator: - instance = BaichuanModel(api_key=credentials["api_key"]) - messages = [self._convert_prompt_message_to_dict(m) for m in prompt_messages] - - # invoke model - response = instance.generate( - model=model, - stream=stream, - messages=messages, - parameters=model_parameters, - timeout=60, - tools=tools, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, prompt_messages, credentials, response) - - return self._handle_chat_generate_response(model, prompt_messages, credentials, response) - - def _handle_chat_generate_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: dict, - ) -> LLMResult: - choices = response.get("choices", []) - assistant_message = AssistantPromptMessage(content="", tool_calls=[]) - if choices and choices[0]["finish_reason"] == "tool_calls": - for choice in choices: - for tool_call in choice["message"]["tool_calls"]: - tool = AssistantPromptMessage.ToolCall( - id=tool_call.get("id", ""), - type=tool_call.get("type", ""), - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_call.get("function", {}).get("name", ""), - arguments=tool_call.get("function", {}).get("arguments", ""), - ), - ) - assistant_message.tool_calls.append(tool) - else: - for choice in choices: - assistant_message.content += choice["message"]["content"] - assistant_message.role = choice["message"]["role"] - - usage = response.get("usage") - if usage: - # transform usage - prompt_tokens = usage["prompt_tokens"] - completion_tokens = usage["completion_tokens"] - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(prompt_messages) - completion_tokens = self._num_tokens_from_messages([assistant_message]) - - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_message, - usage=usage, - ) - - def _handle_chat_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Iterator, - ) -> Generator: - for line in response: - if not line: - continue - line = line.decode("utf-8") - # remove the first `data: ` prefix - if line.startswith("data:"): - line = line[5:].strip() - try: - data = json.loads(line) - except Exception as e: - if line.strip() == "[DONE]": - return - choices = data.get("choices", []) - - stop_reason = "" - for choice in choices: - if choice.get("finish_reason"): - stop_reason = choice["finish_reason"] - - if len(choice["delta"]["content"]) == 0: - continue - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=choice["delta"]["content"], tool_calls=[]), - finish_reason=stop_reason, - ), - ) - - # if there is usage, the response is the last one, yield it and return - if "usage" in data: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=data["usage"]["prompt_tokens"], - completion_tokens=data["usage"]["completion_tokens"], - ) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content="", tool_calls=[]), - usage=usage, - finish_reason=stop_reason, - ), - ) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [InternalServerError], - InvokeRateLimitError: [RateLimitReachedError], - InvokeAuthorizationError: [ - InvalidAuthenticationError, - InsufficientAccountBalanceError, - InvalidAPIKeyError, - ], - InvokeBadRequestError: [BadRequestError, KeyError], - } diff --git a/api/core/model_runtime/model_providers/baichuan/text_embedding/__init__.py b/api/core/model_runtime/model_providers/baichuan/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/baichuan/text_embedding/baichuan-text-embedding.yaml b/api/core/model_runtime/model_providers/baichuan/text_embedding/baichuan-text-embedding.yaml deleted file mode 100644 index 67e5fcc47c..0000000000 --- a/api/core/model_runtime/model_providers/baichuan/text_embedding/baichuan-text-embedding.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: baichuan-text-embedding -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 16 diff --git a/api/core/model_runtime/model_providers/bedrock/__init__.py b/api/core/model_runtime/model_providers/bedrock/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/bedrock/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/bedrock/_assets/icon_l_en.svg deleted file mode 100644 index 667db50800..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/_assets/icon_l_en.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/bedrock/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/bedrock/_assets/icon_s_en.svg deleted file mode 100644 index 6a0235af92..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/_assets/icon_s_en.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/bedrock/bedrock.py b/api/core/model_runtime/model_providers/bedrock/bedrock.py deleted file mode 100644 index 1cfc1d199c..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/bedrock.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class BedrockProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `amazon.titan-text-lite-v1` model by default for validating credentials - model_for_validation = credentials.get("model_for_validation", "amazon.titan-text-lite-v1") - model_instance.validate_credentials(model=model_for_validation, credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/bedrock/bedrock.yaml b/api/core/model_runtime/model_providers/bedrock/bedrock.yaml deleted file mode 100644 index c540ee23b3..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/bedrock.yaml +++ /dev/null @@ -1,89 +0,0 @@ -provider: bedrock -label: - en_US: AWS -description: - en_US: AWS Bedrock's models. -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FCFDFF" -help: - title: - en_US: Get your Access Key and Secret Access Key from AWS Console - url: - en_US: https://console.aws.amazon.com/ -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: aws_access_key_id - required: false - label: - en_US: Access Key (If not provided, credentials are obtained from the running environment.) - zh_Hans: Access Key - type: secret-input - placeholder: - en_US: Enter your Access Key - zh_Hans: 在此输入您的 Access Key - - variable: aws_secret_access_key - required: false - label: - en_US: Secret Access Key - zh_Hans: Secret Access Key - type: secret-input - placeholder: - en_US: Enter your Secret Access Key - zh_Hans: 在此输入您的 Secret Access Key - - variable: aws_region - required: true - label: - en_US: AWS Region - zh_Hans: AWS 地区 - type: select - default: us-east-1 - options: - - value: us-east-1 - label: - en_US: US East (N. Virginia) - zh_Hans: 美国东部 (弗吉尼亚北部) - - value: us-west-2 - label: - en_US: US West (Oregon) - zh_Hans: 美国西部 (俄勒冈州) - - value: ap-southeast-1 - label: - en_US: Asia Pacific (Singapore) - zh_Hans: 亚太地区 (新加坡) - - value: ap-northeast-1 - label: - en_US: Asia Pacific (Tokyo) - zh_Hans: 亚太地区 (东京) - - value: eu-central-1 - label: - en_US: Europe (Frankfurt) - zh_Hans: 欧洲 (法兰克福) - - value: eu-west-2 - label: - en_US: Eu west London (London) - zh_Hans: 欧洲西部 (伦敦) - - value: us-gov-west-1 - label: - en_US: AWS GovCloud (US-West) - zh_Hans: AWS GovCloud (US-West) - - value: ap-southeast-2 - label: - en_US: Asia Pacific (Sydney) - zh_Hans: 亚太地区 (悉尼) - - variable: model_for_validation - required: false - label: - en_US: Available Model Name - zh_Hans: 可用模型名称 - type: text-input - placeholder: - en_US: A model you have access to (e.g. amazon.titan-text-lite-v1) for validation. - zh_Hans: 为了进行验证,请输入一个您可用的模型名称 (例如:amazon.titan-text-lite-v1) diff --git a/api/core/model_runtime/model_providers/bedrock/llm/__init__.py b/api/core/model_runtime/model_providers/bedrock/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/bedrock/llm/_position.yaml b/api/core/model_runtime/model_providers/bedrock/llm/_position.yaml deleted file mode 100644 index 86c8061dee..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/_position.yaml +++ /dev/null @@ -1,24 +0,0 @@ -- amazon.titan-text-express-v1 -- amazon.titan-text-lite-v1 -- anthropic.claude-instant-v1 -- anthropic.claude-v1 -- anthropic.claude-v2 -- anthropic.claude-v2:1 -- anthropic.claude-3-sonnet-v1:0 -- anthropic.claude-3-haiku-v1:0 -- cohere.command-light-text-v14 -- cohere.command-text-v14 -- cohere.command-r-plus-v1.0 -- cohere.command-r-v1.0 -- meta.llama3-1-8b-instruct-v1:0 -- meta.llama3-1-70b-instruct-v1:0 -- meta.llama3-1-405b-instruct-v1:0 -- meta.llama3-8b-instruct-v1:0 -- meta.llama3-70b-instruct-v1:0 -- meta.llama2-13b-chat-v1 -- meta.llama2-70b-chat-v1 -- mistral.mistral-large-2407-v1:0 -- mistral.mistral-small-2402-v1:0 -- mistral.mistral-large-2402-v1:0 -- mistral.mixtral-8x7b-instruct-v0:1 -- mistral.mistral-7b-instruct-v0:2 diff --git a/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-mid-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-mid-v1.yaml deleted file mode 100644 index 65dad02969..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-mid-v1.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: ai21.j2-mid-v1 -label: - en_US: J2 Mid V1 -model_type: llm -model_properties: - mode: completion - context_size: 8191 -parameter_rules: - - name: temperature - use_template: temperature - - name: topP - use_template: top_p - - name: maxTokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 - - name: count_penalty - label: - en_US: Count Penalty - required: false - type: float - default: 0 - min: 0 - max: 1 - - name: presence_penalty - label: - en_US: Presence Penalty - required: false - type: float - default: 0 - min: 0 - max: 5 - - name: frequency_penalty - label: - en_US: Frequency Penalty - required: false - type: float - default: 0 - min: 0 - max: 500 -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-ultra-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-ultra-v1.yaml deleted file mode 100644 index b72f8064bd..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/ai21.j2-ultra-v1.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: ai21.j2-ultra-v1 -label: - en_US: J2 Ultra V1 -model_type: llm -model_properties: - mode: completion - context_size: 8191 -parameter_rules: - - name: temperature - use_template: temperature - - name: topP - use_template: top_p - - name: maxTokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 - - name: count_penalty - label: - en_US: Count Penalty - required: false - type: float - default: 0 - min: 0 - max: 1 - - name: presence_penalty - label: - en_US: Presence Penalty - required: false - type: float - default: 0 - min: 0 - max: 5 - - name: frequency_penalty - label: - en_US: Frequency Penalty - required: false - type: float - default: 0 - min: 0 - max: 500 -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-express-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-express-v1.yaml deleted file mode 100644 index 543c16d5cd..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-express-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: amazon.titan-text-express-v1 -label: - en_US: Titan Text G1 - Express -model_type: llm -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: topP - use_template: top_p - - name: maxTokenCount - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 8000 -pricing: - input: '0.0008' - output: '0.0016' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-lite-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-lite-v1.yaml deleted file mode 100644 index 2c6151c239..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/amazon.titan-text-lite-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: amazon.titan-text-lite-v1 -label: - en_US: Titan Text G1 - Lite -model_type: llm -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: topP - use_template: top_p - - name: maxTokenCount - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 -pricing: - input: '0.0003' - output: '0.0004' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-haiku-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-haiku-v1.yaml deleted file mode 100644 index c2d5eb6471..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-haiku-v1.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: anthropic.claude-3-haiku-20240307-v1:0 -label: - en_US: Claude 3 Haiku -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.00025' - output: '0.00125' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-opus-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-opus-v1.yaml deleted file mode 100644 index f90fa04266..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-opus-v1.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: anthropic.claude-3-opus-20240229-v1:0 -label: - en_US: Claude 3 Opus -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.015' - output: '0.075' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.5.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.5.yaml deleted file mode 100644 index dad0d6b6b6..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.5.yaml +++ /dev/null @@ -1,60 +0,0 @@ -model: anthropic.claude-3-5-sonnet-20240620-v1:0 -label: - en_US: Claude 3.5 Sonnet -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.yaml deleted file mode 100644 index 962def8011..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v1.yaml +++ /dev/null @@ -1,60 +0,0 @@ -model: anthropic.claude-3-sonnet-20240229-v1:0 -label: - en_US: Claude 3 Sonnet -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-instant-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-instant-v1.yaml deleted file mode 100644 index 8422f079c5..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-instant-v1.yaml +++ /dev/null @@ -1,52 +0,0 @@ -model: anthropic.claude-instant-v1 -label: - en_US: Claude Instant 1 -model_type: llm -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.0008' - output: '0.0024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v1.yaml deleted file mode 100644 index 6a714b1055..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v1.yaml +++ /dev/null @@ -1,53 +0,0 @@ -model: anthropic.claude-v1 -label: - en_US: Claude 1 -model_type: llm -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.1.yaml deleted file mode 100644 index 70294e4ad3..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.1.yaml +++ /dev/null @@ -1,54 +0,0 @@ -model: anthropic.claude-v2:1 -label: - en_US: Claude 2.1 -model_type: llm -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.yaml deleted file mode 100644 index 0a8ea61b6d..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-v2.yaml +++ /dev/null @@ -1,54 +0,0 @@ -model: anthropic.claude-v2 -label: - en_US: Claude 2 -model_type: llm -model_properties: - mode: chat - context_size: 100000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. - - name: response_format - use_template: response_format -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-light-text-v14.yaml b/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-light-text-v14.yaml deleted file mode 100644 index 7450009551..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-light-text-v14.yaml +++ /dev/null @@ -1,35 +0,0 @@ -model: cohere.command-light-text-v14 -label: - en_US: Command Light Text V14 -model_type: llm -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: p - use_template: top_p - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - min: 0 - max: 500 - default: 0 - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 -pricing: - input: '0.0003' - output: '0.0006' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-plus-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-plus-v1.0.yaml deleted file mode 100644 index 3c0bb4e8d5..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-plus-v1.0.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: cohere.command-r-plus-v1:0 -label: - en_US: Command R+ -model_type: llm -features: - - tool-call - #- stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '3' - output: '15' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-v1.0.yaml deleted file mode 100644 index a34f48319f..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-r-v1.0.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: cohere.command-r-v1:0 -label: - en_US: Command R -model_type: llm -features: - - tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '0.5' - output: '1.5' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-text-v14.yaml b/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-text-v14.yaml deleted file mode 100644 index 6aea5be170..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/cohere.command-text-v14.yaml +++ /dev/null @@ -1,32 +0,0 @@ -model: cohere.command-text-v14 -label: - en_US: Command Text V14 -model_type: llm -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: p - use_template: top_p - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 -pricing: - input: '0.0015' - output: '0.0020' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-haiku-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-haiku-v1.yaml deleted file mode 100644 index 24a65ef1bb..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-haiku-v1.yaml +++ /dev/null @@ -1,59 +0,0 @@ -model: eu.anthropic.claude-3-haiku-20240307-v1:0 -label: - en_US: Claude 3 Haiku(EU.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.00025' - output: '0.00125' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.5.yaml b/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.5.yaml deleted file mode 100644 index e3d25c7d8f..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.5.yaml +++ /dev/null @@ -1,58 +0,0 @@ -model: eu.anthropic.claude-3-5-sonnet-20240620-v1:0 -label: - en_US: Claude 3.5 Sonnet(EU.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.yaml deleted file mode 100644 index 9a06a4ad6d..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/eu.anthropic.claude-3-sonnet-v1.yaml +++ /dev/null @@ -1,58 +0,0 @@ -model: eu.anthropic.claude-3-sonnet-20240229-v1:0 -label: - en_US: Claude 3 Sonnet(EU.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/llm.py b/api/core/model_runtime/model_providers/bedrock/llm/llm.py deleted file mode 100644 index 77bab0c294..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/llm.py +++ /dev/null @@ -1,903 +0,0 @@ -# standard import -import base64 -import json -import logging -import mimetypes -from collections.abc import Generator -from typing import Optional, Union, cast - -# 3rd import -import boto3 -import requests -from botocore.config import Config -from botocore.exceptions import ( - ClientError, - EndpointConnectionError, - NoRegionError, - ServiceNotInRegionError, - UnknownServiceError, -) - -# local import -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) -ANTHROPIC_BLOCK_MODE_PROMPT = """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 - - -class BedrockLargeLanguageModel(LargeLanguageModel): - # please refer to the documentation: https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html - # TODO There is invoke issue: context limit on Cohere Model, will add them after fixed. - CONVERSE_API_ENABLED_MODEL_INFO = [ - {"prefix": "anthropic.claude-v2", "support_system_prompts": True, "support_tool_use": False}, - {"prefix": "anthropic.claude-v1", "support_system_prompts": True, "support_tool_use": False}, - {"prefix": "us.anthropic.claude-3", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "eu.anthropic.claude-3", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "anthropic.claude-3", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "meta.llama", "support_system_prompts": True, "support_tool_use": False}, - {"prefix": "mistral.mistral-7b-instruct", "support_system_prompts": False, "support_tool_use": False}, - {"prefix": "mistral.mixtral-8x7b-instruct", "support_system_prompts": False, "support_tool_use": False}, - {"prefix": "mistral.mistral-large", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "mistral.mistral-small", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "cohere.command-r", "support_system_prompts": True, "support_tool_use": True}, - {"prefix": "amazon.titan", "support_system_prompts": False, "support_tool_use": False}, - ] - - @staticmethod - def _find_model_info(model_id): - for model in BedrockLargeLanguageModel.CONVERSE_API_ENABLED_MODEL_INFO: - if model_id.startswith(model["prefix"]): - return model - logger.info(f"current model id: {model_id} did not support by Converse API") - return None - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: list[Callback] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - if model_parameters.get("response_format"): - stop = stop or [] - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - response_format = model_parameters.pop("response_format") - format_prompt = SystemPromptMessage( - content=ANTHROPIC_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - if len(prompt_messages) > 0 and isinstance(prompt_messages[0], SystemPromptMessage): - prompt_messages[0] = format_prompt - else: - prompt_messages.insert(0, format_prompt) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - return self._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - - model_info = BedrockLargeLanguageModel._find_model_info(model) - if model_info: - model_info["model"] = model - # invoke models via boto3 converse API - return self._generate_with_converse( - model_info, credentials, prompt_messages, model_parameters, stop, stream, user, tools - ) - # invoke other models via boto3 client - return self._generate(model, credentials, prompt_messages, model_parameters, stop, stream, user) - - def _generate_with_converse( - self, - model_info: dict, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - tools: Optional[list[PromptMessageTool]] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model with converse API - - :param model_info: model information - :param credentials: model credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :return: full response or stream response chunk generator result - """ - bedrock_client = boto3.client( - service_name="bedrock-runtime", - aws_access_key_id=credentials.get("aws_access_key_id"), - aws_secret_access_key=credentials.get("aws_secret_access_key"), - region_name=credentials["aws_region"], - ) - - system, prompt_message_dicts = self._convert_converse_prompt_messages(prompt_messages) - inference_config, additional_model_fields = self._convert_converse_api_model_parameters(model_parameters, stop) - - parameters = { - "modelId": model_info["model"], - "messages": prompt_message_dicts, - "inferenceConfig": inference_config, - "additionalModelRequestFields": additional_model_fields, - } - - if model_info["support_system_prompts"] and system and len(system) > 0: - parameters["system"] = system - - if model_info["support_tool_use"] and tools: - parameters["toolConfig"] = self._convert_converse_tool_config(tools=tools) - try: - if stream: - response = bedrock_client.converse_stream(**parameters) - return self._handle_converse_stream_response( - model_info["model"], credentials, response, prompt_messages - ) - else: - response = bedrock_client.converse(**parameters) - return self._handle_converse_response(model_info["model"], credentials, response, prompt_messages) - except ClientError as ex: - error_code = ex.response["Error"]["Code"] - full_error_msg = f"{error_code}: {ex.response['Error']['Message']}" - raise self._map_client_to_invoke_error(error_code, full_error_msg) - except (EndpointConnectionError, NoRegionError, ServiceNotInRegionError) as ex: - raise InvokeConnectionError(str(ex)) - - except UnknownServiceError as ex: - raise InvokeServerUnavailableError(str(ex)) - - except Exception as ex: - raise InvokeError(str(ex)) - - def _handle_converse_response( - self, model: str, credentials: dict, response: dict, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: full response chunk generator result - """ - response_content = response["output"]["message"]["content"] - # transform assistant message to prompt message - if response["stopReason"] == "tool_use": - tool_calls = [] - text, tool_use = self._extract_tool_use(response_content) - - tool_call = AssistantPromptMessage.ToolCall( - id=tool_use["toolUseId"], - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_use["name"], arguments=json.dumps(tool_use["input"]) - ), - ) - tool_calls.append(tool_call) - - assistant_prompt_message = AssistantPromptMessage(content=text, tool_calls=tool_calls) - else: - assistant_prompt_message = AssistantPromptMessage(content=response_content[0]["text"]) - - # calculate num tokens - if response["usage"]: - # transform usage - prompt_tokens = response["usage"]["inputTokens"] - completion_tokens = response["usage"]["outputTokens"] - else: - # calculate num tokens - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - return result - - def _extract_tool_use(self, content: dict) -> tuple[str, dict]: - tool_use = {} - text = "" - for item in content: - if "toolUse" in item: - tool_use = item["toolUse"] - elif "text" in item: - text = item["text"] - else: - raise ValueError(f"Got unknown item: {item}") - return text, tool_use - - def _handle_converse_stream_response( - self, - model: str, - credentials: dict, - response: dict, - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: full response or stream response chunk generator result - """ - - try: - full_assistant_content = "" - return_model = None - input_tokens = 0 - output_tokens = 0 - finish_reason = None - index = 0 - tool_calls: list[AssistantPromptMessage.ToolCall] = [] - tool_use = {} - - for chunk in response["stream"]: - if "messageStart" in chunk: - return_model = model - elif "messageStop" in chunk: - finish_reason = chunk["messageStop"]["stopReason"] - elif "contentBlockStart" in chunk: - tool = chunk["contentBlockStart"]["start"]["toolUse"] - tool_use["toolUseId"] = tool["toolUseId"] - tool_use["name"] = tool["name"] - elif "metadata" in chunk: - input_tokens = chunk["metadata"]["usage"]["inputTokens"] - output_tokens = chunk["metadata"]["usage"]["outputTokens"] - usage = self._calc_response_usage(model, credentials, input_tokens, output_tokens) - yield LLMResultChunk( - model=return_model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=AssistantPromptMessage(content="", tool_calls=tool_calls), - finish_reason=finish_reason, - usage=usage, - ), - ) - elif "contentBlockDelta" in chunk: - delta = chunk["contentBlockDelta"]["delta"] - if "text" in delta: - chunk_text = delta["text"] or "" - full_assistant_content += chunk_text - assistant_prompt_message = AssistantPromptMessage( - content=chunk_text or "", - ) - index = chunk["contentBlockDelta"]["contentBlockIndex"] - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index + 1, - message=assistant_prompt_message, - ), - ) - elif "toolUse" in delta: - if "input" not in tool_use: - tool_use["input"] = "" - tool_use["input"] += delta["toolUse"]["input"] - elif "contentBlockStop" in chunk: - if "input" in tool_use: - tool_call = AssistantPromptMessage.ToolCall( - id=tool_use["toolUseId"], - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_use["name"], arguments=tool_use["input"] - ), - ) - tool_calls.append(tool_call) - tool_use = {} - - except Exception as ex: - raise InvokeError(str(ex)) - - def _convert_converse_api_model_parameters( - self, model_parameters: dict, stop: Optional[list[str]] = None - ) -> tuple[dict, dict]: - inference_config = {} - additional_model_fields = {} - if "max_tokens" in model_parameters: - inference_config["maxTokens"] = model_parameters["max_tokens"] - - if "temperature" in model_parameters: - inference_config["temperature"] = model_parameters["temperature"] - - if "top_p" in model_parameters: - inference_config["topP"] = model_parameters["temperature"] - - if stop: - inference_config["stopSequences"] = stop - - if "top_k" in model_parameters: - additional_model_fields["top_k"] = model_parameters["top_k"] - - return inference_config, additional_model_fields - - def _convert_converse_prompt_messages(self, prompt_messages: list[PromptMessage]) -> tuple[str, list[dict]]: - """ - Convert prompt messages to dict list and system - """ - - system = [] - prompt_message_dicts = [] - for message in prompt_messages: - if isinstance(message, SystemPromptMessage): - message.content = message.content.strip() - system.append({"text": message.content}) - else: - prompt_message_dicts.append(self._convert_prompt_message_to_dict(message)) - - return system, prompt_message_dicts - - def _convert_converse_tool_config(self, tools: Optional[list[PromptMessageTool]] = None) -> dict: - tool_config = {} - configs = [] - if tools: - for tool in tools: - configs.append( - { - "toolSpec": { - "name": tool.name, - "description": tool.description, - "inputSchema": {"json": tool.parameters}, - } - } - ) - tool_config["tools"] = configs - return tool_config - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": [{"text": message.content}]} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - if not message_content.data.startswith("data:"): - # fetch image data from url - try: - url = message_content.data - image_content = requests.get(url).content - if "?" in url: - url = url.split("?")[0] - mime_type, _ = mimetypes.guess_type(url) - base64_data = base64.b64encode(image_content).decode("utf-8") - except Exception as ex: - raise ValueError(f"Failed to fetch image data from url {message_content.data}, {ex}") - else: - data_split = message_content.data.split(";base64,") - mime_type = data_split[0].replace("data:", "") - base64_data = data_split[1] - image_content = base64.b64decode(base64_data) - - if mime_type not in {"image/jpeg", "image/png", "image/gif", "image/webp"}: - raise ValueError( - f"Unsupported image type {mime_type}, " - f"only support image/jpeg, image/png, image/gif, and image/webp" - ) - - sub_message_dict = { - "image": {"format": mime_type.replace("image/", ""), "source": {"bytes": image_content}} - } - sub_messages.append(sub_message_dict) - - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - if message.tool_calls: - message_dict = { - "role": "assistant", - "content": [ - { - "toolUse": { - "toolUseId": message.tool_calls[0].id, - "name": message.tool_calls[0].function.name, - "input": json.loads(message.tool_calls[0].function.arguments), - } - } - ], - } - else: - message_dict = {"role": "assistant", "content": [{"text": message.content}]} - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = [{"text": message.content}] - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = { - "role": "user", - "content": [ - { - "toolResult": { - "toolUseId": message.tool_call_id, - "content": [{"json": {"text": message.content}}], - } - } - ], - } - else: - raise ValueError(f"Got unknown type {message}") - return message_dict - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage] | str, - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages or message string - :param tools: tools for tool calling - :return:md = genai.GenerativeModel(model) - """ - prefix = model.split(".")[0] - model_name = model.split(".")[1] - - if isinstance(prompt_messages, str): - prompt = prompt_messages - else: - prompt = self._convert_messages_to_prompt(prompt_messages, prefix, model_name) - - return self._get_num_tokens_by_gpt2(prompt) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - required_params = {} - if "anthropic" in model: - required_params = { - "max_tokens": 32, - } - elif "ai21" in model: - # ValidationException: Malformed input request: #/temperature: expected type: Number, - # found: Null#/maxTokens: expected type: Integer, found: Null#/topP: expected type: Number, found: Null, - # please reformat your input and try again. - required_params = { - "temperature": 0.7, - "topP": 0.9, - "maxTokens": 32, - } - - try: - ping_message = UserPromptMessage(content="ping") - self._invoke( - model=model, - credentials=credentials, - prompt_messages=[ping_message], - model_parameters=required_params, - stream=False, - ) - - except ClientError as ex: - error_code = ex.response["Error"]["Code"] - full_error_msg = f"{error_code}: {ex.response['Error']['Message']}" - raise CredentialsValidateFailedError(str(self._map_client_to_invoke_error(error_code, full_error_msg))) - - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _convert_one_message_to_text( - self, message: PromptMessage, model_prefix: str, model_name: Optional[str] = None - ) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt_prefix = "" - human_prompt_postfix = "" - ai_prompt = "" - - content = message.content - - if isinstance(message, UserPromptMessage): - body = content - if isinstance(content, list): - body = "".join([c.data for c in content if c.type == PromptMessageContentType.TEXT]) - message_text = f"{human_prompt_prefix} {body} {human_prompt_postfix}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage): - message_text = content - elif isinstance(message, ToolPromptMessage): - message_text = f"{human_prompt_prefix} {message.content}" - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - def _convert_messages_to_prompt( - self, messages: list[PromptMessage], model_prefix: str, model_name: Optional[str] = None - ) -> str: - """ - Format a list of messages into a full prompt for the Anthropic, Amazon and Llama models - - :param messages: List of PromptMessage to combine. - :param model_name: specific model name.Optional,just to distinguish llama2 and llama3 - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - if not messages: - return "" - - messages = messages.copy() # don't mutate the original list - if not isinstance(messages[-1], AssistantPromptMessage): - messages.append(AssistantPromptMessage(content="")) - - text = "".join(self._convert_one_message_to_text(message, model_prefix, model_name) for message in messages) - - # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() - - def _create_payload( - self, - model: str, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - ): - """ - Create payload for bedrock api call depending on model provider - """ - payload = {} - model_prefix = model.split(".")[0] - model_name = model.split(".")[1] - - if model_prefix == "ai21": - payload["temperature"] = model_parameters.get("temperature") - payload["topP"] = model_parameters.get("topP") - payload["maxTokens"] = model_parameters.get("maxTokens") - payload["prompt"] = self._convert_messages_to_prompt(prompt_messages, model_prefix) - - if model_parameters.get("presencePenalty"): - payload["presencePenalty"] = {model_parameters.get("presencePenalty")} - if model_parameters.get("frequencyPenalty"): - payload["frequencyPenalty"] = {model_parameters.get("frequencyPenalty")} - if model_parameters.get("countPenalty"): - payload["countPenalty"] = {model_parameters.get("countPenalty")} - - elif model_prefix == "cohere": - payload = {**model_parameters} - payload["prompt"] = prompt_messages[0].content - payload["stream"] = stream - - else: - raise ValueError(f"Got unknown model prefix {model_prefix}") - - return payload - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model - - :param model: model name - :param credentials: credentials kwargs - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - client_config = Config(region_name=credentials["aws_region"]) - - runtime_client = boto3.client( - service_name="bedrock-runtime", - config=client_config, - aws_access_key_id=credentials.get("aws_access_key_id"), - aws_secret_access_key=credentials.get("aws_secret_access_key"), - ) - - model_prefix = model.split(".")[0] - payload = self._create_payload(model, prompt_messages, model_parameters, stop, stream) - - # need workaround for ai21 models which doesn't support streaming - if stream and model_prefix != "ai21": - invoke = runtime_client.invoke_model_with_response_stream - else: - invoke = runtime_client.invoke_model - - try: - body_jsonstr = json.dumps(payload) - response = invoke(modelId=model, contentType="application/json", accept="*/*", body=body_jsonstr) - except ClientError as ex: - error_code = ex.response["Error"]["Code"] - full_error_msg = f"{error_code}: {ex.response['Error']['Message']}" - raise self._map_client_to_invoke_error(error_code, full_error_msg) - - except (EndpointConnectionError, NoRegionError, ServiceNotInRegionError) as ex: - raise InvokeConnectionError(str(ex)) - - except UnknownServiceError as ex: - raise InvokeServerUnavailableError(str(ex)) - - except Exception as ex: - raise InvokeError(str(ex)) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: dict, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - response_body = json.loads(response.get("body").read().decode("utf-8")) - - finish_reason = response_body.get("error") - - if finish_reason is not None: - raise InvokeError(finish_reason) - - # get output text and calculate num tokens based on model / provider - model_prefix = model.split(".")[0] - - if model_prefix == "ai21": - output = response_body.get("completions")[0].get("data").get("text") - prompt_tokens = len(response_body.get("prompt").get("tokens")) - completion_tokens = len(response_body.get("completions")[0].get("data").get("tokens")) - - elif model_prefix == "cohere": - output = response_body.get("generations")[0].get("text") - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, output or "") - - else: - raise ValueError(f"Got unknown model prefix {model_prefix} when handling block response") - - # construct assistant message from output - assistant_prompt_message = AssistantPromptMessage(content=output) - - # calculate usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # construct response - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: dict, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - model_prefix = model.split(".")[0] - if model_prefix == "ai21": - response_body = json.loads(response.get("body").read().decode("utf-8")) - - content = response_body.get("completions")[0].get("data").get("text") - finish_reason = response_body.get("completions")[0].get("finish_reason") - - prompt_tokens = len(response_body.get("prompt").get("tokens")) - completion_tokens = len(response_body.get("completions")[0].get("data").get("tokens")) - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, message=AssistantPromptMessage(content=content), finish_reason=finish_reason, usage=usage - ), - ) - return - - stream = response.get("body") - if not stream: - raise InvokeError("No response body") - - index = -1 - for event in stream: - chunk = event.get("chunk") - - if not chunk: - exception_name = next(iter(event)) - full_ex_msg = f"{exception_name}: {event[exception_name]['message']}" - raise self._map_client_to_invoke_error(exception_name, full_ex_msg) - - payload = json.loads(chunk.get("bytes").decode()) - - model_prefix = model.split(".")[0] - if model_prefix == "cohere": - content_delta = payload.get("text") - finish_reason = payload.get("finish_reason") - - else: - raise ValueError(f"Got unknown model prefix {model_prefix} when handling stream response") - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=content_delta or "", - ) - index += 1 - - if not finish_reason: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message), - ) - - else: - # get num tokens from metrics in last chunk - prompt_tokens = payload["amazon-bedrock-invocationMetrics"]["inputTokenCount"] - completion_tokens = payload["amazon-bedrock-invocationMetrics"]["outputTokenCount"] - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, message=assistant_prompt_message, finish_reason=finish_reason, usage=usage - ), - ) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the ermd = genai.GenerativeModel(model) error type thrown to the caller - The value is the md = genai.GenerativeModel(model) error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke emd = genai.GenerativeModel(model) error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [], - InvokeRateLimitError: [], - InvokeAuthorizationError: [], - InvokeBadRequestError: [], - } - - def _map_client_to_invoke_error(self, error_code: str, error_msg: str) -> type[InvokeError]: - """ - Map client error to invoke error - - :param error_code: error code - :param error_msg: error message - :return: invoke error - """ - - if error_code == "AccessDeniedException": - return InvokeAuthorizationError(error_msg) - elif error_code in {"ResourceNotFoundException", "ValidationException"}: - return InvokeBadRequestError(error_msg) - elif error_code in {"ThrottlingException", "ServiceQuotaExceededException"}: - return InvokeRateLimitError(error_msg) - elif error_code in { - "ModelTimeoutException", - "ModelErrorException", - "InternalServerException", - "ModelNotReadyException", - }: - return InvokeServerUnavailableError(error_msg) - elif error_code == "ModelStreamErrorException": - return InvokeConnectionError(error_msg) - - return InvokeError(error_msg) diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-13b-chat-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-13b-chat-v1.yaml deleted file mode 100644 index a8d3704c15..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-13b-chat-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta.llama2-13b-chat-v1 -label: - en_US: Llama 2 Chat 13B -model_type: llm -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_gen_len - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 -pricing: - input: '0.00075' - output: '0.00100' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-70b-chat-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-70b-chat-v1.yaml deleted file mode 100644 index 77525e630b..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama2-70b-chat-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta.llama2-70b-chat-v1 -label: - en_US: Llama 2 Chat 70B -model_type: llm -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_gen_len - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 -pricing: - input: '0.00195' - output: '0.00256' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-405b-instruct-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-405b-instruct-v1.0.yaml deleted file mode 100644 index 401de65f89..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-405b-instruct-v1.0.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: meta.llama3-1-405b-instruct-v1:0 -label: - en_US: Llama 3.1 405B Instruct -model_type: llm -model_properties: - mode: completion - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - - name: top_p - use_template: top_p - default: 0.9 - - name: max_gen_len - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: '0.00532' - output: '0.016' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-70b-instruct-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-70b-instruct-v1.0.yaml deleted file mode 100644 index 10bfa7b1d5..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-70b-instruct-v1.0.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: meta.llama3-1-70b-instruct-v1:0 -label: - en_US: Llama 3.1 Instruct 70B -model_type: llm -model_properties: - mode: completion - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - - name: top_p - use_template: top_p - default: 0.9 - - name: max_gen_len - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: '0.00265' - output: '0.0035' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-8b-instruct-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-8b-instruct-v1.0.yaml deleted file mode 100644 index 81cd53243f..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-1-8b-instruct-v1.0.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: meta.llama3-1-8b-instruct-v1:0 -label: - en_US: Llama 3.1 Instruct 8B -model_type: llm -model_properties: - mode: completion - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - - name: top_p - use_template: top_p - default: 0.9 - - name: max_gen_len - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: '0.0003' - output: '0.0006' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-70b-instruct-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-70b-instruct-v1.yaml deleted file mode 100644 index 204662690e..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-70b-instruct-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta.llama3-70b-instruct-v1:0 -label: - en_US: Llama 3 Instruct 70B -model_type: llm -model_properties: - mode: completion - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_gen_len - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: '0.00265' - output: '0.0035' - unit: '0.00001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-8b-instruct-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-8b-instruct-v1.yaml deleted file mode 100644 index dd4f666a5f..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/meta.llama3-8b-instruct-v1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta.llama3-8b-instruct-v1:0 -label: - en_US: Llama 3 Instruct 8B -model_type: llm -model_properties: - mode: completion - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_gen_len - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: '0.0004' - output: '0.0006' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-7b-instruct-v0.2.yaml b/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-7b-instruct-v0.2.yaml deleted file mode 100644 index 175c14da37..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-7b-instruct-v0.2.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: mistral.mistral-7b-instruct-v0:2 -label: - en_US: Mistral 7B Instruct -model_type: llm -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - required: false - default: 0.5 - - name: top_p - use_template: top_p - required: false - default: 0.9 - - name: top_k - use_template: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 50 - max: 200 - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.00015' - output: '0.0002' - unit: '0.00001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2402-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2402-v1.0.yaml deleted file mode 100644 index 65eed5926b..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2402-v1.0.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: mistral.mistral-large-2402-v1:0 -label: - en_US: Mistral Large -model_type: llm -features: - - tool-call - - agent-thought -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - required: false - default: 0.7 - - name: top_p - use_template: top_p - required: false - default: 1 - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 4096 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2407-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2407-v1.0.yaml deleted file mode 100644 index 19d7843a57..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-large-2407-v1.0.yaml +++ /dev/null @@ -1,29 +0,0 @@ -model: mistral.mistral-large-2407-v1:0 -label: - en_US: Mistral Large 2 (24.07) -model_type: llm -features: - - tool-call -model_properties: - mode: completion - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - required: false - default: 0.7 - - name: top_p - use_template: top_p - required: false - default: 1 - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.003' - output: '0.009' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-small-2402-v1.0.yaml b/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-small-2402-v1.0.yaml deleted file mode 100644 index b97c2a9493..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mistral-small-2402-v1.0.yaml +++ /dev/null @@ -1,29 +0,0 @@ -model: mistral.mistral-small-2402-v1:0 -label: - en_US: Mistral Small -model_type: llm -features: - - tool-call -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - required: false - default: 0.7 - - name: top_p - use_template: top_p - required: false - default: 1 - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 4096 -pricing: - input: '0.001' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mixtral-8x7b-instruct-v0.1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/mistral.mixtral-8x7b-instruct-v0.1.yaml deleted file mode 100644 index 03ec7eddaf..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/mistral.mixtral-8x7b-instruct-v0.1.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: mistral.mixtral-8x7b-instruct-v0:1 -label: - en_US: Mixtral 8X7B Instruct -model_type: llm -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - required: false - default: 0.5 - - name: top_p - use_template: top_p - required: false - default: 0.9 - - name: top_k - use_template: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 50 - max: 200 - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.00045' - output: '0.0007' - unit: '0.00001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-haiku-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-haiku-v1.yaml deleted file mode 100644 index 9247f46974..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-haiku-v1.yaml +++ /dev/null @@ -1,59 +0,0 @@ -model: us.anthropic.claude-3-haiku-20240307-v1:0 -label: - en_US: Claude 3 Haiku(US.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.00025' - output: '0.00125' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-opus-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-opus-v1.yaml deleted file mode 100644 index f9854d51f0..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-opus-v1.yaml +++ /dev/null @@ -1,59 +0,0 @@ -model: us.anthropic.claude-3-opus-20240229-v1:0 -label: - en_US: Claude 3 Opus(US.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.015' - output: '0.075' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.5.yaml b/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.5.yaml deleted file mode 100644 index fbcab2d5f3..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.5.yaml +++ /dev/null @@ -1,58 +0,0 @@ -model: us.anthropic.claude-3-5-sonnet-20240620-v1:0 -label: - en_US: Claude 3.5 Sonnet(US.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.yaml b/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.yaml deleted file mode 100644 index 9f5a1501f0..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/llm/us.anthropic.claude-3-sonnet-v1.yaml +++ /dev/null @@ -1,58 +0,0 @@ -model: us.anthropic.claude-3-sonnet-20240229-v1:0 -label: - en_US: Claude 3 Sonnet(US.Cross Region Inference) -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -# docs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/__init__.py b/api/core/model_runtime/model_providers/bedrock/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/_position.yaml b/api/core/model_runtime/model_providers/bedrock/text_embedding/_position.yaml deleted file mode 100644 index afbea06a3e..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/text_embedding/_position.yaml +++ /dev/null @@ -1,4 +0,0 @@ -- amazon.titan-embed-text-v1 -- amazon.titan-embed-text-v2:0 -- cohere.embed-english-v3 -- cohere.embed-multilingual-v3 diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v1.yaml b/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v1.yaml deleted file mode 100644 index e5a55971a1..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v1.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: amazon.titan-embed-text-v1 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: '0.0001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v2.yaml b/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v2.yaml deleted file mode 100644 index 5069efeb10..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/text_embedding/amazon.titan-embed-text-v2.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: amazon.titan-embed-text-v2:0 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: '0.00002' - unit: '0.00001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-english-v3.yaml b/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-english-v3.yaml deleted file mode 100644 index d49aa2a99c..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-english-v3.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: cohere.embed-english-v3 -model_type: text-embedding -model_properties: - context_size: 512 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-multilingual-v3.yaml b/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-multilingual-v3.yaml deleted file mode 100644 index 63bab59d2c..0000000000 --- a/api/core/model_runtime/model_providers/bedrock/text_embedding/cohere.embed-multilingual-v3.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: cohere.embed-multilingual-v3 -model_type: text-embedding -model_properties: - context_size: 512 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/chatglm/__init__.py b/api/core/model_runtime/model_providers/chatglm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/chatglm/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/chatglm/_assets/icon_l_en.svg deleted file mode 100644 index a824d43d6f..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/_assets/icon_l_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/chatglm/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/chatglm/_assets/icon_s_en.svg deleted file mode 100644 index 466b4fce57..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/_assets/icon_s_en.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/chatglm/chatglm.py b/api/core/model_runtime/model_providers/chatglm/chatglm.py deleted file mode 100644 index 71d9a15322..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/chatglm.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class ChatGLMProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `chatglm3-6b` model for validate, - model_instance.validate_credentials(model="chatglm3-6b", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/chatglm/chatglm.yaml b/api/core/model_runtime/model_providers/chatglm/chatglm.yaml deleted file mode 100644 index 0c1688c350..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/chatglm.yaml +++ /dev/null @@ -1,28 +0,0 @@ -provider: chatglm -label: - en_US: ChatGLM -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#F4F7FF" -help: - title: - en_US: Deploy ChatGLM to your local - zh_Hans: 部署您的本地 ChatGLM - url: - en_US: https://github.com/THUDM/ChatGLM3 -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_base - label: - en_US: API URL - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的 API URL - en_US: Enter your API URL diff --git a/api/core/model_runtime/model_providers/chatglm/llm/__init__.py b/api/core/model_runtime/model_providers/chatglm/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b-32k.yaml b/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b-32k.yaml deleted file mode 100644 index d1075d74b5..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b-32k.yaml +++ /dev/null @@ -1,21 +0,0 @@ -model: chatglm2-6b-32k -label: - en_US: ChatGLM2-6B-32K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 2000 - min: 1 - max: 32000 diff --git a/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b.yaml b/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b.yaml deleted file mode 100644 index e3cfeb9001..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/llm/chatglm2-6b.yaml +++ /dev/null @@ -1,21 +0,0 @@ -model: chatglm2-6b -label: - en_US: ChatGLM2-6B -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 256 - min: 1 - max: 2000 diff --git a/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b-32k.yaml b/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b-32k.yaml deleted file mode 100644 index 6f34743513..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b-32k.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm3-6b-32k -label: - en_US: ChatGLM3-6B-32K -model_type: llm -features: - - tool-call - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 8000 - min: 1 - max: 32000 diff --git a/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b.yaml b/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b.yaml deleted file mode 100644 index d6d87e2e09..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/llm/chatglm3-6b.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm3-6b -label: - en_US: ChatGLM3-6B -model_type: llm -features: - - tool-call - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 256 - min: 1 - max: 8000 diff --git a/api/core/model_runtime/model_providers/chatglm/llm/llm.py b/api/core/model_runtime/model_providers/chatglm/llm/llm.py deleted file mode 100644 index b3eeb48e22..0000000000 --- a/api/core/model_runtime/model_providers/chatglm/llm/llm.py +++ /dev/null @@ -1,507 +0,0 @@ -import logging -from collections.abc import Generator -from typing import Optional, cast - -from httpx import Timeout -from openai import ( - APIConnectionError, - APITimeoutError, - AuthenticationError, - ConflictError, - InternalServerError, - NotFoundError, - OpenAI, - PermissionDeniedError, - RateLimitError, - Stream, - UnprocessableEntityError, -) -from openai.types.chat import ChatCompletion, ChatCompletionChunk -from openai.types.chat.chat_completion_message import FunctionCall -from yarl import URL - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.utils import helper - -logger = logging.getLogger(__name__) - - -class ChatGLMLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> 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 - """ - # invoke model - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - return self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - prompt_messages=[ - UserPromptMessage(content="ping"), - ], - model_parameters={ - "max_tokens": 16, - }, - ) - except Exception as e: - raise CredentialsValidateFailedError(str(e)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - APIConnectionError, - APITimeoutError, - ], - InvokeServerUnavailableError: [ - InternalServerError, - ConflictError, - NotFoundError, - UnprocessableEntityError, - PermissionDeniedError, - ], - InvokeRateLimitError: [RateLimitError], - InvokeAuthorizationError: [AuthenticationError], - InvokeBadRequestError: [ValueError], - } - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - Invoke large language model - - :param model: model name - :param credentials: credentials kwargs - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - - self._check_chatglm_parameters(model=model, model_parameters=model_parameters, tools=tools) - - kwargs = self._to_client_kwargs(credentials) - # init model client - client = OpenAI(**kwargs) - - extra_model_kwargs = {} - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - if tools and len(tools) > 0: - extra_model_kwargs["functions"] = [helper.dump_model(tool) for tool in tools] - - result = client.chat.completions.create( - messages=[self._convert_prompt_message_to_dict(m) for m in prompt_messages], - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response( - model=model, credentials=credentials, response=result, tools=tools, prompt_messages=prompt_messages - ) - - return self._handle_chat_generate_response( - model=model, credentials=credentials, response=result, tools=tools, prompt_messages=prompt_messages - ) - - def _check_chatglm_parameters(self, model: str, model_parameters: dict, tools: list[PromptMessageTool]) -> None: - if model.find("chatglm2") != -1 and tools is not None and len(tools) > 0: - raise InvokeBadRequestError("ChatGLM2 does not support function calling") - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for OpenAI Compatibility API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - raise ValueError("User message content must be str") - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls and len(message.tool_calls) > 0: - message_dict["function_call"] = { - "name": message.tool_calls[0].function.name, - "arguments": message.tool_calls[0].function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - # check if last message is user message - message = cast(ToolPromptMessage, message) - message_dict = {"role": "function", "content": message.content} - else: - raise ValueError(f"Unknown message type {type(message)}") - - return message_dict - - def _extract_response_tool_calls( - self, response_function_calls: list[FunctionCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_function_calls: - for response_tool_call in response_function_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.name, arguments=response_tool_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall(id=0, type="function", function=function) - tool_calls.append(tool_call) - - return tool_calls - - def _to_client_kwargs(self, credentials: dict) -> dict: - """ - Convert invoke kwargs to client kwargs - - :param stream: is stream response - :param model_name: model name - :param credentials: credentials dict - :param model_parameters: model parameters - :return: client kwargs - """ - client_kwargs = { - "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), - "api_key": "1", - "base_url": str(URL(credentials["api_base"]) / "v1"), - } - - return client_kwargs - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Stream[ChatCompletionChunk], - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> Generator: - full_response = "" - - for chunk in response: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ""): - continue - - # check if there is a tool call in the response - function_calls = None - if delta.delta.function_call: - function_calls = [delta.delta.function_call] - - assistant_message_tool_calls = self._extract_response_tool_calls(function_calls or []) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta.delta.content or "", tool_calls=assistant_message_tool_calls - ) - - if delta.finish_reason is not None: - # temp_assistant_prompt_message is used to calculate usage - temp_assistant_prompt_message = AssistantPromptMessage( - content=full_response, tool_calls=assistant_message_tool_calls - ) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[temp_assistant_prompt_message], tools=[]) - - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - full_response += delta.delta.content - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: ChatCompletion, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - if len(response.choices) == 0: - raise InvokeServerUnavailableError("Empty response") - assistant_message = response.choices[0].message - - # convert function call to tool call - function_calls = assistant_message.function_call - tool_calls = self._extract_response_tool_calls([function_calls] if function_calls else []) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[assistant_prompt_message], tools=tools) - - usage = self._calc_response_usage( - model=model, credentials=credentials, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens - ) - - response = LLMResult( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=response.system_fingerprint, - usage=usage, - message=assistant_prompt_message, - ) - - return response - - def _num_tokens_from_string(self, text: str, tools: Optional[list[PromptMessageTool]] = None) -> int: - """ - Calculate num tokens for text completion model with tiktoken package. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - num_tokens = self._get_num_tokens_by_gpt2(text) - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_from_messages( - self, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for chatglm2 and chatglm3 with GPT2 tokenizer. - - it's too complex to calculate num tokens for chatglm2 and chatglm3 with ChatGLM tokenizer, - As a temporary solution we use GPT2 tokenizer instead. - - """ - - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - tokens_per_message = 3 - tokens_per_name = 1 - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - value = text - - if key == "function_call": - for t_key, t_value in value.items(): - num_tokens += tokens(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += tokens(f_key) - num_tokens += tokens(f_value) - else: - num_tokens += tokens(t_key) - num_tokens += tokens(t_value) - else: - num_tokens += tokens(str(value)) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_for_tools(self, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling - - :param encoding: encoding - :param tools: tools for tool calling - :return: number of tokens - """ - - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - num_tokens = 0 - for tool in tools: - # calculate num tokens for function object - num_tokens += tokens("name") - num_tokens += tokens(tool.name) - num_tokens += tokens("description") - num_tokens += tokens(tool.description) - parameters = tool.parameters - num_tokens += tokens("parameters") - num_tokens += tokens("type") - num_tokens += tokens(parameters.get("type")) - if "properties" in parameters: - num_tokens += tokens("properties") - for key, value in parameters.get("properties").items(): - num_tokens += tokens(key) - for field_key, field_value in value.items(): - num_tokens += tokens(field_key) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += tokens(enum_field) - else: - num_tokens += tokens(field_key) - num_tokens += tokens(str(field_value)) - if "required" in parameters: - num_tokens += tokens("required") - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += tokens(required_field) - - return num_tokens diff --git a/api/core/model_runtime/model_providers/cohere/__init__.py b/api/core/model_runtime/model_providers/cohere/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/cohere/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/cohere/_assets/icon_l_en.svg deleted file mode 100644 index 9c176896fe..0000000000 --- a/api/core/model_runtime/model_providers/cohere/_assets/icon_l_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/cohere/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/cohere/_assets/icon_s_en.svg deleted file mode 100644 index 28fe96d390..0000000000 --- a/api/core/model_runtime/model_providers/cohere/_assets/icon_s_en.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/cohere/cohere.py b/api/core/model_runtime/model_providers/cohere/cohere.py deleted file mode 100644 index 8394a45fcf..0000000000 --- a/api/core/model_runtime/model_providers/cohere/cohere.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class CohereProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.RERANK) - - # Use `rerank-english-v2.0` model for validate, - model_instance.validate_credentials(model="rerank-english-v2.0", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/cohere/cohere.yaml b/api/core/model_runtime/model_providers/cohere/cohere.yaml deleted file mode 100644 index bd40057fe9..0000000000 --- a/api/core/model_runtime/model_providers/cohere/cohere.yaml +++ /dev/null @@ -1,90 +0,0 @@ -provider: cohere -label: - zh_Hans: Cohere - en_US: Cohere -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#ECE9E3" -help: - title: - en_US: Get your API key from cohere - zh_Hans: 从 cohere 获取 API Key - url: - en_US: https://dashboard.cohere.com/api-keys -supported_model_types: - - llm - - text-embedding - - rerank -configurate_methods: - - predefined-model - - customizable-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - zh_Hans: API Key - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - show_on: [ ] - - variable: base_url - label: - zh_Hans: API Base - en_US: API Base - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的 API Base,如 https://api.cohere.ai/v1 - en_US: Enter your API Base, e.g. https://api.cohere.ai/v1 -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: base_url - label: - zh_Hans: API Base - en_US: API Base - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的 API Base,如 https://api.cohere.ai/v1 - en_US: Enter your API Base, e.g. https://api.cohere.ai/v1 diff --git a/api/core/model_runtime/model_providers/cohere/llm/__init__.py b/api/core/model_runtime/model_providers/cohere/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/cohere/llm/_position.yaml b/api/core/model_runtime/model_providers/cohere/llm/_position.yaml deleted file mode 100644 index 42d06f49a2..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/_position.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- command-r -- command-r-plus -- command-chat -- command-light-chat -- command-nightly-chat -- command-light-nightly-chat -- command -- command-light -- command-nightly -- command-light-nightly diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-chat.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-chat.yaml deleted file mode 100644 index 5f233f35ce..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: command-chat -label: - zh_Hans: command-chat - en_US: command-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 - - name: preamble_override - label: - zh_Hans: 前导文本 - en_US: Preamble - type: string - help: - zh_Hans: 当指定时,将使用提供的前导文本替换默认的 Cohere 前导文本。 - en_US: When specified, the default Cohere preamble will be replaced with the provided one. - required: false - - name: prompt_truncation - label: - zh_Hans: 提示截断 - en_US: Prompt Truncation - type: string - help: - zh_Hans: 指定如何构造 Prompt。当 prompt_truncation 设置为 "AUTO" 时,将会丢弃一些来自聊天记录的元素,以尝试构造一个符合模型上下文长度限制的 Prompt。 - en_US: Dictates how the prompt will be constructed. With prompt_truncation set to "AUTO", some elements from chat histories will be dropped in an attempt to construct a prompt that fits within the model's context length limit. - required: true - default: 'AUTO' - options: - - 'AUTO' - - 'OFF' -pricing: - input: '1.0' - output: '2.0' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-light-chat.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-light-chat.yaml deleted file mode 100644 index b5f0048770..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-light-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: command-light-chat -label: - zh_Hans: command-light-chat - en_US: command-light-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 - - name: preamble_override - label: - zh_Hans: 前导文本 - en_US: Preamble - type: string - help: - zh_Hans: 当指定时,将使用提供的前导文本替换默认的 Cohere 前导文本。 - en_US: When specified, the default Cohere preamble will be replaced with the provided one. - required: false - - name: prompt_truncation - label: - zh_Hans: 提示截断 - en_US: Prompt Truncation - type: string - help: - zh_Hans: 指定如何构造 Prompt。当 prompt_truncation 设置为 "AUTO" 时,将会丢弃一些来自聊天记录的元素,以尝试构造一个符合模型上下文长度限制的 Prompt。 - en_US: Dictates how the prompt will be constructed. With prompt_truncation set to "AUTO", some elements from chat histories will be dropped in an attempt to construct a prompt that fits within the model's context length limit. - required: true - default: 'AUTO' - options: - - 'AUTO' - - 'OFF' -pricing: - input: '0.3' - output: '0.6' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly-chat.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly-chat.yaml deleted file mode 100644 index 1c96b24030..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: command-light-nightly-chat -label: - zh_Hans: command-light-nightly-chat - en_US: command-light-nightly-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 - - name: preamble_override - label: - zh_Hans: 前导文本 - en_US: Preamble - type: string - help: - zh_Hans: 当指定时,将使用提供的前导文本替换默认的 Cohere 前导文本。 - en_US: When specified, the default Cohere preamble will be replaced with the provided one. - required: false - - name: prompt_truncation - label: - zh_Hans: 提示截断 - en_US: Prompt Truncation - type: string - help: - zh_Hans: 指定如何构造 Prompt。当 prompt_truncation 设置为 "AUTO" 时,将会丢弃一些来自聊天记录的元素,以尝试构造一个符合模型上下文长度限制的 Prompt。 - en_US: Dictates how the prompt will be constructed. With prompt_truncation set to "AUTO", some elements from chat histories will be dropped in an attempt to construct a prompt that fits within the model's context length limit. - required: true - default: 'AUTO' - options: - - 'AUTO' - - 'OFF' -pricing: - input: '0.3' - output: '0.6' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly.yaml deleted file mode 100644 index 4616f76689..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-light-nightly.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: command-light-nightly -label: - zh_Hans: command-light-nightly - en_US: command-light-nightly -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '0.3' - output: '0.6' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-light.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-light.yaml deleted file mode 100644 index 161756b322..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-light.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: command-light -label: - zh_Hans: command-light - en_US: command-light -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '0.3' - output: '0.6' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-nightly-chat.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-nightly-chat.yaml deleted file mode 100644 index 739e09e72e..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-nightly-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: command-nightly-chat -label: - zh_Hans: command-nightly-chat - en_US: command-nightly-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 - - name: preamble_override - label: - zh_Hans: 前导文本 - en_US: Preamble - type: string - help: - zh_Hans: 当指定时,将使用提供的前导文本替换默认的 Cohere 前导文本。 - en_US: When specified, the default Cohere preamble will be replaced with the provided one. - required: false - - name: prompt_truncation - label: - zh_Hans: 提示截断 - en_US: Prompt Truncation - type: string - help: - zh_Hans: 指定如何构造 Prompt。当 prompt_truncation 设置为 "AUTO" 时,将会丢弃一些来自聊天记录的元素,以尝试构造一个符合模型上下文长度限制的 Prompt。 - en_US: Dictates how the prompt will be constructed. With prompt_truncation set to "AUTO", some elements from chat histories will be dropped in an attempt to construct a prompt that fits within the model's context length limit. - required: true - default: 'AUTO' - options: - - 'AUTO' - - 'OFF' -pricing: - input: '1.0' - output: '2.0' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-nightly.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-nightly.yaml deleted file mode 100644 index 1e025e40c4..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-nightly.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: command-nightly -label: - zh_Hans: command-nightly - en_US: command-nightly -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '1.0' - output: '2.0' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-r-plus.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-r-plus.yaml deleted file mode 100644 index 617e6853ea..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-r-plus.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: command-r-plus -label: - en_US: command-r-plus -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '3' - output: '15' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command-r.yaml b/api/core/model_runtime/model_providers/cohere/llm/command-r.yaml deleted file mode 100644 index c36680443b..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command-r.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: command-r -label: - en_US: command-r -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '0.5' - output: '1.5' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/command.yaml b/api/core/model_runtime/model_providers/cohere/llm/command.yaml deleted file mode 100644 index 0cac7c35ea..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/command.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: command -label: - zh_Hans: command - en_US: command -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: '1.0' - output: '2.0' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/llm/llm.py b/api/core/model_runtime/model_providers/cohere/llm/llm.py deleted file mode 100644 index 3863ad3308..0000000000 --- a/api/core/model_runtime/model_providers/cohere/llm/llm.py +++ /dev/null @@ -1,733 +0,0 @@ -import json -import logging -from collections.abc import Generator, Iterator -from typing import Optional, Union, cast - -import cohere -from cohere import ( - ChatMessage, - ChatStreamRequestToolResultsItem, - GenerateStreamedResponse, - GenerateStreamedResponse_StreamEnd, - GenerateStreamedResponse_StreamError, - GenerateStreamedResponse_TextGeneration, - Generation, - NonStreamedChatResponse, - StreamedChatResponse, - StreamedChatResponse_StreamEnd, - StreamedChatResponse_TextGeneration, - StreamedChatResponse_ToolCallsGeneration, - Tool, - ToolCall, - ToolParameterDefinitionsValue, -) -from cohere.core import RequestOptions - -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageContentType, - PromptMessageRole, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, I18nObject, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) - - -class CohereLargeLanguageModel(LargeLanguageModel): - """ - Model class for Cohere large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # get model mode - model_mode = self.get_model_mode(model, credentials) - - if model_mode == LLMMode.CHAT: - return self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - else: - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - # get model mode - model_mode = self.get_model_mode(model) - - try: - if model_mode == LLMMode.CHAT: - return self._num_tokens_from_messages(model, credentials, prompt_messages) - else: - return self._num_tokens_from_string(model, credentials, prompt_messages[0].content) - except Exception as e: - raise self._transform_invoke_error(e) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - # get model mode - model_mode = self.get_model_mode(model) - - if model_mode == LLMMode.CHAT: - self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=[UserPromptMessage(content="ping")], - model_parameters={ - "max_tokens": 20, - "temperature": 0, - }, - stream=False, - ) - else: - self._generate( - model=model, - credentials=credentials, - prompt_messages=[UserPromptMessage(content="ping")], - model_parameters={ - "max_tokens": 20, - "temperature": 0, - }, - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - # initialize client - client = cohere.Client(credentials.get("api_key"), base_url=credentials.get("base_url")) - - if stop: - model_parameters["end_sequences"] = stop - - if stream: - response = client.generate_stream( - prompt=prompt_messages[0].content, - model=model, - **model_parameters, - request_options=RequestOptions(max_retries=0), - ) - - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - else: - response = client.generate( - prompt=prompt_messages[0].content, - model=model, - **model_parameters, - request_options=RequestOptions(max_retries=0), - ) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: Generation, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - assistant_text = response.generations[0].text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_text) - - # calculate num tokens - prompt_tokens = int(response.meta.billed_units.input_tokens) - completion_tokens = int(response.meta.billed_units.output_tokens) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=model, prompt_messages=prompt_messages, message=assistant_prompt_message, usage=usage - ) - - return response - - def _handle_generate_stream_response( - self, - model: str, - credentials: dict, - response: Iterator[GenerateStreamedResponse], - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - index = 1 - full_assistant_content = "" - for chunk in response: - if isinstance(chunk, GenerateStreamedResponse_TextGeneration): - chunk = cast(GenerateStreamedResponse_TextGeneration, chunk) - text = chunk.text - - if text is None: - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=text) - - full_assistant_content += text - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - ), - ) - - index += 1 - elif isinstance(chunk, GenerateStreamedResponse_StreamEnd): - chunk = cast(GenerateStreamedResponse_StreamEnd, chunk) - - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, credentials, prompt_messages) - completion_tokens = self._num_tokens_from_messages( - model, credentials, [AssistantPromptMessage(content=full_assistant_content)] - ) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=AssistantPromptMessage(content=""), - finish_reason=chunk.finish_reason, - usage=usage, - ), - ) - break - elif isinstance(chunk, GenerateStreamedResponse_StreamError): - chunk = cast(GenerateStreamedResponse_StreamError, chunk) - raise InvokeBadRequestError(chunk.err) - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm chat model - - :param model: model name - :param credentials: 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 - """ - # initialize client - client = cohere.Client(credentials.get("api_key"), base_url=credentials.get("base_url")) - - if stop: - model_parameters["stop_sequences"] = stop - - if tools: - if len(tools) == 1: - raise ValueError("Cohere tool call requires at least two tools to be specified.") - - model_parameters["tools"] = self._convert_tools(tools) - - message, chat_histories, tool_results = self._convert_prompt_messages_to_message_and_chat_histories( - prompt_messages - ) - - if tool_results: - model_parameters["tool_results"] = tool_results - - # chat model - real_model = model - if self.get_model_schema(model, credentials).fetch_from == FetchFrom.PREDEFINED_MODEL: - real_model = model.removesuffix("-chat") - - if stream: - response = client.chat_stream( - message=message, - chat_history=chat_histories, - model=real_model, - **model_parameters, - request_options=RequestOptions(max_retries=0), - ) - - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages) - else: - response = client.chat( - message=message, - chat_history=chat_histories, - model=real_model, - **model_parameters, - request_options=RequestOptions(max_retries=0), - ) - - return self._handle_chat_generate_response(model, credentials, response, prompt_messages) - - def _handle_chat_generate_response( - self, model: str, credentials: dict, response: NonStreamedChatResponse, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - assistant_text = response.text - - tool_calls = [] - if response.tool_calls: - for cohere_tool_call in response.tool_calls: - tool_call = AssistantPromptMessage.ToolCall( - id=cohere_tool_call.name, - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=cohere_tool_call.name, arguments=json.dumps(cohere_tool_call.parameters) - ), - ) - tool_calls.append(tool_call) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_text, tool_calls=tool_calls) - - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, credentials, prompt_messages) - completion_tokens = self._num_tokens_from_messages(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=model, prompt_messages=prompt_messages, message=assistant_prompt_message, usage=usage - ) - - return response - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Iterator[StreamedChatResponse], - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - - def final_response( - full_text: str, - tool_calls: list[AssistantPromptMessage.ToolCall], - index: int, - finish_reason: Optional[str] = None, - ) -> LLMResultChunk: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, credentials, prompt_messages) - - full_assistant_prompt_message = AssistantPromptMessage(content=full_text, tool_calls=tool_calls) - completion_tokens = self._num_tokens_from_messages(model, credentials, [full_assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - return LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=AssistantPromptMessage(content="", tool_calls=tool_calls), - finish_reason=finish_reason, - usage=usage, - ), - ) - - index = 1 - full_assistant_content = "" - tool_calls = [] - for chunk in response: - if isinstance(chunk, StreamedChatResponse_TextGeneration): - chunk = cast(StreamedChatResponse_TextGeneration, chunk) - text = chunk.text - - if text is None: - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=text) - - full_assistant_content += text - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - ), - ) - - index += 1 - elif isinstance(chunk, StreamedChatResponse_ToolCallsGeneration): - chunk = cast(StreamedChatResponse_ToolCallsGeneration, chunk) - if chunk.tool_calls: - for cohere_tool_call in chunk.tool_calls: - tool_call = AssistantPromptMessage.ToolCall( - id=cohere_tool_call.name, - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=cohere_tool_call.name, arguments=json.dumps(cohere_tool_call.parameters) - ), - ) - tool_calls.append(tool_call) - elif isinstance(chunk, StreamedChatResponse_StreamEnd): - chunk = cast(StreamedChatResponse_StreamEnd, chunk) - yield final_response(full_assistant_content, tool_calls, index, chunk.finish_reason) - index += 1 - - def _convert_prompt_messages_to_message_and_chat_histories( - self, prompt_messages: list[PromptMessage] - ) -> tuple[str, list[ChatMessage], list[ChatStreamRequestToolResultsItem]]: - """ - Convert prompt messages to message and chat histories - :param prompt_messages: prompt messages - :return: - """ - chat_histories = [] - latest_tool_call_n_outputs = [] - for prompt_message in prompt_messages: - if prompt_message.role == PromptMessageRole.ASSISTANT: - prompt_message = cast(AssistantPromptMessage, prompt_message) - if prompt_message.tool_calls: - for tool_call in prompt_message.tool_calls: - latest_tool_call_n_outputs.append( - ChatStreamRequestToolResultsItem( - call=ToolCall( - name=tool_call.function.name, parameters=json.loads(tool_call.function.arguments) - ), - outputs=[], - ) - ) - else: - cohere_prompt_message = self._convert_prompt_message_to_dict(prompt_message) - if cohere_prompt_message: - chat_histories.append(cohere_prompt_message) - elif prompt_message.role == PromptMessageRole.TOOL: - prompt_message = cast(ToolPromptMessage, prompt_message) - if latest_tool_call_n_outputs: - i = 0 - for tool_call_n_outputs in latest_tool_call_n_outputs: - if tool_call_n_outputs.call.name == prompt_message.tool_call_id: - latest_tool_call_n_outputs[i] = ChatStreamRequestToolResultsItem( - call=ToolCall( - name=tool_call_n_outputs.call.name, parameters=tool_call_n_outputs.call.parameters - ), - outputs=[{"result": prompt_message.content}], - ) - break - i += 1 - else: - cohere_prompt_message = self._convert_prompt_message_to_dict(prompt_message) - if cohere_prompt_message: - chat_histories.append(cohere_prompt_message) - - if latest_tool_call_n_outputs: - new_latest_tool_call_n_outputs = [] - for tool_call_n_outputs in latest_tool_call_n_outputs: - if tool_call_n_outputs.outputs: - new_latest_tool_call_n_outputs.append(tool_call_n_outputs) - - latest_tool_call_n_outputs = new_latest_tool_call_n_outputs - - # get latest message from chat histories and pop it - if len(chat_histories) > 0: - latest_message = chat_histories.pop() - message = latest_message.message - else: - raise ValueError("Prompt messages is empty") - - return message, chat_histories, latest_tool_call_n_outputs - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> Optional[ChatMessage]: - """ - Convert PromptMessage to dict for Cohere model - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - chat_message = ChatMessage(role="USER", message=message.content) - else: - sub_message_text = "" - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_text += message_content.data - - chat_message = ChatMessage(role="USER", message=sub_message_text) - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - if not message.content: - return None - chat_message = ChatMessage(role="CHATBOT", message=message.content) - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - chat_message = ChatMessage(role="USER", message=message.content) - elif isinstance(message, ToolPromptMessage): - return None - else: - raise ValueError(f"Got unknown type {message}") - - return chat_message - - def _convert_tools(self, tools: list[PromptMessageTool]) -> list[Tool]: - """ - Convert tools to Cohere model - """ - cohere_tools = [] - for tool in tools: - properties = tool.parameters["properties"] - required_properties = tool.parameters["required"] - - parameter_definitions = {} - for p_key, p_val in properties.items(): - required = False - if p_key in required_properties: - required = True - - desc = p_val["description"] - if "enum" in p_val: - desc += f"; Only accepts one of the following predefined options: [{', '.join(p_val['enum'])}]" - - parameter_definitions[p_key] = ToolParameterDefinitionsValue( - description=desc, type=p_val["type"], required=required - ) - - cohere_tool = Tool( - name=tool.name, description=tool.description, parameter_definitions=parameter_definitions - ) - - cohere_tools.append(cohere_tool) - - return cohere_tools - - def _num_tokens_from_string(self, model: str, credentials: dict, text: str) -> int: - """ - Calculate num tokens for text completion model. - - :param model: model name - :param credentials: credentials - :param text: prompt text - :return: number of tokens - """ - # initialize client - client = cohere.Client(credentials.get("api_key"), base_url=credentials.get("base_url")) - - response = client.tokenize(text=text, model=model) - - return len(response.tokens) - - def _num_tokens_from_messages(self, model: str, credentials: dict, messages: list[PromptMessage]) -> int: - """Calculate num tokens Cohere model.""" - calc_messages = [] - for message in messages: - cohere_message = self._convert_prompt_message_to_dict(message) - if cohere_message: - calc_messages.append(cohere_message) - message_strs = [f"{message.role}: {message.message}" for message in calc_messages] - message_str = "\n".join(message_strs) - - real_model = model - if self.get_model_schema(model, credentials).fetch_from == FetchFrom.PREDEFINED_MODEL: - real_model = model.removesuffix("-chat") - - return self._num_tokens_from_string(real_model, credentials, message_str) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - Cohere supports fine-tuning of their models. This method returns the schema of the base model - but renamed to the fine-tuned model name. - - :param model: model name - :param credentials: credentials - - :return: model schema - """ - # get model schema - models = self.predefined_models() - model_map = {model.model: model for model in models} - - mode = credentials.get("mode") - - if mode == "chat": - base_model_schema = model_map["command-light-chat"] - else: - base_model_schema = model_map["command-light"] - - base_model_schema = cast(AIModelEntity, base_model_schema) - - base_model_schema_features = base_model_schema.features or [] - base_model_schema_model_properties = base_model_schema.model_properties or {} - base_model_schema_parameters_rules = base_model_schema.parameter_rules or [] - - entity = AIModelEntity( - model=model, - label=I18nObject(zh_Hans=model, en_US=model), - model_type=ModelType.LLM, - features=list(base_model_schema_features), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties=dict(base_model_schema_model_properties.items()), - parameter_rules=list(base_model_schema_parameters_rules), - pricing=base_model_schema.pricing, - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [cohere.errors.service_unavailable_error.ServiceUnavailableError], - InvokeServerUnavailableError: [cohere.errors.internal_server_error.InternalServerError], - InvokeRateLimitError: [cohere.errors.too_many_requests_error.TooManyRequestsError], - InvokeAuthorizationError: [ - cohere.errors.unauthorized_error.UnauthorizedError, - cohere.errors.forbidden_error.ForbiddenError, - ], - InvokeBadRequestError: [ - cohere.core.api_error.ApiError, - cohere.errors.bad_request_error.BadRequestError, - cohere.errors.not_found_error.NotFoundError, - ], - } diff --git a/api/core/model_runtime/model_providers/cohere/rerank/__init__.py b/api/core/model_runtime/model_providers/cohere/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/_position.yaml b/api/core/model_runtime/model_providers/cohere/rerank/_position.yaml deleted file mode 100644 index 4dd58fc170..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/_position.yaml +++ /dev/null @@ -1,4 +0,0 @@ -- rerank-english-v2.0 -- rerank-english-v3.0 -- rerank-multilingual-v2.0 -- rerank-multilingual-v3.0 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v2.0.yaml b/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v2.0.yaml deleted file mode 100644 index 70b4a91a0d..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v2.0.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: rerank-english-v2.0 -model_type: rerank -model_properties: - context_size: 5120 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v3.0.yaml deleted file mode 100644 index 3779f0b6c2..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank-english-v3.0.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: rerank-english-v3.0 -model_type: rerank -model_properties: - context_size: 5120 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v2.0.yaml b/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v2.0.yaml deleted file mode 100644 index c9b90387cf..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v2.0.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: rerank-multilingual-v2.0 -model_type: rerank -model_properties: - context_size: 5120 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v3.0.yaml deleted file mode 100644 index 4f6690ba76..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank-multilingual-v3.0.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: rerank-multilingual-v3.0 -model_type: rerank -model_properties: - context_size: 5120 diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank.py b/api/core/model_runtime/model_providers/cohere/rerank/rerank.py deleted file mode 100644 index aba8fedbc0..0000000000 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import Optional - -import cohere -from cohere.core import RequestOptions - -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class CohereRerankModel(RerankModel): - """ - Model class for Cohere rerank model. - """ - - 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 - """ - if len(docs) == 0: - return RerankResult(model=model, docs=docs) - - # initialize client - client = cohere.Client(credentials.get("api_key"), base_url=credentials.get("base_url")) - response = client.rerank( - query=query, - documents=docs, - model=model, - top_n=top_n, - return_documents=True, - request_options=RequestOptions(max_retries=0), - ) - - rerank_documents = [] - for idx, result in enumerate(response.results): - # format document - rerank_document = RerankDocument( - index=result.index, - text=result.document.text, - score=result.relevance_score, - ) - - # score threshold check - if score_threshold is not None: - if result.relevance_score >= score_threshold: - rerank_documents.append(rerank_document) - else: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self.invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [cohere.errors.service_unavailable_error.ServiceUnavailableError], - InvokeServerUnavailableError: [cohere.errors.internal_server_error.InternalServerError], - InvokeRateLimitError: [cohere.errors.too_many_requests_error.TooManyRequestsError], - InvokeAuthorizationError: [ - cohere.errors.unauthorized_error.UnauthorizedError, - cohere.errors.forbidden_error.ForbiddenError, - ], - InvokeBadRequestError: [ - cohere.core.api_error.ApiError, - cohere.errors.bad_request_error.BadRequestError, - cohere.errors.not_found_error.NotFoundError, - ], - } diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/__init__.py b/api/core/model_runtime/model_providers/cohere/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/_position.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/_position.yaml deleted file mode 100644 index 967a946f34..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/_position.yaml +++ /dev/null @@ -1,7 +0,0 @@ -- embed-multilingual-v3.0 -- embed-multilingual-light-v3.0 -- embed-english-v3.0 -- embed-english-light-v3.0 -- embed-multilingual-v2.0 -- embed-english-v2.0 -- embed-english-light-v2.0 diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v2.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v2.0.yaml deleted file mode 100644 index 8d2aaf1737..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v2.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-english-light-v2.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v3.0.yaml deleted file mode 100644 index 43b79922e3..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-light-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-english-light-v3.0 -model_type: text-embedding -model_properties: - context_size: 384 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v2.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v2.0.yaml deleted file mode 100644 index acee82b202..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v2.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-english-v2.0 -model_type: text-embedding -model_properties: - context_size: 4096 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v3.0.yaml deleted file mode 100644 index 0ad713253e..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-english-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-english-v3.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-light-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-light-v3.0.yaml deleted file mode 100644 index c253067233..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-light-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-multilingual-light-v3.0 -model_type: text-embedding -model_properties: - context_size: 384 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v2.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v2.0.yaml deleted file mode 100644 index 4dbc37d5e8..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v2.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-multilingual-v2.0 -model_type: text-embedding -model_properties: - context_size: 768 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v3.0.yaml b/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v3.0.yaml deleted file mode 100644 index ec689ada1b..0000000000 --- a/api/core/model_runtime/model_providers/cohere/text_embedding/embed-multilingual-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embed-multilingual-v3.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/deepseek/__init__.py b/api/core/model_runtime/model_providers/deepseek/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg deleted file mode 100644 index 425494404f..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - Created with Pixso. - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg deleted file mode 100644 index aa854a7504..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/api/core/model_runtime/model_providers/deepseek/deepseek.py b/api/core/model_runtime/model_providers/deepseek/deepseek.py deleted file mode 100644 index 10feef8972..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/deepseek.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class DeepSeekProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `deepseek-chat` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="deepseek-chat", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/deepseek/deepseek.yaml b/api/core/model_runtime/model_providers/deepseek/deepseek.yaml deleted file mode 100644 index 16abd358d6..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/deepseek.yaml +++ /dev/null @@ -1,41 +0,0 @@ -provider: deepseek -label: - en_US: deepseek - zh_Hans: 深度求索 -description: - en_US: Models provided by deepseek, such as deepseek-chat、deepseek-coder. - zh_Hans: 深度求索提供的模型,例如 deepseek-chat、deepseek-coder 。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#c0cdff" -help: - title: - en_US: Get your API Key from deepseek - zh_Hans: 从深度求索获取 API Key - url: - en_US: https://platform.deepseek.com/api_keys -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: endpoint_url - label: - zh_Hans: 自定义 API endpoint 地址 - en_US: Custom API endpoint URL - type: text-input - required: false - placeholder: - zh_Hans: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com - en_US: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com diff --git a/api/core/model_runtime/model_providers/deepseek/llm/__init__.py b/api/core/model_runtime/model_providers/deepseek/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/deepseek/llm/_position.yaml b/api/core/model_runtime/model_providers/deepseek/llm/_position.yaml deleted file mode 100644 index 43d03f2ee9..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/llm/_position.yaml +++ /dev/null @@ -1,2 +0,0 @@ -- deepseek-chat -- deepseek-coder diff --git a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml b/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml deleted file mode 100644 index 4973ac8ad6..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml +++ /dev/null @@ -1,78 +0,0 @@ -model: deepseek-chat -label: - zh_Hans: deepseek-chat - en_US: deepseek-chat -model_type: llm -features: - - agent-thought - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 1 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 1 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. - - name: logprobs - help: - zh_Hans: 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。 - en_US: Whether to return the log probability of the output token. If true, returns the log probability of each output token in the content of message . - type: boolean - - name: top_logprobs - type: int - default: 0 - min: 0 - max: 20 - help: - zh_Hans: 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。 - en_US: An integer N between 0 and 20, specifying that each output position returns the top N tokens with output probability, and returns the logarithmic probability of these tokens. When specifying this parameter, logprobs must be true. - - name: frequency_penalty - use_template: frequency_penalty - default: 0 - min: -2.0 - max: 2.0 - help: - zh_Hans: 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 - en_US: A number between -2.0 and 2.0. If the value is positive, new tokens are penalized based on their frequency of occurrence in existing text, reducing the likelihood that the model will repeat the same content. - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '1' - output: '2' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-coder.yaml b/api/core/model_runtime/model_providers/deepseek/llm/deepseek-coder.yaml deleted file mode 100644 index caafeadadd..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-coder.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: deepseek-coder -label: - zh_Hans: deepseek-coder - en_US: deepseek-coder -model_type: llm -features: - - agent-thought - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 diff --git a/api/core/model_runtime/model_providers/deepseek/llm/llm.py b/api/core/model_runtime/model_providers/deepseek/llm/llm.py deleted file mode 100644 index 6d0a3ee262..0000000000 --- a/api/core/model_runtime/model_providers/deepseek/llm/llm.py +++ /dev/null @@ -1,116 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union -from urllib.parse import urlparse - -import tiktoken - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import ( - PromptMessage, - PromptMessageTool, -) -from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel - - -class DeepSeekLargeLanguageModel(OpenAILargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_string(self, model: str, text: str, tools: Optional[list[PromptMessageTool]] = None) -> int: - """ - Calculate num tokens for text completion model with tiktoken package. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - encoding = tiktoken.get_encoding("cl100k_base") - num_tokens = len(encoding.encode(text)) - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_messages( - self, model: str, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. - - Official documentation: https://github.com/openai/openai-cookbook/blob/ - main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - encoding = tiktoken.get_encoding("cl100k_base") - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += len(encoding.encode(t_key)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(encoding.encode(f_key)) - num_tokens += len(encoding.encode(f_value)) - else: - num_tokens += len(encoding.encode(t_key)) - num_tokens += len(encoding.encode(t_value)) - else: - num_tokens += len(encoding.encode(str(value))) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - @staticmethod - def _add_custom_parameters(credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["openai_api_key"] = credentials["api_key"] - if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": - credentials["openai_api_base"] = "https://api.deepseek.com" - else: - parsed_url = urlparse(credentials["endpoint_url"]) - credentials["openai_api_base"] = f"{parsed_url.scheme}://{parsed_url.netloc}" diff --git a/api/core/model_runtime/model_providers/fireworks/__init__.py b/api/core/model_runtime/model_providers/fireworks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/fireworks/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/fireworks/_assets/icon_l_en.svg deleted file mode 100644 index 582605cc42..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/_assets/icon_l_en.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/fireworks/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/fireworks/_assets/icon_s_en.svg deleted file mode 100644 index 86eeba66f9..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/_assets/icon_s_en.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/api/core/model_runtime/model_providers/fireworks/_common.py b/api/core/model_runtime/model_providers/fireworks/_common.py deleted file mode 100644 index 378ced3a40..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/_common.py +++ /dev/null @@ -1,52 +0,0 @@ -from collections.abc import Mapping - -import openai - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonFireworks: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["fireworks_api_key"], - "base_url": "https://api.fireworks.ai/inference/v1", - "max_retries": 1, - } - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [openai.APIConnectionError, openai.APITimeoutError], - InvokeServerUnavailableError: [openai.InternalServerError], - InvokeRateLimitError: [openai.RateLimitError], - InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError], - InvokeBadRequestError: [ - openai.BadRequestError, - openai.NotFoundError, - openai.UnprocessableEntityError, - openai.APIError, - ], - } diff --git a/api/core/model_runtime/model_providers/fireworks/fireworks.py b/api/core/model_runtime/model_providers/fireworks/fireworks.py deleted file mode 100644 index 15f25badab..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/fireworks.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class FireworksProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - model_instance.validate_credentials( - model="accounts/fireworks/models/llama-v3p1-8b-instruct", credentials=credentials - ) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/fireworks/llm/__init__.py b/api/core/model_runtime/model_providers/fireworks/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/fireworks/llm/_position.yaml b/api/core/model_runtime/model_providers/fireworks/llm/_position.yaml deleted file mode 100644 index 9f7c1af68c..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/_position.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- llama-v3p1-405b-instruct -- llama-v3p1-70b-instruct -- llama-v3p1-8b-instruct -- llama-v3-70b-instruct -- mixtral-8x22b-instruct -- mixtral-8x7b-instruct -- firefunction-v2 -- firefunction-v1 -- gemma2-9b-it -- llama-v3-70b-instruct-hf -- llama-v3-8b-instruct -- llama-v3-8b-instruct-hf -- mixtral-8x7b-instruct-hf -- mythomax-l2-13b -- phi-3-vision-128k-instruct -- yi-large diff --git a/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v1.yaml b/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v1.yaml deleted file mode 100644 index f6bac12832..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v1.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/firefunction-v1 -label: - zh_Hans: Firefunction V1 - en_US: Firefunction V1 -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.5' - output: '0.5' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v2.yaml b/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v2.yaml deleted file mode 100644 index 2979cb46d5..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/firefunction-v2.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/firefunction-v2 -label: - zh_Hans: Firefunction V2 - en_US: Firefunction V2 -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.9' - output: '0.9' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/gemma2-9b-it.yaml b/api/core/model_runtime/model_providers/fireworks/llm/gemma2-9b-it.yaml deleted file mode 100644 index ee41a7e2fd..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/gemma2-9b-it.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: accounts/fireworks/models/gemma2-9b-it -label: - zh_Hans: Gemma2 9B Instruct - en_US: Gemma2 9B Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct-hf.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct-hf.yaml deleted file mode 100644 index 2ae89b8816..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct-hf.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3-70b-instruct-hf -label: - zh_Hans: Llama3 70B Instruct(HF version) - en_US: Llama3 70B Instruct(HF version) -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.9' - output: '0.9' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct.yaml deleted file mode 100644 index 7c24b08ca5..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-70b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3-70b-instruct -label: - zh_Hans: Llama3 70B Instruct - en_US: Llama3 70B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.9' - output: '0.9' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct-hf.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct-hf.yaml deleted file mode 100644 index 83507ef3e5..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct-hf.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3-8b-instruct-hf -label: - zh_Hans: Llama3 8B Instruct(HF version) - en_US: Llama3 8B Instruct(HF version) -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct.yaml deleted file mode 100644 index d8ac9537b8..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3-8b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3-8b-instruct -label: - zh_Hans: Llama3 8B Instruct - en_US: Llama3 8B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-405b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-405b-instruct.yaml deleted file mode 100644 index c4ddb3e924..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-405b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3p1-405b-instruct -label: - zh_Hans: Llama3.1 405B Instruct - en_US: Llama3.1 405B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '3' - output: '3' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-70b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-70b-instruct.yaml deleted file mode 100644 index 62f84f87fa..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-70b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3p1-70b-instruct -label: - zh_Hans: Llama3.1 70B Instruct - en_US: Llama3.1 70B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-8b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-8b-instruct.yaml deleted file mode 100644 index 9bb99c91b6..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llama-v3p1-8b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/llama-v3p1-8b-instruct -label: - zh_Hans: Llama3.1 8B Instruct - en_US: Llama3.1 8B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/llm.py b/api/core/model_runtime/model_providers/fireworks/llm/llm.py deleted file mode 100644 index 2dcf1adba6..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/llm.py +++ /dev/null @@ -1,610 +0,0 @@ -import logging -from collections.abc import Generator -from typing import Optional, Union, cast - -from openai import OpenAI, Stream -from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall -from openai.types.chat.chat_completion_message import FunctionCall - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.fireworks._common import _CommonFireworks - -logger = logging.getLogger(__name__) - -FIREWORKS_BLOCK_MODE_PROMPT = """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 - - -class FireworksLargeLanguageModel(_CommonFireworks, LargeLanguageModel): - """ - Model class for Fireworks large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - - return self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: Optional[list[Callback]] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - if "response_format" in model_parameters and model_parameters["response_format"] in {"JSON", "XML"}: - stop = stop or [] - self._transform_chat_json_prompts( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - response_format=model_parameters["response_format"], - ) - model_parameters.pop("response_format") - - return self._invoke( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _transform_chat_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts - """ - if stop is None: - stop = [] - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - - if len(prompt_messages) > 0 and isinstance(prompt_messages[0], SystemPromptMessage): - prompt_messages[0] = SystemPromptMessage( - content=FIREWORKS_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}\n")) - else: - prompt_messages.insert( - 0, - SystemPromptMessage( - content=FIREWORKS_BLOCK_MODE_PROMPT.replace( - "{{instructions}}", f"Please output a valid {response_format} object." - ).replace("{{block}}", response_format) - ), - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - return self._num_tokens_from_messages(model, prompt_messages, tools) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - client.chat.completions.create( - messages=[{"role": "user", "content": "ping"}], model=model, temperature=0, max_tokens=10, stream=False - ) - except Exception as e: - raise CredentialsValidateFailedError(str(e)) - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - extra_model_kwargs = {} - - if tools: - extra_model_kwargs["functions"] = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} for tool in tools - ] - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - # chat model - response = client.chat.completions.create( - messages=[self._convert_prompt_message_to_dict(m) for m in prompt_messages], - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages, tools) - return self._handle_chat_generate_response(model, credentials, response, prompt_messages, tools) - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: ChatCompletion, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - assistant_message = response.choices[0].message - # assistant_message_tool_calls = assistant_message.tool_calls - assistant_message_function_call = assistant_message.function_call - - # extract tool calls from response - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - completion_tokens = self._num_tokens_from_messages(model, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=response.model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return response - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Stream[ChatCompletionChunk], - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response chunk generator - """ - full_assistant_content = "" - delta_assistant_message_function_call_storage: Optional[ChoiceDeltaFunctionCall] = None - prompt_tokens = 0 - completion_tokens = 0 - final_tool_calls = [] - final_chunk = LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=""), - ), - ) - - for chunk in response: - if len(chunk.choices) == 0: - if chunk.usage: - # calculate num tokens - prompt_tokens = chunk.usage.prompt_tokens - completion_tokens = chunk.usage.completion_tokens - continue - - delta = chunk.choices[0] - has_finish_reason = delta.finish_reason is not None - - if ( - not has_finish_reason - and (delta.delta.content is None or delta.delta.content == "") - and delta.delta.function_call is None - ): - continue - - # assistant_message_tool_calls = delta.delta.tool_calls - assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if delta_assistant_message_function_call_storage is not None: - # handle process of stream function call - if assistant_message_function_call: - # message has not ended ever - delta_assistant_message_function_call_storage.arguments += assistant_message_function_call.arguments - continue - else: - # message has ended - assistant_message_function_call = delta_assistant_message_function_call_storage - delta_assistant_message_function_call_storage = None - else: - if assistant_message_function_call: - # start of stream function call - delta_assistant_message_function_call_storage = assistant_message_function_call - if delta_assistant_message_function_call_storage.arguments is None: - delta_assistant_message_function_call_storage.arguments = "" - if not has_finish_reason: - continue - - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - if tool_calls: - final_tool_calls.extend(tool_calls) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.delta.content or "", tool_calls=tool_calls) - - full_assistant_content += delta.delta.content or "" - - if has_finish_reason: - final_chunk = LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - if not prompt_tokens: - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - - if not completion_tokens: - full_assistant_prompt_message = AssistantPromptMessage( - content=full_assistant_content, tool_calls=final_tool_calls - ) - completion_tokens = self._num_tokens_from_messages(model, [full_assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - final_chunk.delta.usage = usage - - yield final_chunk - - def _extract_response_tool_calls( - self, response_tool_calls: list[ChatCompletionMessageToolCall | ChoiceDeltaToolCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - - return tool_calls - - def _extract_response_function_call( - self, response_function_call: FunctionCall | ChoiceDeltaFunctionCall - ) -> AssistantPromptMessage.ToolCall: - """ - Extract function call from response - - :param response_function_call: response function call - :return: tool call - """ - tool_call = None - if response_function_call: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function_call.name, arguments=response_function_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_function_call.name, type="function", function=function - ) - - return tool_call - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for Fireworks API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - # message_dict["tool_calls"] = [tool_call.dict() for tool_call in - # message.tool_calls] - function_call = message.tool_calls[0] - message_dict["function_call"] = { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - # message_dict = { - # "role": "tool", - # "content": message.content, - # "tool_call_id": message.tool_call_id - # } - message_dict = {"role": "function", "content": message.content, "name": message.tool_call_id} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _num_tokens_from_messages( - self, - model: str, - messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - credentials: dict = None, - ) -> int: - """ - Approximate num tokens with GPT2 tokenizer. - """ - - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += self._get_num_tokens_by_gpt2(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += self._get_num_tokens_by_gpt2(f_key) - num_tokens += self._get_num_tokens_by_gpt2(f_value) - else: - num_tokens += self._get_num_tokens_by_gpt2(t_key) - num_tokens += self._get_num_tokens_by_gpt2(t_value) - else: - num_tokens += self._get_num_tokens_by_gpt2(str(value)) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_for_tools(self, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling with tiktoken package. - - :param tools: tools for tool calling - :return: number of tokens - """ - num_tokens = 0 - for tool in tools: - num_tokens += self._get_num_tokens_by_gpt2("type") - num_tokens += self._get_num_tokens_by_gpt2("function") - num_tokens += self._get_num_tokens_by_gpt2("function") - - # calculate num tokens for function object - num_tokens += self._get_num_tokens_by_gpt2("name") - num_tokens += self._get_num_tokens_by_gpt2(tool.name) - num_tokens += self._get_num_tokens_by_gpt2("description") - num_tokens += self._get_num_tokens_by_gpt2(tool.description) - parameters = tool.parameters - num_tokens += self._get_num_tokens_by_gpt2("parameters") - if "title" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("title") - num_tokens += self._get_num_tokens_by_gpt2(parameters.get("title")) - num_tokens += self._get_num_tokens_by_gpt2("type") - num_tokens += self._get_num_tokens_by_gpt2(parameters.get("type")) - if "properties" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("properties") - for key, value in parameters.get("properties").items(): - num_tokens += self._get_num_tokens_by_gpt2(key) - for field_key, field_value in value.items(): - num_tokens += self._get_num_tokens_by_gpt2(field_key) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += self._get_num_tokens_by_gpt2(enum_field) - else: - num_tokens += self._get_num_tokens_by_gpt2(field_key) - num_tokens += self._get_num_tokens_by_gpt2(str(field_value)) - if "required" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("required") - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += self._get_num_tokens_by_gpt2(required_field) - - return num_tokens diff --git a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x22b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x22b-instruct.yaml deleted file mode 100644 index 87d977e26c..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x22b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/mixtral-8x22b-instruct -label: - zh_Hans: Mixtral MoE 8x22B Instruct - en_US: Mixtral MoE 8x22B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 65536 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '1.2' - output: '1.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct-hf.yaml b/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct-hf.yaml deleted file mode 100644 index e3d5a90858..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct-hf.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/mixtral-8x7b-instruct-hf -label: - zh_Hans: Mixtral MoE 8x7B Instruct(HF version) - en_US: Mixtral MoE 8x7B Instruct(HF version) -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.5' - output: '0.5' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct.yaml deleted file mode 100644 index 45f632ceff..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/mixtral-8x7b-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/mixtral-8x7b-instruct -label: - zh_Hans: Mixtral MoE 8x7B Instruct - en_US: Mixtral MoE 8x7B Instruct -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.5' - output: '0.5' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/mythomax-l2-13b.yaml b/api/core/model_runtime/model_providers/fireworks/llm/mythomax-l2-13b.yaml deleted file mode 100644 index 9c3486ba10..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/mythomax-l2-13b.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/mythomax-l2-13b -label: - zh_Hans: MythoMax L2 13b - en_US: MythoMax L2 13b -model_type: llm -features: - - agent-thought - - tool-call -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/phi-3-vision-128k-instruct.yaml b/api/core/model_runtime/model_providers/fireworks/llm/phi-3-vision-128k-instruct.yaml deleted file mode 100644 index e399f2edb1..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/phi-3-vision-128k-instruct.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: accounts/fireworks/models/phi-3-vision-128k-instruct -label: - zh_Hans: Phi3.5 Vision Instruct - en_US: Phi3.5 Vision Instruct -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '0.2' - output: '0.2' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fireworks/llm/yi-large.yaml b/api/core/model_runtime/model_providers/fireworks/llm/yi-large.yaml deleted file mode 100644 index bb4b6f994e..0000000000 --- a/api/core/model_runtime/model_providers/fireworks/llm/yi-large.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: accounts/yi-01-ai/models/yi-large -label: - zh_Hans: Yi-Large - en_US: Yi-Large -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - - name: max_tokens - use_template: max_tokens - - name: context_length_exceeded_behavior - default: None - label: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - help: - zh_Hans: 上下文长度超出行为 - en_US: Context Length Exceeded Behavior - type: string - options: - - None - - truncate - - error - - name: response_format - use_template: response_format -pricing: - input: '3' - output: '3' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/fishaudio/__init__.py b/api/core/model_runtime/model_providers/fishaudio/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_l_en.svg b/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_l_en.svg deleted file mode 100644 index d6f7723bd5..0000000000 --- a/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_l_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_s_en.svg b/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_s_en.svg deleted file mode 100644 index d6f7723bd5..0000000000 --- a/api/core/model_runtime/model_providers/fishaudio/_assets/fishaudio_s_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/fishaudio/fishaudio.py b/api/core/model_runtime/model_providers/fishaudio/fishaudio.py deleted file mode 100644 index 3bc4b533e0..0000000000 --- a/api/core/model_runtime/model_providers/fishaudio/fishaudio.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class FishAudioProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - For debugging purposes, this method now always passes validation. - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.TTS) - model_instance.validate_credentials(credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/fishaudio/tts/__init__.py b/api/core/model_runtime/model_providers/fishaudio/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/fishaudio/tts/tts.py b/api/core/model_runtime/model_providers/fishaudio/tts/tts.py deleted file mode 100644 index 895a7a914c..0000000000 --- a/api/core/model_runtime/model_providers/fishaudio/tts/tts.py +++ /dev/null @@ -1,158 +0,0 @@ -from typing import Optional - -import httpx - -from core.model_runtime.errors.invoke import InvokeBadRequestError, InvokeError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.tts_model import TTSModel - - -class FishAudioText2SpeechModel(TTSModel): - """ - Model class for Fish.audio Text to Speech model. - """ - - def get_tts_model_voices(self, model: str, credentials: dict, language: Optional[str] = None) -> list: - api_base = credentials.get("api_base", "https://api.fish.audio") - api_key = credentials.get("api_key") - use_public_models = credentials.get("use_public_models", "false") == "true" - - params = { - "self": str(not use_public_models).lower(), - "page_size": "100", - } - - if language is not None: - if "-" in language: - language = language.split("-")[0] - params["language"] = language - - results = httpx.get( - f"{api_base}/model", - headers={"Authorization": f"Bearer {api_key}"}, - params=params, - ) - - results.raise_for_status() - data = results.json() - - return [{"name": i["title"], "value": i["_id"]} for i in data["items"]] - - def _invoke( - self, - model: str, - tenant_id: str, - credentials: dict, - content_text: str, - voice: str, - user: Optional[str] = None, - ) -> any: - """ - Invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param voice: model timbre - :param content_text: text content to be translated - :param user: unique user id - :return: generator yielding audio chunks - """ - - return self._tts_invoke_streaming( - model=model, - credentials=credentials, - content_text=content_text, - voice=voice, - ) - - def validate_credentials(self, credentials: dict, user: Optional[str] = None) -> None: - """ - Validate credentials for text2speech model - - :param credentials: model credentials - :param user: unique user id - """ - - try: - self.get_tts_model_voices( - None, - credentials={ - "api_key": credentials["api_key"], - "api_base": credentials["api_base"], - # Disable public models will trigger a 403 error if user is not logged in - "use_public_models": "false", - }, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: - """ - Invoke streaming text2speech model - :param model: model name - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: ID of the reference audio (if any) - :return: generator yielding audio chunks - """ - - try: - word_limit = self._get_model_word_limit(model, credentials) - if len(content_text) > word_limit: - sentences = self._split_text_into_sentences(content_text, max_length=word_limit) - else: - sentences = [content_text.strip()] - - for i in range(len(sentences)): - yield from self._tts_invoke_streaming_sentence( - credentials=credentials, content_text=sentences[i], voice=voice - ) - - except Exception as ex: - raise InvokeBadRequestError(str(ex)) - - def _tts_invoke_streaming_sentence(self, credentials: dict, content_text: str, voice: Optional[str] = None) -> any: - """ - Invoke streaming text2speech model - - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: ID of the reference audio (if any) - :return: generator yielding audio chunks - """ - api_key = credentials.get("api_key") - api_url = credentials.get("api_base", "https://api.fish.audio") - latency = credentials.get("latency") - - if not api_key: - raise InvokeBadRequestError("API key is required") - - with httpx.stream( - "POST", - api_url + "/v1/tts", - json={"text": content_text, "reference_id": voice, "latency": latency}, - headers={ - "Authorization": f"Bearer {api_key}", - }, - timeout=None, - ) as response: - if response.status_code != 200: - raise InvokeBadRequestError(f"Error: {response.status_code} - {response.text}") - yield from response.iter_bytes() - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeBadRequestError: [ - httpx.HTTPStatusError, - ], - } diff --git a/api/core/model_runtime/model_providers/fishaudio/tts/tts.yaml b/api/core/model_runtime/model_providers/fishaudio/tts/tts.yaml deleted file mode 100644 index b4a446a957..0000000000 --- a/api/core/model_runtime/model_providers/fishaudio/tts/tts.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: tts-default -model_type: tts -model_properties: - word_limit: 1000 - audio_type: 'mp3' diff --git a/api/core/model_runtime/model_providers/google/__init__.py b/api/core/model_runtime/model_providers/google/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/google/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/google/_assets/icon_l_en.svg deleted file mode 100644 index bb23bffcf1..0000000000 --- a/api/core/model_runtime/model_providers/google/_assets/icon_l_en.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/google/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/google/_assets/icon_s_en.svg deleted file mode 100644 index c5c608cd7c..0000000000 --- a/api/core/model_runtime/model_providers/google/_assets/icon_s_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/google/google.py b/api/core/model_runtime/model_providers/google/google.py deleted file mode 100644 index 70f56a8337..0000000000 --- a/api/core/model_runtime/model_providers/google/google.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class GoogleProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `gemini-pro` model for validate, - model_instance.validate_credentials(model="gemini-pro", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/google/google.yaml b/api/core/model_runtime/model_providers/google/google.yaml deleted file mode 100644 index 69d4e371c4..0000000000 --- a/api/core/model_runtime/model_providers/google/google.yaml +++ /dev/null @@ -1,31 +0,0 @@ -provider: google -label: - en_US: Google -description: - en_US: Google's Gemini model. - zh_Hans: 谷歌提供的 Gemini 模型. -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FCFDFF" -help: - title: - en_US: Get your API Key from Google - zh_Hans: 从 Google 获取 API Key - url: - en_US: https://ai.google.dev/ -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: google_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/google/llm/__init__.py b/api/core/model_runtime/model_providers/google/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml deleted file mode 100644 index 4e0209890a..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-flash-8b-exp-0827 -label: - en_US: Gemini 1.5 Flash 8B 0827 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 1048576 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml deleted file mode 100644 index faabc5e4d1..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-flash-exp-0827 -label: - en_US: Gemini 1.5 Flash 0827 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 1048576 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml deleted file mode 100644 index a22fcca941..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-flash-latest -label: - en_US: Gemini 1.5 Flash Latest -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 1048576 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml deleted file mode 100644 index 97c68f7a18..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-pro-exp-0801 -label: - en_US: Gemini 1.5 Pro 0801 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 2097152 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml deleted file mode 100644 index 860e4816a1..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-pro-exp-0827 -label: - en_US: Gemini 1.5 Pro 0827 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 2097152 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml deleted file mode 100644 index d1bf7d269d..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml +++ /dev/null @@ -1,48 +0,0 @@ -model: gemini-1.5-pro-latest -label: - en_US: Gemini 1.5 Pro Latest -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 2097152 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-pro-vision.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-pro-vision.yaml deleted file mode 100644 index 2d213d56ad..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-pro-vision.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: gemini-pro-vision -label: - en_US: Gemini Pro Vision -model_type: llm -features: - - vision -model_properties: - mode: chat - context_size: 12288 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-pro.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-pro.yaml deleted file mode 100644 index e2f487c1ee..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/gemini-pro.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: gemini-pro -label: - en_US: Gemini Pro -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 30720 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens_to_sample - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 - - name: response_format - use_template: response_format - - name: stream - label: - zh_Hans: 流式输出 - en_US: Stream - type: boolean - help: - zh_Hans: 流式输出允许模型在生成文本的过程中逐步返回结果,而不是一次性生成全部结果后再返回。 - en_US: Streaming output allows the model to return results incrementally as it generates text, rather than generating all the results at once. - default: false -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/google/llm/llm.py b/api/core/model_runtime/model_providers/google/llm/llm.py deleted file mode 100644 index e686ad08d9..0000000000 --- a/api/core/model_runtime/model_providers/google/llm/llm.py +++ /dev/null @@ -1,443 +0,0 @@ -import base64 -import io -import json -import logging -from collections.abc import Generator -from typing import Optional, Union, cast - -import google.ai.generativelanguage as glm -import google.generativeai as genai -import requests -from google.api_core import exceptions -from google.generativeai.client import _ClientManager -from google.generativeai.types import ContentType, GenerateContentResponse -from google.generativeai.types.content_types import to_part -from PIL import Image - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) - -GEMINI_BLOCK_MODE_PROMPT = """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 - - -class GoogleLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # invoke model - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return:md = genai.GenerativeModel(model) - """ - prompt = self._convert_messages_to_prompt(prompt_messages) - - return self._get_num_tokens_by_gpt2(prompt) - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - """ - Format a list of messages into a full prompt for the Google model - - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - return text.rstrip() - - def _convert_tools_to_glm_tool(self, tools: list[PromptMessageTool]) -> glm.Tool: - """ - Convert tool messages to glm tools - - :param tools: tool messages - :return: glm tools - """ - return glm.Tool( - function_declarations=[ - glm.FunctionDeclaration( - name=tool.name, - parameters=glm.Schema( - type=glm.Type.OBJECT, - properties={ - key: { - "type_": value.get("type", "string").upper(), - "description": value.get("description", ""), - "enum": value.get("enum", []), - } - for key, value in tool.parameters.get("properties", {}).items() - }, - required=tool.parameters.get("required", []), - ), - ) - for tool in tools - ] - ) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - - try: - ping_message = SystemPromptMessage(content="ping") - self._generate(model, credentials, [ping_message], {"max_tokens_to_sample": 5}) - - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model - - :param model: model name - :param credentials: credentials kwargs - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - config_kwargs = model_parameters.copy() - config_kwargs["max_output_tokens"] = config_kwargs.pop("max_tokens_to_sample", None) - - if stop: - config_kwargs["stop_sequences"] = stop - - google_model = genai.GenerativeModel(model_name=model) - - history = [] - - # hack for gemini-pro-vision, which currently does not support multi-turn chat - if model == "gemini-pro-vision": - last_msg = prompt_messages[-1] - content = self._format_message_to_glm_content(last_msg) - history.append(content) - else: - for msg in prompt_messages: # makes message roles strictly alternating - content = self._format_message_to_glm_content(msg) - if history and history[-1]["role"] == content["role"]: - history[-1]["parts"].extend(content["parts"]) - else: - history.append(content) - - # Create a new ClientManager with tenant's API key - new_client_manager = _ClientManager() - new_client_manager.configure(api_key=credentials["google_api_key"]) - new_custom_client = new_client_manager.make_client("generative") - - google_model._client = new_custom_client - - response = google_model.generate_content( - contents=history, - generation_config=genai.types.GenerationConfig(**config_kwargs), - stream=stream, - tools=self._convert_tools_to_glm_tool(tools) if tools else None, - request_options={"timeout": 600}, - ) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: GenerateContentResponse, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=response.text) - - # calculate num tokens - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: GenerateContentResponse, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - index = -1 - for chunk in response: - for part in chunk.parts: - assistant_prompt_message = AssistantPromptMessage(content="") - - if part.text: - assistant_prompt_message.content += part.text - - if part.function_call: - assistant_prompt_message.tool_calls = [ - AssistantPromptMessage.ToolCall( - id=part.function_call.name, - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=part.function_call.name, - arguments=json.dumps(dict(part.function_call.args.items())), - ), - ) - ] - - index += 1 - - if not response._done: - # transform assistant message to prompt message - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message), - ) - else: - # calculate num tokens - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - finish_reason=str(chunk.candidates[0].finish_reason), - usage=usage, - ), - ) - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nuser:" - ai_prompt = "\n\nmodel:" - - content = message.content - if isinstance(content, list): - content = "".join(c.data for c in content if c.type != PromptMessageContentType.IMAGE) - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage | ToolPromptMessage): - message_text = f"{human_prompt} {content}" - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - def _format_message_to_glm_content(self, message: PromptMessage) -> ContentType: - """ - Format a single message into glm.Content for Google API - - :param message: one PromptMessage - :return: glm Content representation of message - """ - if isinstance(message, UserPromptMessage): - glm_content = {"role": "user", "parts": []} - if isinstance(message.content, str): - glm_content["parts"].append(to_part(message.content)) - else: - for c in message.content: - if c.type == PromptMessageContentType.TEXT: - glm_content["parts"].append(to_part(c.data)) - elif c.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, c) - if message_content.data.startswith("data:"): - metadata, base64_data = c.data.split(",", 1) - mime_type = metadata.split(";", 1)[0].split(":")[1] - else: - # fetch image data from url - try: - image_content = requests.get(message_content.data).content - with Image.open(io.BytesIO(image_content)) as img: - mime_type = f"image/{img.format.lower()}" - base64_data = base64.b64encode(image_content).decode("utf-8") - except Exception as ex: - raise ValueError(f"Failed to fetch image data from url {message_content.data}, {ex}") - blob = {"inline_data": {"mime_type": mime_type, "data": base64_data}} - glm_content["parts"].append(blob) - - return glm_content - elif isinstance(message, AssistantPromptMessage): - glm_content = {"role": "model", "parts": []} - if message.content: - glm_content["parts"].append(to_part(message.content)) - if message.tool_calls: - glm_content["parts"].append( - to_part( - glm.FunctionCall( - name=message.tool_calls[0].function.name, - args=json.loads(message.tool_calls[0].function.arguments), - ) - ) - ) - return glm_content - elif isinstance(message, SystemPromptMessage): - return {"role": "user", "parts": [to_part(message.content)]} - elif isinstance(message, ToolPromptMessage): - return { - "role": "function", - "parts": [ - glm.Part( - function_response=glm.FunctionResponse( - name=message.name, response={"response": message.content} - ) - ) - ], - } - else: - raise ValueError(f"Got unknown type {message}") - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the ermd = genai.GenerativeModel(model) error type thrown to the caller - The value is the md = genai.GenerativeModel(model) error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke emd = genai.GenerativeModel(model) error mapping - """ - return { - InvokeConnectionError: [exceptions.RetryError], - InvokeServerUnavailableError: [ - exceptions.ServiceUnavailable, - exceptions.InternalServerError, - exceptions.BadGateway, - exceptions.GatewayTimeout, - exceptions.DeadlineExceeded, - ], - InvokeRateLimitError: [exceptions.ResourceExhausted, exceptions.TooManyRequests], - InvokeAuthorizationError: [ - exceptions.Unauthenticated, - exceptions.PermissionDenied, - exceptions.Unauthenticated, - exceptions.Forbidden, - ], - InvokeBadRequestError: [ - exceptions.BadRequest, - exceptions.InvalidArgument, - exceptions.FailedPrecondition, - exceptions.OutOfRange, - exceptions.NotFound, - exceptions.MethodNotAllowed, - exceptions.Conflict, - exceptions.AlreadyExists, - exceptions.Aborted, - exceptions.LengthRequired, - exceptions.PreconditionFailed, - exceptions.RequestRangeNotSatisfiable, - exceptions.Cancelled, - ], - } diff --git a/api/core/model_runtime/model_providers/groq/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/groq/_assets/icon_l_en.svg deleted file mode 100644 index 2505a5f493..0000000000 --- a/api/core/model_runtime/model_providers/groq/_assets/icon_l_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/groq/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/groq/_assets/icon_s_en.svg deleted file mode 100644 index 087f37e471..0000000000 --- a/api/core/model_runtime/model_providers/groq/_assets/icon_s_en.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/api/core/model_runtime/model_providers/groq/groq.py b/api/core/model_runtime/model_providers/groq/groq.py deleted file mode 100644 index d0d5ff68f8..0000000000 --- a/api/core/model_runtime/model_providers/groq/groq.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class GroqProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="llama3-8b-8192", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/groq/groq.yaml b/api/core/model_runtime/model_providers/groq/groq.yaml deleted file mode 100644 index db17cc8bdd..0000000000 --- a/api/core/model_runtime/model_providers/groq/groq.yaml +++ /dev/null @@ -1,32 +0,0 @@ -provider: groq -label: - zh_Hans: GroqCloud - en_US: GroqCloud -description: - en_US: GroqCloud provides access to the Groq Cloud API, which hosts models like LLama2 and Mixtral. - zh_Hans: GroqCloud 提供对 Groq Cloud API 的访问,其中托管了 LLama2 和 Mixtral 等模型。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#F5F5F4" -help: - title: - en_US: Get your API Key from GroqCloud - zh_Hans: 从 GroqCloud 获取 API Key - url: - en_US: https://console.groq.com/ -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/groq/llm/_position.yaml b/api/core/model_runtime/model_providers/groq/llm/_position.yaml deleted file mode 100644 index be115ca920..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/_position.yaml +++ /dev/null @@ -1,7 +0,0 @@ -- llama-3.1-405b-reasoning -- llama-3.1-70b-versatile -- llama-3.1-8b-instant -- llama3-70b-8192 -- llama3-8b-8192 -- mixtral-8x7b-32768 -- llama2-70b-4096 diff --git a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-405b-reasoning.yaml b/api/core/model_runtime/model_providers/groq/llm/llama-3.1-405b-reasoning.yaml deleted file mode 100644 index 217785cea2..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-405b-reasoning.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama-3.1-405b-reasoning -label: - zh_Hans: Llama-3.1-405b-reasoning - en_US: Llama-3.1-405b-reasoning -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.05' - output: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-70b-versatile.yaml b/api/core/model_runtime/model_providers/groq/llm/llama-3.1-70b-versatile.yaml deleted file mode 100644 index ab5f6ab05e..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-70b-versatile.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama-3.1-70b-versatile -label: - zh_Hans: Llama-3.1-70b-versatile - en_US: Llama-3.1-70b-versatile -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.05' - output: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-8b-instant.yaml b/api/core/model_runtime/model_providers/groq/llm/llama-3.1-8b-instant.yaml deleted file mode 100644 index a82e64532e..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama-3.1-8b-instant.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama-3.1-8b-instant -label: - zh_Hans: Llama-3.1-8b-instant - en_US: Llama-3.1-8b-instant -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.05' - output: '0.1' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llama2-70b-4096.yaml b/api/core/model_runtime/model_providers/groq/llm/llama2-70b-4096.yaml deleted file mode 100644 index 384912b0dd..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama2-70b-4096.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama2-70b-4096 -label: - zh_Hans: Llama-2-70B-4096 - en_US: Llama-2-70B-4096 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 -pricing: - input: '0.7' - output: '0.8' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llama3-70b-8192.yaml b/api/core/model_runtime/model_providers/groq/llm/llama3-70b-8192.yaml deleted file mode 100644 index 91d0e30765..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama3-70b-8192.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama3-70b-8192 -label: - zh_Hans: Llama-3-70B-8192 - en_US: Llama-3-70B-8192 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.59' - output: '0.79' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llama3-8b-8192.yaml b/api/core/model_runtime/model_providers/groq/llm/llama3-8b-8192.yaml deleted file mode 100644 index b6154f761f..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llama3-8b-8192.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: llama3-8b-8192 -label: - zh_Hans: Llama-3-8B-8192 - en_US: Llama-3-8B-8192 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.05' - output: '0.08' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/groq/llm/llm.py b/api/core/model_runtime/model_providers/groq/llm/llm.py deleted file mode 100644 index 352a7b519e..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/llm.py +++ /dev/null @@ -1,31 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class GroqLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - @staticmethod - def _add_custom_parameters(credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = "https://api.groq.com/openai/v1" diff --git a/api/core/model_runtime/model_providers/groq/llm/mixtral-8x7b-instruct-v0.1.yaml b/api/core/model_runtime/model_providers/groq/llm/mixtral-8x7b-instruct-v0.1.yaml deleted file mode 100644 index 0dc6678fa2..0000000000 --- a/api/core/model_runtime/model_providers/groq/llm/mixtral-8x7b-instruct-v0.1.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: mixtral-8x7b-32768 -label: - zh_Hans: Mixtral-8x7b-Instruct-v0.1 - en_US: Mixtral-8x7b-Instruct-v0.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 20480 -pricing: - input: '0.27' - output: '0.27' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/huggingface_hub/__init__.py b/api/core/model_runtime/model_providers/huggingface_hub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_l_en.svg deleted file mode 100644 index 70135a08de..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_l_en.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_s_en.svg deleted file mode 100644 index 5a444f127f..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/_assets/icon_s_en.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/huggingface_hub/_common.py b/api/core/model_runtime/model_providers/huggingface_hub/_common.py deleted file mode 100644 index 3c4020b6ee..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/_common.py +++ /dev/null @@ -1,9 +0,0 @@ -from huggingface_hub.utils import BadRequestError, HfHubHTTPError - -from core.model_runtime.errors.invoke import InvokeBadRequestError, InvokeError - - -class _CommonHuggingfaceHub: - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - return {InvokeBadRequestError: [HfHubHTTPError, BadRequestError]} diff --git a/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.py b/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.py deleted file mode 100644 index 54d2a2bf39..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class HuggingfaceHubProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.yaml b/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.yaml deleted file mode 100644 index 1df234cf26..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/huggingface_hub.yaml +++ /dev/null @@ -1,102 +0,0 @@ -provider: huggingface_hub -label: - en_US: Hugging Face Model -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FFF8DC" -help: - title: - en_US: Get your API key from Hugging Face Hub - zh_Hans: 从 Hugging Face Hub 获取 API Key - url: - en_US: https://huggingface.co/settings/tokens -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - credential_form_schemas: - - variable: huggingfacehub_api_type - label: - en_US: Endpoint Type - zh_Hans: 端点类型 - type: radio - required: true - default: hosted_inference_api - options: - - value: hosted_inference_api - label: - en_US: Hosted Inference API - - value: inference_endpoints - label: - en_US: Inference Endpoints - - variable: huggingfacehub_api_token - label: - en_US: API Token - zh_Hans: API Token - type: secret-input - required: true - placeholder: - en_US: Enter your Hugging Face Hub API Token here - zh_Hans: 在此输入您的 Hugging Face Hub API Token - - variable: huggingface_namespace - label: - en_US: 'User Name / Organization Name' - zh_Hans: '用户名 / 组织名称' - type: text-input - required: true - placeholder: - en_US: 'Enter your User Name / Organization Name here' - zh_Hans: '在此输入您的用户名 / 组织名称' - show_on: - - variable: __model_type - value: text-embedding - - variable: huggingfacehub_api_type - value: inference_endpoints - - variable: huggingfacehub_endpoint_url - label: - en_US: Endpoint URL - zh_Hans: 端点 URL - type: text-input - required: true - placeholder: - en_US: Enter your Endpoint URL here - zh_Hans: 在此输入您的端点 URL - show_on: - - variable: huggingfacehub_api_type - value: inference_endpoints - - variable: task_type - label: - en_US: Task - zh_Hans: Task - type: select - options: - - value: text2text-generation - label: - en_US: Text-to-Text Generation - show_on: - - variable: __model_type - value: llm - - value: text-generation - label: - en_US: Text Generation - zh_Hans: 文本生成 - show_on: - - variable: __model_type - value: llm - - value: feature-extraction - label: - en_US: Feature Extraction - show_on: - - variable: __model_type - value: text-embedding - show_on: - - variable: huggingfacehub_api_type - value: inference_endpoints diff --git a/api/core/model_runtime/model_providers/huggingface_hub/llm/__init__.py b/api/core/model_runtime/model_providers/huggingface_hub/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py b/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py deleted file mode 100644 index 9d29237fdd..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py +++ /dev/null @@ -1,313 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from huggingface_hub import InferenceClient -from huggingface_hub.hf_api import HfApi -from huggingface_hub.utils import BadRequestError - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.defaults import PARAMETER_RULE_TEMPLATE -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.huggingface_hub._common import _CommonHuggingfaceHub - - -class HuggingfaceHubLargeLanguageModel(_CommonHuggingfaceHub, LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - client = InferenceClient(token=credentials["huggingfacehub_api_token"]) - - if credentials["huggingfacehub_api_type"] == "inference_endpoints": - model = credentials["huggingfacehub_endpoint_url"] - - if "baichuan" in model.lower(): - stream = False - - response = client.text_generation( - prompt=prompt_messages[0].content, - details=True, - stream=stream, - model=model, - stop_sequences=stop, - **model_parameters, - ) - - if stream: - return self._handle_generate_stream_response(model, credentials, prompt_messages, response) - - return self._handle_generate_response(model, credentials, prompt_messages, response) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - prompt = self._convert_messages_to_prompt(prompt_messages) - return self._get_num_tokens_by_gpt2(prompt) - - def validate_credentials(self, model: str, credentials: dict) -> None: - try: - if "huggingfacehub_api_type" not in credentials: - raise CredentialsValidateFailedError("Huggingface Hub Endpoint Type must be provided.") - - if credentials["huggingfacehub_api_type"] not in {"inference_endpoints", "hosted_inference_api"}: - raise CredentialsValidateFailedError("Huggingface Hub Endpoint Type is invalid.") - - if "huggingfacehub_api_token" not in credentials: - raise CredentialsValidateFailedError("Huggingface Hub Access Token must be provided.") - - if credentials["huggingfacehub_api_type"] == "inference_endpoints": - if "huggingfacehub_endpoint_url" not in credentials: - raise CredentialsValidateFailedError("Huggingface Hub Endpoint URL must be provided.") - - if "task_type" not in credentials: - raise CredentialsValidateFailedError("Huggingface Hub Task Type must be provided.") - elif credentials["huggingfacehub_api_type"] == "hosted_inference_api": - credentials["task_type"] = self._get_hosted_model_task_type( - credentials["huggingfacehub_api_token"], model - ) - - if credentials["task_type"] not in {"text2text-generation", "text-generation"}: - raise CredentialsValidateFailedError( - "Huggingface Hub Task Type must be one of text2text-generation, text-generation." - ) - - client = InferenceClient(token=credentials["huggingfacehub_api_token"]) - - if credentials["huggingfacehub_api_type"] == "inference_endpoints": - model = credentials["huggingfacehub_endpoint_url"] - - try: - client.text_generation(prompt="Who are you?", stream=True, model=model) - except BadRequestError as e: - raise CredentialsValidateFailedError( - "Only available for models running on with the `text-generation-inference`. " - "To learn more about the TGI project, please refer to https://github.com/huggingface/text-generation-inference." - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties={ModelPropertyKey.MODE: LLMMode.COMPLETION.value}, - parameter_rules=self._get_customizable_model_parameter_rules(), - ) - - return entity - - @staticmethod - def _get_customizable_model_parameter_rules() -> list[ParameterRule]: - temperature_rule_dict = PARAMETER_RULE_TEMPLATE.get(DefaultParameterName.TEMPERATURE).copy() - temperature_rule_dict["name"] = "temperature" - temperature_rule = ParameterRule(**temperature_rule_dict) - temperature_rule.default = 0.5 - - top_p_rule_dict = PARAMETER_RULE_TEMPLATE.get(DefaultParameterName.TOP_P).copy() - top_p_rule_dict["name"] = "top_p" - top_p_rule = ParameterRule(**top_p_rule_dict) - top_p_rule.default = 0.5 - - top_k_rule = ParameterRule( - name="top_k", - label={ - "en_US": "Top K", - "zh_Hans": "Top K", - }, - type="int", - help={ - "en_US": "The number of highest probability vocabulary tokens to keep for top-k-filtering.", - "zh_Hans": "保留的最高概率词汇标记的数量。", - }, - required=False, - default=2, - min=1, - max=10, - precision=0, - ) - - max_new_tokens = ParameterRule( - name="max_new_tokens", - label={ - "en_US": "Max New Tokens", - "zh_Hans": "最大新标记", - }, - type="int", - help={ - "en_US": "Maximum number of generated tokens.", - "zh_Hans": "生成的标记的最大数量。", - }, - required=False, - default=20, - min=1, - max=4096, - precision=0, - ) - - seed = ParameterRule( - name="seed", - label={ - "en_US": "Random sampling seed", - "zh_Hans": "随机采样种子", - }, - type="int", - help={ - "en_US": "Random sampling seed.", - "zh_Hans": "随机采样种子。", - }, - required=False, - precision=0, - ) - - repetition_penalty = ParameterRule( - name="repetition_penalty", - label={ - "en_US": "Repetition Penalty", - "zh_Hans": "重复惩罚", - }, - type="float", - help={ - "en_US": "The parameter for repetition penalty. 1.0 means no penalty.", - "zh_Hans": "重复惩罚的参数。1.0 表示没有惩罚。", - }, - required=False, - precision=1, - ) - - return [temperature_rule, top_k_rule, top_p_rule, max_new_tokens, seed, repetition_penalty] - - def _handle_generate_stream_response( - self, model: str, credentials: dict, prompt_messages: list[PromptMessage], response: Generator - ) -> Generator: - index = -1 - for chunk in response: - # skip special tokens - if chunk.token.special: - continue - - index += 1 - - assistant_prompt_message = AssistantPromptMessage(content=chunk.token.text) - - if chunk.details: - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - usage=usage, - finish_reason=chunk.details.finish_reason, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - ), - ) - - def _handle_generate_response( - self, model: str, credentials: dict, prompt_messages: list[PromptMessage], response: any - ) -> LLMResult: - if isinstance(response, str): - content = response - else: - content = response.generated_text - - assistant_prompt_message = AssistantPromptMessage(content=content) - - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - return result - - @staticmethod - def _get_hosted_model_task_type(huggingfacehub_api_token: str, model_name: str): - hf_api = HfApi(token=huggingfacehub_api_token) - model_info = hf_api.model_info(repo_id=model_name) - - try: - if not model_info: - raise ValueError(f"Model {model_name} not found.") - - if "inference" in model_info.cardData and not model_info.cardData["inference"]: - raise ValueError(f"Inference API has been turned off for this model {model_name}.") - - valid_tasks = ("text2text-generation", "text-generation") - if model_info.pipeline_tag not in valid_tasks: - raise ValueError(f"Model {model_name} is not a valid task, must be one of {valid_tasks}.") - except Exception as e: - raise CredentialsValidateFailedError(f"{str(e)}") - - return model_info.pipeline_tag - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - return text.rstrip() - - @staticmethod - def _convert_one_message_to_text(message: PromptMessage) -> str: - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - content = message.content - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage): - message_text = content - else: - raise ValueError(f"Got unknown type {message}") - - return message_text diff --git a/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/__init__.py b/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/huggingface_tei/__init__.py b/api/core/model_runtime/model_providers/huggingface_tei/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.py b/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.py deleted file mode 100644 index 97d7e28dc6..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class HuggingfaceTeiProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.yaml b/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.yaml deleted file mode 100644 index f3a912d84d..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_tei/huggingface_tei.yaml +++ /dev/null @@ -1,36 +0,0 @@ -provider: huggingface_tei -label: - en_US: Text Embedding Inference -description: - en_US: A blazing fast inference solution for text embeddings models. - zh_Hans: 用于文本嵌入模型的超快速推理解决方案。 -background: "#FFF8DC" -help: - title: - en_US: How to deploy Text Embedding Inference - zh_Hans: 如何部署 Text Embedding Inference - url: - en_US: https://github.com/huggingface/text-embeddings-inference -supported_model_types: - - text-embedding - - rerank -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: server_url - label: - zh_Hans: 服务器URL - en_US: Server url - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入Text Embedding Inference的服务器地址,如 http://192.168.1.100:8080 - en_US: Enter the url of your Text Embedding Inference, e.g. http://192.168.1.100:8080 diff --git a/api/core/model_runtime/model_providers/huggingface_tei/rerank/__init__.py b/api/core/model_runtime/model_providers/huggingface_tei/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/huggingface_tei/rerank/rerank.py b/api/core/model_runtime/model_providers/huggingface_tei/rerank/rerank.py deleted file mode 100644 index 74a1dfc3ff..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_tei/rerank/rerank.py +++ /dev/null @@ -1,136 +0,0 @@ -from typing import Optional - -import httpx - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelPropertyKey, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel -from core.model_runtime.model_providers.huggingface_tei.tei_helper import TeiHelper - - -class HuggingfaceTeiRerankModel(RerankModel): - """ - Model class for Text Embedding Inference rerank model. - """ - - 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 - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - server_url = credentials["server_url"] - - server_url = server_url.removesuffix("/") - - try: - results = TeiHelper.invoke_rerank(server_url, query, docs) - - rerank_documents = [] - for result in results: - rerank_document = RerankDocument( - index=result["index"], - text=result["text"], - score=result["score"], - ) - if score_threshold is None or result["score"] >= score_threshold: - rerank_documents.append(rerank_document) - if top_n is not None and len(rerank_documents) >= top_n: - break - - return RerankResult(model=model, docs=rerank_documents) - except httpx.HTTPStatusError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - server_url = credentials["server_url"] - extra_args = TeiHelper.get_tei_extra_parameter(server_url, model) - if extra_args.model_type != "reranker": - raise CredentialsValidateFailedError("Current model is not a rerank model") - - credentials["context_size"] = extra_args.max_input_length - - self.invoke( - model=model, - credentials=credentials, - query="Whose 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.RERANK, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", 512)), - }, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/huggingface_tei/tei_helper.py b/api/core/model_runtime/model_providers/huggingface_tei/tei_helper.py deleted file mode 100644 index 81ab249214..0000000000 --- a/api/core/model_runtime/model_providers/huggingface_tei/tei_helper.py +++ /dev/null @@ -1,182 +0,0 @@ -from threading import Lock -from time import time -from typing import Optional - -import httpx -from requests.adapters import HTTPAdapter -from requests.exceptions import ConnectionError, MissingSchema, Timeout -from requests.sessions import Session -from yarl import URL - - -class TeiModelExtraParameter: - model_type: str - max_input_length: int - max_client_batch_size: int - - def __init__(self, model_type: str, max_input_length: int, max_client_batch_size: Optional[int] = None) -> None: - self.model_type = model_type - self.max_input_length = max_input_length - self.max_client_batch_size = max_client_batch_size - - -cache = {} -cache_lock = Lock() - - -class TeiHelper: - @staticmethod - def get_tei_extra_parameter(server_url: str, model_name: str) -> TeiModelExtraParameter: - TeiHelper._clean_cache() - with cache_lock: - if model_name not in cache: - cache[model_name] = { - "expires": time() + 300, - "value": TeiHelper._get_tei_extra_parameter(server_url), - } - return cache[model_name]["value"] - - @staticmethod - def _clean_cache() -> None: - try: - with cache_lock: - expired_keys = [model_uid for model_uid, model in cache.items() if model["expires"] < time()] - for model_uid in expired_keys: - del cache[model_uid] - except RuntimeError as e: - pass - - @staticmethod - def _get_tei_extra_parameter(server_url: str) -> TeiModelExtraParameter: - """ - get tei model extra parameter like model_type, max_input_length, max_batch_requests - """ - - url = str(URL(server_url) / "info") - - # this method is surrounded by a lock, and default requests may hang forever, - # so we just set a Adapter with max_retries=3 - session = Session() - session.mount("http://", HTTPAdapter(max_retries=3)) - session.mount("https://", HTTPAdapter(max_retries=3)) - - try: - response = session.get(url, timeout=10) - except (MissingSchema, ConnectionError, Timeout) as e: - raise RuntimeError(f"get tei model extra parameter failed, url: {url}, error: {e}") - if response.status_code != 200: - raise RuntimeError( - f"get tei model extra parameter failed, status code: {response.status_code}, response: {response.text}" - ) - - response_json = response.json() - - model_type = response_json.get("model_type", {}) - if len(model_type.keys()) < 1: - raise RuntimeError("model_type is empty") - model_type = list(model_type.keys())[0] - if model_type not in {"embedding", "reranker"}: - raise RuntimeError(f"invalid model_type: {model_type}") - - max_input_length = response_json.get("max_input_length", 512) - max_client_batch_size = response_json.get("max_client_batch_size", 1) - - return TeiModelExtraParameter( - model_type=model_type, max_input_length=max_input_length, max_client_batch_size=max_client_batch_size - ) - - @staticmethod - def invoke_tokenize(server_url: str, texts: list[str]) -> list[list[dict]]: - """ - Invoke tokenize endpoint - - Example response: - [ - [ - { - "id": 0, - "text": "", - "special": true, - "start": null, - "stop": null - }, - { - "id": 7704, - "text": "str", - "special": false, - "start": 0, - "stop": 3 - }, - < MORE TOKENS > - ] - ] - - :param server_url: server url - :param texts: texts to tokenize - """ - resp = httpx.post( - f"{server_url}/tokenize", - json={"inputs": texts}, - ) - resp.raise_for_status() - return resp.json() - - @staticmethod - def invoke_embeddings(server_url: str, texts: list[str]) -> dict: - """ - Invoke embeddings endpoint - - Example response: - { - "object": "list", - "data": [ - { - "object": "embedding", - "embedding": [...], - "index": 0 - } - ], - "model": "MODEL_NAME", - "usage": { - "prompt_tokens": 3, - "total_tokens": 3 - } - } - - :param server_url: server url - :param texts: texts to embed - """ - # Use OpenAI compatible API here, which has usage tracking - resp = httpx.post( - f"{server_url}/v1/embeddings", - json={"input": texts}, - ) - resp.raise_for_status() - return resp.json() - - @staticmethod - def invoke_rerank(server_url: str, query: str, docs: list[str]) -> list[dict]: - """ - Invoke rerank endpoint - - Example response: - [ - { - "index": 0, - "text": "Deep Learning is ...", - "score": 0.9950755 - } - ] - - :param server_url: server url - :param texts: texts to rerank - :param candidates: candidates to rerank - """ - params = {"query": query, "texts": docs, "return_text": True} - - response = httpx.post( - server_url + "/rerank", - json=params, - ) - response.raise_for_status() - return response.json() diff --git a/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/__init__.py b/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/hunyuan/__init__.py b/api/core/model_runtime/model_providers/hunyuan/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/hunyuan/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/hunyuan/_assets/icon_l_en.png deleted file mode 100644 index 1303055ef8d1ee2b7bd127fa55c8f28a47a2ca4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68638 zcmZsCRa70@7G-dUV8Jc8ySux)ySqcM5G=U6L-0Uwcb9`haCZyt)|K0%Ki<9l0S6cy zsM>q2xu>iOS5lBfg2#pb@ZkfJw3L|2hYt`Iz}Mekp@Glzhb#BM3z(~lq{xS=3H+lE zABaCliwUcF8Ju>&_^K_WzrAPamRQe0NfCcxE`|>-Eod=uitY&CSyXT(8FBEallG{4 zAE&^hT<41EII+X0RJd~8UR2PlMyhf~9K>cK?lzK>#DH^;X7k%`ynb)@WYh9O?fAaC zIte4e>N>`~;d8M5yn4?+IYvw&EcEdsBqZ4XyaXc1eAS<-jEqJA|Mr!b7#8+_UW8y- zoj?BXkN)}2DG(0U*dx&M>tg$V-UACRk``|I|NU2TLa=oR?GSIc9FyFdIHvoCqK0Bb z|G6OHO-G$H=ZUg+7e9WrJ`{P)f`~J&n1x|>6iiZn?eEcvJQSIknPD~=7JGYp^Q?oX zi8@jsvVUDUEVv}>%RC(GM9a)?Xvdi_{3V5hANbUgwb{LFI|DJ}kz8I3FxSj~4v|yc zC%n5u&n%IBHr)k{#+lsnQ+3D2!yiK6vTGLsq3$Stgo-X zX|VS}WBX-zYf};F+u8Rm@;}Rv#TOW9kT&mBl@hF+Qy)E2Y^}g%|BSY>Z~n>i3d{>> zWd_@9%~%i0vei#5mYfT5^Q*1mk<-)3;fOPS+N!kk1@e&da&$cot37MguuDbp(>>LC zd$P9@$2jgVj{@Pd;YJn`(m1DkKbj%z!>eOb>ZkVvW{a2GTa_EBe{C}tCnST6abvVA zJ!1x8((X@|^u`LgF!@?KTd`#p1iP`6nMNoyHI9&OjB!kZue)!#m=;lEe|#9s4Xj-b z{WzT^3dS4lu^XYrYK2aU?o+Zye(xkqV0IW)GFZz-_>CSHfS=}k5`!^4eWz)6t$Y76 zu5+TxL*W)GBKVvCEtv7pr}`&xbL#?|0-M;suIz9Rk^$HFcYli@j!cX^&%WAhZAnH7 zdr!Txjg(GI-yW$*6lA)%3TOnDS<^{bEs9msi-4bbco>#7<6!tjKVS-$Z4eu=L^ew~ zfz!@uLe@k@cC>!-h%tB25yG0CG} z)(8G}!nKbl6Ct1$+lG#+M^caHf%ycv!N_>?cA0S1?Q=cG|NLcOU0^7>)bS5CQN&

8BrTmGxskG>j;_+WHOA}tK_II*N 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≫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

V79T5P``9UYA`t9GS?v0rsqd;8cS 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%0Amw?TXi)6xvr~2=}yc* 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Ë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 None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `hunyuan-standard` model for validate, - model_instance.validate_credentials(model="hunyuan-standard", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/hunyuan/hunyuan.yaml b/api/core/model_runtime/model_providers/hunyuan/hunyuan.yaml deleted file mode 100644 index 812b51ddcd..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/hunyuan.yaml +++ /dev/null @@ -1,41 +0,0 @@ -provider: hunyuan -label: - zh_Hans: 腾讯混元 - en_US: Hunyuan -description: - en_US: Models provided by Tencent Hunyuan, such as hunyuan-standard, hunyuan-standard-256k, hunyuan-pro and hunyuan-lite. - zh_Hans: 腾讯混元提供的模型,例如 hunyuan-standard、 hunyuan-standard-256k, hunyuan-pro 和 hunyuan-lite。 -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#F6F7F7" -help: - title: - en_US: Get your API Key from Tencent Hunyuan - zh_Hans: 从腾讯混元获取 API Key - url: - en_US: https://console.cloud.tencent.com/cam/capi -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: secret_id - label: - en_US: Secret ID - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 Secret ID - en_US: Enter your Secret ID - - variable: secret_key - label: - en_US: Secret Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 Secret Key - en_US: Enter your Secret Key diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/__init__.py b/api/core/model_runtime/model_providers/hunyuan/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/_position.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/_position.yaml deleted file mode 100644 index f494984443..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/_position.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- hunyuan-lite -- hunyuan-standard -- hunyuan-standard-256k -- hunyuan-pro -- hunyuan-turbo -- hunyuan-vision diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-lite.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-lite.yaml deleted file mode 100644 index 4f5a5dfb48..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-lite.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: hunyuan-lite -label: - zh_Hans: hunyuan-lite - en_US: hunyuan-lite -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 256000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 256000 -pricing: - input: '0.00' - output: '0.00' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-pro.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-pro.yaml deleted file mode 100644 index b173ffbe77..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-pro.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: hunyuan-pro -label: - zh_Hans: hunyuan-pro - en_US: hunyuan-pro -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 - - name: enable_enhance - label: - zh_Hans: 功能增强 - en_US: Enable Enhancement - type: boolean - help: - zh_Hans: 功能增强(如搜索)开关,关闭时将直接由主模型生成回复内容,可以降低响应时延(对于流式输出时的首字时延尤为明显)。但在少数场景里,回复效果可能会下降。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false - default: true -pricing: - input: '0.03' - output: '0.10' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard-256k.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard-256k.yaml deleted file mode 100644 index 1f94a8623b..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard-256k.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: hunyuan-standard-256k -label: - zh_Hans: hunyuan-standard-256k - en_US: hunyuan-standard-256k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 256000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 256000 - - name: enable_enhance - label: - zh_Hans: 功能增强 - en_US: Enable Enhancement - type: boolean - help: - zh_Hans: 功能增强(如搜索)开关,关闭时将直接由主模型生成回复内容,可以降低响应时延(对于流式输出时的首字时延尤为明显)。但在少数场景里,回复效果可能会下降。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false - default: true -pricing: - input: '0.015' - output: '0.06' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard.yaml deleted file mode 100644 index 1db25930fc..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-standard.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: hunyuan-standard -label: - zh_Hans: hunyuan-standard - en_US: hunyuan-standard -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 - - name: enable_enhance - label: - zh_Hans: 功能增强 - en_US: Enable Enhancement - type: boolean - help: - zh_Hans: 功能增强(如搜索)开关,关闭时将直接由主模型生成回复内容,可以降低响应时延(对于流式输出时的首字时延尤为明显)。但在少数场景里,回复效果可能会下降。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false - default: true -pricing: - input: '0.0045' - output: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-turbo.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-turbo.yaml deleted file mode 100644 index 4837fed4ba..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-turbo.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: hunyuan-turbo -label: - zh_Hans: hunyuan-turbo - en_US: hunyuan-turbo -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 - - name: enable_enhance - label: - zh_Hans: 功能增强 - en_US: Enable Enhancement - type: boolean - help: - zh_Hans: 功能增强(如搜索)开关,关闭时将直接由主模型生成回复内容,可以降低响应时延(对于流式输出时的首字时延尤为明显)。但在少数场景里,回复效果可能会下降。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false - default: true -pricing: - input: '0.015' - output: '0.05' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-vision.yaml b/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-vision.yaml deleted file mode 100644 index 9edc7f4710..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/hunyuan-vision.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: hunyuan-vision -label: - zh_Hans: hunyuan-vision - en_US: hunyuan-vision -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: enable_enhance - label: - zh_Hans: 功能增强 - en_US: Enable Enhancement - type: boolean - help: - zh_Hans: 功能增强(如搜索)开关,关闭时将直接由主模型生成回复内容,可以降低响应时延(对于流式输出时的首字时延尤为明显)。但在少数场景里,回复效果可能会下降。 - en_US: Allow the model to perform external search to enhance the generation results. - required: false - default: true -pricing: - input: '0.018' - output: '0.018' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/llm.py b/api/core/model_runtime/model_providers/hunyuan/llm/llm.py deleted file mode 100644 index 2014de8516..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/llm/llm.py +++ /dev/null @@ -1,348 +0,0 @@ -import json -import logging -from collections.abc import Generator -from typing import cast - -from tencentcloud.common import credential -from tencentcloud.common.exception import TencentCloudSDKException -from tencentcloud.common.profile.client_profile import ClientProfile -from tencentcloud.common.profile.http_profile import HttpProfile -from tencentcloud.hunyuan.v20230901 import hunyuan_client, models - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import InvokeError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) - - -class HunyuanLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - client = self._setup_hunyuan_client(credentials) - request = models.ChatCompletionsRequest() - messages_dict = self._convert_prompt_messages_to_dicts(prompt_messages) - - custom_parameters = { - "Temperature": model_parameters.get("temperature", 0.0), - "TopP": model_parameters.get("top_p", 1.0), - "EnableEnhancement": model_parameters.get("enable_enhance", True), - } - - params = { - "Model": model, - "Messages": messages_dict, - "Stream": stream, - **custom_parameters, - } - # add Tools and ToolChoice - if tools and len(tools) > 0: - params["ToolChoice"] = "auto" - params["Tools"] = [ - { - "Type": "function", - "Function": { - "Name": tool.name, - "Description": tool.description, - "Parameters": json.dumps(tool.parameters), - }, - } - for tool in tools - ] - - request.from_json_string(json.dumps(params)) - response = client.ChatCompletions(request) - - if stream: - return self._handle_stream_chat_response(model, credentials, prompt_messages, response) - - return self._handle_chat_response(credentials, model, prompt_messages, response) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate credentials - """ - try: - client = self._setup_hunyuan_client(credentials) - - req = models.ChatCompletionsRequest() - params = { - "Model": model, - "Messages": [{"Role": "user", "Content": "hello"}], - "TopP": 1, - "Temperature": 0, - "Stream": False, - } - req.from_json_string(json.dumps(params)) - client.ChatCompletions(req) - except Exception as e: - raise CredentialsValidateFailedError(f"Credentials validation failed: {e}") - - def _setup_hunyuan_client(self, credentials): - secret_id = credentials["secret_id"] - secret_key = credentials["secret_key"] - cred = credential.Credential(secret_id, secret_key) - httpProfile = HttpProfile() - httpProfile.endpoint = "hunyuan.tencentcloudapi.com" - clientProfile = ClientProfile() - clientProfile.httpProfile = httpProfile - client = hunyuan_client.HunyuanClient(cred, "", clientProfile) - return client - - def _convert_prompt_messages_to_dicts(self, prompt_messages: list[PromptMessage]) -> list[dict]: - """Convert a list of PromptMessage objects to a list of dictionaries with 'Role' and 'Content' keys.""" - dict_list = [] - for message in prompt_messages: - if isinstance(message, AssistantPromptMessage): - tool_calls = message.tool_calls - if tool_calls and len(tool_calls) > 0: - dict_tool_calls = [ - { - "Id": tool_call.id, - "Type": tool_call.type, - "Function": { - "Name": tool_call.function.name, - "Arguments": tool_call.function.arguments - if (tool_call.function.arguments == "") - else "{}", - }, - } - for tool_call in tool_calls - ] - - dict_list.append( - { - "Role": message.role.value, - # fix set content = "" while tool_call request - # fix [hunyuan] None, [TencentCloudSDKException] code:InvalidParameter - # message:Messages Content and Contents not allowed empty at the same time. - "Content": " ", # message.content if (message.content is not None) else "", - "ToolCalls": dict_tool_calls, - } - ) - else: - dict_list.append({"Role": message.role.value, "Content": message.content}) - elif isinstance(message, ToolPromptMessage): - tool_execute_result = {"result": message.content} - content = json.dumps(tool_execute_result, ensure_ascii=False) - dict_list.append({"Role": message.role.value, "Content": content, "ToolCallId": message.tool_call_id}) - elif isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - dict_list.append({"Role": message.role.value, "Content": message.content}) - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"Type": "text", "Text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "Type": "image_url", - "ImageUrl": {"Url": message_content.data}, - } - sub_messages.append(sub_message_dict) - dict_list.append({"Role": message.role.value, "Contents": sub_messages}) - else: - dict_list.append({"Role": message.role.value, "Content": message.content}) - return dict_list - - def _handle_stream_chat_response(self, model, credentials, prompt_messages, resp): - tool_call = None - tool_calls = [] - - for index, event in enumerate(resp): - logging.debug("_handle_stream_chat_response, event: %s", event) - - data_str = event["data"] - data = json.loads(data_str) - - choices = data.get("Choices", []) - if not choices: - continue - choice = choices[0] - delta = choice.get("Delta", {}) - message_content = delta.get("Content", "") - finish_reason = choice.get("FinishReason", "") - - usage = data.get("Usage", {}) - prompt_tokens = usage.get("PromptTokens", 0) - completion_tokens = usage.get("CompletionTokens", 0) - - response_tool_calls = delta.get("ToolCalls") - if response_tool_calls is not None: - new_tool_calls = self._extract_response_tool_calls(response_tool_calls) - if len(new_tool_calls) > 0: - new_tool_call = new_tool_calls[0] - if tool_call is None: - tool_call = new_tool_call - elif tool_call.id != new_tool_call.id: - tool_calls.append(tool_call) - tool_call = new_tool_call - else: - tool_call.function.name += new_tool_call.function.name - tool_call.function.arguments += new_tool_call.function.arguments - if tool_call is not None and len(tool_call.function.name) > 0 and len(tool_call.function.arguments) > 0: - tool_calls.append(tool_call) - tool_call = None - - assistant_prompt_message = AssistantPromptMessage(content=message_content, tool_calls=[]) - # rewrite content = "" while tool_call to avoid show content on web page - if len(tool_calls) > 0: - assistant_prompt_message.content = "" - - # add tool_calls to assistant_prompt_message - if finish_reason == "tool_calls": - assistant_prompt_message.tool_calls = tool_calls - tool_call = None - tool_calls = [] - - if len(finish_reason) > 0: - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - delta_chunk = LLMResultChunkDelta( - index=index, - role=delta.get("Role", "assistant"), - message=assistant_prompt_message, - usage=usage, - finish_reason=finish_reason, - ) - tool_call = None - tool_calls = [] - - else: - delta_chunk = LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=delta_chunk, - ) - - def _handle_chat_response(self, credentials, model, prompt_messages, response): - usage = self._calc_response_usage( - model, credentials, response.Usage.PromptTokens, response.Usage.CompletionTokens - ) - assistant_prompt_message = AssistantPromptMessage() - assistant_prompt_message.content = response.Choices[0].Message.Content - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - - return result - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - if len(prompt_messages) == 0: - return 0 - prompt = self._convert_messages_to_prompt(prompt_messages) - return self._get_num_tokens_by_gpt2(prompt) - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - """ - Format a list of messages into a full prompt for the Anthropic model - - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - tool_prompt = "\n\nTool:" - content = message.content - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, ToolPromptMessage): - message_text = f"{tool_prompt} {content}" - elif isinstance(message, SystemPromptMessage): - message_text = content - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeError: [TencentCloudSDKException], - } - - def _extract_response_tool_calls(self, response_tool_calls: list[dict]) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - response_function = response_tool_call.get("Function", {}) - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function.get("Name", ""), arguments=response_function.get("Arguments", "") - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.get("Id", 0), type="function", function=function - ) - tool_calls.append(tool_call) - - return tool_calls diff --git a/api/core/model_runtime/model_providers/hunyuan/text_embedding/__init__.py b/api/core/model_runtime/model_providers/hunyuan/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/hunyuan/text_embedding/hunyuan-text-embedding.yaml b/api/core/model_runtime/model_providers/hunyuan/text_embedding/hunyuan-text-embedding.yaml deleted file mode 100644 index ab014e4344..0000000000 --- a/api/core/model_runtime/model_providers/hunyuan/text_embedding/hunyuan-text-embedding.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: hunyuan-embedding -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/jina/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/jina/_assets/icon_l_en.svg deleted file mode 100644 index 6a241fc9ae..0000000000 --- a/api/core/model_runtime/model_providers/jina/_assets/icon_l_en.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/jina/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/jina/_assets/icon_s_en.svg deleted file mode 100644 index 2e1b00fa52..0000000000 --- a/api/core/model_runtime/model_providers/jina/_assets/icon_s_en.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/api/core/model_runtime/model_providers/jina/jina.py b/api/core/model_runtime/model_providers/jina/jina.py deleted file mode 100644 index 186a0a0fa7..0000000000 --- a/api/core/model_runtime/model_providers/jina/jina.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class JinaProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.TEXT_EMBEDDING) - - # Use `jina-embeddings-v3` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="jina-embeddings-v3", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/jina/rerank/__init__.py b/api/core/model_runtime/model_providers/jina/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/jina/rerank/_position.yaml b/api/core/model_runtime/model_providers/jina/rerank/_position.yaml deleted file mode 100644 index c9ddaad758..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/_position.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- jina-reranker-v2-base-multilingual -- jina-reranker-v1-base-en -- jina-reranker-v1-turbo-en -- jina-colbert-v1-en -- jina-reranker-v1-tiny-en diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml deleted file mode 100644 index 320370f242..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: jina-colbert-v1-en -model_type: rerank -model_properties: - context_size: 8192 diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-base-en.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-base-en.yaml deleted file mode 100644 index bd3f31fbd1..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-base-en.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: jina-reranker-v1-base-en -model_type: rerank -model_properties: - context_size: 8192 diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-tiny-en.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-tiny-en.yaml deleted file mode 100644 index b81711195b..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-tiny-en.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: jina-reranker-v1-tiny-en -model_type: rerank -model_properties: - context_size: 8192 diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-turbo-en.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-turbo-en.yaml deleted file mode 100644 index d05f4bb4a2..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v1-turbo-en.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: jina-reranker-v1-turbo-en -model_type: rerank -model_properties: - context_size: 8192 diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v2-base-multilingual.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v2-base-multilingual.yaml deleted file mode 100644 index e6af62107e..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/jina-reranker-v2-base-multilingual.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: jina-reranker-v2-base-multilingual -model_type: rerank -model_properties: - context_size: 1024 diff --git a/api/core/model_runtime/model_providers/jina/rerank/rerank.py b/api/core/model_runtime/model_providers/jina/rerank/rerank.py deleted file mode 100644 index 79ca68914f..0000000000 --- a/api/core/model_runtime/model_providers/jina/rerank/rerank.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import Optional - -import httpx - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelPropertyKey, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class JinaRerankModel(RerankModel): - """ - Model class for Jina rerank model. - """ - - 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 documents to return - :param user: unique user id - :return: rerank result - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - base_url = credentials.get("base_url", "https://api.jina.ai/v1") - base_url = base_url.removesuffix("/") - - try: - response = httpx.post( - base_url + "/rerank", - json={"model": model, "query": query, "documents": docs, "top_n": top_n}, - headers={"Authorization": f"Bearer {credentials.get('api_key')}"}, - ) - response.raise_for_status() - results = response.json() - - rerank_documents = [] - for result in results["results"]: - rerank_document = RerankDocument( - index=result["index"], - text=result["document"]["text"], - score=result["relevance_score"], - ) - if score_threshold is None or result["relevance_score"] >= score_threshold: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - except httpx.HTTPStatusError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - """ - return { - InvokeConnectionError: [httpx.ConnectError], - InvokeServerUnavailableError: [httpx.RemoteProtocolError], - InvokeRateLimitError: [], - InvokeAuthorizationError: [httpx.HTTPStatusError], - InvokeBadRequestError: [httpx.RequestError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - generate custom model entities from credentials - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - model_type=ModelType.RERANK, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size"))}, - ) - - return entity diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-clip-v1.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-clip-v1.yaml deleted file mode 100644 index c06bfd7ebe..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-clip-v1.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-clip-v1 -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-de.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-de.yaml deleted file mode 100644 index 09f7023acb..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-de.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-embeddings-v2-base-de -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-en.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-en.yaml deleted file mode 100644 index a9b2cd4efb..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-en.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-embeddings-v2-base-en -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-zh.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-zh.yaml deleted file mode 100644 index 2a66b4729b..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-base-zh.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-embeddings-v2-base-zh -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-small-en.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-small-en.yaml deleted file mode 100644 index c92779d499..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v2-small-en.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-embeddings-v2-small-en -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v3.yaml b/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v3.yaml deleted file mode 100644 index 4e5374dc9d..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina-embeddings-v3.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: jina-embeddings-v3 -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 2048 -pricing: - input: '0.001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py b/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py deleted file mode 100644 index d80cbfa83d..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py +++ /dev/null @@ -1,32 +0,0 @@ -from os.path import abspath, dirname, join -from threading import Lock - -from transformers import AutoTokenizer - - -class JinaTokenizer: - _tokenizer = None - _lock = Lock() - - @classmethod - def _get_tokenizer(cls): - if cls._tokenizer is None: - with cls._lock: - if cls._tokenizer is None: - base_path = abspath(__file__) - gpt2_tokenizer_path = join(dirname(base_path), "tokenizer") - cls._tokenizer = AutoTokenizer.from_pretrained(gpt2_tokenizer_path) - return cls._tokenizer - - @classmethod - def _get_num_tokens_by_jina_base(cls, text: str) -> int: - """ - use jina tokenizer to get num tokens - """ - tokenizer = cls._get_tokenizer() - tokens = tokenizer.encode(text) - return len(tokens) - - @classmethod - def get_num_tokens(cls, text: str) -> int: - return cls._get_num_tokens_by_jina_base(text) diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer.json b/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer.json deleted file mode 100644 index 65f2b2bc19..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer.json +++ /dev/null @@ -1,30678 +0,0 @@ -{ - "version": "1.0", - "truncation": null, - "padding": null, - "added_tokens": [ - { - "id": 0, - "content": "[PAD]", - "single_word": false, - "lstrip": false, - "rstrip": false, - "normalized": false, - "special": true - }, - { - "id": 100, - "content": "[UNK]", - "single_word": false, - "lstrip": false, - "rstrip": false, - "normalized": false, - "special": true - }, - { - "id": 101, - "content": "[CLS]", - "single_word": false, - "lstrip": false, - "rstrip": false, - "normalized": false, - "special": true - }, - { - "id": 102, - "content": "[SEP]", - "single_word": false, - "lstrip": false, - "rstrip": false, - "normalized": false, - "special": true - }, - { - "id": 103, - "content": "[MASK]", - "single_word": false, - "lstrip": false, - "rstrip": false, - "normalized": false, - "special": true - } - ], - "normalizer": { - "type": "BertNormalizer", - "clean_text": true, - "handle_chinese_chars": true, - "strip_accents": null, - "lowercase": true - }, - "pre_tokenizer": { - "type": "BertPreTokenizer" - }, - "post_processor": { - "type": "TemplateProcessing", - "single": [ - { - "SpecialToken": { - "id": "[CLS]", - "type_id": 0 - } - }, - { - "Sequence": { - "id": "A", - "type_id": 0 - } - }, - { - "SpecialToken": { - "id": "[SEP]", - "type_id": 0 - } - } - ], - "pair": [ - { - "SpecialToken": { - "id": "[CLS]", - "type_id": 0 - } - }, - { - "Sequence": { - "id": "A", - "type_id": 0 - } - }, - { - "SpecialToken": { - "id": "[SEP]", - "type_id": 0 - } - }, - { - "Sequence": { - "id": "B", - "type_id": 1 - } - }, - { - "SpecialToken": { - "id": "[SEP]", - "type_id": 1 - } - } - ], - "special_tokens": { - "[CLS]": { - "id": "[CLS]", - "ids": [ - 101 - ], - "tokens": [ - "[CLS]" - ] - }, - "[SEP]": { - "id": "[SEP]", - "ids": [ - 102 - ], - "tokens": [ - "[SEP]" - ] - } - } - }, - "decoder": { - "type": "WordPiece", - "prefix": "##", - "cleanup": true - }, - "model": { - "type": "WordPiece", - "unk_token": "[UNK]", - "continuing_subword_prefix": "##", - "max_input_chars_per_word": 100, - "vocab": { - "[PAD]": 0, - "[unused0]": 1, - "[unused1]": 2, - "[unused2]": 3, - "[unused3]": 4, - "[unused4]": 5, - "[unused5]": 6, - "[unused6]": 7, - "[unused7]": 8, - "[unused8]": 9, - "[unused9]": 10, - "[unused10]": 11, - "[unused11]": 12, - "[unused12]": 13, - "[unused13]": 14, - "[unused14]": 15, - "[unused15]": 16, - "[unused16]": 17, - "[unused17]": 18, - "[unused18]": 19, - "[unused19]": 20, - "[unused20]": 21, - "[unused21]": 22, - "[unused22]": 23, - "[unused23]": 24, - "[unused24]": 25, - "[unused25]": 26, - "[unused26]": 27, - "[unused27]": 28, - "[unused28]": 29, - "[unused29]": 30, - "[unused30]": 31, - "[unused31]": 32, - "[unused32]": 33, - "[unused33]": 34, - "[unused34]": 35, - "[unused35]": 36, - "[unused36]": 37, - "[unused37]": 38, - "[unused38]": 39, - "[unused39]": 40, - "[unused40]": 41, - "[unused41]": 42, - "[unused42]": 43, - "[unused43]": 44, - "[unused44]": 45, - "[unused45]": 46, - "[unused46]": 47, - "[unused47]": 48, - "[unused48]": 49, - "[unused49]": 50, - "[unused50]": 51, - "[unused51]": 52, - "[unused52]": 53, - "[unused53]": 54, - "[unused54]": 55, - "[unused55]": 56, - "[unused56]": 57, - "[unused57]": 58, - "[unused58]": 59, - "[unused59]": 60, - "[unused60]": 61, - "[unused61]": 62, - "[unused62]": 63, - "[unused63]": 64, - "[unused64]": 65, - "[unused65]": 66, - "[unused66]": 67, - "[unused67]": 68, - "[unused68]": 69, - "[unused69]": 70, - "[unused70]": 71, - "[unused71]": 72, - "[unused72]": 73, - "[unused73]": 74, - "[unused74]": 75, - "[unused75]": 76, - "[unused76]": 77, - "[unused77]": 78, - "[unused78]": 79, - "[unused79]": 80, - "[unused80]": 81, - "[unused81]": 82, - "[unused82]": 83, - "[unused83]": 84, - "[unused84]": 85, - "[unused85]": 86, - "[unused86]": 87, - "[unused87]": 88, - "[unused88]": 89, - "[unused89]": 90, - "[unused90]": 91, - "[unused91]": 92, - "[unused92]": 93, - "[unused93]": 94, - "[unused94]": 95, - "[unused95]": 96, - "[unused96]": 97, - "[unused97]": 98, - "[unused98]": 99, - "[UNK]": 100, - "[CLS]": 101, - "[SEP]": 102, - "[MASK]": 103, - "[unused99]": 104, - "[unused100]": 105, - "[unused101]": 106, - "[unused102]": 107, - "[unused103]": 108, - "[unused104]": 109, - "[unused105]": 110, - "[unused106]": 111, - "[unused107]": 112, - "[unused108]": 113, - "[unused109]": 114, - "[unused110]": 115, - "[unused111]": 116, - "[unused112]": 117, - "[unused113]": 118, - "[unused114]": 119, - "[unused115]": 120, - "[unused116]": 121, - "[unused117]": 122, - "[unused118]": 123, - "[unused119]": 124, - "[unused120]": 125, - "[unused121]": 126, - "[unused122]": 127, - "[unused123]": 128, - "[unused124]": 129, - "[unused125]": 130, - "[unused126]": 131, - "[unused127]": 132, - "[unused128]": 133, - "[unused129]": 134, - "[unused130]": 135, - "[unused131]": 136, - "[unused132]": 137, - "[unused133]": 138, - "[unused134]": 139, - "[unused135]": 140, - "[unused136]": 141, - "[unused137]": 142, - "[unused138]": 143, - "[unused139]": 144, - "[unused140]": 145, - "[unused141]": 146, - "[unused142]": 147, - "[unused143]": 148, - "[unused144]": 149, - "[unused145]": 150, - "[unused146]": 151, - "[unused147]": 152, - "[unused148]": 153, - "[unused149]": 154, - "[unused150]": 155, - "[unused151]": 156, - "[unused152]": 157, - "[unused153]": 158, - "[unused154]": 159, - "[unused155]": 160, - "[unused156]": 161, - "[unused157]": 162, - "[unused158]": 163, - "[unused159]": 164, - "[unused160]": 165, - "[unused161]": 166, - "[unused162]": 167, - "[unused163]": 168, - "[unused164]": 169, - "[unused165]": 170, - "[unused166]": 171, - "[unused167]": 172, - "[unused168]": 173, - "[unused169]": 174, - "[unused170]": 175, - "[unused171]": 176, - "[unused172]": 177, - "[unused173]": 178, - "[unused174]": 179, - "[unused175]": 180, - "[unused176]": 181, - "[unused177]": 182, - "[unused178]": 183, - "[unused179]": 184, - "[unused180]": 185, - "[unused181]": 186, - "[unused182]": 187, - "[unused183]": 188, - "[unused184]": 189, - "[unused185]": 190, - "[unused186]": 191, - "[unused187]": 192, - "[unused188]": 193, - "[unused189]": 194, - "[unused190]": 195, - "[unused191]": 196, - "[unused192]": 197, - "[unused193]": 198, - "[unused194]": 199, - "[unused195]": 200, - "[unused196]": 201, - "[unused197]": 202, - "[unused198]": 203, - "[unused199]": 204, - "[unused200]": 205, - "[unused201]": 206, - "[unused202]": 207, - "[unused203]": 208, - "[unused204]": 209, - "[unused205]": 210, - "[unused206]": 211, - "[unused207]": 212, - "[unused208]": 213, - "[unused209]": 214, - "[unused210]": 215, - "[unused211]": 216, - "[unused212]": 217, - "[unused213]": 218, - "[unused214]": 219, - "[unused215]": 220, - "[unused216]": 221, - "[unused217]": 222, - "[unused218]": 223, - "[unused219]": 224, - "[unused220]": 225, - "[unused221]": 226, - "[unused222]": 227, - "[unused223]": 228, - "[unused224]": 229, - "[unused225]": 230, - "[unused226]": 231, - "[unused227]": 232, - "[unused228]": 233, - "[unused229]": 234, - "[unused230]": 235, - "[unused231]": 236, - "[unused232]": 237, - "[unused233]": 238, - "[unused234]": 239, - "[unused235]": 240, - "[unused236]": 241, - "[unused237]": 242, - "[unused238]": 243, - "[unused239]": 244, - "[unused240]": 245, - "[unused241]": 246, - "[unused242]": 247, - "[unused243]": 248, - "[unused244]": 249, - "[unused245]": 250, - "[unused246]": 251, - "[unused247]": 252, - "[unused248]": 253, - "[unused249]": 254, - "[unused250]": 255, - "[unused251]": 256, - "[unused252]": 257, - "[unused253]": 258, - "[unused254]": 259, - "[unused255]": 260, - "[unused256]": 261, - "[unused257]": 262, - "[unused258]": 263, - "[unused259]": 264, - "[unused260]": 265, - "[unused261]": 266, - "[unused262]": 267, - "[unused263]": 268, - "[unused264]": 269, - "[unused265]": 270, - "[unused266]": 271, - "[unused267]": 272, - "[unused268]": 273, - "[unused269]": 274, - "[unused270]": 275, - "[unused271]": 276, - "[unused272]": 277, - "[unused273]": 278, - "[unused274]": 279, - "[unused275]": 280, - "[unused276]": 281, - "[unused277]": 282, - "[unused278]": 283, - "[unused279]": 284, - "[unused280]": 285, - "[unused281]": 286, - "[unused282]": 287, - "[unused283]": 288, - "[unused284]": 289, - "[unused285]": 290, - "[unused286]": 291, - "[unused287]": 292, - "[unused288]": 293, - "[unused289]": 294, - "[unused290]": 295, - "[unused291]": 296, - "[unused292]": 297, - "[unused293]": 298, - "[unused294]": 299, - "[unused295]": 300, - "[unused296]": 301, - "[unused297]": 302, - "[unused298]": 303, - "[unused299]": 304, - "[unused300]": 305, - "[unused301]": 306, - "[unused302]": 307, - "[unused303]": 308, - "[unused304]": 309, - "[unused305]": 310, - "[unused306]": 311, - "[unused307]": 312, - "[unused308]": 313, - "[unused309]": 314, - "[unused310]": 315, - "[unused311]": 316, - "[unused312]": 317, - "[unused313]": 318, - "[unused314]": 319, - "[unused315]": 320, - "[unused316]": 321, - "[unused317]": 322, - "[unused318]": 323, - "[unused319]": 324, - "[unused320]": 325, - "[unused321]": 326, - "[unused322]": 327, - "[unused323]": 328, - "[unused324]": 329, - "[unused325]": 330, - "[unused326]": 331, - "[unused327]": 332, - "[unused328]": 333, - "[unused329]": 334, - "[unused330]": 335, - "[unused331]": 336, - "[unused332]": 337, - "[unused333]": 338, - "[unused334]": 339, - "[unused335]": 340, - "[unused336]": 341, - "[unused337]": 342, - "[unused338]": 343, - "[unused339]": 344, - "[unused340]": 345, - "[unused341]": 346, - "[unused342]": 347, - "[unused343]": 348, - "[unused344]": 349, - "[unused345]": 350, - "[unused346]": 351, - "[unused347]": 352, - "[unused348]": 353, - "[unused349]": 354, - "[unused350]": 355, - "[unused351]": 356, - "[unused352]": 357, - "[unused353]": 358, - "[unused354]": 359, - "[unused355]": 360, - "[unused356]": 361, - "[unused357]": 362, - "[unused358]": 363, - "[unused359]": 364, - "[unused360]": 365, - "[unused361]": 366, - "[unused362]": 367, - "[unused363]": 368, - "[unused364]": 369, - "[unused365]": 370, - "[unused366]": 371, - "[unused367]": 372, - "[unused368]": 373, - "[unused369]": 374, - "[unused370]": 375, - "[unused371]": 376, - "[unused372]": 377, - "[unused373]": 378, - "[unused374]": 379, - "[unused375]": 380, - "[unused376]": 381, - "[unused377]": 382, - "[unused378]": 383, - "[unused379]": 384, - "[unused380]": 385, - "[unused381]": 386, - "[unused382]": 387, - "[unused383]": 388, - "[unused384]": 389, - "[unused385]": 390, - "[unused386]": 391, - "[unused387]": 392, - "[unused388]": 393, - "[unused389]": 394, - "[unused390]": 395, - "[unused391]": 396, - "[unused392]": 397, - "[unused393]": 398, - "[unused394]": 399, - "[unused395]": 400, - "[unused396]": 401, - "[unused397]": 402, - "[unused398]": 403, - "[unused399]": 404, - "[unused400]": 405, - "[unused401]": 406, - "[unused402]": 407, - "[unused403]": 408, - "[unused404]": 409, - "[unused405]": 410, - "[unused406]": 411, - "[unused407]": 412, - "[unused408]": 413, - "[unused409]": 414, - "[unused410]": 415, - "[unused411]": 416, - "[unused412]": 417, - "[unused413]": 418, - "[unused414]": 419, - "[unused415]": 420, - "[unused416]": 421, - "[unused417]": 422, - "[unused418]": 423, - "[unused419]": 424, - "[unused420]": 425, - "[unused421]": 426, - "[unused422]": 427, - "[unused423]": 428, - "[unused424]": 429, - "[unused425]": 430, - "[unused426]": 431, - "[unused427]": 432, - "[unused428]": 433, - "[unused429]": 434, - "[unused430]": 435, - "[unused431]": 436, - "[unused432]": 437, - "[unused433]": 438, - "[unused434]": 439, - "[unused435]": 440, - "[unused436]": 441, - "[unused437]": 442, - "[unused438]": 443, - "[unused439]": 444, - "[unused440]": 445, - "[unused441]": 446, - "[unused442]": 447, - "[unused443]": 448, - "[unused444]": 449, - "[unused445]": 450, - "[unused446]": 451, - "[unused447]": 452, - "[unused448]": 453, - "[unused449]": 454, - "[unused450]": 455, - "[unused451]": 456, - "[unused452]": 457, - "[unused453]": 458, - "[unused454]": 459, - "[unused455]": 460, - "[unused456]": 461, - "[unused457]": 462, - "[unused458]": 463, - "[unused459]": 464, - "[unused460]": 465, - "[unused461]": 466, - "[unused462]": 467, - "[unused463]": 468, - "[unused464]": 469, - "[unused465]": 470, - "[unused466]": 471, - "[unused467]": 472, - "[unused468]": 473, - "[unused469]": 474, - "[unused470]": 475, - "[unused471]": 476, - "[unused472]": 477, - "[unused473]": 478, - "[unused474]": 479, - "[unused475]": 480, - "[unused476]": 481, - "[unused477]": 482, - "[unused478]": 483, - "[unused479]": 484, - "[unused480]": 485, - "[unused481]": 486, - "[unused482]": 487, - "[unused483]": 488, - "[unused484]": 489, - "[unused485]": 490, - "[unused486]": 491, - "[unused487]": 492, - "[unused488]": 493, - "[unused489]": 494, - "[unused490]": 495, - "[unused491]": 496, - "[unused492]": 497, - "[unused493]": 498, - "[unused494]": 499, - "[unused495]": 500, - "[unused496]": 501, - "[unused497]": 502, - "[unused498]": 503, - "[unused499]": 504, - "[unused500]": 505, - "[unused501]": 506, - "[unused502]": 507, - "[unused503]": 508, - "[unused504]": 509, - "[unused505]": 510, - "[unused506]": 511, - "[unused507]": 512, - "[unused508]": 513, - "[unused509]": 514, - "[unused510]": 515, - "[unused511]": 516, - "[unused512]": 517, - "[unused513]": 518, - "[unused514]": 519, - "[unused515]": 520, - "[unused516]": 521, - "[unused517]": 522, - "[unused518]": 523, - "[unused519]": 524, - "[unused520]": 525, - "[unused521]": 526, - "[unused522]": 527, - "[unused523]": 528, - "[unused524]": 529, - "[unused525]": 530, - "[unused526]": 531, - "[unused527]": 532, - "[unused528]": 533, - "[unused529]": 534, - "[unused530]": 535, - "[unused531]": 536, - "[unused532]": 537, - "[unused533]": 538, - "[unused534]": 539, - "[unused535]": 540, - "[unused536]": 541, - "[unused537]": 542, - "[unused538]": 543, - "[unused539]": 544, - "[unused540]": 545, - "[unused541]": 546, - "[unused542]": 547, - "[unused543]": 548, - "[unused544]": 549, - "[unused545]": 550, - "[unused546]": 551, - "[unused547]": 552, - "[unused548]": 553, - "[unused549]": 554, - "[unused550]": 555, - "[unused551]": 556, - "[unused552]": 557, - "[unused553]": 558, - "[unused554]": 559, - "[unused555]": 560, - "[unused556]": 561, - "[unused557]": 562, - "[unused558]": 563, - "[unused559]": 564, - "[unused560]": 565, - "[unused561]": 566, - "[unused562]": 567, - "[unused563]": 568, - "[unused564]": 569, - "[unused565]": 570, - "[unused566]": 571, - "[unused567]": 572, - "[unused568]": 573, - "[unused569]": 574, - "[unused570]": 575, - "[unused571]": 576, - "[unused572]": 577, - "[unused573]": 578, - "[unused574]": 579, - "[unused575]": 580, - "[unused576]": 581, - "[unused577]": 582, - "[unused578]": 583, - "[unused579]": 584, - "[unused580]": 585, - "[unused581]": 586, - "[unused582]": 587, - "[unused583]": 588, - "[unused584]": 589, - "[unused585]": 590, - "[unused586]": 591, - "[unused587]": 592, - "[unused588]": 593, - "[unused589]": 594, - "[unused590]": 595, - "[unused591]": 596, - "[unused592]": 597, - "[unused593]": 598, - "[unused594]": 599, - "[unused595]": 600, - "[unused596]": 601, - "[unused597]": 602, - "[unused598]": 603, - "[unused599]": 604, - "[unused600]": 605, - "[unused601]": 606, - "[unused602]": 607, - "[unused603]": 608, - "[unused604]": 609, - "[unused605]": 610, - "[unused606]": 611, - "[unused607]": 612, - "[unused608]": 613, - "[unused609]": 614, - "[unused610]": 615, - "[unused611]": 616, - "[unused612]": 617, - "[unused613]": 618, - "[unused614]": 619, - "[unused615]": 620, - "[unused616]": 621, - "[unused617]": 622, - "[unused618]": 623, - "[unused619]": 624, - "[unused620]": 625, - "[unused621]": 626, - "[unused622]": 627, - "[unused623]": 628, - "[unused624]": 629, - "[unused625]": 630, - "[unused626]": 631, - "[unused627]": 632, - "[unused628]": 633, - "[unused629]": 634, - "[unused630]": 635, - "[unused631]": 636, - "[unused632]": 637, - "[unused633]": 638, - "[unused634]": 639, - "[unused635]": 640, - "[unused636]": 641, - "[unused637]": 642, - "[unused638]": 643, - "[unused639]": 644, - "[unused640]": 645, - "[unused641]": 646, - "[unused642]": 647, - "[unused643]": 648, - "[unused644]": 649, - "[unused645]": 650, - "[unused646]": 651, - "[unused647]": 652, - "[unused648]": 653, - "[unused649]": 654, - "[unused650]": 655, - "[unused651]": 656, - "[unused652]": 657, - "[unused653]": 658, - "[unused654]": 659, - "[unused655]": 660, - "[unused656]": 661, - "[unused657]": 662, - "[unused658]": 663, - "[unused659]": 664, - "[unused660]": 665, - "[unused661]": 666, - "[unused662]": 667, - "[unused663]": 668, - "[unused664]": 669, - "[unused665]": 670, - "[unused666]": 671, - "[unused667]": 672, - "[unused668]": 673, - "[unused669]": 674, - "[unused670]": 675, - "[unused671]": 676, - "[unused672]": 677, - "[unused673]": 678, - "[unused674]": 679, - "[unused675]": 680, - "[unused676]": 681, - "[unused677]": 682, - "[unused678]": 683, - "[unused679]": 684, - "[unused680]": 685, - "[unused681]": 686, - "[unused682]": 687, - "[unused683]": 688, - "[unused684]": 689, - "[unused685]": 690, - "[unused686]": 691, - "[unused687]": 692, - "[unused688]": 693, - "[unused689]": 694, - "[unused690]": 695, - "[unused691]": 696, - "[unused692]": 697, - "[unused693]": 698, - "[unused694]": 699, - "[unused695]": 700, - "[unused696]": 701, - "[unused697]": 702, - "[unused698]": 703, - "[unused699]": 704, - "[unused700]": 705, - "[unused701]": 706, - "[unused702]": 707, - "[unused703]": 708, - "[unused704]": 709, - "[unused705]": 710, - "[unused706]": 711, - "[unused707]": 712, - "[unused708]": 713, - "[unused709]": 714, - "[unused710]": 715, - "[unused711]": 716, - "[unused712]": 717, - "[unused713]": 718, - "[unused714]": 719, - "[unused715]": 720, - "[unused716]": 721, - "[unused717]": 722, - "[unused718]": 723, - "[unused719]": 724, - "[unused720]": 725, - "[unused721]": 726, - "[unused722]": 727, - "[unused723]": 728, - "[unused724]": 729, - "[unused725]": 730, - "[unused726]": 731, - "[unused727]": 732, - "[unused728]": 733, - "[unused729]": 734, - "[unused730]": 735, - "[unused731]": 736, - "[unused732]": 737, - "[unused733]": 738, - "[unused734]": 739, - "[unused735]": 740, - "[unused736]": 741, - "[unused737]": 742, - "[unused738]": 743, - "[unused739]": 744, - "[unused740]": 745, - "[unused741]": 746, - "[unused742]": 747, - "[unused743]": 748, - "[unused744]": 749, - "[unused745]": 750, - "[unused746]": 751, - "[unused747]": 752, - "[unused748]": 753, - "[unused749]": 754, - "[unused750]": 755, - "[unused751]": 756, - "[unused752]": 757, - "[unused753]": 758, - "[unused754]": 759, - "[unused755]": 760, - "[unused756]": 761, - "[unused757]": 762, - "[unused758]": 763, - "[unused759]": 764, - "[unused760]": 765, - "[unused761]": 766, - "[unused762]": 767, - "[unused763]": 768, - "[unused764]": 769, - "[unused765]": 770, - "[unused766]": 771, - "[unused767]": 772, - "[unused768]": 773, - "[unused769]": 774, - "[unused770]": 775, - "[unused771]": 776, - "[unused772]": 777, - "[unused773]": 778, - "[unused774]": 779, - "[unused775]": 780, - "[unused776]": 781, - "[unused777]": 782, - "[unused778]": 783, - "[unused779]": 784, - "[unused780]": 785, - "[unused781]": 786, - "[unused782]": 787, - "[unused783]": 788, - "[unused784]": 789, - "[unused785]": 790, - "[unused786]": 791, - "[unused787]": 792, - "[unused788]": 793, - "[unused789]": 794, - "[unused790]": 795, - "[unused791]": 796, - "[unused792]": 797, - "[unused793]": 798, - "[unused794]": 799, - "[unused795]": 800, - "[unused796]": 801, - "[unused797]": 802, - "[unused798]": 803, - "[unused799]": 804, - "[unused800]": 805, - "[unused801]": 806, - "[unused802]": 807, - "[unused803]": 808, - "[unused804]": 809, - "[unused805]": 810, - "[unused806]": 811, - "[unused807]": 812, - "[unused808]": 813, - "[unused809]": 814, - "[unused810]": 815, - "[unused811]": 816, - "[unused812]": 817, - "[unused813]": 818, - "[unused814]": 819, - "[unused815]": 820, - "[unused816]": 821, - "[unused817]": 822, - "[unused818]": 823, - "[unused819]": 824, - "[unused820]": 825, - "[unused821]": 826, - "[unused822]": 827, - "[unused823]": 828, - "[unused824]": 829, - "[unused825]": 830, - "[unused826]": 831, - "[unused827]": 832, - "[unused828]": 833, - "[unused829]": 834, - "[unused830]": 835, - "[unused831]": 836, - "[unused832]": 837, - "[unused833]": 838, - "[unused834]": 839, - "[unused835]": 840, - "[unused836]": 841, - "[unused837]": 842, - "[unused838]": 843, - "[unused839]": 844, - "[unused840]": 845, - "[unused841]": 846, - "[unused842]": 847, - "[unused843]": 848, - "[unused844]": 849, - "[unused845]": 850, - "[unused846]": 851, - "[unused847]": 852, - "[unused848]": 853, - "[unused849]": 854, - "[unused850]": 855, - "[unused851]": 856, - "[unused852]": 857, - "[unused853]": 858, - "[unused854]": 859, - "[unused855]": 860, - "[unused856]": 861, - "[unused857]": 862, - "[unused858]": 863, - "[unused859]": 864, - "[unused860]": 865, - "[unused861]": 866, - "[unused862]": 867, - "[unused863]": 868, - "[unused864]": 869, - "[unused865]": 870, - "[unused866]": 871, - "[unused867]": 872, - "[unused868]": 873, - "[unused869]": 874, - "[unused870]": 875, - "[unused871]": 876, - "[unused872]": 877, - "[unused873]": 878, - "[unused874]": 879, - "[unused875]": 880, - "[unused876]": 881, - "[unused877]": 882, - "[unused878]": 883, - "[unused879]": 884, - "[unused880]": 885, - "[unused881]": 886, - "[unused882]": 887, - "[unused883]": 888, - "[unused884]": 889, - "[unused885]": 890, - "[unused886]": 891, - "[unused887]": 892, - "[unused888]": 893, - "[unused889]": 894, - "[unused890]": 895, - "[unused891]": 896, - "[unused892]": 897, - "[unused893]": 898, - "[unused894]": 899, - "[unused895]": 900, - "[unused896]": 901, - "[unused897]": 902, - "[unused898]": 903, - "[unused899]": 904, - "[unused900]": 905, - "[unused901]": 906, - "[unused902]": 907, - "[unused903]": 908, - "[unused904]": 909, - "[unused905]": 910, - "[unused906]": 911, - "[unused907]": 912, - "[unused908]": 913, - "[unused909]": 914, - "[unused910]": 915, - "[unused911]": 916, - "[unused912]": 917, - "[unused913]": 918, - "[unused914]": 919, - "[unused915]": 920, - "[unused916]": 921, - "[unused917]": 922, - "[unused918]": 923, - "[unused919]": 924, - "[unused920]": 925, - "[unused921]": 926, - "[unused922]": 927, - "[unused923]": 928, - "[unused924]": 929, - "[unused925]": 930, - "[unused926]": 931, - "[unused927]": 932, - "[unused928]": 933, - "[unused929]": 934, - "[unused930]": 935, - "[unused931]": 936, - "[unused932]": 937, - "[unused933]": 938, - "[unused934]": 939, - "[unused935]": 940, - "[unused936]": 941, - "[unused937]": 942, - "[unused938]": 943, - "[unused939]": 944, - "[unused940]": 945, - "[unused941]": 946, - "[unused942]": 947, - "[unused943]": 948, - "[unused944]": 949, - "[unused945]": 950, - "[unused946]": 951, - "[unused947]": 952, - "[unused948]": 953, - "[unused949]": 954, - "[unused950]": 955, - "[unused951]": 956, - "[unused952]": 957, - "[unused953]": 958, - "[unused954]": 959, - "[unused955]": 960, - "[unused956]": 961, - "[unused957]": 962, - "[unused958]": 963, - "[unused959]": 964, - "[unused960]": 965, - "[unused961]": 966, - "[unused962]": 967, - "[unused963]": 968, - "[unused964]": 969, - "[unused965]": 970, - "[unused966]": 971, - "[unused967]": 972, - "[unused968]": 973, - "[unused969]": 974, - "[unused970]": 975, - "[unused971]": 976, - "[unused972]": 977, - "[unused973]": 978, - "[unused974]": 979, - "[unused975]": 980, - "[unused976]": 981, - "[unused977]": 982, - "[unused978]": 983, - "[unused979]": 984, - "[unused980]": 985, - "[unused981]": 986, - "[unused982]": 987, - "[unused983]": 988, - "[unused984]": 989, - "[unused985]": 990, - "[unused986]": 991, - "[unused987]": 992, - "[unused988]": 993, - "[unused989]": 994, - "[unused990]": 995, - "[unused991]": 996, - "[unused992]": 997, - "[unused993]": 998, - "!": 999, - "\"": 1000, - "#": 1001, - "$": 1002, - "%": 1003, - "&": 1004, - "'": 1005, - "(": 1006, - ")": 1007, - "*": 1008, - "+": 1009, - ",": 1010, - "-": 1011, - ".": 1012, - "/": 1013, - "0": 1014, - "1": 1015, - "2": 1016, - "3": 1017, - "4": 1018, - "5": 1019, - "6": 1020, - "7": 1021, - "8": 1022, - "9": 1023, - ":": 1024, - ";": 1025, - "<": 1026, - "=": 1027, - ">": 1028, - "?": 1029, - "@": 1030, - "[": 1031, - "\\": 1032, - "]": 1033, - "^": 1034, - "_": 1035, - "`": 1036, - "a": 1037, - "b": 1038, - "c": 1039, - "d": 1040, - "e": 1041, - "f": 1042, - "g": 1043, - "h": 1044, - "i": 1045, - "j": 1046, - "k": 1047, - "l": 1048, - "m": 1049, - "n": 1050, - "o": 1051, - "p": 1052, - "q": 1053, - "r": 1054, - "s": 1055, - "t": 1056, - "u": 1057, - "v": 1058, - "w": 1059, - "x": 1060, - "y": 1061, - "z": 1062, - "{": 1063, - "|": 1064, - "}": 1065, - "~": 1066, - "¡": 1067, - "¢": 1068, - "£": 1069, - "¤": 1070, - "¥": 1071, - "¦": 1072, - "§": 1073, - "¨": 1074, - "©": 1075, - "ª": 1076, - "«": 1077, - "¬": 1078, - "®": 1079, - "°": 1080, - "±": 1081, - "²": 1082, - "³": 1083, - "´": 1084, - "µ": 1085, - "¶": 1086, - "·": 1087, - "¹": 1088, - "º": 1089, - "»": 1090, - "¼": 1091, - "½": 1092, - "¾": 1093, - "¿": 1094, - "×": 1095, - "ß": 1096, - "æ": 1097, - "ð": 1098, - "÷": 1099, - "ø": 1100, - "þ": 1101, - "đ": 1102, - "ħ": 1103, - "ı": 1104, - "ł": 1105, - "ŋ": 1106, - "œ": 1107, - "ƒ": 1108, - "ɐ": 1109, - "ɑ": 1110, - "ɒ": 1111, - "ɔ": 1112, - "ɕ": 1113, - "ə": 1114, - "ɛ": 1115, - "ɡ": 1116, - "ɣ": 1117, - "ɨ": 1118, - "ɪ": 1119, - "ɫ": 1120, - "ɬ": 1121, - "ɯ": 1122, - "ɲ": 1123, - "ɴ": 1124, - "ɹ": 1125, - "ɾ": 1126, - "ʀ": 1127, - "ʁ": 1128, - "ʂ": 1129, - "ʃ": 1130, - "ʉ": 1131, - "ʊ": 1132, - "ʋ": 1133, - "ʌ": 1134, - "ʎ": 1135, - "ʐ": 1136, - "ʑ": 1137, - "ʒ": 1138, - "ʔ": 1139, - "ʰ": 1140, - "ʲ": 1141, - "ʳ": 1142, - "ʷ": 1143, - "ʸ": 1144, - "ʻ": 1145, - "ʼ": 1146, - "ʾ": 1147, - "ʿ": 1148, - "ˈ": 1149, - "ː": 1150, - "ˡ": 1151, - "ˢ": 1152, - "ˣ": 1153, - "ˤ": 1154, - "α": 1155, - "β": 1156, - "γ": 1157, - "δ": 1158, - "ε": 1159, - "ζ": 1160, - "η": 1161, - "θ": 1162, - "ι": 1163, - "κ": 1164, - "λ": 1165, - "μ": 1166, - "ν": 1167, - "ξ": 1168, - "ο": 1169, - "π": 1170, - "ρ": 1171, - "ς": 1172, - "σ": 1173, - "τ": 1174, - "υ": 1175, - "φ": 1176, - "χ": 1177, - "ψ": 1178, - "ω": 1179, - "а": 1180, - "б": 1181, - "в": 1182, - "г": 1183, - "д": 1184, - "е": 1185, - "ж": 1186, - "з": 1187, - "и": 1188, - "к": 1189, - "л": 1190, - "м": 1191, - "н": 1192, - "о": 1193, - "п": 1194, - "р": 1195, - "с": 1196, - "т": 1197, - "у": 1198, - "ф": 1199, - "х": 1200, - "ц": 1201, - "ч": 1202, - "ш": 1203, - "щ": 1204, - "ъ": 1205, - "ы": 1206, - "ь": 1207, - "э": 1208, - "ю": 1209, - "я": 1210, - "ђ": 1211, - "є": 1212, - "і": 1213, - "ј": 1214, - "љ": 1215, - "њ": 1216, - "ћ": 1217, - "ӏ": 1218, - "ա": 1219, - "բ": 1220, - "գ": 1221, - "դ": 1222, - "ե": 1223, - "թ": 1224, - "ի": 1225, - "լ": 1226, - "կ": 1227, - "հ": 1228, - "մ": 1229, - "յ": 1230, - "ն": 1231, - "ո": 1232, - "պ": 1233, - "ս": 1234, - "վ": 1235, - "տ": 1236, - "ր": 1237, - "ւ": 1238, - "ք": 1239, - "־": 1240, - "א": 1241, - "ב": 1242, - "ג": 1243, - "ד": 1244, - "ה": 1245, - "ו": 1246, - "ז": 1247, - "ח": 1248, - "ט": 1249, - "י": 1250, - "ך": 1251, - "כ": 1252, - "ל": 1253, - "ם": 1254, - "מ": 1255, - "ן": 1256, - "נ": 1257, - "ס": 1258, - "ע": 1259, - "ף": 1260, - "פ": 1261, - "ץ": 1262, - "צ": 1263, - "ק": 1264, - "ר": 1265, - "ש": 1266, - "ת": 1267, - "،": 1268, - "ء": 1269, - "ا": 1270, - "ب": 1271, - "ة": 1272, - "ت": 1273, - "ث": 1274, - "ج": 1275, - "ح": 1276, - "خ": 1277, - "د": 1278, - "ذ": 1279, - "ر": 1280, - "ز": 1281, - "س": 1282, - "ش": 1283, - "ص": 1284, - "ض": 1285, - "ط": 1286, - "ظ": 1287, - "ع": 1288, - "غ": 1289, - "ـ": 1290, - "ف": 1291, - "ق": 1292, - "ك": 1293, - "ل": 1294, - "م": 1295, - "ن": 1296, - "ه": 1297, - "و": 1298, - "ى": 1299, - "ي": 1300, - "ٹ": 1301, - "پ": 1302, - "چ": 1303, - "ک": 1304, - "گ": 1305, - "ں": 1306, - "ھ": 1307, - "ہ": 1308, - "ی": 1309, - "ے": 1310, - "अ": 1311, - "आ": 1312, - "उ": 1313, - "ए": 1314, - "क": 1315, - "ख": 1316, - "ग": 1317, - "च": 1318, - "ज": 1319, - "ट": 1320, - "ड": 1321, - "ण": 1322, - "त": 1323, - "थ": 1324, - "द": 1325, - "ध": 1326, - "न": 1327, - "प": 1328, - "ब": 1329, - "भ": 1330, - "म": 1331, - "य": 1332, - "र": 1333, - "ल": 1334, - "व": 1335, - "श": 1336, - "ष": 1337, - "स": 1338, - "ह": 1339, - "ा": 1340, - "ि": 1341, - "ी": 1342, - "ो": 1343, - "।": 1344, - "॥": 1345, - "ং": 1346, - "অ": 1347, - "আ": 1348, - "ই": 1349, - "উ": 1350, - "এ": 1351, - "ও": 1352, - "ক": 1353, - "খ": 1354, - "গ": 1355, - "চ": 1356, - "ছ": 1357, - "জ": 1358, - "ট": 1359, - "ড": 1360, - "ণ": 1361, - "ত": 1362, - "থ": 1363, - "দ": 1364, - "ধ": 1365, - "ন": 1366, - "প": 1367, - "ব": 1368, - "ভ": 1369, - "ম": 1370, - "য": 1371, - "র": 1372, - "ল": 1373, - "শ": 1374, - "ষ": 1375, - "স": 1376, - "হ": 1377, - "া": 1378, - "ি": 1379, - "ী": 1380, - "ে": 1381, - "க": 1382, - "ச": 1383, - "ட": 1384, - "த": 1385, - "ந": 1386, - "ன": 1387, - "ப": 1388, - "ம": 1389, - "ய": 1390, - "ர": 1391, - "ல": 1392, - "ள": 1393, - "வ": 1394, - "ா": 1395, - "ி": 1396, - "ு": 1397, - "ே": 1398, - "ை": 1399, - "ನ": 1400, - "ರ": 1401, - "ಾ": 1402, - "ක": 1403, - "ය": 1404, - "ර": 1405, - "ල": 1406, - "ව": 1407, - "ා": 1408, - "ก": 1409, - "ง": 1410, - "ต": 1411, - "ท": 1412, - "น": 1413, - "พ": 1414, - "ม": 1415, - "ย": 1416, - "ร": 1417, - "ล": 1418, - "ว": 1419, - "ส": 1420, - "อ": 1421, - "า": 1422, - "เ": 1423, - "་": 1424, - "།": 1425, - "ག": 1426, - "ང": 1427, - "ད": 1428, - "ན": 1429, - "པ": 1430, - "བ": 1431, - "མ": 1432, - "འ": 1433, - "ར": 1434, - "ལ": 1435, - "ས": 1436, - "မ": 1437, - "ა": 1438, - "ბ": 1439, - "გ": 1440, - "დ": 1441, - "ე": 1442, - "ვ": 1443, - "თ": 1444, - "ი": 1445, - "კ": 1446, - "ლ": 1447, - "მ": 1448, - "ნ": 1449, - "ო": 1450, - "რ": 1451, - "ს": 1452, - "ტ": 1453, - "უ": 1454, - "ᄀ": 1455, - "ᄂ": 1456, - "ᄃ": 1457, - "ᄅ": 1458, - "ᄆ": 1459, - "ᄇ": 1460, - "ᄉ": 1461, - "ᄊ": 1462, - "ᄋ": 1463, - "ᄌ": 1464, - "ᄎ": 1465, - "ᄏ": 1466, - "ᄐ": 1467, - "ᄑ": 1468, - "ᄒ": 1469, - "ᅡ": 1470, - "ᅢ": 1471, - "ᅥ": 1472, - "ᅦ": 1473, - "ᅧ": 1474, - "ᅩ": 1475, - "ᅪ": 1476, - "ᅭ": 1477, - "ᅮ": 1478, - "ᅯ": 1479, - "ᅲ": 1480, - "ᅳ": 1481, - "ᅴ": 1482, - "ᅵ": 1483, - "ᆨ": 1484, - "ᆫ": 1485, - "ᆯ": 1486, - "ᆷ": 1487, - "ᆸ": 1488, - "ᆼ": 1489, - "ᴬ": 1490, - "ᴮ": 1491, - "ᴰ": 1492, - "ᴵ": 1493, - "ᴺ": 1494, - "ᵀ": 1495, - "ᵃ": 1496, - "ᵇ": 1497, - "ᵈ": 1498, - "ᵉ": 1499, - "ᵍ": 1500, - "ᵏ": 1501, - "ᵐ": 1502, - "ᵒ": 1503, - "ᵖ": 1504, - "ᵗ": 1505, - "ᵘ": 1506, - "ᵢ": 1507, - "ᵣ": 1508, - "ᵤ": 1509, - "ᵥ": 1510, - "ᶜ": 1511, - "ᶠ": 1512, - "‐": 1513, - "‑": 1514, - "‒": 1515, - "–": 1516, - "—": 1517, - "―": 1518, - "‖": 1519, - "‘": 1520, - "’": 1521, - "‚": 1522, - "“": 1523, - "”": 1524, - "„": 1525, - "†": 1526, - "‡": 1527, - "•": 1528, - "…": 1529, - "‰": 1530, - "′": 1531, - "″": 1532, - "›": 1533, - "‿": 1534, - "⁄": 1535, - "⁰": 1536, - "ⁱ": 1537, - "⁴": 1538, - "⁵": 1539, - "⁶": 1540, - "⁷": 1541, - "⁸": 1542, - "⁹": 1543, - "⁺": 1544, - "⁻": 1545, - "ⁿ": 1546, - "₀": 1547, - "₁": 1548, - "₂": 1549, - "₃": 1550, - "₄": 1551, - "₅": 1552, - "₆": 1553, - "₇": 1554, - "₈": 1555, - "₉": 1556, - "₊": 1557, - "₍": 1558, - "₎": 1559, - "ₐ": 1560, - "ₑ": 1561, - "ₒ": 1562, - "ₓ": 1563, - "ₕ": 1564, - "ₖ": 1565, - "ₗ": 1566, - "ₘ": 1567, - "ₙ": 1568, - "ₚ": 1569, - "ₛ": 1570, - "ₜ": 1571, - "₤": 1572, - "₩": 1573, - "€": 1574, - "₱": 1575, - "₹": 1576, - "ℓ": 1577, - "№": 1578, - "ℝ": 1579, - "™": 1580, - "⅓": 1581, - "⅔": 1582, - "←": 1583, - "↑": 1584, - "→": 1585, - "↓": 1586, - "↔": 1587, - "↦": 1588, - "⇄": 1589, - "⇌": 1590, - "⇒": 1591, - "∂": 1592, - "∅": 1593, - "∆": 1594, - "∇": 1595, - "∈": 1596, - "−": 1597, - "∗": 1598, - "∘": 1599, - "√": 1600, - "∞": 1601, - "∧": 1602, - "∨": 1603, - "∩": 1604, - "∪": 1605, - "≈": 1606, - "≡": 1607, - "≤": 1608, - "≥": 1609, - "⊂": 1610, - "⊆": 1611, - "⊕": 1612, - "⊗": 1613, - "⋅": 1614, - "─": 1615, - "│": 1616, - "■": 1617, - "▪": 1618, - "●": 1619, - "★": 1620, - "☆": 1621, - "☉": 1622, - "♠": 1623, - "♣": 1624, - "♥": 1625, - "♦": 1626, - "♭": 1627, - "♯": 1628, - "⟨": 1629, - "⟩": 1630, - "ⱼ": 1631, - "⺩": 1632, - "⺼": 1633, - "⽥": 1634, - "、": 1635, - "。": 1636, - "〈": 1637, - "〉": 1638, - "《": 1639, - "》": 1640, - "「": 1641, - "」": 1642, - "『": 1643, - "』": 1644, - "〜": 1645, - "あ": 1646, - "い": 1647, - "う": 1648, - "え": 1649, - "お": 1650, - "か": 1651, - "き": 1652, - "く": 1653, - "け": 1654, - "こ": 1655, - "さ": 1656, - "し": 1657, - "す": 1658, - "せ": 1659, - "そ": 1660, - "た": 1661, - "ち": 1662, - "っ": 1663, - "つ": 1664, - "て": 1665, - "と": 1666, - "な": 1667, - "に": 1668, - "ぬ": 1669, - "ね": 1670, - "の": 1671, - "は": 1672, - "ひ": 1673, - "ふ": 1674, - "へ": 1675, - "ほ": 1676, - "ま": 1677, - "み": 1678, - "む": 1679, - "め": 1680, - "も": 1681, - "や": 1682, - "ゆ": 1683, - "よ": 1684, - "ら": 1685, - "り": 1686, - "る": 1687, - "れ": 1688, - "ろ": 1689, - "を": 1690, - "ん": 1691, - "ァ": 1692, - "ア": 1693, - "ィ": 1694, - "イ": 1695, - "ウ": 1696, - "ェ": 1697, - "エ": 1698, - "オ": 1699, - "カ": 1700, - "キ": 1701, - "ク": 1702, - "ケ": 1703, - "コ": 1704, - "サ": 1705, - "シ": 1706, - "ス": 1707, - "セ": 1708, - "タ": 1709, - "チ": 1710, - "ッ": 1711, - "ツ": 1712, - "テ": 1713, - "ト": 1714, - "ナ": 1715, - "ニ": 1716, - "ノ": 1717, - "ハ": 1718, - "ヒ": 1719, - "フ": 1720, - "ヘ": 1721, - "ホ": 1722, - "マ": 1723, - "ミ": 1724, - "ム": 1725, - "メ": 1726, - "モ": 1727, - "ャ": 1728, - "ュ": 1729, - "ョ": 1730, - "ラ": 1731, - "リ": 1732, - "ル": 1733, - "レ": 1734, - "ロ": 1735, - "ワ": 1736, - "ン": 1737, - "・": 1738, - "ー": 1739, - "一": 1740, - "三": 1741, - "上": 1742, - "下": 1743, - "不": 1744, - "世": 1745, - "中": 1746, - "主": 1747, - "久": 1748, - "之": 1749, - "也": 1750, - "事": 1751, - "二": 1752, - "五": 1753, - "井": 1754, - "京": 1755, - "人": 1756, - "亻": 1757, - "仁": 1758, - "介": 1759, - "代": 1760, - "仮": 1761, - "伊": 1762, - "会": 1763, - "佐": 1764, - "侍": 1765, - "保": 1766, - "信": 1767, - "健": 1768, - "元": 1769, - "光": 1770, - "八": 1771, - "公": 1772, - "内": 1773, - "出": 1774, - "分": 1775, - "前": 1776, - "劉": 1777, - "力": 1778, - "加": 1779, - "勝": 1780, - "北": 1781, - "区": 1782, - "十": 1783, - "千": 1784, - "南": 1785, - "博": 1786, - "原": 1787, - "口": 1788, - "古": 1789, - "史": 1790, - "司": 1791, - "合": 1792, - "吉": 1793, - "同": 1794, - "名": 1795, - "和": 1796, - "囗": 1797, - "四": 1798, - "国": 1799, - "國": 1800, - "土": 1801, - "地": 1802, - "坂": 1803, - "城": 1804, - "堂": 1805, - "場": 1806, - "士": 1807, - "夏": 1808, - "外": 1809, - "大": 1810, - "天": 1811, - "太": 1812, - "夫": 1813, - "奈": 1814, - "女": 1815, - "子": 1816, - "学": 1817, - "宀": 1818, - "宇": 1819, - "安": 1820, - "宗": 1821, - "定": 1822, - "宣": 1823, - "宮": 1824, - "家": 1825, - "宿": 1826, - "寺": 1827, - "將": 1828, - "小": 1829, - "尚": 1830, - "山": 1831, - "岡": 1832, - "島": 1833, - "崎": 1834, - "川": 1835, - "州": 1836, - "巿": 1837, - "帝": 1838, - "平": 1839, - "年": 1840, - "幸": 1841, - "广": 1842, - "弘": 1843, - "張": 1844, - "彳": 1845, - "後": 1846, - "御": 1847, - "德": 1848, - "心": 1849, - "忄": 1850, - "志": 1851, - "忠": 1852, - "愛": 1853, - "成": 1854, - "我": 1855, - "戦": 1856, - "戸": 1857, - "手": 1858, - "扌": 1859, - "政": 1860, - "文": 1861, - "新": 1862, - "方": 1863, - "日": 1864, - "明": 1865, - "星": 1866, - "春": 1867, - "昭": 1868, - "智": 1869, - "曲": 1870, - "書": 1871, - "月": 1872, - "有": 1873, - "朝": 1874, - "木": 1875, - "本": 1876, - "李": 1877, - "村": 1878, - "東": 1879, - "松": 1880, - "林": 1881, - "森": 1882, - "楊": 1883, - "樹": 1884, - "橋": 1885, - "歌": 1886, - "止": 1887, - "正": 1888, - "武": 1889, - "比": 1890, - "氏": 1891, - "民": 1892, - "水": 1893, - "氵": 1894, - "氷": 1895, - "永": 1896, - "江": 1897, - "沢": 1898, - "河": 1899, - "治": 1900, - "法": 1901, - "海": 1902, - "清": 1903, - "漢": 1904, - "瀬": 1905, - "火": 1906, - "版": 1907, - "犬": 1908, - "王": 1909, - "生": 1910, - "田": 1911, - "男": 1912, - "疒": 1913, - "発": 1914, - "白": 1915, - "的": 1916, - "皇": 1917, - "目": 1918, - "相": 1919, - "省": 1920, - "真": 1921, - "石": 1922, - "示": 1923, - "社": 1924, - "神": 1925, - "福": 1926, - "禾": 1927, - "秀": 1928, - "秋": 1929, - "空": 1930, - "立": 1931, - "章": 1932, - "竹": 1933, - "糹": 1934, - "美": 1935, - "義": 1936, - "耳": 1937, - "良": 1938, - "艹": 1939, - "花": 1940, - "英": 1941, - "華": 1942, - "葉": 1943, - "藤": 1944, - "行": 1945, - "街": 1946, - "西": 1947, - "見": 1948, - "訁": 1949, - "語": 1950, - "谷": 1951, - "貝": 1952, - "貴": 1953, - "車": 1954, - "軍": 1955, - "辶": 1956, - "道": 1957, - "郎": 1958, - "郡": 1959, - "部": 1960, - "都": 1961, - "里": 1962, - "野": 1963, - "金": 1964, - "鈴": 1965, - "镇": 1966, - "長": 1967, - "門": 1968, - "間": 1969, - "阝": 1970, - "阿": 1971, - "陳": 1972, - "陽": 1973, - "雄": 1974, - "青": 1975, - "面": 1976, - "風": 1977, - "食": 1978, - "香": 1979, - "馬": 1980, - "高": 1981, - "龍": 1982, - "龸": 1983, - "fi": 1984, - "fl": 1985, - "!": 1986, - "(": 1987, - ")": 1988, - ",": 1989, - "-": 1990, - ".": 1991, - "/": 1992, - ":": 1993, - "?": 1994, - "~": 1995, - "the": 1996, - "of": 1997, - "and": 1998, - "in": 1999, - "to": 2000, - "was": 2001, - "he": 2002, - "is": 2003, - "as": 2004, - "for": 2005, - "on": 2006, - "with": 2007, - "that": 2008, - "it": 2009, - "his": 2010, - "by": 2011, - "at": 2012, - "from": 2013, - "her": 2014, - "##s": 2015, - "she": 2016, - "you": 2017, - "had": 2018, - "an": 2019, - "were": 2020, - "but": 2021, - "be": 2022, - "this": 2023, - "are": 2024, - "not": 2025, - "my": 2026, - "they": 2027, - "one": 2028, - "which": 2029, - "or": 2030, - "have": 2031, - "him": 2032, - "me": 2033, - "first": 2034, - "all": 2035, - "also": 2036, - "their": 2037, - "has": 2038, - "up": 2039, - "who": 2040, - "out": 2041, - "been": 2042, - "when": 2043, - "after": 2044, - "there": 2045, - "into": 2046, - "new": 2047, - "two": 2048, - "its": 2049, - "##a": 2050, - "time": 2051, - "would": 2052, - "no": 2053, - "what": 2054, - "about": 2055, - "said": 2056, - "we": 2057, - "over": 2058, - "then": 2059, - "other": 2060, - "so": 2061, - "more": 2062, - "##e": 2063, - "can": 2064, - "if": 2065, - "like": 2066, - "back": 2067, - "them": 2068, - "only": 2069, - "some": 2070, - "could": 2071, - "##i": 2072, - "where": 2073, - "just": 2074, - "##ing": 2075, - "during": 2076, - "before": 2077, - "##n": 2078, - "do": 2079, - "##o": 2080, - "made": 2081, - "school": 2082, - "through": 2083, - "than": 2084, - "now": 2085, - "years": 2086, - "most": 2087, - "world": 2088, - "may": 2089, - "between": 2090, - "down": 2091, - "well": 2092, - "three": 2093, - "##d": 2094, - "year": 2095, - "while": 2096, - "will": 2097, - "##ed": 2098, - "##r": 2099, - "##y": 2100, - "later": 2101, - "##t": 2102, - "city": 2103, - "under": 2104, - "around": 2105, - "did": 2106, - "such": 2107, - "being": 2108, - "used": 2109, - "state": 2110, - "people": 2111, - "part": 2112, - "know": 2113, - "against": 2114, - "your": 2115, - "many": 2116, - "second": 2117, - "university": 2118, - "both": 2119, - "national": 2120, - "##er": 2121, - "these": 2122, - "don": 2123, - "known": 2124, - "off": 2125, - "way": 2126, - "until": 2127, - "re": 2128, - "how": 2129, - "even": 2130, - "get": 2131, - "head": 2132, - "...": 2133, - "didn": 2134, - "##ly": 2135, - "team": 2136, - "american": 2137, - "because": 2138, - "de": 2139, - "##l": 2140, - "born": 2141, - "united": 2142, - "film": 2143, - "since": 2144, - "still": 2145, - "long": 2146, - "work": 2147, - "south": 2148, - "us": 2149, - "became": 2150, - "any": 2151, - "high": 2152, - "again": 2153, - "day": 2154, - "family": 2155, - "see": 2156, - "right": 2157, - "man": 2158, - "eyes": 2159, - "house": 2160, - "season": 2161, - "war": 2162, - "states": 2163, - "including": 2164, - "took": 2165, - "life": 2166, - "north": 2167, - "same": 2168, - "each": 2169, - "called": 2170, - "name": 2171, - "much": 2172, - "place": 2173, - "however": 2174, - "go": 2175, - "four": 2176, - "group": 2177, - "another": 2178, - "found": 2179, - "won": 2180, - "area": 2181, - "here": 2182, - "going": 2183, - "10": 2184, - "away": 2185, - "series": 2186, - "left": 2187, - "home": 2188, - "music": 2189, - "best": 2190, - "make": 2191, - "hand": 2192, - "number": 2193, - "company": 2194, - "several": 2195, - "never": 2196, - "last": 2197, - "john": 2198, - "000": 2199, - "very": 2200, - "album": 2201, - "take": 2202, - "end": 2203, - "good": 2204, - "too": 2205, - "following": 2206, - "released": 2207, - "game": 2208, - "played": 2209, - "little": 2210, - "began": 2211, - "district": 2212, - "##m": 2213, - "old": 2214, - "want": 2215, - "those": 2216, - "side": 2217, - "held": 2218, - "own": 2219, - "early": 2220, - "county": 2221, - "ll": 2222, - "league": 2223, - "use": 2224, - "west": 2225, - "##u": 2226, - "face": 2227, - "think": 2228, - "##es": 2229, - "2010": 2230, - "government": 2231, - "##h": 2232, - "march": 2233, - "came": 2234, - "small": 2235, - "general": 2236, - "town": 2237, - "june": 2238, - "##on": 2239, - "line": 2240, - "based": 2241, - "something": 2242, - "##k": 2243, - "september": 2244, - "thought": 2245, - "looked": 2246, - "along": 2247, - "international": 2248, - "2011": 2249, - "air": 2250, - "july": 2251, - "club": 2252, - "went": 2253, - "january": 2254, - "october": 2255, - "our": 2256, - "august": 2257, - "april": 2258, - "york": 2259, - "12": 2260, - "few": 2261, - "2012": 2262, - "2008": 2263, - "east": 2264, - "show": 2265, - "member": 2266, - "college": 2267, - "2009": 2268, - "father": 2269, - "public": 2270, - "##us": 2271, - "come": 2272, - "men": 2273, - "five": 2274, - "set": 2275, - "station": 2276, - "church": 2277, - "##c": 2278, - "next": 2279, - "former": 2280, - "november": 2281, - "room": 2282, - "party": 2283, - "located": 2284, - "december": 2285, - "2013": 2286, - "age": 2287, - "got": 2288, - "2007": 2289, - "##g": 2290, - "system": 2291, - "let": 2292, - "love": 2293, - "2006": 2294, - "though": 2295, - "every": 2296, - "2014": 2297, - "look": 2298, - "song": 2299, - "water": 2300, - "century": 2301, - "without": 2302, - "body": 2303, - "black": 2304, - "night": 2305, - "within": 2306, - "great": 2307, - "women": 2308, - "single": 2309, - "ve": 2310, - "building": 2311, - "large": 2312, - "population": 2313, - "river": 2314, - "named": 2315, - "band": 2316, - "white": 2317, - "started": 2318, - "##an": 2319, - "once": 2320, - "15": 2321, - "20": 2322, - "should": 2323, - "18": 2324, - "2015": 2325, - "service": 2326, - "top": 2327, - "built": 2328, - "british": 2329, - "open": 2330, - "death": 2331, - "king": 2332, - "moved": 2333, - "local": 2334, - "times": 2335, - "children": 2336, - "february": 2337, - "book": 2338, - "why": 2339, - "11": 2340, - "door": 2341, - "need": 2342, - "president": 2343, - "order": 2344, - "final": 2345, - "road": 2346, - "wasn": 2347, - "although": 2348, - "due": 2349, - "major": 2350, - "died": 2351, - "village": 2352, - "third": 2353, - "knew": 2354, - "2016": 2355, - "asked": 2356, - "turned": 2357, - "st": 2358, - "wanted": 2359, - "say": 2360, - "##p": 2361, - "together": 2362, - "received": 2363, - "main": 2364, - "son": 2365, - "served": 2366, - "different": 2367, - "##en": 2368, - "behind": 2369, - "himself": 2370, - "felt": 2371, - "members": 2372, - "power": 2373, - "football": 2374, - "law": 2375, - "voice": 2376, - "play": 2377, - "##in": 2378, - "near": 2379, - "park": 2380, - "history": 2381, - "30": 2382, - "having": 2383, - "2005": 2384, - "16": 2385, - "##man": 2386, - "saw": 2387, - "mother": 2388, - "##al": 2389, - "army": 2390, - "point": 2391, - "front": 2392, - "help": 2393, - "english": 2394, - "street": 2395, - "art": 2396, - "late": 2397, - "hands": 2398, - "games": 2399, - "award": 2400, - "##ia": 2401, - "young": 2402, - "14": 2403, - "put": 2404, - "published": 2405, - "country": 2406, - "division": 2407, - "across": 2408, - "told": 2409, - "13": 2410, - "often": 2411, - "ever": 2412, - "french": 2413, - "london": 2414, - "center": 2415, - "six": 2416, - "red": 2417, - "2017": 2418, - "led": 2419, - "days": 2420, - "include": 2421, - "light": 2422, - "25": 2423, - "find": 2424, - "tell": 2425, - "among": 2426, - "species": 2427, - "really": 2428, - "according": 2429, - "central": 2430, - "half": 2431, - "2004": 2432, - "form": 2433, - "original": 2434, - "gave": 2435, - "office": 2436, - "making": 2437, - "enough": 2438, - "lost": 2439, - "full": 2440, - "opened": 2441, - "must": 2442, - "included": 2443, - "live": 2444, - "given": 2445, - "german": 2446, - "player": 2447, - "run": 2448, - "business": 2449, - "woman": 2450, - "community": 2451, - "cup": 2452, - "might": 2453, - "million": 2454, - "land": 2455, - "2000": 2456, - "court": 2457, - "development": 2458, - "17": 2459, - "short": 2460, - "round": 2461, - "ii": 2462, - "km": 2463, - "seen": 2464, - "class": 2465, - "story": 2466, - "always": 2467, - "become": 2468, - "sure": 2469, - "research": 2470, - "almost": 2471, - "director": 2472, - "council": 2473, - "la": 2474, - "##2": 2475, - "career": 2476, - "things": 2477, - "using": 2478, - "island": 2479, - "##z": 2480, - "couldn": 2481, - "car": 2482, - "##is": 2483, - "24": 2484, - "close": 2485, - "force": 2486, - "##1": 2487, - "better": 2488, - "free": 2489, - "support": 2490, - "control": 2491, - "field": 2492, - "students": 2493, - "2003": 2494, - "education": 2495, - "married": 2496, - "##b": 2497, - "nothing": 2498, - "worked": 2499, - "others": 2500, - "record": 2501, - "big": 2502, - "inside": 2503, - "level": 2504, - "anything": 2505, - "continued": 2506, - "give": 2507, - "james": 2508, - "##3": 2509, - "military": 2510, - "established": 2511, - "non": 2512, - "returned": 2513, - "feel": 2514, - "does": 2515, - "title": 2516, - "written": 2517, - "thing": 2518, - "feet": 2519, - "william": 2520, - "far": 2521, - "co": 2522, - "association": 2523, - "hard": 2524, - "already": 2525, - "2002": 2526, - "##ra": 2527, - "championship": 2528, - "human": 2529, - "western": 2530, - "100": 2531, - "##na": 2532, - "department": 2533, - "hall": 2534, - "role": 2535, - "various": 2536, - "production": 2537, - "21": 2538, - "19": 2539, - "heart": 2540, - "2001": 2541, - "living": 2542, - "fire": 2543, - "version": 2544, - "##ers": 2545, - "##f": 2546, - "television": 2547, - "royal": 2548, - "##4": 2549, - "produced": 2550, - "working": 2551, - "act": 2552, - "case": 2553, - "society": 2554, - "region": 2555, - "present": 2556, - "radio": 2557, - "period": 2558, - "looking": 2559, - "least": 2560, - "total": 2561, - "keep": 2562, - "england": 2563, - "wife": 2564, - "program": 2565, - "per": 2566, - "brother": 2567, - "mind": 2568, - "special": 2569, - "22": 2570, - "##le": 2571, - "am": 2572, - "works": 2573, - "soon": 2574, - "##6": 2575, - "political": 2576, - "george": 2577, - "services": 2578, - "taken": 2579, - "created": 2580, - "##7": 2581, - "further": 2582, - "able": 2583, - "reached": 2584, - "david": 2585, - "union": 2586, - "joined": 2587, - "upon": 2588, - "done": 2589, - "important": 2590, - "social": 2591, - "information": 2592, - "either": 2593, - "##ic": 2594, - "##x": 2595, - "appeared": 2596, - "position": 2597, - "ground": 2598, - "lead": 2599, - "rock": 2600, - "dark": 2601, - "election": 2602, - "23": 2603, - "board": 2604, - "france": 2605, - "hair": 2606, - "course": 2607, - "arms": 2608, - "site": 2609, - "police": 2610, - "girl": 2611, - "instead": 2612, - "real": 2613, - "sound": 2614, - "##v": 2615, - "words": 2616, - "moment": 2617, - "##te": 2618, - "someone": 2619, - "##8": 2620, - "summer": 2621, - "project": 2622, - "announced": 2623, - "san": 2624, - "less": 2625, - "wrote": 2626, - "past": 2627, - "followed": 2628, - "##5": 2629, - "blue": 2630, - "founded": 2631, - "al": 2632, - "finally": 2633, - "india": 2634, - "taking": 2635, - "records": 2636, - "america": 2637, - "##ne": 2638, - "1999": 2639, - "design": 2640, - "considered": 2641, - "northern": 2642, - "god": 2643, - "stop": 2644, - "battle": 2645, - "toward": 2646, - "european": 2647, - "outside": 2648, - "described": 2649, - "track": 2650, - "today": 2651, - "playing": 2652, - "language": 2653, - "28": 2654, - "call": 2655, - "26": 2656, - "heard": 2657, - "professional": 2658, - "low": 2659, - "australia": 2660, - "miles": 2661, - "california": 2662, - "win": 2663, - "yet": 2664, - "green": 2665, - "##ie": 2666, - "trying": 2667, - "blood": 2668, - "##ton": 2669, - "southern": 2670, - "science": 2671, - "maybe": 2672, - "everything": 2673, - "match": 2674, - "square": 2675, - "27": 2676, - "mouth": 2677, - "video": 2678, - "race": 2679, - "recorded": 2680, - "leave": 2681, - "above": 2682, - "##9": 2683, - "daughter": 2684, - "points": 2685, - "space": 2686, - "1998": 2687, - "museum": 2688, - "change": 2689, - "middle": 2690, - "common": 2691, - "##0": 2692, - "move": 2693, - "tv": 2694, - "post": 2695, - "##ta": 2696, - "lake": 2697, - "seven": 2698, - "tried": 2699, - "elected": 2700, - "closed": 2701, - "ten": 2702, - "paul": 2703, - "minister": 2704, - "##th": 2705, - "months": 2706, - "start": 2707, - "chief": 2708, - "return": 2709, - "canada": 2710, - "person": 2711, - "sea": 2712, - "release": 2713, - "similar": 2714, - "modern": 2715, - "brought": 2716, - "rest": 2717, - "hit": 2718, - "formed": 2719, - "mr": 2720, - "##la": 2721, - "1997": 2722, - "floor": 2723, - "event": 2724, - "doing": 2725, - "thomas": 2726, - "1996": 2727, - "robert": 2728, - "care": 2729, - "killed": 2730, - "training": 2731, - "star": 2732, - "week": 2733, - "needed": 2734, - "turn": 2735, - "finished": 2736, - "railway": 2737, - "rather": 2738, - "news": 2739, - "health": 2740, - "sent": 2741, - "example": 2742, - "ran": 2743, - "term": 2744, - "michael": 2745, - "coming": 2746, - "currently": 2747, - "yes": 2748, - "forces": 2749, - "despite": 2750, - "gold": 2751, - "areas": 2752, - "50": 2753, - "stage": 2754, - "fact": 2755, - "29": 2756, - "dead": 2757, - "says": 2758, - "popular": 2759, - "2018": 2760, - "originally": 2761, - "germany": 2762, - "probably": 2763, - "developed": 2764, - "result": 2765, - "pulled": 2766, - "friend": 2767, - "stood": 2768, - "money": 2769, - "running": 2770, - "mi": 2771, - "signed": 2772, - "word": 2773, - "songs": 2774, - "child": 2775, - "eventually": 2776, - "met": 2777, - "tour": 2778, - "average": 2779, - "teams": 2780, - "minutes": 2781, - "festival": 2782, - "current": 2783, - "deep": 2784, - "kind": 2785, - "1995": 2786, - "decided": 2787, - "usually": 2788, - "eastern": 2789, - "seemed": 2790, - "##ness": 2791, - "episode": 2792, - "bed": 2793, - "added": 2794, - "table": 2795, - "indian": 2796, - "private": 2797, - "charles": 2798, - "route": 2799, - "available": 2800, - "idea": 2801, - "throughout": 2802, - "centre": 2803, - "addition": 2804, - "appointed": 2805, - "style": 2806, - "1994": 2807, - "books": 2808, - "eight": 2809, - "construction": 2810, - "press": 2811, - "mean": 2812, - "wall": 2813, - "friends": 2814, - "remained": 2815, - "schools": 2816, - "study": 2817, - "##ch": 2818, - "##um": 2819, - "institute": 2820, - "oh": 2821, - "chinese": 2822, - "sometimes": 2823, - "events": 2824, - "possible": 2825, - "1992": 2826, - "australian": 2827, - "type": 2828, - "brown": 2829, - "forward": 2830, - "talk": 2831, - "process": 2832, - "food": 2833, - "debut": 2834, - "seat": 2835, - "performance": 2836, - "committee": 2837, - "features": 2838, - "character": 2839, - "arts": 2840, - "herself": 2841, - "else": 2842, - "lot": 2843, - "strong": 2844, - "russian": 2845, - "range": 2846, - "hours": 2847, - "peter": 2848, - "arm": 2849, - "##da": 2850, - "morning": 2851, - "dr": 2852, - "sold": 2853, - "##ry": 2854, - "quickly": 2855, - "directed": 2856, - "1993": 2857, - "guitar": 2858, - "china": 2859, - "##w": 2860, - "31": 2861, - "list": 2862, - "##ma": 2863, - "performed": 2864, - "media": 2865, - "uk": 2866, - "players": 2867, - "smile": 2868, - "##rs": 2869, - "myself": 2870, - "40": 2871, - "placed": 2872, - "coach": 2873, - "province": 2874, - "towards": 2875, - "wouldn": 2876, - "leading": 2877, - "whole": 2878, - "boy": 2879, - "official": 2880, - "designed": 2881, - "grand": 2882, - "census": 2883, - "##el": 2884, - "europe": 2885, - "attack": 2886, - "japanese": 2887, - "henry": 2888, - "1991": 2889, - "##re": 2890, - "##os": 2891, - "cross": 2892, - "getting": 2893, - "alone": 2894, - "action": 2895, - "lower": 2896, - "network": 2897, - "wide": 2898, - "washington": 2899, - "japan": 2900, - "1990": 2901, - "hospital": 2902, - "believe": 2903, - "changed": 2904, - "sister": 2905, - "##ar": 2906, - "hold": 2907, - "gone": 2908, - "sir": 2909, - "hadn": 2910, - "ship": 2911, - "##ka": 2912, - "studies": 2913, - "academy": 2914, - "shot": 2915, - "rights": 2916, - "below": 2917, - "base": 2918, - "bad": 2919, - "involved": 2920, - "kept": 2921, - "largest": 2922, - "##ist": 2923, - "bank": 2924, - "future": 2925, - "especially": 2926, - "beginning": 2927, - "mark": 2928, - "movement": 2929, - "section": 2930, - "female": 2931, - "magazine": 2932, - "plan": 2933, - "professor": 2934, - "lord": 2935, - "longer": 2936, - "##ian": 2937, - "sat": 2938, - "walked": 2939, - "hill": 2940, - "actually": 2941, - "civil": 2942, - "energy": 2943, - "model": 2944, - "families": 2945, - "size": 2946, - "thus": 2947, - "aircraft": 2948, - "completed": 2949, - "includes": 2950, - "data": 2951, - "captain": 2952, - "##or": 2953, - "fight": 2954, - "vocals": 2955, - "featured": 2956, - "richard": 2957, - "bridge": 2958, - "fourth": 2959, - "1989": 2960, - "officer": 2961, - "stone": 2962, - "hear": 2963, - "##ism": 2964, - "means": 2965, - "medical": 2966, - "groups": 2967, - "management": 2968, - "self": 2969, - "lips": 2970, - "competition": 2971, - "entire": 2972, - "lived": 2973, - "technology": 2974, - "leaving": 2975, - "federal": 2976, - "tournament": 2977, - "bit": 2978, - "passed": 2979, - "hot": 2980, - "independent": 2981, - "awards": 2982, - "kingdom": 2983, - "mary": 2984, - "spent": 2985, - "fine": 2986, - "doesn": 2987, - "reported": 2988, - "##ling": 2989, - "jack": 2990, - "fall": 2991, - "raised": 2992, - "itself": 2993, - "stay": 2994, - "true": 2995, - "studio": 2996, - "1988": 2997, - "sports": 2998, - "replaced": 2999, - "paris": 3000, - "systems": 3001, - "saint": 3002, - "leader": 3003, - "theatre": 3004, - "whose": 3005, - "market": 3006, - "capital": 3007, - "parents": 3008, - "spanish": 3009, - "canadian": 3010, - "earth": 3011, - "##ity": 3012, - "cut": 3013, - "degree": 3014, - "writing": 3015, - "bay": 3016, - "christian": 3017, - "awarded": 3018, - "natural": 3019, - "higher": 3020, - "bill": 3021, - "##as": 3022, - "coast": 3023, - "provided": 3024, - "previous": 3025, - "senior": 3026, - "ft": 3027, - "valley": 3028, - "organization": 3029, - "stopped": 3030, - "onto": 3031, - "countries": 3032, - "parts": 3033, - "conference": 3034, - "queen": 3035, - "security": 3036, - "interest": 3037, - "saying": 3038, - "allowed": 3039, - "master": 3040, - "earlier": 3041, - "phone": 3042, - "matter": 3043, - "smith": 3044, - "winning": 3045, - "try": 3046, - "happened": 3047, - "moving": 3048, - "campaign": 3049, - "los": 3050, - "##ley": 3051, - "breath": 3052, - "nearly": 3053, - "mid": 3054, - "1987": 3055, - "certain": 3056, - "girls": 3057, - "date": 3058, - "italian": 3059, - "african": 3060, - "standing": 3061, - "fell": 3062, - "artist": 3063, - "##ted": 3064, - "shows": 3065, - "deal": 3066, - "mine": 3067, - "industry": 3068, - "1986": 3069, - "##ng": 3070, - "everyone": 3071, - "republic": 3072, - "provide": 3073, - "collection": 3074, - "library": 3075, - "student": 3076, - "##ville": 3077, - "primary": 3078, - "owned": 3079, - "older": 3080, - "via": 3081, - "heavy": 3082, - "1st": 3083, - "makes": 3084, - "##able": 3085, - "attention": 3086, - "anyone": 3087, - "africa": 3088, - "##ri": 3089, - "stated": 3090, - "length": 3091, - "ended": 3092, - "fingers": 3093, - "command": 3094, - "staff": 3095, - "skin": 3096, - "foreign": 3097, - "opening": 3098, - "governor": 3099, - "okay": 3100, - "medal": 3101, - "kill": 3102, - "sun": 3103, - "cover": 3104, - "job": 3105, - "1985": 3106, - "introduced": 3107, - "chest": 3108, - "hell": 3109, - "feeling": 3110, - "##ies": 3111, - "success": 3112, - "meet": 3113, - "reason": 3114, - "standard": 3115, - "meeting": 3116, - "novel": 3117, - "1984": 3118, - "trade": 3119, - "source": 3120, - "buildings": 3121, - "##land": 3122, - "rose": 3123, - "guy": 3124, - "goal": 3125, - "##ur": 3126, - "chapter": 3127, - "native": 3128, - "husband": 3129, - "previously": 3130, - "unit": 3131, - "limited": 3132, - "entered": 3133, - "weeks": 3134, - "producer": 3135, - "operations": 3136, - "mountain": 3137, - "takes": 3138, - "covered": 3139, - "forced": 3140, - "related": 3141, - "roman": 3142, - "complete": 3143, - "successful": 3144, - "key": 3145, - "texas": 3146, - "cold": 3147, - "##ya": 3148, - "channel": 3149, - "1980": 3150, - "traditional": 3151, - "films": 3152, - "dance": 3153, - "clear": 3154, - "approximately": 3155, - "500": 3156, - "nine": 3157, - "van": 3158, - "prince": 3159, - "question": 3160, - "active": 3161, - "tracks": 3162, - "ireland": 3163, - "regional": 3164, - "silver": 3165, - "author": 3166, - "personal": 3167, - "sense": 3168, - "operation": 3169, - "##ine": 3170, - "economic": 3171, - "1983": 3172, - "holding": 3173, - "twenty": 3174, - "isbn": 3175, - "additional": 3176, - "speed": 3177, - "hour": 3178, - "edition": 3179, - "regular": 3180, - "historic": 3181, - "places": 3182, - "whom": 3183, - "shook": 3184, - "movie": 3185, - "km²": 3186, - "secretary": 3187, - "prior": 3188, - "report": 3189, - "chicago": 3190, - "read": 3191, - "foundation": 3192, - "view": 3193, - "engine": 3194, - "scored": 3195, - "1982": 3196, - "units": 3197, - "ask": 3198, - "airport": 3199, - "property": 3200, - "ready": 3201, - "immediately": 3202, - "lady": 3203, - "month": 3204, - "listed": 3205, - "contract": 3206, - "##de": 3207, - "manager": 3208, - "themselves": 3209, - "lines": 3210, - "##ki": 3211, - "navy": 3212, - "writer": 3213, - "meant": 3214, - "##ts": 3215, - "runs": 3216, - "##ro": 3217, - "practice": 3218, - "championships": 3219, - "singer": 3220, - "glass": 3221, - "commission": 3222, - "required": 3223, - "forest": 3224, - "starting": 3225, - "culture": 3226, - "generally": 3227, - "giving": 3228, - "access": 3229, - "attended": 3230, - "test": 3231, - "couple": 3232, - "stand": 3233, - "catholic": 3234, - "martin": 3235, - "caught": 3236, - "executive": 3237, - "##less": 3238, - "eye": 3239, - "##ey": 3240, - "thinking": 3241, - "chair": 3242, - "quite": 3243, - "shoulder": 3244, - "1979": 3245, - "hope": 3246, - "decision": 3247, - "plays": 3248, - "defeated": 3249, - "municipality": 3250, - "whether": 3251, - "structure": 3252, - "offered": 3253, - "slowly": 3254, - "pain": 3255, - "ice": 3256, - "direction": 3257, - "##ion": 3258, - "paper": 3259, - "mission": 3260, - "1981": 3261, - "mostly": 3262, - "200": 3263, - "noted": 3264, - "individual": 3265, - "managed": 3266, - "nature": 3267, - "lives": 3268, - "plant": 3269, - "##ha": 3270, - "helped": 3271, - "except": 3272, - "studied": 3273, - "computer": 3274, - "figure": 3275, - "relationship": 3276, - "issue": 3277, - "significant": 3278, - "loss": 3279, - "die": 3280, - "smiled": 3281, - "gun": 3282, - "ago": 3283, - "highest": 3284, - "1972": 3285, - "##am": 3286, - "male": 3287, - "bring": 3288, - "goals": 3289, - "mexico": 3290, - "problem": 3291, - "distance": 3292, - "commercial": 3293, - "completely": 3294, - "location": 3295, - "annual": 3296, - "famous": 3297, - "drive": 3298, - "1976": 3299, - "neck": 3300, - "1978": 3301, - "surface": 3302, - "caused": 3303, - "italy": 3304, - "understand": 3305, - "greek": 3306, - "highway": 3307, - "wrong": 3308, - "hotel": 3309, - "comes": 3310, - "appearance": 3311, - "joseph": 3312, - "double": 3313, - "issues": 3314, - "musical": 3315, - "companies": 3316, - "castle": 3317, - "income": 3318, - "review": 3319, - "assembly": 3320, - "bass": 3321, - "initially": 3322, - "parliament": 3323, - "artists": 3324, - "experience": 3325, - "1974": 3326, - "particular": 3327, - "walk": 3328, - "foot": 3329, - "engineering": 3330, - "talking": 3331, - "window": 3332, - "dropped": 3333, - "##ter": 3334, - "miss": 3335, - "baby": 3336, - "boys": 3337, - "break": 3338, - "1975": 3339, - "stars": 3340, - "edge": 3341, - "remember": 3342, - "policy": 3343, - "carried": 3344, - "train": 3345, - "stadium": 3346, - "bar": 3347, - "sex": 3348, - "angeles": 3349, - "evidence": 3350, - "##ge": 3351, - "becoming": 3352, - "assistant": 3353, - "soviet": 3354, - "1977": 3355, - "upper": 3356, - "step": 3357, - "wing": 3358, - "1970": 3359, - "youth": 3360, - "financial": 3361, - "reach": 3362, - "##ll": 3363, - "actor": 3364, - "numerous": 3365, - "##se": 3366, - "##st": 3367, - "nodded": 3368, - "arrived": 3369, - "##ation": 3370, - "minute": 3371, - "##nt": 3372, - "believed": 3373, - "sorry": 3374, - "complex": 3375, - "beautiful": 3376, - "victory": 3377, - "associated": 3378, - "temple": 3379, - "1968": 3380, - "1973": 3381, - "chance": 3382, - "perhaps": 3383, - "metal": 3384, - "##son": 3385, - "1945": 3386, - "bishop": 3387, - "##et": 3388, - "lee": 3389, - "launched": 3390, - "particularly": 3391, - "tree": 3392, - "le": 3393, - "retired": 3394, - "subject": 3395, - "prize": 3396, - "contains": 3397, - "yeah": 3398, - "theory": 3399, - "empire": 3400, - "##ce": 3401, - "suddenly": 3402, - "waiting": 3403, - "trust": 3404, - "recording": 3405, - "##to": 3406, - "happy": 3407, - "terms": 3408, - "camp": 3409, - "champion": 3410, - "1971": 3411, - "religious": 3412, - "pass": 3413, - "zealand": 3414, - "names": 3415, - "2nd": 3416, - "port": 3417, - "ancient": 3418, - "tom": 3419, - "corner": 3420, - "represented": 3421, - "watch": 3422, - "legal": 3423, - "anti": 3424, - "justice": 3425, - "cause": 3426, - "watched": 3427, - "brothers": 3428, - "45": 3429, - "material": 3430, - "changes": 3431, - "simply": 3432, - "response": 3433, - "louis": 3434, - "fast": 3435, - "##ting": 3436, - "answer": 3437, - "60": 3438, - "historical": 3439, - "1969": 3440, - "stories": 3441, - "straight": 3442, - "create": 3443, - "feature": 3444, - "increased": 3445, - "rate": 3446, - "administration": 3447, - "virginia": 3448, - "el": 3449, - "activities": 3450, - "cultural": 3451, - "overall": 3452, - "winner": 3453, - "programs": 3454, - "basketball": 3455, - "legs": 3456, - "guard": 3457, - "beyond": 3458, - "cast": 3459, - "doctor": 3460, - "mm": 3461, - "flight": 3462, - "results": 3463, - "remains": 3464, - "cost": 3465, - "effect": 3466, - "winter": 3467, - "##ble": 3468, - "larger": 3469, - "islands": 3470, - "problems": 3471, - "chairman": 3472, - "grew": 3473, - "commander": 3474, - "isn": 3475, - "1967": 3476, - "pay": 3477, - "failed": 3478, - "selected": 3479, - "hurt": 3480, - "fort": 3481, - "box": 3482, - "regiment": 3483, - "majority": 3484, - "journal": 3485, - "35": 3486, - "edward": 3487, - "plans": 3488, - "##ke": 3489, - "##ni": 3490, - "shown": 3491, - "pretty": 3492, - "irish": 3493, - "characters": 3494, - "directly": 3495, - "scene": 3496, - "likely": 3497, - "operated": 3498, - "allow": 3499, - "spring": 3500, - "##j": 3501, - "junior": 3502, - "matches": 3503, - "looks": 3504, - "mike": 3505, - "houses": 3506, - "fellow": 3507, - "##tion": 3508, - "beach": 3509, - "marriage": 3510, - "##ham": 3511, - "##ive": 3512, - "rules": 3513, - "oil": 3514, - "65": 3515, - "florida": 3516, - "expected": 3517, - "nearby": 3518, - "congress": 3519, - "sam": 3520, - "peace": 3521, - "recent": 3522, - "iii": 3523, - "wait": 3524, - "subsequently": 3525, - "cell": 3526, - "##do": 3527, - "variety": 3528, - "serving": 3529, - "agreed": 3530, - "please": 3531, - "poor": 3532, - "joe": 3533, - "pacific": 3534, - "attempt": 3535, - "wood": 3536, - "democratic": 3537, - "piece": 3538, - "prime": 3539, - "##ca": 3540, - "rural": 3541, - "mile": 3542, - "touch": 3543, - "appears": 3544, - "township": 3545, - "1964": 3546, - "1966": 3547, - "soldiers": 3548, - "##men": 3549, - "##ized": 3550, - "1965": 3551, - "pennsylvania": 3552, - "closer": 3553, - "fighting": 3554, - "claimed": 3555, - "score": 3556, - "jones": 3557, - "physical": 3558, - "editor": 3559, - "##ous": 3560, - "filled": 3561, - "genus": 3562, - "specific": 3563, - "sitting": 3564, - "super": 3565, - "mom": 3566, - "##va": 3567, - "therefore": 3568, - "supported": 3569, - "status": 3570, - "fear": 3571, - "cases": 3572, - "store": 3573, - "meaning": 3574, - "wales": 3575, - "minor": 3576, - "spain": 3577, - "tower": 3578, - "focus": 3579, - "vice": 3580, - "frank": 3581, - "follow": 3582, - "parish": 3583, - "separate": 3584, - "golden": 3585, - "horse": 3586, - "fifth": 3587, - "remaining": 3588, - "branch": 3589, - "32": 3590, - "presented": 3591, - "stared": 3592, - "##id": 3593, - "uses": 3594, - "secret": 3595, - "forms": 3596, - "##co": 3597, - "baseball": 3598, - "exactly": 3599, - "##ck": 3600, - "choice": 3601, - "note": 3602, - "discovered": 3603, - "travel": 3604, - "composed": 3605, - "truth": 3606, - "russia": 3607, - "ball": 3608, - "color": 3609, - "kiss": 3610, - "dad": 3611, - "wind": 3612, - "continue": 3613, - "ring": 3614, - "referred": 3615, - "numbers": 3616, - "digital": 3617, - "greater": 3618, - "##ns": 3619, - "metres": 3620, - "slightly": 3621, - "direct": 3622, - "increase": 3623, - "1960": 3624, - "responsible": 3625, - "crew": 3626, - "rule": 3627, - "trees": 3628, - "troops": 3629, - "##no": 3630, - "broke": 3631, - "goes": 3632, - "individuals": 3633, - "hundred": 3634, - "weight": 3635, - "creek": 3636, - "sleep": 3637, - "memory": 3638, - "defense": 3639, - "provides": 3640, - "ordered": 3641, - "code": 3642, - "value": 3643, - "jewish": 3644, - "windows": 3645, - "1944": 3646, - "safe": 3647, - "judge": 3648, - "whatever": 3649, - "corps": 3650, - "realized": 3651, - "growing": 3652, - "pre": 3653, - "##ga": 3654, - "cities": 3655, - "alexander": 3656, - "gaze": 3657, - "lies": 3658, - "spread": 3659, - "scott": 3660, - "letter": 3661, - "showed": 3662, - "situation": 3663, - "mayor": 3664, - "transport": 3665, - "watching": 3666, - "workers": 3667, - "extended": 3668, - "##li": 3669, - "expression": 3670, - "normal": 3671, - "##ment": 3672, - "chart": 3673, - "multiple": 3674, - "border": 3675, - "##ba": 3676, - "host": 3677, - "##ner": 3678, - "daily": 3679, - "mrs": 3680, - "walls": 3681, - "piano": 3682, - "##ko": 3683, - "heat": 3684, - "cannot": 3685, - "##ate": 3686, - "earned": 3687, - "products": 3688, - "drama": 3689, - "era": 3690, - "authority": 3691, - "seasons": 3692, - "join": 3693, - "grade": 3694, - "##io": 3695, - "sign": 3696, - "difficult": 3697, - "machine": 3698, - "1963": 3699, - "territory": 3700, - "mainly": 3701, - "##wood": 3702, - "stations": 3703, - "squadron": 3704, - "1962": 3705, - "stepped": 3706, - "iron": 3707, - "19th": 3708, - "##led": 3709, - "serve": 3710, - "appear": 3711, - "sky": 3712, - "speak": 3713, - "broken": 3714, - "charge": 3715, - "knowledge": 3716, - "kilometres": 3717, - "removed": 3718, - "ships": 3719, - "article": 3720, - "campus": 3721, - "simple": 3722, - "##ty": 3723, - "pushed": 3724, - "britain": 3725, - "##ve": 3726, - "leaves": 3727, - "recently": 3728, - "cd": 3729, - "soft": 3730, - "boston": 3731, - "latter": 3732, - "easy": 3733, - "acquired": 3734, - "poland": 3735, - "##sa": 3736, - "quality": 3737, - "officers": 3738, - "presence": 3739, - "planned": 3740, - "nations": 3741, - "mass": 3742, - "broadcast": 3743, - "jean": 3744, - "share": 3745, - "image": 3746, - "influence": 3747, - "wild": 3748, - "offer": 3749, - "emperor": 3750, - "electric": 3751, - "reading": 3752, - "headed": 3753, - "ability": 3754, - "promoted": 3755, - "yellow": 3756, - "ministry": 3757, - "1942": 3758, - "throat": 3759, - "smaller": 3760, - "politician": 3761, - "##by": 3762, - "latin": 3763, - "spoke": 3764, - "cars": 3765, - "williams": 3766, - "males": 3767, - "lack": 3768, - "pop": 3769, - "80": 3770, - "##ier": 3771, - "acting": 3772, - "seeing": 3773, - "consists": 3774, - "##ti": 3775, - "estate": 3776, - "1961": 3777, - "pressure": 3778, - "johnson": 3779, - "newspaper": 3780, - "jr": 3781, - "chris": 3782, - "olympics": 3783, - "online": 3784, - "conditions": 3785, - "beat": 3786, - "elements": 3787, - "walking": 3788, - "vote": 3789, - "##field": 3790, - "needs": 3791, - "carolina": 3792, - "text": 3793, - "featuring": 3794, - "global": 3795, - "block": 3796, - "shirt": 3797, - "levels": 3798, - "francisco": 3799, - "purpose": 3800, - "females": 3801, - "et": 3802, - "dutch": 3803, - "duke": 3804, - "ahead": 3805, - "gas": 3806, - "twice": 3807, - "safety": 3808, - "serious": 3809, - "turning": 3810, - "highly": 3811, - "lieutenant": 3812, - "firm": 3813, - "maria": 3814, - "amount": 3815, - "mixed": 3816, - "daniel": 3817, - "proposed": 3818, - "perfect": 3819, - "agreement": 3820, - "affairs": 3821, - "3rd": 3822, - "seconds": 3823, - "contemporary": 3824, - "paid": 3825, - "1943": 3826, - "prison": 3827, - "save": 3828, - "kitchen": 3829, - "label": 3830, - "administrative": 3831, - "intended": 3832, - "constructed": 3833, - "academic": 3834, - "nice": 3835, - "teacher": 3836, - "races": 3837, - "1956": 3838, - "formerly": 3839, - "corporation": 3840, - "ben": 3841, - "nation": 3842, - "issued": 3843, - "shut": 3844, - "1958": 3845, - "drums": 3846, - "housing": 3847, - "victoria": 3848, - "seems": 3849, - "opera": 3850, - "1959": 3851, - "graduated": 3852, - "function": 3853, - "von": 3854, - "mentioned": 3855, - "picked": 3856, - "build": 3857, - "recognized": 3858, - "shortly": 3859, - "protection": 3860, - "picture": 3861, - "notable": 3862, - "exchange": 3863, - "elections": 3864, - "1980s": 3865, - "loved": 3866, - "percent": 3867, - "racing": 3868, - "fish": 3869, - "elizabeth": 3870, - "garden": 3871, - "volume": 3872, - "hockey": 3873, - "1941": 3874, - "beside": 3875, - "settled": 3876, - "##ford": 3877, - "1940": 3878, - "competed": 3879, - "replied": 3880, - "drew": 3881, - "1948": 3882, - "actress": 3883, - "marine": 3884, - "scotland": 3885, - "steel": 3886, - "glanced": 3887, - "farm": 3888, - "steve": 3889, - "1957": 3890, - "risk": 3891, - "tonight": 3892, - "positive": 3893, - "magic": 3894, - "singles": 3895, - "effects": 3896, - "gray": 3897, - "screen": 3898, - "dog": 3899, - "##ja": 3900, - "residents": 3901, - "bus": 3902, - "sides": 3903, - "none": 3904, - "secondary": 3905, - "literature": 3906, - "polish": 3907, - "destroyed": 3908, - "flying": 3909, - "founder": 3910, - "households": 3911, - "1939": 3912, - "lay": 3913, - "reserve": 3914, - "usa": 3915, - "gallery": 3916, - "##ler": 3917, - "1946": 3918, - "industrial": 3919, - "younger": 3920, - "approach": 3921, - "appearances": 3922, - "urban": 3923, - "ones": 3924, - "1950": 3925, - "finish": 3926, - "avenue": 3927, - "powerful": 3928, - "fully": 3929, - "growth": 3930, - "page": 3931, - "honor": 3932, - "jersey": 3933, - "projects": 3934, - "advanced": 3935, - "revealed": 3936, - "basic": 3937, - "90": 3938, - "infantry": 3939, - "pair": 3940, - "equipment": 3941, - "visit": 3942, - "33": 3943, - "evening": 3944, - "search": 3945, - "grant": 3946, - "effort": 3947, - "solo": 3948, - "treatment": 3949, - "buried": 3950, - "republican": 3951, - "primarily": 3952, - "bottom": 3953, - "owner": 3954, - "1970s": 3955, - "israel": 3956, - "gives": 3957, - "jim": 3958, - "dream": 3959, - "bob": 3960, - "remain": 3961, - "spot": 3962, - "70": 3963, - "notes": 3964, - "produce": 3965, - "champions": 3966, - "contact": 3967, - "ed": 3968, - "soul": 3969, - "accepted": 3970, - "ways": 3971, - "del": 3972, - "##ally": 3973, - "losing": 3974, - "split": 3975, - "price": 3976, - "capacity": 3977, - "basis": 3978, - "trial": 3979, - "questions": 3980, - "##ina": 3981, - "1955": 3982, - "20th": 3983, - "guess": 3984, - "officially": 3985, - "memorial": 3986, - "naval": 3987, - "initial": 3988, - "##ization": 3989, - "whispered": 3990, - "median": 3991, - "engineer": 3992, - "##ful": 3993, - "sydney": 3994, - "##go": 3995, - "columbia": 3996, - "strength": 3997, - "300": 3998, - "1952": 3999, - "tears": 4000, - "senate": 4001, - "00": 4002, - "card": 4003, - "asian": 4004, - "agent": 4005, - "1947": 4006, - "software": 4007, - "44": 4008, - "draw": 4009, - "warm": 4010, - "supposed": 4011, - "com": 4012, - "pro": 4013, - "##il": 4014, - "transferred": 4015, - "leaned": 4016, - "##at": 4017, - "candidate": 4018, - "escape": 4019, - "mountains": 4020, - "asia": 4021, - "potential": 4022, - "activity": 4023, - "entertainment": 4024, - "seem": 4025, - "traffic": 4026, - "jackson": 4027, - "murder": 4028, - "36": 4029, - "slow": 4030, - "product": 4031, - "orchestra": 4032, - "haven": 4033, - "agency": 4034, - "bbc": 4035, - "taught": 4036, - "website": 4037, - "comedy": 4038, - "unable": 4039, - "storm": 4040, - "planning": 4041, - "albums": 4042, - "rugby": 4043, - "environment": 4044, - "scientific": 4045, - "grabbed": 4046, - "protect": 4047, - "##hi": 4048, - "boat": 4049, - "typically": 4050, - "1954": 4051, - "1953": 4052, - "damage": 4053, - "principal": 4054, - "divided": 4055, - "dedicated": 4056, - "mount": 4057, - "ohio": 4058, - "##berg": 4059, - "pick": 4060, - "fought": 4061, - "driver": 4062, - "##der": 4063, - "empty": 4064, - "shoulders": 4065, - "sort": 4066, - "thank": 4067, - "berlin": 4068, - "prominent": 4069, - "account": 4070, - "freedom": 4071, - "necessary": 4072, - "efforts": 4073, - "alex": 4074, - "headquarters": 4075, - "follows": 4076, - "alongside": 4077, - "des": 4078, - "simon": 4079, - "andrew": 4080, - "suggested": 4081, - "operating": 4082, - "learning": 4083, - "steps": 4084, - "1949": 4085, - "sweet": 4086, - "technical": 4087, - "begin": 4088, - "easily": 4089, - "34": 4090, - "teeth": 4091, - "speaking": 4092, - "settlement": 4093, - "scale": 4094, - "##sh": 4095, - "renamed": 4096, - "ray": 4097, - "max": 4098, - "enemy": 4099, - "semi": 4100, - "joint": 4101, - "compared": 4102, - "##rd": 4103, - "scottish": 4104, - "leadership": 4105, - "analysis": 4106, - "offers": 4107, - "georgia": 4108, - "pieces": 4109, - "captured": 4110, - "animal": 4111, - "deputy": 4112, - "guest": 4113, - "organized": 4114, - "##lin": 4115, - "tony": 4116, - "combined": 4117, - "method": 4118, - "challenge": 4119, - "1960s": 4120, - "huge": 4121, - "wants": 4122, - "battalion": 4123, - "sons": 4124, - "rise": 4125, - "crime": 4126, - "types": 4127, - "facilities": 4128, - "telling": 4129, - "path": 4130, - "1951": 4131, - "platform": 4132, - "sit": 4133, - "1990s": 4134, - "##lo": 4135, - "tells": 4136, - "assigned": 4137, - "rich": 4138, - "pull": 4139, - "##ot": 4140, - "commonly": 4141, - "alive": 4142, - "##za": 4143, - "letters": 4144, - "concept": 4145, - "conducted": 4146, - "wearing": 4147, - "happen": 4148, - "bought": 4149, - "becomes": 4150, - "holy": 4151, - "gets": 4152, - "ocean": 4153, - "defeat": 4154, - "languages": 4155, - "purchased": 4156, - "coffee": 4157, - "occurred": 4158, - "titled": 4159, - "##q": 4160, - "declared": 4161, - "applied": 4162, - "sciences": 4163, - "concert": 4164, - "sounds": 4165, - "jazz": 4166, - "brain": 4167, - "##me": 4168, - "painting": 4169, - "fleet": 4170, - "tax": 4171, - "nick": 4172, - "##ius": 4173, - "michigan": 4174, - "count": 4175, - "animals": 4176, - "leaders": 4177, - "episodes": 4178, - "##line": 4179, - "content": 4180, - "##den": 4181, - "birth": 4182, - "##it": 4183, - "clubs": 4184, - "64": 4185, - "palace": 4186, - "critical": 4187, - "refused": 4188, - "fair": 4189, - "leg": 4190, - "laughed": 4191, - "returning": 4192, - "surrounding": 4193, - "participated": 4194, - "formation": 4195, - "lifted": 4196, - "pointed": 4197, - "connected": 4198, - "rome": 4199, - "medicine": 4200, - "laid": 4201, - "taylor": 4202, - "santa": 4203, - "powers": 4204, - "adam": 4205, - "tall": 4206, - "shared": 4207, - "focused": 4208, - "knowing": 4209, - "yards": 4210, - "entrance": 4211, - "falls": 4212, - "##wa": 4213, - "calling": 4214, - "##ad": 4215, - "sources": 4216, - "chosen": 4217, - "beneath": 4218, - "resources": 4219, - "yard": 4220, - "##ite": 4221, - "nominated": 4222, - "silence": 4223, - "zone": 4224, - "defined": 4225, - "##que": 4226, - "gained": 4227, - "thirty": 4228, - "38": 4229, - "bodies": 4230, - "moon": 4231, - "##ard": 4232, - "adopted": 4233, - "christmas": 4234, - "widely": 4235, - "register": 4236, - "apart": 4237, - "iran": 4238, - "premier": 4239, - "serves": 4240, - "du": 4241, - "unknown": 4242, - "parties": 4243, - "##les": 4244, - "generation": 4245, - "##ff": 4246, - "continues": 4247, - "quick": 4248, - "fields": 4249, - "brigade": 4250, - "quiet": 4251, - "teaching": 4252, - "clothes": 4253, - "impact": 4254, - "weapons": 4255, - "partner": 4256, - "flat": 4257, - "theater": 4258, - "supreme": 4259, - "1938": 4260, - "37": 4261, - "relations": 4262, - "##tor": 4263, - "plants": 4264, - "suffered": 4265, - "1936": 4266, - "wilson": 4267, - "kids": 4268, - "begins": 4269, - "##age": 4270, - "1918": 4271, - "seats": 4272, - "armed": 4273, - "internet": 4274, - "models": 4275, - "worth": 4276, - "laws": 4277, - "400": 4278, - "communities": 4279, - "classes": 4280, - "background": 4281, - "knows": 4282, - "thanks": 4283, - "quarter": 4284, - "reaching": 4285, - "humans": 4286, - "carry": 4287, - "killing": 4288, - "format": 4289, - "kong": 4290, - "hong": 4291, - "setting": 4292, - "75": 4293, - "architecture": 4294, - "disease": 4295, - "railroad": 4296, - "inc": 4297, - "possibly": 4298, - "wish": 4299, - "arthur": 4300, - "thoughts": 4301, - "harry": 4302, - "doors": 4303, - "density": 4304, - "##di": 4305, - "crowd": 4306, - "illinois": 4307, - "stomach": 4308, - "tone": 4309, - "unique": 4310, - "reports": 4311, - "anyway": 4312, - "##ir": 4313, - "liberal": 4314, - "der": 4315, - "vehicle": 4316, - "thick": 4317, - "dry": 4318, - "drug": 4319, - "faced": 4320, - "largely": 4321, - "facility": 4322, - "theme": 4323, - "holds": 4324, - "creation": 4325, - "strange": 4326, - "colonel": 4327, - "##mi": 4328, - "revolution": 4329, - "bell": 4330, - "politics": 4331, - "turns": 4332, - "silent": 4333, - "rail": 4334, - "relief": 4335, - "independence": 4336, - "combat": 4337, - "shape": 4338, - "write": 4339, - "determined": 4340, - "sales": 4341, - "learned": 4342, - "4th": 4343, - "finger": 4344, - "oxford": 4345, - "providing": 4346, - "1937": 4347, - "heritage": 4348, - "fiction": 4349, - "situated": 4350, - "designated": 4351, - "allowing": 4352, - "distribution": 4353, - "hosted": 4354, - "##est": 4355, - "sight": 4356, - "interview": 4357, - "estimated": 4358, - "reduced": 4359, - "##ria": 4360, - "toronto": 4361, - "footballer": 4362, - "keeping": 4363, - "guys": 4364, - "damn": 4365, - "claim": 4366, - "motion": 4367, - "sport": 4368, - "sixth": 4369, - "stayed": 4370, - "##ze": 4371, - "en": 4372, - "rear": 4373, - "receive": 4374, - "handed": 4375, - "twelve": 4376, - "dress": 4377, - "audience": 4378, - "granted": 4379, - "brazil": 4380, - "##well": 4381, - "spirit": 4382, - "##ated": 4383, - "noticed": 4384, - "etc": 4385, - "olympic": 4386, - "representative": 4387, - "eric": 4388, - "tight": 4389, - "trouble": 4390, - "reviews": 4391, - "drink": 4392, - "vampire": 4393, - "missing": 4394, - "roles": 4395, - "ranked": 4396, - "newly": 4397, - "household": 4398, - "finals": 4399, - "wave": 4400, - "critics": 4401, - "##ee": 4402, - "phase": 4403, - "massachusetts": 4404, - "pilot": 4405, - "unlike": 4406, - "philadelphia": 4407, - "bright": 4408, - "guns": 4409, - "crown": 4410, - "organizations": 4411, - "roof": 4412, - "42": 4413, - "respectively": 4414, - "clearly": 4415, - "tongue": 4416, - "marked": 4417, - "circle": 4418, - "fox": 4419, - "korea": 4420, - "bronze": 4421, - "brian": 4422, - "expanded": 4423, - "sexual": 4424, - "supply": 4425, - "yourself": 4426, - "inspired": 4427, - "labour": 4428, - "fc": 4429, - "##ah": 4430, - "reference": 4431, - "vision": 4432, - "draft": 4433, - "connection": 4434, - "brand": 4435, - "reasons": 4436, - "1935": 4437, - "classic": 4438, - "driving": 4439, - "trip": 4440, - "jesus": 4441, - "cells": 4442, - "entry": 4443, - "1920": 4444, - "neither": 4445, - "trail": 4446, - "claims": 4447, - "atlantic": 4448, - "orders": 4449, - "labor": 4450, - "nose": 4451, - "afraid": 4452, - "identified": 4453, - "intelligence": 4454, - "calls": 4455, - "cancer": 4456, - "attacked": 4457, - "passing": 4458, - "stephen": 4459, - "positions": 4460, - "imperial": 4461, - "grey": 4462, - "jason": 4463, - "39": 4464, - "sunday": 4465, - "48": 4466, - "swedish": 4467, - "avoid": 4468, - "extra": 4469, - "uncle": 4470, - "message": 4471, - "covers": 4472, - "allows": 4473, - "surprise": 4474, - "materials": 4475, - "fame": 4476, - "hunter": 4477, - "##ji": 4478, - "1930": 4479, - "citizens": 4480, - "figures": 4481, - "davis": 4482, - "environmental": 4483, - "confirmed": 4484, - "shit": 4485, - "titles": 4486, - "di": 4487, - "performing": 4488, - "difference": 4489, - "acts": 4490, - "attacks": 4491, - "##ov": 4492, - "existing": 4493, - "votes": 4494, - "opportunity": 4495, - "nor": 4496, - "shop": 4497, - "entirely": 4498, - "trains": 4499, - "opposite": 4500, - "pakistan": 4501, - "##pa": 4502, - "develop": 4503, - "resulted": 4504, - "representatives": 4505, - "actions": 4506, - "reality": 4507, - "pressed": 4508, - "##ish": 4509, - "barely": 4510, - "wine": 4511, - "conversation": 4512, - "faculty": 4513, - "northwest": 4514, - "ends": 4515, - "documentary": 4516, - "nuclear": 4517, - "stock": 4518, - "grace": 4519, - "sets": 4520, - "eat": 4521, - "alternative": 4522, - "##ps": 4523, - "bag": 4524, - "resulting": 4525, - "creating": 4526, - "surprised": 4527, - "cemetery": 4528, - "1919": 4529, - "drop": 4530, - "finding": 4531, - "sarah": 4532, - "cricket": 4533, - "streets": 4534, - "tradition": 4535, - "ride": 4536, - "1933": 4537, - "exhibition": 4538, - "target": 4539, - "ear": 4540, - "explained": 4541, - "rain": 4542, - "composer": 4543, - "injury": 4544, - "apartment": 4545, - "municipal": 4546, - "educational": 4547, - "occupied": 4548, - "netherlands": 4549, - "clean": 4550, - "billion": 4551, - "constitution": 4552, - "learn": 4553, - "1914": 4554, - "maximum": 4555, - "classical": 4556, - "francis": 4557, - "lose": 4558, - "opposition": 4559, - "jose": 4560, - "ontario": 4561, - "bear": 4562, - "core": 4563, - "hills": 4564, - "rolled": 4565, - "ending": 4566, - "drawn": 4567, - "permanent": 4568, - "fun": 4569, - "##tes": 4570, - "##lla": 4571, - "lewis": 4572, - "sites": 4573, - "chamber": 4574, - "ryan": 4575, - "##way": 4576, - "scoring": 4577, - "height": 4578, - "1934": 4579, - "##house": 4580, - "lyrics": 4581, - "staring": 4582, - "55": 4583, - "officials": 4584, - "1917": 4585, - "snow": 4586, - "oldest": 4587, - "##tic": 4588, - "orange": 4589, - "##ger": 4590, - "qualified": 4591, - "interior": 4592, - "apparently": 4593, - "succeeded": 4594, - "thousand": 4595, - "dinner": 4596, - "lights": 4597, - "existence": 4598, - "fans": 4599, - "heavily": 4600, - "41": 4601, - "greatest": 4602, - "conservative": 4603, - "send": 4604, - "bowl": 4605, - "plus": 4606, - "enter": 4607, - "catch": 4608, - "##un": 4609, - "economy": 4610, - "duty": 4611, - "1929": 4612, - "speech": 4613, - "authorities": 4614, - "princess": 4615, - "performances": 4616, - "versions": 4617, - "shall": 4618, - "graduate": 4619, - "pictures": 4620, - "effective": 4621, - "remembered": 4622, - "poetry": 4623, - "desk": 4624, - "crossed": 4625, - "starring": 4626, - "starts": 4627, - "passenger": 4628, - "sharp": 4629, - "##ant": 4630, - "acres": 4631, - "ass": 4632, - "weather": 4633, - "falling": 4634, - "rank": 4635, - "fund": 4636, - "supporting": 4637, - "check": 4638, - "adult": 4639, - "publishing": 4640, - "heads": 4641, - "cm": 4642, - "southeast": 4643, - "lane": 4644, - "##burg": 4645, - "application": 4646, - "bc": 4647, - "##ura": 4648, - "les": 4649, - "condition": 4650, - "transfer": 4651, - "prevent": 4652, - "display": 4653, - "ex": 4654, - "regions": 4655, - "earl": 4656, - "federation": 4657, - "cool": 4658, - "relatively": 4659, - "answered": 4660, - "besides": 4661, - "1928": 4662, - "obtained": 4663, - "portion": 4664, - "##town": 4665, - "mix": 4666, - "##ding": 4667, - "reaction": 4668, - "liked": 4669, - "dean": 4670, - "express": 4671, - "peak": 4672, - "1932": 4673, - "##tte": 4674, - "counter": 4675, - "religion": 4676, - "chain": 4677, - "rare": 4678, - "miller": 4679, - "convention": 4680, - "aid": 4681, - "lie": 4682, - "vehicles": 4683, - "mobile": 4684, - "perform": 4685, - "squad": 4686, - "wonder": 4687, - "lying": 4688, - "crazy": 4689, - "sword": 4690, - "##ping": 4691, - "attempted": 4692, - "centuries": 4693, - "weren": 4694, - "philosophy": 4695, - "category": 4696, - "##ize": 4697, - "anna": 4698, - "interested": 4699, - "47": 4700, - "sweden": 4701, - "wolf": 4702, - "frequently": 4703, - "abandoned": 4704, - "kg": 4705, - "literary": 4706, - "alliance": 4707, - "task": 4708, - "entitled": 4709, - "##ay": 4710, - "threw": 4711, - "promotion": 4712, - "factory": 4713, - "tiny": 4714, - "soccer": 4715, - "visited": 4716, - "matt": 4717, - "fm": 4718, - "achieved": 4719, - "52": 4720, - "defence": 4721, - "internal": 4722, - "persian": 4723, - "43": 4724, - "methods": 4725, - "##ging": 4726, - "arrested": 4727, - "otherwise": 4728, - "cambridge": 4729, - "programming": 4730, - "villages": 4731, - "elementary": 4732, - "districts": 4733, - "rooms": 4734, - "criminal": 4735, - "conflict": 4736, - "worry": 4737, - "trained": 4738, - "1931": 4739, - "attempts": 4740, - "waited": 4741, - "signal": 4742, - "bird": 4743, - "truck": 4744, - "subsequent": 4745, - "programme": 4746, - "##ol": 4747, - "ad": 4748, - "49": 4749, - "communist": 4750, - "details": 4751, - "faith": 4752, - "sector": 4753, - "patrick": 4754, - "carrying": 4755, - "laugh": 4756, - "##ss": 4757, - "controlled": 4758, - "korean": 4759, - "showing": 4760, - "origin": 4761, - "fuel": 4762, - "evil": 4763, - "1927": 4764, - "##ent": 4765, - "brief": 4766, - "identity": 4767, - "darkness": 4768, - "address": 4769, - "pool": 4770, - "missed": 4771, - "publication": 4772, - "web": 4773, - "planet": 4774, - "ian": 4775, - "anne": 4776, - "wings": 4777, - "invited": 4778, - "##tt": 4779, - "briefly": 4780, - "standards": 4781, - "kissed": 4782, - "##be": 4783, - "ideas": 4784, - "climate": 4785, - "causing": 4786, - "walter": 4787, - "worse": 4788, - "albert": 4789, - "articles": 4790, - "winners": 4791, - "desire": 4792, - "aged": 4793, - "northeast": 4794, - "dangerous": 4795, - "gate": 4796, - "doubt": 4797, - "1922": 4798, - "wooden": 4799, - "multi": 4800, - "##ky": 4801, - "poet": 4802, - "rising": 4803, - "funding": 4804, - "46": 4805, - "communications": 4806, - "communication": 4807, - "violence": 4808, - "copies": 4809, - "prepared": 4810, - "ford": 4811, - "investigation": 4812, - "skills": 4813, - "1924": 4814, - "pulling": 4815, - "electronic": 4816, - "##ak": 4817, - "##ial": 4818, - "##han": 4819, - "containing": 4820, - "ultimately": 4821, - "offices": 4822, - "singing": 4823, - "understanding": 4824, - "restaurant": 4825, - "tomorrow": 4826, - "fashion": 4827, - "christ": 4828, - "ward": 4829, - "da": 4830, - "pope": 4831, - "stands": 4832, - "5th": 4833, - "flow": 4834, - "studios": 4835, - "aired": 4836, - "commissioned": 4837, - "contained": 4838, - "exist": 4839, - "fresh": 4840, - "americans": 4841, - "##per": 4842, - "wrestling": 4843, - "approved": 4844, - "kid": 4845, - "employed": 4846, - "respect": 4847, - "suit": 4848, - "1925": 4849, - "angel": 4850, - "asking": 4851, - "increasing": 4852, - "frame": 4853, - "angry": 4854, - "selling": 4855, - "1950s": 4856, - "thin": 4857, - "finds": 4858, - "##nd": 4859, - "temperature": 4860, - "statement": 4861, - "ali": 4862, - "explain": 4863, - "inhabitants": 4864, - "towns": 4865, - "extensive": 4866, - "narrow": 4867, - "51": 4868, - "jane": 4869, - "flowers": 4870, - "images": 4871, - "promise": 4872, - "somewhere": 4873, - "object": 4874, - "fly": 4875, - "closely": 4876, - "##ls": 4877, - "1912": 4878, - "bureau": 4879, - "cape": 4880, - "1926": 4881, - "weekly": 4882, - "presidential": 4883, - "legislative": 4884, - "1921": 4885, - "##ai": 4886, - "##au": 4887, - "launch": 4888, - "founding": 4889, - "##ny": 4890, - "978": 4891, - "##ring": 4892, - "artillery": 4893, - "strike": 4894, - "un": 4895, - "institutions": 4896, - "roll": 4897, - "writers": 4898, - "landing": 4899, - "chose": 4900, - "kevin": 4901, - "anymore": 4902, - "pp": 4903, - "##ut": 4904, - "attorney": 4905, - "fit": 4906, - "dan": 4907, - "billboard": 4908, - "receiving": 4909, - "agricultural": 4910, - "breaking": 4911, - "sought": 4912, - "dave": 4913, - "admitted": 4914, - "lands": 4915, - "mexican": 4916, - "##bury": 4917, - "charlie": 4918, - "specifically": 4919, - "hole": 4920, - "iv": 4921, - "howard": 4922, - "credit": 4923, - "moscow": 4924, - "roads": 4925, - "accident": 4926, - "1923": 4927, - "proved": 4928, - "wear": 4929, - "struck": 4930, - "hey": 4931, - "guards": 4932, - "stuff": 4933, - "slid": 4934, - "expansion": 4935, - "1915": 4936, - "cat": 4937, - "anthony": 4938, - "##kin": 4939, - "melbourne": 4940, - "opposed": 4941, - "sub": 4942, - "southwest": 4943, - "architect": 4944, - "failure": 4945, - "plane": 4946, - "1916": 4947, - "##ron": 4948, - "map": 4949, - "camera": 4950, - "tank": 4951, - "listen": 4952, - "regarding": 4953, - "wet": 4954, - "introduction": 4955, - "metropolitan": 4956, - "link": 4957, - "ep": 4958, - "fighter": 4959, - "inch": 4960, - "grown": 4961, - "gene": 4962, - "anger": 4963, - "fixed": 4964, - "buy": 4965, - "dvd": 4966, - "khan": 4967, - "domestic": 4968, - "worldwide": 4969, - "chapel": 4970, - "mill": 4971, - "functions": 4972, - "examples": 4973, - "##head": 4974, - "developing": 4975, - "1910": 4976, - "turkey": 4977, - "hits": 4978, - "pocket": 4979, - "antonio": 4980, - "papers": 4981, - "grow": 4982, - "unless": 4983, - "circuit": 4984, - "18th": 4985, - "concerned": 4986, - "attached": 4987, - "journalist": 4988, - "selection": 4989, - "journey": 4990, - "converted": 4991, - "provincial": 4992, - "painted": 4993, - "hearing": 4994, - "aren": 4995, - "bands": 4996, - "negative": 4997, - "aside": 4998, - "wondered": 4999, - "knight": 5000, - "lap": 5001, - "survey": 5002, - "ma": 5003, - "##ow": 5004, - "noise": 5005, - "billy": 5006, - "##ium": 5007, - "shooting": 5008, - "guide": 5009, - "bedroom": 5010, - "priest": 5011, - "resistance": 5012, - "motor": 5013, - "homes": 5014, - "sounded": 5015, - "giant": 5016, - "##mer": 5017, - "150": 5018, - "scenes": 5019, - "equal": 5020, - "comic": 5021, - "patients": 5022, - "hidden": 5023, - "solid": 5024, - "actual": 5025, - "bringing": 5026, - "afternoon": 5027, - "touched": 5028, - "funds": 5029, - "wedding": 5030, - "consisted": 5031, - "marie": 5032, - "canal": 5033, - "sr": 5034, - "kim": 5035, - "treaty": 5036, - "turkish": 5037, - "recognition": 5038, - "residence": 5039, - "cathedral": 5040, - "broad": 5041, - "knees": 5042, - "incident": 5043, - "shaped": 5044, - "fired": 5045, - "norwegian": 5046, - "handle": 5047, - "cheek": 5048, - "contest": 5049, - "represent": 5050, - "##pe": 5051, - "representing": 5052, - "beauty": 5053, - "##sen": 5054, - "birds": 5055, - "advantage": 5056, - "emergency": 5057, - "wrapped": 5058, - "drawing": 5059, - "notice": 5060, - "pink": 5061, - "broadcasting": 5062, - "##ong": 5063, - "somehow": 5064, - "bachelor": 5065, - "seventh": 5066, - "collected": 5067, - "registered": 5068, - "establishment": 5069, - "alan": 5070, - "assumed": 5071, - "chemical": 5072, - "personnel": 5073, - "roger": 5074, - "retirement": 5075, - "jeff": 5076, - "portuguese": 5077, - "wore": 5078, - "tied": 5079, - "device": 5080, - "threat": 5081, - "progress": 5082, - "advance": 5083, - "##ised": 5084, - "banks": 5085, - "hired": 5086, - "manchester": 5087, - "nfl": 5088, - "teachers": 5089, - "structures": 5090, - "forever": 5091, - "##bo": 5092, - "tennis": 5093, - "helping": 5094, - "saturday": 5095, - "sale": 5096, - "applications": 5097, - "junction": 5098, - "hip": 5099, - "incorporated": 5100, - "neighborhood": 5101, - "dressed": 5102, - "ceremony": 5103, - "##ds": 5104, - "influenced": 5105, - "hers": 5106, - "visual": 5107, - "stairs": 5108, - "decades": 5109, - "inner": 5110, - "kansas": 5111, - "hung": 5112, - "hoped": 5113, - "gain": 5114, - "scheduled": 5115, - "downtown": 5116, - "engaged": 5117, - "austria": 5118, - "clock": 5119, - "norway": 5120, - "certainly": 5121, - "pale": 5122, - "protected": 5123, - "1913": 5124, - "victor": 5125, - "employees": 5126, - "plate": 5127, - "putting": 5128, - "surrounded": 5129, - "##ists": 5130, - "finishing": 5131, - "blues": 5132, - "tropical": 5133, - "##ries": 5134, - "minnesota": 5135, - "consider": 5136, - "philippines": 5137, - "accept": 5138, - "54": 5139, - "retrieved": 5140, - "1900": 5141, - "concern": 5142, - "anderson": 5143, - "properties": 5144, - "institution": 5145, - "gordon": 5146, - "successfully": 5147, - "vietnam": 5148, - "##dy": 5149, - "backing": 5150, - "outstanding": 5151, - "muslim": 5152, - "crossing": 5153, - "folk": 5154, - "producing": 5155, - "usual": 5156, - "demand": 5157, - "occurs": 5158, - "observed": 5159, - "lawyer": 5160, - "educated": 5161, - "##ana": 5162, - "kelly": 5163, - "string": 5164, - "pleasure": 5165, - "budget": 5166, - "items": 5167, - "quietly": 5168, - "colorado": 5169, - "philip": 5170, - "typical": 5171, - "##worth": 5172, - "derived": 5173, - "600": 5174, - "survived": 5175, - "asks": 5176, - "mental": 5177, - "##ide": 5178, - "56": 5179, - "jake": 5180, - "jews": 5181, - "distinguished": 5182, - "ltd": 5183, - "1911": 5184, - "sri": 5185, - "extremely": 5186, - "53": 5187, - "athletic": 5188, - "loud": 5189, - "thousands": 5190, - "worried": 5191, - "shadow": 5192, - "transportation": 5193, - "horses": 5194, - "weapon": 5195, - "arena": 5196, - "importance": 5197, - "users": 5198, - "tim": 5199, - "objects": 5200, - "contributed": 5201, - "dragon": 5202, - "douglas": 5203, - "aware": 5204, - "senator": 5205, - "johnny": 5206, - "jordan": 5207, - "sisters": 5208, - "engines": 5209, - "flag": 5210, - "investment": 5211, - "samuel": 5212, - "shock": 5213, - "capable": 5214, - "clark": 5215, - "row": 5216, - "wheel": 5217, - "refers": 5218, - "session": 5219, - "familiar": 5220, - "biggest": 5221, - "wins": 5222, - "hate": 5223, - "maintained": 5224, - "drove": 5225, - "hamilton": 5226, - "request": 5227, - "expressed": 5228, - "injured": 5229, - "underground": 5230, - "churches": 5231, - "walker": 5232, - "wars": 5233, - "tunnel": 5234, - "passes": 5235, - "stupid": 5236, - "agriculture": 5237, - "softly": 5238, - "cabinet": 5239, - "regarded": 5240, - "joining": 5241, - "indiana": 5242, - "##ea": 5243, - "##ms": 5244, - "push": 5245, - "dates": 5246, - "spend": 5247, - "behavior": 5248, - "woods": 5249, - "protein": 5250, - "gently": 5251, - "chase": 5252, - "morgan": 5253, - "mention": 5254, - "burning": 5255, - "wake": 5256, - "combination": 5257, - "occur": 5258, - "mirror": 5259, - "leads": 5260, - "jimmy": 5261, - "indeed": 5262, - "impossible": 5263, - "singapore": 5264, - "paintings": 5265, - "covering": 5266, - "##nes": 5267, - "soldier": 5268, - "locations": 5269, - "attendance": 5270, - "sell": 5271, - "historian": 5272, - "wisconsin": 5273, - "invasion": 5274, - "argued": 5275, - "painter": 5276, - "diego": 5277, - "changing": 5278, - "egypt": 5279, - "##don": 5280, - "experienced": 5281, - "inches": 5282, - "##ku": 5283, - "missouri": 5284, - "vol": 5285, - "grounds": 5286, - "spoken": 5287, - "switzerland": 5288, - "##gan": 5289, - "reform": 5290, - "rolling": 5291, - "ha": 5292, - "forget": 5293, - "massive": 5294, - "resigned": 5295, - "burned": 5296, - "allen": 5297, - "tennessee": 5298, - "locked": 5299, - "values": 5300, - "improved": 5301, - "##mo": 5302, - "wounded": 5303, - "universe": 5304, - "sick": 5305, - "dating": 5306, - "facing": 5307, - "pack": 5308, - "purchase": 5309, - "user": 5310, - "##pur": 5311, - "moments": 5312, - "##ul": 5313, - "merged": 5314, - "anniversary": 5315, - "1908": 5316, - "coal": 5317, - "brick": 5318, - "understood": 5319, - "causes": 5320, - "dynasty": 5321, - "queensland": 5322, - "establish": 5323, - "stores": 5324, - "crisis": 5325, - "promote": 5326, - "hoping": 5327, - "views": 5328, - "cards": 5329, - "referee": 5330, - "extension": 5331, - "##si": 5332, - "raise": 5333, - "arizona": 5334, - "improve": 5335, - "colonial": 5336, - "formal": 5337, - "charged": 5338, - "##rt": 5339, - "palm": 5340, - "lucky": 5341, - "hide": 5342, - "rescue": 5343, - "faces": 5344, - "95": 5345, - "feelings": 5346, - "candidates": 5347, - "juan": 5348, - "##ell": 5349, - "goods": 5350, - "6th": 5351, - "courses": 5352, - "weekend": 5353, - "59": 5354, - "luke": 5355, - "cash": 5356, - "fallen": 5357, - "##om": 5358, - "delivered": 5359, - "affected": 5360, - "installed": 5361, - "carefully": 5362, - "tries": 5363, - "swiss": 5364, - "hollywood": 5365, - "costs": 5366, - "lincoln": 5367, - "responsibility": 5368, - "##he": 5369, - "shore": 5370, - "file": 5371, - "proper": 5372, - "normally": 5373, - "maryland": 5374, - "assistance": 5375, - "jump": 5376, - "constant": 5377, - "offering": 5378, - "friendly": 5379, - "waters": 5380, - "persons": 5381, - "realize": 5382, - "contain": 5383, - "trophy": 5384, - "800": 5385, - "partnership": 5386, - "factor": 5387, - "58": 5388, - "musicians": 5389, - "cry": 5390, - "bound": 5391, - "oregon": 5392, - "indicated": 5393, - "hero": 5394, - "houston": 5395, - "medium": 5396, - "##ure": 5397, - "consisting": 5398, - "somewhat": 5399, - "##ara": 5400, - "57": 5401, - "cycle": 5402, - "##che": 5403, - "beer": 5404, - "moore": 5405, - "frederick": 5406, - "gotten": 5407, - "eleven": 5408, - "worst": 5409, - "weak": 5410, - "approached": 5411, - "arranged": 5412, - "chin": 5413, - "loan": 5414, - "universal": 5415, - "bond": 5416, - "fifteen": 5417, - "pattern": 5418, - "disappeared": 5419, - "##ney": 5420, - "translated": 5421, - "##zed": 5422, - "lip": 5423, - "arab": 5424, - "capture": 5425, - "interests": 5426, - "insurance": 5427, - "##chi": 5428, - "shifted": 5429, - "cave": 5430, - "prix": 5431, - "warning": 5432, - "sections": 5433, - "courts": 5434, - "coat": 5435, - "plot": 5436, - "smell": 5437, - "feed": 5438, - "golf": 5439, - "favorite": 5440, - "maintain": 5441, - "knife": 5442, - "vs": 5443, - "voted": 5444, - "degrees": 5445, - "finance": 5446, - "quebec": 5447, - "opinion": 5448, - "translation": 5449, - "manner": 5450, - "ruled": 5451, - "operate": 5452, - "productions": 5453, - "choose": 5454, - "musician": 5455, - "discovery": 5456, - "confused": 5457, - "tired": 5458, - "separated": 5459, - "stream": 5460, - "techniques": 5461, - "committed": 5462, - "attend": 5463, - "ranking": 5464, - "kings": 5465, - "throw": 5466, - "passengers": 5467, - "measure": 5468, - "horror": 5469, - "fan": 5470, - "mining": 5471, - "sand": 5472, - "danger": 5473, - "salt": 5474, - "calm": 5475, - "decade": 5476, - "dam": 5477, - "require": 5478, - "runner": 5479, - "##ik": 5480, - "rush": 5481, - "associate": 5482, - "greece": 5483, - "##ker": 5484, - "rivers": 5485, - "consecutive": 5486, - "matthew": 5487, - "##ski": 5488, - "sighed": 5489, - "sq": 5490, - "documents": 5491, - "steam": 5492, - "edited": 5493, - "closing": 5494, - "tie": 5495, - "accused": 5496, - "1905": 5497, - "##ini": 5498, - "islamic": 5499, - "distributed": 5500, - "directors": 5501, - "organisation": 5502, - "bruce": 5503, - "7th": 5504, - "breathing": 5505, - "mad": 5506, - "lit": 5507, - "arrival": 5508, - "concrete": 5509, - "taste": 5510, - "08": 5511, - "composition": 5512, - "shaking": 5513, - "faster": 5514, - "amateur": 5515, - "adjacent": 5516, - "stating": 5517, - "1906": 5518, - "twin": 5519, - "flew": 5520, - "##ran": 5521, - "tokyo": 5522, - "publications": 5523, - "##tone": 5524, - "obviously": 5525, - "ridge": 5526, - "storage": 5527, - "1907": 5528, - "carl": 5529, - "pages": 5530, - "concluded": 5531, - "desert": 5532, - "driven": 5533, - "universities": 5534, - "ages": 5535, - "terminal": 5536, - "sequence": 5537, - "borough": 5538, - "250": 5539, - "constituency": 5540, - "creative": 5541, - "cousin": 5542, - "economics": 5543, - "dreams": 5544, - "margaret": 5545, - "notably": 5546, - "reduce": 5547, - "montreal": 5548, - "mode": 5549, - "17th": 5550, - "ears": 5551, - "saved": 5552, - "jan": 5553, - "vocal": 5554, - "##ica": 5555, - "1909": 5556, - "andy": 5557, - "##jo": 5558, - "riding": 5559, - "roughly": 5560, - "threatened": 5561, - "##ise": 5562, - "meters": 5563, - "meanwhile": 5564, - "landed": 5565, - "compete": 5566, - "repeated": 5567, - "grass": 5568, - "czech": 5569, - "regularly": 5570, - "charges": 5571, - "tea": 5572, - "sudden": 5573, - "appeal": 5574, - "##ung": 5575, - "solution": 5576, - "describes": 5577, - "pierre": 5578, - "classification": 5579, - "glad": 5580, - "parking": 5581, - "##ning": 5582, - "belt": 5583, - "physics": 5584, - "99": 5585, - "rachel": 5586, - "add": 5587, - "hungarian": 5588, - "participate": 5589, - "expedition": 5590, - "damaged": 5591, - "gift": 5592, - "childhood": 5593, - "85": 5594, - "fifty": 5595, - "##red": 5596, - "mathematics": 5597, - "jumped": 5598, - "letting": 5599, - "defensive": 5600, - "mph": 5601, - "##ux": 5602, - "##gh": 5603, - "testing": 5604, - "##hip": 5605, - "hundreds": 5606, - "shoot": 5607, - "owners": 5608, - "matters": 5609, - "smoke": 5610, - "israeli": 5611, - "kentucky": 5612, - "dancing": 5613, - "mounted": 5614, - "grandfather": 5615, - "emma": 5616, - "designs": 5617, - "profit": 5618, - "argentina": 5619, - "##gs": 5620, - "truly": 5621, - "li": 5622, - "lawrence": 5623, - "cole": 5624, - "begun": 5625, - "detroit": 5626, - "willing": 5627, - "branches": 5628, - "smiling": 5629, - "decide": 5630, - "miami": 5631, - "enjoyed": 5632, - "recordings": 5633, - "##dale": 5634, - "poverty": 5635, - "ethnic": 5636, - "gay": 5637, - "##bi": 5638, - "gary": 5639, - "arabic": 5640, - "09": 5641, - "accompanied": 5642, - "##one": 5643, - "##ons": 5644, - "fishing": 5645, - "determine": 5646, - "residential": 5647, - "acid": 5648, - "##ary": 5649, - "alice": 5650, - "returns": 5651, - "starred": 5652, - "mail": 5653, - "##ang": 5654, - "jonathan": 5655, - "strategy": 5656, - "##ue": 5657, - "net": 5658, - "forty": 5659, - "cook": 5660, - "businesses": 5661, - "equivalent": 5662, - "commonwealth": 5663, - "distinct": 5664, - "ill": 5665, - "##cy": 5666, - "seriously": 5667, - "##ors": 5668, - "##ped": 5669, - "shift": 5670, - "harris": 5671, - "replace": 5672, - "rio": 5673, - "imagine": 5674, - "formula": 5675, - "ensure": 5676, - "##ber": 5677, - "additionally": 5678, - "scheme": 5679, - "conservation": 5680, - "occasionally": 5681, - "purposes": 5682, - "feels": 5683, - "favor": 5684, - "##and": 5685, - "##ore": 5686, - "1930s": 5687, - "contrast": 5688, - "hanging": 5689, - "hunt": 5690, - "movies": 5691, - "1904": 5692, - "instruments": 5693, - "victims": 5694, - "danish": 5695, - "christopher": 5696, - "busy": 5697, - "demon": 5698, - "sugar": 5699, - "earliest": 5700, - "colony": 5701, - "studying": 5702, - "balance": 5703, - "duties": 5704, - "##ks": 5705, - "belgium": 5706, - "slipped": 5707, - "carter": 5708, - "05": 5709, - "visible": 5710, - "stages": 5711, - "iraq": 5712, - "fifa": 5713, - "##im": 5714, - "commune": 5715, - "forming": 5716, - "zero": 5717, - "07": 5718, - "continuing": 5719, - "talked": 5720, - "counties": 5721, - "legend": 5722, - "bathroom": 5723, - "option": 5724, - "tail": 5725, - "clay": 5726, - "daughters": 5727, - "afterwards": 5728, - "severe": 5729, - "jaw": 5730, - "visitors": 5731, - "##ded": 5732, - "devices": 5733, - "aviation": 5734, - "russell": 5735, - "kate": 5736, - "##vi": 5737, - "entering": 5738, - "subjects": 5739, - "##ino": 5740, - "temporary": 5741, - "swimming": 5742, - "forth": 5743, - "smooth": 5744, - "ghost": 5745, - "audio": 5746, - "bush": 5747, - "operates": 5748, - "rocks": 5749, - "movements": 5750, - "signs": 5751, - "eddie": 5752, - "##tz": 5753, - "ann": 5754, - "voices": 5755, - "honorary": 5756, - "06": 5757, - "memories": 5758, - "dallas": 5759, - "pure": 5760, - "measures": 5761, - "racial": 5762, - "promised": 5763, - "66": 5764, - "harvard": 5765, - "ceo": 5766, - "16th": 5767, - "parliamentary": 5768, - "indicate": 5769, - "benefit": 5770, - "flesh": 5771, - "dublin": 5772, - "louisiana": 5773, - "1902": 5774, - "1901": 5775, - "patient": 5776, - "sleeping": 5777, - "1903": 5778, - "membership": 5779, - "coastal": 5780, - "medieval": 5781, - "wanting": 5782, - "element": 5783, - "scholars": 5784, - "rice": 5785, - "62": 5786, - "limit": 5787, - "survive": 5788, - "makeup": 5789, - "rating": 5790, - "definitely": 5791, - "collaboration": 5792, - "obvious": 5793, - "##tan": 5794, - "boss": 5795, - "ms": 5796, - "baron": 5797, - "birthday": 5798, - "linked": 5799, - "soil": 5800, - "diocese": 5801, - "##lan": 5802, - "ncaa": 5803, - "##mann": 5804, - "offensive": 5805, - "shell": 5806, - "shouldn": 5807, - "waist": 5808, - "##tus": 5809, - "plain": 5810, - "ross": 5811, - "organ": 5812, - "resolution": 5813, - "manufacturing": 5814, - "adding": 5815, - "relative": 5816, - "kennedy": 5817, - "98": 5818, - "whilst": 5819, - "moth": 5820, - "marketing": 5821, - "gardens": 5822, - "crash": 5823, - "72": 5824, - "heading": 5825, - "partners": 5826, - "credited": 5827, - "carlos": 5828, - "moves": 5829, - "cable": 5830, - "##zi": 5831, - "marshall": 5832, - "##out": 5833, - "depending": 5834, - "bottle": 5835, - "represents": 5836, - "rejected": 5837, - "responded": 5838, - "existed": 5839, - "04": 5840, - "jobs": 5841, - "denmark": 5842, - "lock": 5843, - "##ating": 5844, - "treated": 5845, - "graham": 5846, - "routes": 5847, - "talent": 5848, - "commissioner": 5849, - "drugs": 5850, - "secure": 5851, - "tests": 5852, - "reign": 5853, - "restored": 5854, - "photography": 5855, - "##gi": 5856, - "contributions": 5857, - "oklahoma": 5858, - "designer": 5859, - "disc": 5860, - "grin": 5861, - "seattle": 5862, - "robin": 5863, - "paused": 5864, - "atlanta": 5865, - "unusual": 5866, - "##gate": 5867, - "praised": 5868, - "las": 5869, - "laughing": 5870, - "satellite": 5871, - "hungary": 5872, - "visiting": 5873, - "##sky": 5874, - "interesting": 5875, - "factors": 5876, - "deck": 5877, - "poems": 5878, - "norman": 5879, - "##water": 5880, - "stuck": 5881, - "speaker": 5882, - "rifle": 5883, - "domain": 5884, - "premiered": 5885, - "##her": 5886, - "dc": 5887, - "comics": 5888, - "actors": 5889, - "01": 5890, - "reputation": 5891, - "eliminated": 5892, - "8th": 5893, - "ceiling": 5894, - "prisoners": 5895, - "script": 5896, - "##nce": 5897, - "leather": 5898, - "austin": 5899, - "mississippi": 5900, - "rapidly": 5901, - "admiral": 5902, - "parallel": 5903, - "charlotte": 5904, - "guilty": 5905, - "tools": 5906, - "gender": 5907, - "divisions": 5908, - "fruit": 5909, - "##bs": 5910, - "laboratory": 5911, - "nelson": 5912, - "fantasy": 5913, - "marry": 5914, - "rapid": 5915, - "aunt": 5916, - "tribe": 5917, - "requirements": 5918, - "aspects": 5919, - "suicide": 5920, - "amongst": 5921, - "adams": 5922, - "bone": 5923, - "ukraine": 5924, - "abc": 5925, - "kick": 5926, - "sees": 5927, - "edinburgh": 5928, - "clothing": 5929, - "column": 5930, - "rough": 5931, - "gods": 5932, - "hunting": 5933, - "broadway": 5934, - "gathered": 5935, - "concerns": 5936, - "##ek": 5937, - "spending": 5938, - "ty": 5939, - "12th": 5940, - "snapped": 5941, - "requires": 5942, - "solar": 5943, - "bones": 5944, - "cavalry": 5945, - "##tta": 5946, - "iowa": 5947, - "drinking": 5948, - "waste": 5949, - "index": 5950, - "franklin": 5951, - "charity": 5952, - "thompson": 5953, - "stewart": 5954, - "tip": 5955, - "flash": 5956, - "landscape": 5957, - "friday": 5958, - "enjoy": 5959, - "singh": 5960, - "poem": 5961, - "listening": 5962, - "##back": 5963, - "eighth": 5964, - "fred": 5965, - "differences": 5966, - "adapted": 5967, - "bomb": 5968, - "ukrainian": 5969, - "surgery": 5970, - "corporate": 5971, - "masters": 5972, - "anywhere": 5973, - "##more": 5974, - "waves": 5975, - "odd": 5976, - "sean": 5977, - "portugal": 5978, - "orleans": 5979, - "dick": 5980, - "debate": 5981, - "kent": 5982, - "eating": 5983, - "puerto": 5984, - "cleared": 5985, - "96": 5986, - "expect": 5987, - "cinema": 5988, - "97": 5989, - "guitarist": 5990, - "blocks": 5991, - "electrical": 5992, - "agree": 5993, - "involving": 5994, - "depth": 5995, - "dying": 5996, - "panel": 5997, - "struggle": 5998, - "##ged": 5999, - "peninsula": 6000, - "adults": 6001, - "novels": 6002, - "emerged": 6003, - "vienna": 6004, - "metro": 6005, - "debuted": 6006, - "shoes": 6007, - "tamil": 6008, - "songwriter": 6009, - "meets": 6010, - "prove": 6011, - "beating": 6012, - "instance": 6013, - "heaven": 6014, - "scared": 6015, - "sending": 6016, - "marks": 6017, - "artistic": 6018, - "passage": 6019, - "superior": 6020, - "03": 6021, - "significantly": 6022, - "shopping": 6023, - "##tive": 6024, - "retained": 6025, - "##izing": 6026, - "malaysia": 6027, - "technique": 6028, - "cheeks": 6029, - "##ola": 6030, - "warren": 6031, - "maintenance": 6032, - "destroy": 6033, - "extreme": 6034, - "allied": 6035, - "120": 6036, - "appearing": 6037, - "##yn": 6038, - "fill": 6039, - "advice": 6040, - "alabama": 6041, - "qualifying": 6042, - "policies": 6043, - "cleveland": 6044, - "hat": 6045, - "battery": 6046, - "smart": 6047, - "authors": 6048, - "10th": 6049, - "soundtrack": 6050, - "acted": 6051, - "dated": 6052, - "lb": 6053, - "glance": 6054, - "equipped": 6055, - "coalition": 6056, - "funny": 6057, - "outer": 6058, - "ambassador": 6059, - "roy": 6060, - "possibility": 6061, - "couples": 6062, - "campbell": 6063, - "dna": 6064, - "loose": 6065, - "ethan": 6066, - "supplies": 6067, - "1898": 6068, - "gonna": 6069, - "88": 6070, - "monster": 6071, - "##res": 6072, - "shake": 6073, - "agents": 6074, - "frequency": 6075, - "springs": 6076, - "dogs": 6077, - "practices": 6078, - "61": 6079, - "gang": 6080, - "plastic": 6081, - "easier": 6082, - "suggests": 6083, - "gulf": 6084, - "blade": 6085, - "exposed": 6086, - "colors": 6087, - "industries": 6088, - "markets": 6089, - "pan": 6090, - "nervous": 6091, - "electoral": 6092, - "charts": 6093, - "legislation": 6094, - "ownership": 6095, - "##idae": 6096, - "mac": 6097, - "appointment": 6098, - "shield": 6099, - "copy": 6100, - "assault": 6101, - "socialist": 6102, - "abbey": 6103, - "monument": 6104, - "license": 6105, - "throne": 6106, - "employment": 6107, - "jay": 6108, - "93": 6109, - "replacement": 6110, - "charter": 6111, - "cloud": 6112, - "powered": 6113, - "suffering": 6114, - "accounts": 6115, - "oak": 6116, - "connecticut": 6117, - "strongly": 6118, - "wright": 6119, - "colour": 6120, - "crystal": 6121, - "13th": 6122, - "context": 6123, - "welsh": 6124, - "networks": 6125, - "voiced": 6126, - "gabriel": 6127, - "jerry": 6128, - "##cing": 6129, - "forehead": 6130, - "mp": 6131, - "##ens": 6132, - "manage": 6133, - "schedule": 6134, - "totally": 6135, - "remix": 6136, - "##ii": 6137, - "forests": 6138, - "occupation": 6139, - "print": 6140, - "nicholas": 6141, - "brazilian": 6142, - "strategic": 6143, - "vampires": 6144, - "engineers": 6145, - "76": 6146, - "roots": 6147, - "seek": 6148, - "correct": 6149, - "instrumental": 6150, - "und": 6151, - "alfred": 6152, - "backed": 6153, - "hop": 6154, - "##des": 6155, - "stanley": 6156, - "robinson": 6157, - "traveled": 6158, - "wayne": 6159, - "welcome": 6160, - "austrian": 6161, - "achieve": 6162, - "67": 6163, - "exit": 6164, - "rates": 6165, - "1899": 6166, - "strip": 6167, - "whereas": 6168, - "##cs": 6169, - "sing": 6170, - "deeply": 6171, - "adventure": 6172, - "bobby": 6173, - "rick": 6174, - "jamie": 6175, - "careful": 6176, - "components": 6177, - "cap": 6178, - "useful": 6179, - "personality": 6180, - "knee": 6181, - "##shi": 6182, - "pushing": 6183, - "hosts": 6184, - "02": 6185, - "protest": 6186, - "ca": 6187, - "ottoman": 6188, - "symphony": 6189, - "##sis": 6190, - "63": 6191, - "boundary": 6192, - "1890": 6193, - "processes": 6194, - "considering": 6195, - "considerable": 6196, - "tons": 6197, - "##work": 6198, - "##ft": 6199, - "##nia": 6200, - "cooper": 6201, - "trading": 6202, - "dear": 6203, - "conduct": 6204, - "91": 6205, - "illegal": 6206, - "apple": 6207, - "revolutionary": 6208, - "holiday": 6209, - "definition": 6210, - "harder": 6211, - "##van": 6212, - "jacob": 6213, - "circumstances": 6214, - "destruction": 6215, - "##lle": 6216, - "popularity": 6217, - "grip": 6218, - "classified": 6219, - "liverpool": 6220, - "donald": 6221, - "baltimore": 6222, - "flows": 6223, - "seeking": 6224, - "honour": 6225, - "approval": 6226, - "92": 6227, - "mechanical": 6228, - "till": 6229, - "happening": 6230, - "statue": 6231, - "critic": 6232, - "increasingly": 6233, - "immediate": 6234, - "describe": 6235, - "commerce": 6236, - "stare": 6237, - "##ster": 6238, - "indonesia": 6239, - "meat": 6240, - "rounds": 6241, - "boats": 6242, - "baker": 6243, - "orthodox": 6244, - "depression": 6245, - "formally": 6246, - "worn": 6247, - "naked": 6248, - "claire": 6249, - "muttered": 6250, - "sentence": 6251, - "11th": 6252, - "emily": 6253, - "document": 6254, - "77": 6255, - "criticism": 6256, - "wished": 6257, - "vessel": 6258, - "spiritual": 6259, - "bent": 6260, - "virgin": 6261, - "parker": 6262, - "minimum": 6263, - "murray": 6264, - "lunch": 6265, - "danny": 6266, - "printed": 6267, - "compilation": 6268, - "keyboards": 6269, - "false": 6270, - "blow": 6271, - "belonged": 6272, - "68": 6273, - "raising": 6274, - "78": 6275, - "cutting": 6276, - "##board": 6277, - "pittsburgh": 6278, - "##up": 6279, - "9th": 6280, - "shadows": 6281, - "81": 6282, - "hated": 6283, - "indigenous": 6284, - "jon": 6285, - "15th": 6286, - "barry": 6287, - "scholar": 6288, - "ah": 6289, - "##zer": 6290, - "oliver": 6291, - "##gy": 6292, - "stick": 6293, - "susan": 6294, - "meetings": 6295, - "attracted": 6296, - "spell": 6297, - "romantic": 6298, - "##ver": 6299, - "ye": 6300, - "1895": 6301, - "photo": 6302, - "demanded": 6303, - "customers": 6304, - "##ac": 6305, - "1896": 6306, - "logan": 6307, - "revival": 6308, - "keys": 6309, - "modified": 6310, - "commanded": 6311, - "jeans": 6312, - "##ious": 6313, - "upset": 6314, - "raw": 6315, - "phil": 6316, - "detective": 6317, - "hiding": 6318, - "resident": 6319, - "vincent": 6320, - "##bly": 6321, - "experiences": 6322, - "diamond": 6323, - "defeating": 6324, - "coverage": 6325, - "lucas": 6326, - "external": 6327, - "parks": 6328, - "franchise": 6329, - "helen": 6330, - "bible": 6331, - "successor": 6332, - "percussion": 6333, - "celebrated": 6334, - "il": 6335, - "lift": 6336, - "profile": 6337, - "clan": 6338, - "romania": 6339, - "##ied": 6340, - "mills": 6341, - "##su": 6342, - "nobody": 6343, - "achievement": 6344, - "shrugged": 6345, - "fault": 6346, - "1897": 6347, - "rhythm": 6348, - "initiative": 6349, - "breakfast": 6350, - "carbon": 6351, - "700": 6352, - "69": 6353, - "lasted": 6354, - "violent": 6355, - "74": 6356, - "wound": 6357, - "ken": 6358, - "killer": 6359, - "gradually": 6360, - "filmed": 6361, - "°c": 6362, - "dollars": 6363, - "processing": 6364, - "94": 6365, - "remove": 6366, - "criticized": 6367, - "guests": 6368, - "sang": 6369, - "chemistry": 6370, - "##vin": 6371, - "legislature": 6372, - "disney": 6373, - "##bridge": 6374, - "uniform": 6375, - "escaped": 6376, - "integrated": 6377, - "proposal": 6378, - "purple": 6379, - "denied": 6380, - "liquid": 6381, - "karl": 6382, - "influential": 6383, - "morris": 6384, - "nights": 6385, - "stones": 6386, - "intense": 6387, - "experimental": 6388, - "twisted": 6389, - "71": 6390, - "84": 6391, - "##ld": 6392, - "pace": 6393, - "nazi": 6394, - "mitchell": 6395, - "ny": 6396, - "blind": 6397, - "reporter": 6398, - "newspapers": 6399, - "14th": 6400, - "centers": 6401, - "burn": 6402, - "basin": 6403, - "forgotten": 6404, - "surviving": 6405, - "filed": 6406, - "collections": 6407, - "monastery": 6408, - "losses": 6409, - "manual": 6410, - "couch": 6411, - "description": 6412, - "appropriate": 6413, - "merely": 6414, - "tag": 6415, - "missions": 6416, - "sebastian": 6417, - "restoration": 6418, - "replacing": 6419, - "triple": 6420, - "73": 6421, - "elder": 6422, - "julia": 6423, - "warriors": 6424, - "benjamin": 6425, - "julian": 6426, - "convinced": 6427, - "stronger": 6428, - "amazing": 6429, - "declined": 6430, - "versus": 6431, - "merchant": 6432, - "happens": 6433, - "output": 6434, - "finland": 6435, - "bare": 6436, - "barbara": 6437, - "absence": 6438, - "ignored": 6439, - "dawn": 6440, - "injuries": 6441, - "##port": 6442, - "producers": 6443, - "##ram": 6444, - "82": 6445, - "luis": 6446, - "##ities": 6447, - "kw": 6448, - "admit": 6449, - "expensive": 6450, - "electricity": 6451, - "nba": 6452, - "exception": 6453, - "symbol": 6454, - "##ving": 6455, - "ladies": 6456, - "shower": 6457, - "sheriff": 6458, - "characteristics": 6459, - "##je": 6460, - "aimed": 6461, - "button": 6462, - "ratio": 6463, - "effectively": 6464, - "summit": 6465, - "angle": 6466, - "jury": 6467, - "bears": 6468, - "foster": 6469, - "vessels": 6470, - "pants": 6471, - "executed": 6472, - "evans": 6473, - "dozen": 6474, - "advertising": 6475, - "kicked": 6476, - "patrol": 6477, - "1889": 6478, - "competitions": 6479, - "lifetime": 6480, - "principles": 6481, - "athletics": 6482, - "##logy": 6483, - "birmingham": 6484, - "sponsored": 6485, - "89": 6486, - "rob": 6487, - "nomination": 6488, - "1893": 6489, - "acoustic": 6490, - "##sm": 6491, - "creature": 6492, - "longest": 6493, - "##tra": 6494, - "credits": 6495, - "harbor": 6496, - "dust": 6497, - "josh": 6498, - "##so": 6499, - "territories": 6500, - "milk": 6501, - "infrastructure": 6502, - "completion": 6503, - "thailand": 6504, - "indians": 6505, - "leon": 6506, - "archbishop": 6507, - "##sy": 6508, - "assist": 6509, - "pitch": 6510, - "blake": 6511, - "arrangement": 6512, - "girlfriend": 6513, - "serbian": 6514, - "operational": 6515, - "hence": 6516, - "sad": 6517, - "scent": 6518, - "fur": 6519, - "dj": 6520, - "sessions": 6521, - "hp": 6522, - "refer": 6523, - "rarely": 6524, - "##ora": 6525, - "exists": 6526, - "1892": 6527, - "##ten": 6528, - "scientists": 6529, - "dirty": 6530, - "penalty": 6531, - "burst": 6532, - "portrait": 6533, - "seed": 6534, - "79": 6535, - "pole": 6536, - "limits": 6537, - "rival": 6538, - "1894": 6539, - "stable": 6540, - "alpha": 6541, - "grave": 6542, - "constitutional": 6543, - "alcohol": 6544, - "arrest": 6545, - "flower": 6546, - "mystery": 6547, - "devil": 6548, - "architectural": 6549, - "relationships": 6550, - "greatly": 6551, - "habitat": 6552, - "##istic": 6553, - "larry": 6554, - "progressive": 6555, - "remote": 6556, - "cotton": 6557, - "##ics": 6558, - "##ok": 6559, - "preserved": 6560, - "reaches": 6561, - "##ming": 6562, - "cited": 6563, - "86": 6564, - "vast": 6565, - "scholarship": 6566, - "decisions": 6567, - "cbs": 6568, - "joy": 6569, - "teach": 6570, - "1885": 6571, - "editions": 6572, - "knocked": 6573, - "eve": 6574, - "searching": 6575, - "partly": 6576, - "participation": 6577, - "gap": 6578, - "animated": 6579, - "fate": 6580, - "excellent": 6581, - "##ett": 6582, - "na": 6583, - "87": 6584, - "alternate": 6585, - "saints": 6586, - "youngest": 6587, - "##ily": 6588, - "climbed": 6589, - "##ita": 6590, - "##tors": 6591, - "suggest": 6592, - "##ct": 6593, - "discussion": 6594, - "staying": 6595, - "choir": 6596, - "lakes": 6597, - "jacket": 6598, - "revenue": 6599, - "nevertheless": 6600, - "peaked": 6601, - "instrument": 6602, - "wondering": 6603, - "annually": 6604, - "managing": 6605, - "neil": 6606, - "1891": 6607, - "signing": 6608, - "terry": 6609, - "##ice": 6610, - "apply": 6611, - "clinical": 6612, - "brooklyn": 6613, - "aim": 6614, - "catherine": 6615, - "fuck": 6616, - "farmers": 6617, - "figured": 6618, - "ninth": 6619, - "pride": 6620, - "hugh": 6621, - "evolution": 6622, - "ordinary": 6623, - "involvement": 6624, - "comfortable": 6625, - "shouted": 6626, - "tech": 6627, - "encouraged": 6628, - "taiwan": 6629, - "representation": 6630, - "sharing": 6631, - "##lia": 6632, - "##em": 6633, - "panic": 6634, - "exact": 6635, - "cargo": 6636, - "competing": 6637, - "fat": 6638, - "cried": 6639, - "83": 6640, - "1920s": 6641, - "occasions": 6642, - "pa": 6643, - "cabin": 6644, - "borders": 6645, - "utah": 6646, - "marcus": 6647, - "##isation": 6648, - "badly": 6649, - "muscles": 6650, - "##ance": 6651, - "victorian": 6652, - "transition": 6653, - "warner": 6654, - "bet": 6655, - "permission": 6656, - "##rin": 6657, - "slave": 6658, - "terrible": 6659, - "similarly": 6660, - "shares": 6661, - "seth": 6662, - "uefa": 6663, - "possession": 6664, - "medals": 6665, - "benefits": 6666, - "colleges": 6667, - "lowered": 6668, - "perfectly": 6669, - "mall": 6670, - "transit": 6671, - "##ye": 6672, - "##kar": 6673, - "publisher": 6674, - "##ened": 6675, - "harrison": 6676, - "deaths": 6677, - "elevation": 6678, - "##ae": 6679, - "asleep": 6680, - "machines": 6681, - "sigh": 6682, - "ash": 6683, - "hardly": 6684, - "argument": 6685, - "occasion": 6686, - "parent": 6687, - "leo": 6688, - "decline": 6689, - "1888": 6690, - "contribution": 6691, - "##ua": 6692, - "concentration": 6693, - "1000": 6694, - "opportunities": 6695, - "hispanic": 6696, - "guardian": 6697, - "extent": 6698, - "emotions": 6699, - "hips": 6700, - "mason": 6701, - "volumes": 6702, - "bloody": 6703, - "controversy": 6704, - "diameter": 6705, - "steady": 6706, - "mistake": 6707, - "phoenix": 6708, - "identify": 6709, - "violin": 6710, - "##sk": 6711, - "departure": 6712, - "richmond": 6713, - "spin": 6714, - "funeral": 6715, - "enemies": 6716, - "1864": 6717, - "gear": 6718, - "literally": 6719, - "connor": 6720, - "random": 6721, - "sergeant": 6722, - "grab": 6723, - "confusion": 6724, - "1865": 6725, - "transmission": 6726, - "informed": 6727, - "op": 6728, - "leaning": 6729, - "sacred": 6730, - "suspended": 6731, - "thinks": 6732, - "gates": 6733, - "portland": 6734, - "luck": 6735, - "agencies": 6736, - "yours": 6737, - "hull": 6738, - "expert": 6739, - "muscle": 6740, - "layer": 6741, - "practical": 6742, - "sculpture": 6743, - "jerusalem": 6744, - "latest": 6745, - "lloyd": 6746, - "statistics": 6747, - "deeper": 6748, - "recommended": 6749, - "warrior": 6750, - "arkansas": 6751, - "mess": 6752, - "supports": 6753, - "greg": 6754, - "eagle": 6755, - "1880": 6756, - "recovered": 6757, - "rated": 6758, - "concerts": 6759, - "rushed": 6760, - "##ano": 6761, - "stops": 6762, - "eggs": 6763, - "files": 6764, - "premiere": 6765, - "keith": 6766, - "##vo": 6767, - "delhi": 6768, - "turner": 6769, - "pit": 6770, - "affair": 6771, - "belief": 6772, - "paint": 6773, - "##zing": 6774, - "mate": 6775, - "##ach": 6776, - "##ev": 6777, - "victim": 6778, - "##ology": 6779, - "withdrew": 6780, - "bonus": 6781, - "styles": 6782, - "fled": 6783, - "##ud": 6784, - "glasgow": 6785, - "technologies": 6786, - "funded": 6787, - "nbc": 6788, - "adaptation": 6789, - "##ata": 6790, - "portrayed": 6791, - "cooperation": 6792, - "supporters": 6793, - "judges": 6794, - "bernard": 6795, - "justin": 6796, - "hallway": 6797, - "ralph": 6798, - "##ick": 6799, - "graduating": 6800, - "controversial": 6801, - "distant": 6802, - "continental": 6803, - "spider": 6804, - "bite": 6805, - "##ho": 6806, - "recognize": 6807, - "intention": 6808, - "mixing": 6809, - "##ese": 6810, - "egyptian": 6811, - "bow": 6812, - "tourism": 6813, - "suppose": 6814, - "claiming": 6815, - "tiger": 6816, - "dominated": 6817, - "participants": 6818, - "vi": 6819, - "##ru": 6820, - "nurse": 6821, - "partially": 6822, - "tape": 6823, - "##rum": 6824, - "psychology": 6825, - "##rn": 6826, - "essential": 6827, - "touring": 6828, - "duo": 6829, - "voting": 6830, - "civilian": 6831, - "emotional": 6832, - "channels": 6833, - "##king": 6834, - "apparent": 6835, - "hebrew": 6836, - "1887": 6837, - "tommy": 6838, - "carrier": 6839, - "intersection": 6840, - "beast": 6841, - "hudson": 6842, - "##gar": 6843, - "##zo": 6844, - "lab": 6845, - "nova": 6846, - "bench": 6847, - "discuss": 6848, - "costa": 6849, - "##ered": 6850, - "detailed": 6851, - "behalf": 6852, - "drivers": 6853, - "unfortunately": 6854, - "obtain": 6855, - "##lis": 6856, - "rocky": 6857, - "##dae": 6858, - "siege": 6859, - "friendship": 6860, - "honey": 6861, - "##rian": 6862, - "1861": 6863, - "amy": 6864, - "hang": 6865, - "posted": 6866, - "governments": 6867, - "collins": 6868, - "respond": 6869, - "wildlife": 6870, - "preferred": 6871, - "operator": 6872, - "##po": 6873, - "laura": 6874, - "pregnant": 6875, - "videos": 6876, - "dennis": 6877, - "suspected": 6878, - "boots": 6879, - "instantly": 6880, - "weird": 6881, - "automatic": 6882, - "businessman": 6883, - "alleged": 6884, - "placing": 6885, - "throwing": 6886, - "ph": 6887, - "mood": 6888, - "1862": 6889, - "perry": 6890, - "venue": 6891, - "jet": 6892, - "remainder": 6893, - "##lli": 6894, - "##ci": 6895, - "passion": 6896, - "biological": 6897, - "boyfriend": 6898, - "1863": 6899, - "dirt": 6900, - "buffalo": 6901, - "ron": 6902, - "segment": 6903, - "fa": 6904, - "abuse": 6905, - "##era": 6906, - "genre": 6907, - "thrown": 6908, - "stroke": 6909, - "colored": 6910, - "stress": 6911, - "exercise": 6912, - "displayed": 6913, - "##gen": 6914, - "struggled": 6915, - "##tti": 6916, - "abroad": 6917, - "dramatic": 6918, - "wonderful": 6919, - "thereafter": 6920, - "madrid": 6921, - "component": 6922, - "widespread": 6923, - "##sed": 6924, - "tale": 6925, - "citizen": 6926, - "todd": 6927, - "monday": 6928, - "1886": 6929, - "vancouver": 6930, - "overseas": 6931, - "forcing": 6932, - "crying": 6933, - "descent": 6934, - "##ris": 6935, - "discussed": 6936, - "substantial": 6937, - "ranks": 6938, - "regime": 6939, - "1870": 6940, - "provinces": 6941, - "switch": 6942, - "drum": 6943, - "zane": 6944, - "ted": 6945, - "tribes": 6946, - "proof": 6947, - "lp": 6948, - "cream": 6949, - "researchers": 6950, - "volunteer": 6951, - "manor": 6952, - "silk": 6953, - "milan": 6954, - "donated": 6955, - "allies": 6956, - "venture": 6957, - "principle": 6958, - "delivery": 6959, - "enterprise": 6960, - "##ves": 6961, - "##ans": 6962, - "bars": 6963, - "traditionally": 6964, - "witch": 6965, - "reminded": 6966, - "copper": 6967, - "##uk": 6968, - "pete": 6969, - "inter": 6970, - "links": 6971, - "colin": 6972, - "grinned": 6973, - "elsewhere": 6974, - "competitive": 6975, - "frequent": 6976, - "##oy": 6977, - "scream": 6978, - "##hu": 6979, - "tension": 6980, - "texts": 6981, - "submarine": 6982, - "finnish": 6983, - "defending": 6984, - "defend": 6985, - "pat": 6986, - "detail": 6987, - "1884": 6988, - "affiliated": 6989, - "stuart": 6990, - "themes": 6991, - "villa": 6992, - "periods": 6993, - "tool": 6994, - "belgian": 6995, - "ruling": 6996, - "crimes": 6997, - "answers": 6998, - "folded": 6999, - "licensed": 7000, - "resort": 7001, - "demolished": 7002, - "hans": 7003, - "lucy": 7004, - "1881": 7005, - "lion": 7006, - "traded": 7007, - "photographs": 7008, - "writes": 7009, - "craig": 7010, - "##fa": 7011, - "trials": 7012, - "generated": 7013, - "beth": 7014, - "noble": 7015, - "debt": 7016, - "percentage": 7017, - "yorkshire": 7018, - "erected": 7019, - "ss": 7020, - "viewed": 7021, - "grades": 7022, - "confidence": 7023, - "ceased": 7024, - "islam": 7025, - "telephone": 7026, - "retail": 7027, - "##ible": 7028, - "chile": 7029, - "m²": 7030, - "roberts": 7031, - "sixteen": 7032, - "##ich": 7033, - "commented": 7034, - "hampshire": 7035, - "innocent": 7036, - "dual": 7037, - "pounds": 7038, - "checked": 7039, - "regulations": 7040, - "afghanistan": 7041, - "sung": 7042, - "rico": 7043, - "liberty": 7044, - "assets": 7045, - "bigger": 7046, - "options": 7047, - "angels": 7048, - "relegated": 7049, - "tribute": 7050, - "wells": 7051, - "attending": 7052, - "leaf": 7053, - "##yan": 7054, - "butler": 7055, - "romanian": 7056, - "forum": 7057, - "monthly": 7058, - "lisa": 7059, - "patterns": 7060, - "gmina": 7061, - "##tory": 7062, - "madison": 7063, - "hurricane": 7064, - "rev": 7065, - "##ians": 7066, - "bristol": 7067, - "##ula": 7068, - "elite": 7069, - "valuable": 7070, - "disaster": 7071, - "democracy": 7072, - "awareness": 7073, - "germans": 7074, - "freyja": 7075, - "##ins": 7076, - "loop": 7077, - "absolutely": 7078, - "paying": 7079, - "populations": 7080, - "maine": 7081, - "sole": 7082, - "prayer": 7083, - "spencer": 7084, - "releases": 7085, - "doorway": 7086, - "bull": 7087, - "##ani": 7088, - "lover": 7089, - "midnight": 7090, - "conclusion": 7091, - "##sson": 7092, - "thirteen": 7093, - "lily": 7094, - "mediterranean": 7095, - "##lt": 7096, - "nhl": 7097, - "proud": 7098, - "sample": 7099, - "##hill": 7100, - "drummer": 7101, - "guinea": 7102, - "##ova": 7103, - "murphy": 7104, - "climb": 7105, - "##ston": 7106, - "instant": 7107, - "attributed": 7108, - "horn": 7109, - "ain": 7110, - "railways": 7111, - "steven": 7112, - "##ao": 7113, - "autumn": 7114, - "ferry": 7115, - "opponent": 7116, - "root": 7117, - "traveling": 7118, - "secured": 7119, - "corridor": 7120, - "stretched": 7121, - "tales": 7122, - "sheet": 7123, - "trinity": 7124, - "cattle": 7125, - "helps": 7126, - "indicates": 7127, - "manhattan": 7128, - "murdered": 7129, - "fitted": 7130, - "1882": 7131, - "gentle": 7132, - "grandmother": 7133, - "mines": 7134, - "shocked": 7135, - "vegas": 7136, - "produces": 7137, - "##light": 7138, - "caribbean": 7139, - "##ou": 7140, - "belong": 7141, - "continuous": 7142, - "desperate": 7143, - "drunk": 7144, - "historically": 7145, - "trio": 7146, - "waved": 7147, - "raf": 7148, - "dealing": 7149, - "nathan": 7150, - "bat": 7151, - "murmured": 7152, - "interrupted": 7153, - "residing": 7154, - "scientist": 7155, - "pioneer": 7156, - "harold": 7157, - "aaron": 7158, - "##net": 7159, - "delta": 7160, - "attempting": 7161, - "minority": 7162, - "mini": 7163, - "believes": 7164, - "chorus": 7165, - "tend": 7166, - "lots": 7167, - "eyed": 7168, - "indoor": 7169, - "load": 7170, - "shots": 7171, - "updated": 7172, - "jail": 7173, - "##llo": 7174, - "concerning": 7175, - "connecting": 7176, - "wealth": 7177, - "##ved": 7178, - "slaves": 7179, - "arrive": 7180, - "rangers": 7181, - "sufficient": 7182, - "rebuilt": 7183, - "##wick": 7184, - "cardinal": 7185, - "flood": 7186, - "muhammad": 7187, - "whenever": 7188, - "relation": 7189, - "runners": 7190, - "moral": 7191, - "repair": 7192, - "viewers": 7193, - "arriving": 7194, - "revenge": 7195, - "punk": 7196, - "assisted": 7197, - "bath": 7198, - "fairly": 7199, - "breathe": 7200, - "lists": 7201, - "innings": 7202, - "illustrated": 7203, - "whisper": 7204, - "nearest": 7205, - "voters": 7206, - "clinton": 7207, - "ties": 7208, - "ultimate": 7209, - "screamed": 7210, - "beijing": 7211, - "lions": 7212, - "andre": 7213, - "fictional": 7214, - "gathering": 7215, - "comfort": 7216, - "radar": 7217, - "suitable": 7218, - "dismissed": 7219, - "hms": 7220, - "ban": 7221, - "pine": 7222, - "wrist": 7223, - "atmosphere": 7224, - "voivodeship": 7225, - "bid": 7226, - "timber": 7227, - "##ned": 7228, - "##nan": 7229, - "giants": 7230, - "##ane": 7231, - "cameron": 7232, - "recovery": 7233, - "uss": 7234, - "identical": 7235, - "categories": 7236, - "switched": 7237, - "serbia": 7238, - "laughter": 7239, - "noah": 7240, - "ensemble": 7241, - "therapy": 7242, - "peoples": 7243, - "touching": 7244, - "##off": 7245, - "locally": 7246, - "pearl": 7247, - "platforms": 7248, - "everywhere": 7249, - "ballet": 7250, - "tables": 7251, - "lanka": 7252, - "herbert": 7253, - "outdoor": 7254, - "toured": 7255, - "derek": 7256, - "1883": 7257, - "spaces": 7258, - "contested": 7259, - "swept": 7260, - "1878": 7261, - "exclusive": 7262, - "slight": 7263, - "connections": 7264, - "##dra": 7265, - "winds": 7266, - "prisoner": 7267, - "collective": 7268, - "bangladesh": 7269, - "tube": 7270, - "publicly": 7271, - "wealthy": 7272, - "thai": 7273, - "##ys": 7274, - "isolated": 7275, - "select": 7276, - "##ric": 7277, - "insisted": 7278, - "pen": 7279, - "fortune": 7280, - "ticket": 7281, - "spotted": 7282, - "reportedly": 7283, - "animation": 7284, - "enforcement": 7285, - "tanks": 7286, - "110": 7287, - "decides": 7288, - "wider": 7289, - "lowest": 7290, - "owen": 7291, - "##time": 7292, - "nod": 7293, - "hitting": 7294, - "##hn": 7295, - "gregory": 7296, - "furthermore": 7297, - "magazines": 7298, - "fighters": 7299, - "solutions": 7300, - "##ery": 7301, - "pointing": 7302, - "requested": 7303, - "peru": 7304, - "reed": 7305, - "chancellor": 7306, - "knights": 7307, - "mask": 7308, - "worker": 7309, - "eldest": 7310, - "flames": 7311, - "reduction": 7312, - "1860": 7313, - "volunteers": 7314, - "##tis": 7315, - "reporting": 7316, - "##hl": 7317, - "wire": 7318, - "advisory": 7319, - "endemic": 7320, - "origins": 7321, - "settlers": 7322, - "pursue": 7323, - "knock": 7324, - "consumer": 7325, - "1876": 7326, - "eu": 7327, - "compound": 7328, - "creatures": 7329, - "mansion": 7330, - "sentenced": 7331, - "ivan": 7332, - "deployed": 7333, - "guitars": 7334, - "frowned": 7335, - "involves": 7336, - "mechanism": 7337, - "kilometers": 7338, - "perspective": 7339, - "shops": 7340, - "maps": 7341, - "terminus": 7342, - "duncan": 7343, - "alien": 7344, - "fist": 7345, - "bridges": 7346, - "##pers": 7347, - "heroes": 7348, - "fed": 7349, - "derby": 7350, - "swallowed": 7351, - "##ros": 7352, - "patent": 7353, - "sara": 7354, - "illness": 7355, - "characterized": 7356, - "adventures": 7357, - "slide": 7358, - "hawaii": 7359, - "jurisdiction": 7360, - "##op": 7361, - "organised": 7362, - "##side": 7363, - "adelaide": 7364, - "walks": 7365, - "biology": 7366, - "se": 7367, - "##ties": 7368, - "rogers": 7369, - "swing": 7370, - "tightly": 7371, - "boundaries": 7372, - "##rie": 7373, - "prepare": 7374, - "implementation": 7375, - "stolen": 7376, - "##sha": 7377, - "certified": 7378, - "colombia": 7379, - "edwards": 7380, - "garage": 7381, - "##mm": 7382, - "recalled": 7383, - "##ball": 7384, - "rage": 7385, - "harm": 7386, - "nigeria": 7387, - "breast": 7388, - "##ren": 7389, - "furniture": 7390, - "pupils": 7391, - "settle": 7392, - "##lus": 7393, - "cuba": 7394, - "balls": 7395, - "client": 7396, - "alaska": 7397, - "21st": 7398, - "linear": 7399, - "thrust": 7400, - "celebration": 7401, - "latino": 7402, - "genetic": 7403, - "terror": 7404, - "##cia": 7405, - "##ening": 7406, - "lightning": 7407, - "fee": 7408, - "witness": 7409, - "lodge": 7410, - "establishing": 7411, - "skull": 7412, - "##ique": 7413, - "earning": 7414, - "hood": 7415, - "##ei": 7416, - "rebellion": 7417, - "wang": 7418, - "sporting": 7419, - "warned": 7420, - "missile": 7421, - "devoted": 7422, - "activist": 7423, - "porch": 7424, - "worship": 7425, - "fourteen": 7426, - "package": 7427, - "1871": 7428, - "decorated": 7429, - "##shire": 7430, - "housed": 7431, - "##ock": 7432, - "chess": 7433, - "sailed": 7434, - "doctors": 7435, - "oscar": 7436, - "joan": 7437, - "treat": 7438, - "garcia": 7439, - "harbour": 7440, - "jeremy": 7441, - "##ire": 7442, - "traditions": 7443, - "dominant": 7444, - "jacques": 7445, - "##gon": 7446, - "##wan": 7447, - "relocated": 7448, - "1879": 7449, - "amendment": 7450, - "sized": 7451, - "companion": 7452, - "simultaneously": 7453, - "volleyball": 7454, - "spun": 7455, - "acre": 7456, - "increases": 7457, - "stopping": 7458, - "loves": 7459, - "belongs": 7460, - "affect": 7461, - "drafted": 7462, - "tossed": 7463, - "scout": 7464, - "battles": 7465, - "1875": 7466, - "filming": 7467, - "shoved": 7468, - "munich": 7469, - "tenure": 7470, - "vertical": 7471, - "romance": 7472, - "pc": 7473, - "##cher": 7474, - "argue": 7475, - "##ical": 7476, - "craft": 7477, - "ranging": 7478, - "www": 7479, - "opens": 7480, - "honest": 7481, - "tyler": 7482, - "yesterday": 7483, - "virtual": 7484, - "##let": 7485, - "muslims": 7486, - "reveal": 7487, - "snake": 7488, - "immigrants": 7489, - "radical": 7490, - "screaming": 7491, - "speakers": 7492, - "firing": 7493, - "saving": 7494, - "belonging": 7495, - "ease": 7496, - "lighting": 7497, - "prefecture": 7498, - "blame": 7499, - "farmer": 7500, - "hungry": 7501, - "grows": 7502, - "rubbed": 7503, - "beam": 7504, - "sur": 7505, - "subsidiary": 7506, - "##cha": 7507, - "armenian": 7508, - "sao": 7509, - "dropping": 7510, - "conventional": 7511, - "##fer": 7512, - "microsoft": 7513, - "reply": 7514, - "qualify": 7515, - "spots": 7516, - "1867": 7517, - "sweat": 7518, - "festivals": 7519, - "##ken": 7520, - "immigration": 7521, - "physician": 7522, - "discover": 7523, - "exposure": 7524, - "sandy": 7525, - "explanation": 7526, - "isaac": 7527, - "implemented": 7528, - "##fish": 7529, - "hart": 7530, - "initiated": 7531, - "connect": 7532, - "stakes": 7533, - "presents": 7534, - "heights": 7535, - "householder": 7536, - "pleased": 7537, - "tourist": 7538, - "regardless": 7539, - "slip": 7540, - "closest": 7541, - "##ction": 7542, - "surely": 7543, - "sultan": 7544, - "brings": 7545, - "riley": 7546, - "preparation": 7547, - "aboard": 7548, - "slammed": 7549, - "baptist": 7550, - "experiment": 7551, - "ongoing": 7552, - "interstate": 7553, - "organic": 7554, - "playoffs": 7555, - "##ika": 7556, - "1877": 7557, - "130": 7558, - "##tar": 7559, - "hindu": 7560, - "error": 7561, - "tours": 7562, - "tier": 7563, - "plenty": 7564, - "arrangements": 7565, - "talks": 7566, - "trapped": 7567, - "excited": 7568, - "sank": 7569, - "ho": 7570, - "athens": 7571, - "1872": 7572, - "denver": 7573, - "welfare": 7574, - "suburb": 7575, - "athletes": 7576, - "trick": 7577, - "diverse": 7578, - "belly": 7579, - "exclusively": 7580, - "yelled": 7581, - "1868": 7582, - "##med": 7583, - "conversion": 7584, - "##ette": 7585, - "1874": 7586, - "internationally": 7587, - "computers": 7588, - "conductor": 7589, - "abilities": 7590, - "sensitive": 7591, - "hello": 7592, - "dispute": 7593, - "measured": 7594, - "globe": 7595, - "rocket": 7596, - "prices": 7597, - "amsterdam": 7598, - "flights": 7599, - "tigers": 7600, - "inn": 7601, - "municipalities": 7602, - "emotion": 7603, - "references": 7604, - "3d": 7605, - "##mus": 7606, - "explains": 7607, - "airlines": 7608, - "manufactured": 7609, - "pm": 7610, - "archaeological": 7611, - "1873": 7612, - "interpretation": 7613, - "devon": 7614, - "comment": 7615, - "##ites": 7616, - "settlements": 7617, - "kissing": 7618, - "absolute": 7619, - "improvement": 7620, - "suite": 7621, - "impressed": 7622, - "barcelona": 7623, - "sullivan": 7624, - "jefferson": 7625, - "towers": 7626, - "jesse": 7627, - "julie": 7628, - "##tin": 7629, - "##lu": 7630, - "grandson": 7631, - "hi": 7632, - "gauge": 7633, - "regard": 7634, - "rings": 7635, - "interviews": 7636, - "trace": 7637, - "raymond": 7638, - "thumb": 7639, - "departments": 7640, - "burns": 7641, - "serial": 7642, - "bulgarian": 7643, - "scores": 7644, - "demonstrated": 7645, - "##ix": 7646, - "1866": 7647, - "kyle": 7648, - "alberta": 7649, - "underneath": 7650, - "romanized": 7651, - "##ward": 7652, - "relieved": 7653, - "acquisition": 7654, - "phrase": 7655, - "cliff": 7656, - "reveals": 7657, - "han": 7658, - "cuts": 7659, - "merger": 7660, - "custom": 7661, - "##dar": 7662, - "nee": 7663, - "gilbert": 7664, - "graduation": 7665, - "##nts": 7666, - "assessment": 7667, - "cafe": 7668, - "difficulty": 7669, - "demands": 7670, - "swung": 7671, - "democrat": 7672, - "jennifer": 7673, - "commons": 7674, - "1940s": 7675, - "grove": 7676, - "##yo": 7677, - "completing": 7678, - "focuses": 7679, - "sum": 7680, - "substitute": 7681, - "bearing": 7682, - "stretch": 7683, - "reception": 7684, - "##py": 7685, - "reflected": 7686, - "essentially": 7687, - "destination": 7688, - "pairs": 7689, - "##ched": 7690, - "survival": 7691, - "resource": 7692, - "##bach": 7693, - "promoting": 7694, - "doubles": 7695, - "messages": 7696, - "tear": 7697, - "##down": 7698, - "##fully": 7699, - "parade": 7700, - "florence": 7701, - "harvey": 7702, - "incumbent": 7703, - "partial": 7704, - "framework": 7705, - "900": 7706, - "pedro": 7707, - "frozen": 7708, - "procedure": 7709, - "olivia": 7710, - "controls": 7711, - "##mic": 7712, - "shelter": 7713, - "personally": 7714, - "temperatures": 7715, - "##od": 7716, - "brisbane": 7717, - "tested": 7718, - "sits": 7719, - "marble": 7720, - "comprehensive": 7721, - "oxygen": 7722, - "leonard": 7723, - "##kov": 7724, - "inaugural": 7725, - "iranian": 7726, - "referring": 7727, - "quarters": 7728, - "attitude": 7729, - "##ivity": 7730, - "mainstream": 7731, - "lined": 7732, - "mars": 7733, - "dakota": 7734, - "norfolk": 7735, - "unsuccessful": 7736, - "##°": 7737, - "explosion": 7738, - "helicopter": 7739, - "congressional": 7740, - "##sing": 7741, - "inspector": 7742, - "bitch": 7743, - "seal": 7744, - "departed": 7745, - "divine": 7746, - "##ters": 7747, - "coaching": 7748, - "examination": 7749, - "punishment": 7750, - "manufacturer": 7751, - "sink": 7752, - "columns": 7753, - "unincorporated": 7754, - "signals": 7755, - "nevada": 7756, - "squeezed": 7757, - "dylan": 7758, - "dining": 7759, - "photos": 7760, - "martial": 7761, - "manuel": 7762, - "eighteen": 7763, - "elevator": 7764, - "brushed": 7765, - "plates": 7766, - "ministers": 7767, - "ivy": 7768, - "congregation": 7769, - "##len": 7770, - "slept": 7771, - "specialized": 7772, - "taxes": 7773, - "curve": 7774, - "restricted": 7775, - "negotiations": 7776, - "likes": 7777, - "statistical": 7778, - "arnold": 7779, - "inspiration": 7780, - "execution": 7781, - "bold": 7782, - "intermediate": 7783, - "significance": 7784, - "margin": 7785, - "ruler": 7786, - "wheels": 7787, - "gothic": 7788, - "intellectual": 7789, - "dependent": 7790, - "listened": 7791, - "eligible": 7792, - "buses": 7793, - "widow": 7794, - "syria": 7795, - "earn": 7796, - "cincinnati": 7797, - "collapsed": 7798, - "recipient": 7799, - "secrets": 7800, - "accessible": 7801, - "philippine": 7802, - "maritime": 7803, - "goddess": 7804, - "clerk": 7805, - "surrender": 7806, - "breaks": 7807, - "playoff": 7808, - "database": 7809, - "##ified": 7810, - "##lon": 7811, - "ideal": 7812, - "beetle": 7813, - "aspect": 7814, - "soap": 7815, - "regulation": 7816, - "strings": 7817, - "expand": 7818, - "anglo": 7819, - "shorter": 7820, - "crosses": 7821, - "retreat": 7822, - "tough": 7823, - "coins": 7824, - "wallace": 7825, - "directions": 7826, - "pressing": 7827, - "##oon": 7828, - "shipping": 7829, - "locomotives": 7830, - "comparison": 7831, - "topics": 7832, - "nephew": 7833, - "##mes": 7834, - "distinction": 7835, - "honors": 7836, - "travelled": 7837, - "sierra": 7838, - "ibn": 7839, - "##over": 7840, - "fortress": 7841, - "sa": 7842, - "recognised": 7843, - "carved": 7844, - "1869": 7845, - "clients": 7846, - "##dan": 7847, - "intent": 7848, - "##mar": 7849, - "coaches": 7850, - "describing": 7851, - "bread": 7852, - "##ington": 7853, - "beaten": 7854, - "northwestern": 7855, - "##ona": 7856, - "merit": 7857, - "youtube": 7858, - "collapse": 7859, - "challenges": 7860, - "em": 7861, - "historians": 7862, - "objective": 7863, - "submitted": 7864, - "virus": 7865, - "attacking": 7866, - "drake": 7867, - "assume": 7868, - "##ere": 7869, - "diseases": 7870, - "marc": 7871, - "stem": 7872, - "leeds": 7873, - "##cus": 7874, - "##ab": 7875, - "farming": 7876, - "glasses": 7877, - "##lock": 7878, - "visits": 7879, - "nowhere": 7880, - "fellowship": 7881, - "relevant": 7882, - "carries": 7883, - "restaurants": 7884, - "experiments": 7885, - "101": 7886, - "constantly": 7887, - "bases": 7888, - "targets": 7889, - "shah": 7890, - "tenth": 7891, - "opponents": 7892, - "verse": 7893, - "territorial": 7894, - "##ira": 7895, - "writings": 7896, - "corruption": 7897, - "##hs": 7898, - "instruction": 7899, - "inherited": 7900, - "reverse": 7901, - "emphasis": 7902, - "##vic": 7903, - "employee": 7904, - "arch": 7905, - "keeps": 7906, - "rabbi": 7907, - "watson": 7908, - "payment": 7909, - "uh": 7910, - "##ala": 7911, - "nancy": 7912, - "##tre": 7913, - "venice": 7914, - "fastest": 7915, - "sexy": 7916, - "banned": 7917, - "adrian": 7918, - "properly": 7919, - "ruth": 7920, - "touchdown": 7921, - "dollar": 7922, - "boards": 7923, - "metre": 7924, - "circles": 7925, - "edges": 7926, - "favour": 7927, - "comments": 7928, - "ok": 7929, - "travels": 7930, - "liberation": 7931, - "scattered": 7932, - "firmly": 7933, - "##ular": 7934, - "holland": 7935, - "permitted": 7936, - "diesel": 7937, - "kenya": 7938, - "den": 7939, - "originated": 7940, - "##ral": 7941, - "demons": 7942, - "resumed": 7943, - "dragged": 7944, - "rider": 7945, - "##rus": 7946, - "servant": 7947, - "blinked": 7948, - "extend": 7949, - "torn": 7950, - "##ias": 7951, - "##sey": 7952, - "input": 7953, - "meal": 7954, - "everybody": 7955, - "cylinder": 7956, - "kinds": 7957, - "camps": 7958, - "##fe": 7959, - "bullet": 7960, - "logic": 7961, - "##wn": 7962, - "croatian": 7963, - "evolved": 7964, - "healthy": 7965, - "fool": 7966, - "chocolate": 7967, - "wise": 7968, - "preserve": 7969, - "pradesh": 7970, - "##ess": 7971, - "respective": 7972, - "1850": 7973, - "##ew": 7974, - "chicken": 7975, - "artificial": 7976, - "gross": 7977, - "corresponding": 7978, - "convicted": 7979, - "cage": 7980, - "caroline": 7981, - "dialogue": 7982, - "##dor": 7983, - "narrative": 7984, - "stranger": 7985, - "mario": 7986, - "br": 7987, - "christianity": 7988, - "failing": 7989, - "trent": 7990, - "commanding": 7991, - "buddhist": 7992, - "1848": 7993, - "maurice": 7994, - "focusing": 7995, - "yale": 7996, - "bike": 7997, - "altitude": 7998, - "##ering": 7999, - "mouse": 8000, - "revised": 8001, - "##sley": 8002, - "veteran": 8003, - "##ig": 8004, - "pulls": 8005, - "theology": 8006, - "crashed": 8007, - "campaigns": 8008, - "legion": 8009, - "##ability": 8010, - "drag": 8011, - "excellence": 8012, - "customer": 8013, - "cancelled": 8014, - "intensity": 8015, - "excuse": 8016, - "##lar": 8017, - "liga": 8018, - "participating": 8019, - "contributing": 8020, - "printing": 8021, - "##burn": 8022, - "variable": 8023, - "##rk": 8024, - "curious": 8025, - "bin": 8026, - "legacy": 8027, - "renaissance": 8028, - "##my": 8029, - "symptoms": 8030, - "binding": 8031, - "vocalist": 8032, - "dancer": 8033, - "##nie": 8034, - "grammar": 8035, - "gospel": 8036, - "democrats": 8037, - "ya": 8038, - "enters": 8039, - "sc": 8040, - "diplomatic": 8041, - "hitler": 8042, - "##ser": 8043, - "clouds": 8044, - "mathematical": 8045, - "quit": 8046, - "defended": 8047, - "oriented": 8048, - "##heim": 8049, - "fundamental": 8050, - "hardware": 8051, - "impressive": 8052, - "equally": 8053, - "convince": 8054, - "confederate": 8055, - "guilt": 8056, - "chuck": 8057, - "sliding": 8058, - "##ware": 8059, - "magnetic": 8060, - "narrowed": 8061, - "petersburg": 8062, - "bulgaria": 8063, - "otto": 8064, - "phd": 8065, - "skill": 8066, - "##ama": 8067, - "reader": 8068, - "hopes": 8069, - "pitcher": 8070, - "reservoir": 8071, - "hearts": 8072, - "automatically": 8073, - "expecting": 8074, - "mysterious": 8075, - "bennett": 8076, - "extensively": 8077, - "imagined": 8078, - "seeds": 8079, - "monitor": 8080, - "fix": 8081, - "##ative": 8082, - "journalism": 8083, - "struggling": 8084, - "signature": 8085, - "ranch": 8086, - "encounter": 8087, - "photographer": 8088, - "observation": 8089, - "protests": 8090, - "##pin": 8091, - "influences": 8092, - "##hr": 8093, - "calendar": 8094, - "##all": 8095, - "cruz": 8096, - "croatia": 8097, - "locomotive": 8098, - "hughes": 8099, - "naturally": 8100, - "shakespeare": 8101, - "basement": 8102, - "hook": 8103, - "uncredited": 8104, - "faded": 8105, - "theories": 8106, - "approaches": 8107, - "dare": 8108, - "phillips": 8109, - "filling": 8110, - "fury": 8111, - "obama": 8112, - "##ain": 8113, - "efficient": 8114, - "arc": 8115, - "deliver": 8116, - "min": 8117, - "raid": 8118, - "breeding": 8119, - "inducted": 8120, - "leagues": 8121, - "efficiency": 8122, - "axis": 8123, - "montana": 8124, - "eagles": 8125, - "##ked": 8126, - "supplied": 8127, - "instructions": 8128, - "karen": 8129, - "picking": 8130, - "indicating": 8131, - "trap": 8132, - "anchor": 8133, - "practically": 8134, - "christians": 8135, - "tomb": 8136, - "vary": 8137, - "occasional": 8138, - "electronics": 8139, - "lords": 8140, - "readers": 8141, - "newcastle": 8142, - "faint": 8143, - "innovation": 8144, - "collect": 8145, - "situations": 8146, - "engagement": 8147, - "160": 8148, - "claude": 8149, - "mixture": 8150, - "##feld": 8151, - "peer": 8152, - "tissue": 8153, - "logo": 8154, - "lean": 8155, - "##ration": 8156, - "°f": 8157, - "floors": 8158, - "##ven": 8159, - "architects": 8160, - "reducing": 8161, - "##our": 8162, - "##ments": 8163, - "rope": 8164, - "1859": 8165, - "ottawa": 8166, - "##har": 8167, - "samples": 8168, - "banking": 8169, - "declaration": 8170, - "proteins": 8171, - "resignation": 8172, - "francois": 8173, - "saudi": 8174, - "advocate": 8175, - "exhibited": 8176, - "armor": 8177, - "twins": 8178, - "divorce": 8179, - "##ras": 8180, - "abraham": 8181, - "reviewed": 8182, - "jo": 8183, - "temporarily": 8184, - "matrix": 8185, - "physically": 8186, - "pulse": 8187, - "curled": 8188, - "##ena": 8189, - "difficulties": 8190, - "bengal": 8191, - "usage": 8192, - "##ban": 8193, - "annie": 8194, - "riders": 8195, - "certificate": 8196, - "##pi": 8197, - "holes": 8198, - "warsaw": 8199, - "distinctive": 8200, - "jessica": 8201, - "##mon": 8202, - "mutual": 8203, - "1857": 8204, - "customs": 8205, - "circular": 8206, - "eugene": 8207, - "removal": 8208, - "loaded": 8209, - "mere": 8210, - "vulnerable": 8211, - "depicted": 8212, - "generations": 8213, - "dame": 8214, - "heir": 8215, - "enormous": 8216, - "lightly": 8217, - "climbing": 8218, - "pitched": 8219, - "lessons": 8220, - "pilots": 8221, - "nepal": 8222, - "ram": 8223, - "google": 8224, - "preparing": 8225, - "brad": 8226, - "louise": 8227, - "renowned": 8228, - "##₂": 8229, - "liam": 8230, - "##ably": 8231, - "plaza": 8232, - "shaw": 8233, - "sophie": 8234, - "brilliant": 8235, - "bills": 8236, - "##bar": 8237, - "##nik": 8238, - "fucking": 8239, - "mainland": 8240, - "server": 8241, - "pleasant": 8242, - "seized": 8243, - "veterans": 8244, - "jerked": 8245, - "fail": 8246, - "beta": 8247, - "brush": 8248, - "radiation": 8249, - "stored": 8250, - "warmth": 8251, - "southeastern": 8252, - "nate": 8253, - "sin": 8254, - "raced": 8255, - "berkeley": 8256, - "joke": 8257, - "athlete": 8258, - "designation": 8259, - "trunk": 8260, - "##low": 8261, - "roland": 8262, - "qualification": 8263, - "archives": 8264, - "heels": 8265, - "artwork": 8266, - "receives": 8267, - "judicial": 8268, - "reserves": 8269, - "##bed": 8270, - "woke": 8271, - "installation": 8272, - "abu": 8273, - "floating": 8274, - "fake": 8275, - "lesser": 8276, - "excitement": 8277, - "interface": 8278, - "concentrated": 8279, - "addressed": 8280, - "characteristic": 8281, - "amanda": 8282, - "saxophone": 8283, - "monk": 8284, - "auto": 8285, - "##bus": 8286, - "releasing": 8287, - "egg": 8288, - "dies": 8289, - "interaction": 8290, - "defender": 8291, - "ce": 8292, - "outbreak": 8293, - "glory": 8294, - "loving": 8295, - "##bert": 8296, - "sequel": 8297, - "consciousness": 8298, - "http": 8299, - "awake": 8300, - "ski": 8301, - "enrolled": 8302, - "##ress": 8303, - "handling": 8304, - "rookie": 8305, - "brow": 8306, - "somebody": 8307, - "biography": 8308, - "warfare": 8309, - "amounts": 8310, - "contracts": 8311, - "presentation": 8312, - "fabric": 8313, - "dissolved": 8314, - "challenged": 8315, - "meter": 8316, - "psychological": 8317, - "lt": 8318, - "elevated": 8319, - "rally": 8320, - "accurate": 8321, - "##tha": 8322, - "hospitals": 8323, - "undergraduate": 8324, - "specialist": 8325, - "venezuela": 8326, - "exhibit": 8327, - "shed": 8328, - "nursing": 8329, - "protestant": 8330, - "fluid": 8331, - "structural": 8332, - "footage": 8333, - "jared": 8334, - "consistent": 8335, - "prey": 8336, - "##ska": 8337, - "succession": 8338, - "reflect": 8339, - "exile": 8340, - "lebanon": 8341, - "wiped": 8342, - "suspect": 8343, - "shanghai": 8344, - "resting": 8345, - "integration": 8346, - "preservation": 8347, - "marvel": 8348, - "variant": 8349, - "pirates": 8350, - "sheep": 8351, - "rounded": 8352, - "capita": 8353, - "sailing": 8354, - "colonies": 8355, - "manuscript": 8356, - "deemed": 8357, - "variations": 8358, - "clarke": 8359, - "functional": 8360, - "emerging": 8361, - "boxing": 8362, - "relaxed": 8363, - "curse": 8364, - "azerbaijan": 8365, - "heavyweight": 8366, - "nickname": 8367, - "editorial": 8368, - "rang": 8369, - "grid": 8370, - "tightened": 8371, - "earthquake": 8372, - "flashed": 8373, - "miguel": 8374, - "rushing": 8375, - "##ches": 8376, - "improvements": 8377, - "boxes": 8378, - "brooks": 8379, - "180": 8380, - "consumption": 8381, - "molecular": 8382, - "felix": 8383, - "societies": 8384, - "repeatedly": 8385, - "variation": 8386, - "aids": 8387, - "civic": 8388, - "graphics": 8389, - "professionals": 8390, - "realm": 8391, - "autonomous": 8392, - "receiver": 8393, - "delayed": 8394, - "workshop": 8395, - "militia": 8396, - "chairs": 8397, - "trump": 8398, - "canyon": 8399, - "##point": 8400, - "harsh": 8401, - "extending": 8402, - "lovely": 8403, - "happiness": 8404, - "##jan": 8405, - "stake": 8406, - "eyebrows": 8407, - "embassy": 8408, - "wellington": 8409, - "hannah": 8410, - "##ella": 8411, - "sony": 8412, - "corners": 8413, - "bishops": 8414, - "swear": 8415, - "cloth": 8416, - "contents": 8417, - "xi": 8418, - "namely": 8419, - "commenced": 8420, - "1854": 8421, - "stanford": 8422, - "nashville": 8423, - "courage": 8424, - "graphic": 8425, - "commitment": 8426, - "garrison": 8427, - "##bin": 8428, - "hamlet": 8429, - "clearing": 8430, - "rebels": 8431, - "attraction": 8432, - "literacy": 8433, - "cooking": 8434, - "ruins": 8435, - "temples": 8436, - "jenny": 8437, - "humanity": 8438, - "celebrate": 8439, - "hasn": 8440, - "freight": 8441, - "sixty": 8442, - "rebel": 8443, - "bastard": 8444, - "##art": 8445, - "newton": 8446, - "##ada": 8447, - "deer": 8448, - "##ges": 8449, - "##ching": 8450, - "smiles": 8451, - "delaware": 8452, - "singers": 8453, - "##ets": 8454, - "approaching": 8455, - "assists": 8456, - "flame": 8457, - "##ph": 8458, - "boulevard": 8459, - "barrel": 8460, - "planted": 8461, - "##ome": 8462, - "pursuit": 8463, - "##sia": 8464, - "consequences": 8465, - "posts": 8466, - "shallow": 8467, - "invitation": 8468, - "rode": 8469, - "depot": 8470, - "ernest": 8471, - "kane": 8472, - "rod": 8473, - "concepts": 8474, - "preston": 8475, - "topic": 8476, - "chambers": 8477, - "striking": 8478, - "blast": 8479, - "arrives": 8480, - "descendants": 8481, - "montgomery": 8482, - "ranges": 8483, - "worlds": 8484, - "##lay": 8485, - "##ari": 8486, - "span": 8487, - "chaos": 8488, - "praise": 8489, - "##ag": 8490, - "fewer": 8491, - "1855": 8492, - "sanctuary": 8493, - "mud": 8494, - "fbi": 8495, - "##ions": 8496, - "programmes": 8497, - "maintaining": 8498, - "unity": 8499, - "harper": 8500, - "bore": 8501, - "handsome": 8502, - "closure": 8503, - "tournaments": 8504, - "thunder": 8505, - "nebraska": 8506, - "linda": 8507, - "facade": 8508, - "puts": 8509, - "satisfied": 8510, - "argentine": 8511, - "dale": 8512, - "cork": 8513, - "dome": 8514, - "panama": 8515, - "##yl": 8516, - "1858": 8517, - "tasks": 8518, - "experts": 8519, - "##ates": 8520, - "feeding": 8521, - "equation": 8522, - "##las": 8523, - "##ida": 8524, - "##tu": 8525, - "engage": 8526, - "bryan": 8527, - "##ax": 8528, - "um": 8529, - "quartet": 8530, - "melody": 8531, - "disbanded": 8532, - "sheffield": 8533, - "blocked": 8534, - "gasped": 8535, - "delay": 8536, - "kisses": 8537, - "maggie": 8538, - "connects": 8539, - "##non": 8540, - "sts": 8541, - "poured": 8542, - "creator": 8543, - "publishers": 8544, - "##we": 8545, - "guided": 8546, - "ellis": 8547, - "extinct": 8548, - "hug": 8549, - "gaining": 8550, - "##ord": 8551, - "complicated": 8552, - "##bility": 8553, - "poll": 8554, - "clenched": 8555, - "investigate": 8556, - "##use": 8557, - "thereby": 8558, - "quantum": 8559, - "spine": 8560, - "cdp": 8561, - "humor": 8562, - "kills": 8563, - "administered": 8564, - "semifinals": 8565, - "##du": 8566, - "encountered": 8567, - "ignore": 8568, - "##bu": 8569, - "commentary": 8570, - "##maker": 8571, - "bother": 8572, - "roosevelt": 8573, - "140": 8574, - "plains": 8575, - "halfway": 8576, - "flowing": 8577, - "cultures": 8578, - "crack": 8579, - "imprisoned": 8580, - "neighboring": 8581, - "airline": 8582, - "##ses": 8583, - "##view": 8584, - "##mate": 8585, - "##ec": 8586, - "gather": 8587, - "wolves": 8588, - "marathon": 8589, - "transformed": 8590, - "##ill": 8591, - "cruise": 8592, - "organisations": 8593, - "carol": 8594, - "punch": 8595, - "exhibitions": 8596, - "numbered": 8597, - "alarm": 8598, - "ratings": 8599, - "daddy": 8600, - "silently": 8601, - "##stein": 8602, - "queens": 8603, - "colours": 8604, - "impression": 8605, - "guidance": 8606, - "liu": 8607, - "tactical": 8608, - "##rat": 8609, - "marshal": 8610, - "della": 8611, - "arrow": 8612, - "##ings": 8613, - "rested": 8614, - "feared": 8615, - "tender": 8616, - "owns": 8617, - "bitter": 8618, - "advisor": 8619, - "escort": 8620, - "##ides": 8621, - "spare": 8622, - "farms": 8623, - "grants": 8624, - "##ene": 8625, - "dragons": 8626, - "encourage": 8627, - "colleagues": 8628, - "cameras": 8629, - "##und": 8630, - "sucked": 8631, - "pile": 8632, - "spirits": 8633, - "prague": 8634, - "statements": 8635, - "suspension": 8636, - "landmark": 8637, - "fence": 8638, - "torture": 8639, - "recreation": 8640, - "bags": 8641, - "permanently": 8642, - "survivors": 8643, - "pond": 8644, - "spy": 8645, - "predecessor": 8646, - "bombing": 8647, - "coup": 8648, - "##og": 8649, - "protecting": 8650, - "transformation": 8651, - "glow": 8652, - "##lands": 8653, - "##book": 8654, - "dug": 8655, - "priests": 8656, - "andrea": 8657, - "feat": 8658, - "barn": 8659, - "jumping": 8660, - "##chen": 8661, - "##ologist": 8662, - "##con": 8663, - "casualties": 8664, - "stern": 8665, - "auckland": 8666, - "pipe": 8667, - "serie": 8668, - "revealing": 8669, - "ba": 8670, - "##bel": 8671, - "trevor": 8672, - "mercy": 8673, - "spectrum": 8674, - "yang": 8675, - "consist": 8676, - "governing": 8677, - "collaborated": 8678, - "possessed": 8679, - "epic": 8680, - "comprises": 8681, - "blew": 8682, - "shane": 8683, - "##ack": 8684, - "lopez": 8685, - "honored": 8686, - "magical": 8687, - "sacrifice": 8688, - "judgment": 8689, - "perceived": 8690, - "hammer": 8691, - "mtv": 8692, - "baronet": 8693, - "tune": 8694, - "das": 8695, - "missionary": 8696, - "sheets": 8697, - "350": 8698, - "neutral": 8699, - "oral": 8700, - "threatening": 8701, - "attractive": 8702, - "shade": 8703, - "aims": 8704, - "seminary": 8705, - "##master": 8706, - "estates": 8707, - "1856": 8708, - "michel": 8709, - "wounds": 8710, - "refugees": 8711, - "manufacturers": 8712, - "##nic": 8713, - "mercury": 8714, - "syndrome": 8715, - "porter": 8716, - "##iya": 8717, - "##din": 8718, - "hamburg": 8719, - "identification": 8720, - "upstairs": 8721, - "purse": 8722, - "widened": 8723, - "pause": 8724, - "cared": 8725, - "breathed": 8726, - "affiliate": 8727, - "santiago": 8728, - "prevented": 8729, - "celtic": 8730, - "fisher": 8731, - "125": 8732, - "recruited": 8733, - "byzantine": 8734, - "reconstruction": 8735, - "farther": 8736, - "##mp": 8737, - "diet": 8738, - "sake": 8739, - "au": 8740, - "spite": 8741, - "sensation": 8742, - "##ert": 8743, - "blank": 8744, - "separation": 8745, - "105": 8746, - "##hon": 8747, - "vladimir": 8748, - "armies": 8749, - "anime": 8750, - "##lie": 8751, - "accommodate": 8752, - "orbit": 8753, - "cult": 8754, - "sofia": 8755, - "archive": 8756, - "##ify": 8757, - "##box": 8758, - "founders": 8759, - "sustained": 8760, - "disorder": 8761, - "honours": 8762, - "northeastern": 8763, - "mia": 8764, - "crops": 8765, - "violet": 8766, - "threats": 8767, - "blanket": 8768, - "fires": 8769, - "canton": 8770, - "followers": 8771, - "southwestern": 8772, - "prototype": 8773, - "voyage": 8774, - "assignment": 8775, - "altered": 8776, - "moderate": 8777, - "protocol": 8778, - "pistol": 8779, - "##eo": 8780, - "questioned": 8781, - "brass": 8782, - "lifting": 8783, - "1852": 8784, - "math": 8785, - "authored": 8786, - "##ual": 8787, - "doug": 8788, - "dimensional": 8789, - "dynamic": 8790, - "##san": 8791, - "1851": 8792, - "pronounced": 8793, - "grateful": 8794, - "quest": 8795, - "uncomfortable": 8796, - "boom": 8797, - "presidency": 8798, - "stevens": 8799, - "relating": 8800, - "politicians": 8801, - "chen": 8802, - "barrier": 8803, - "quinn": 8804, - "diana": 8805, - "mosque": 8806, - "tribal": 8807, - "cheese": 8808, - "palmer": 8809, - "portions": 8810, - "sometime": 8811, - "chester": 8812, - "treasure": 8813, - "wu": 8814, - "bend": 8815, - "download": 8816, - "millions": 8817, - "reforms": 8818, - "registration": 8819, - "##osa": 8820, - "consequently": 8821, - "monitoring": 8822, - "ate": 8823, - "preliminary": 8824, - "brandon": 8825, - "invented": 8826, - "ps": 8827, - "eaten": 8828, - "exterior": 8829, - "intervention": 8830, - "ports": 8831, - "documented": 8832, - "log": 8833, - "displays": 8834, - "lecture": 8835, - "sally": 8836, - "favourite": 8837, - "##itz": 8838, - "vermont": 8839, - "lo": 8840, - "invisible": 8841, - "isle": 8842, - "breed": 8843, - "##ator": 8844, - "journalists": 8845, - "relay": 8846, - "speaks": 8847, - "backward": 8848, - "explore": 8849, - "midfielder": 8850, - "actively": 8851, - "stefan": 8852, - "procedures": 8853, - "cannon": 8854, - "blond": 8855, - "kenneth": 8856, - "centered": 8857, - "servants": 8858, - "chains": 8859, - "libraries": 8860, - "malcolm": 8861, - "essex": 8862, - "henri": 8863, - "slavery": 8864, - "##hal": 8865, - "facts": 8866, - "fairy": 8867, - "coached": 8868, - "cassie": 8869, - "cats": 8870, - "washed": 8871, - "cop": 8872, - "##fi": 8873, - "announcement": 8874, - "item": 8875, - "2000s": 8876, - "vinyl": 8877, - "activated": 8878, - "marco": 8879, - "frontier": 8880, - "growled": 8881, - "curriculum": 8882, - "##das": 8883, - "loyal": 8884, - "accomplished": 8885, - "leslie": 8886, - "ritual": 8887, - "kenny": 8888, - "##00": 8889, - "vii": 8890, - "napoleon": 8891, - "hollow": 8892, - "hybrid": 8893, - "jungle": 8894, - "stationed": 8895, - "friedrich": 8896, - "counted": 8897, - "##ulated": 8898, - "platinum": 8899, - "theatrical": 8900, - "seated": 8901, - "col": 8902, - "rubber": 8903, - "glen": 8904, - "1840": 8905, - "diversity": 8906, - "healing": 8907, - "extends": 8908, - "id": 8909, - "provisions": 8910, - "administrator": 8911, - "columbus": 8912, - "##oe": 8913, - "tributary": 8914, - "te": 8915, - "assured": 8916, - "org": 8917, - "##uous": 8918, - "prestigious": 8919, - "examined": 8920, - "lectures": 8921, - "grammy": 8922, - "ronald": 8923, - "associations": 8924, - "bailey": 8925, - "allan": 8926, - "essays": 8927, - "flute": 8928, - "believing": 8929, - "consultant": 8930, - "proceedings": 8931, - "travelling": 8932, - "1853": 8933, - "kit": 8934, - "kerala": 8935, - "yugoslavia": 8936, - "buddy": 8937, - "methodist": 8938, - "##ith": 8939, - "burial": 8940, - "centres": 8941, - "batman": 8942, - "##nda": 8943, - "discontinued": 8944, - "bo": 8945, - "dock": 8946, - "stockholm": 8947, - "lungs": 8948, - "severely": 8949, - "##nk": 8950, - "citing": 8951, - "manga": 8952, - "##ugh": 8953, - "steal": 8954, - "mumbai": 8955, - "iraqi": 8956, - "robot": 8957, - "celebrity": 8958, - "bride": 8959, - "broadcasts": 8960, - "abolished": 8961, - "pot": 8962, - "joel": 8963, - "overhead": 8964, - "franz": 8965, - "packed": 8966, - "reconnaissance": 8967, - "johann": 8968, - "acknowledged": 8969, - "introduce": 8970, - "handled": 8971, - "doctorate": 8972, - "developments": 8973, - "drinks": 8974, - "alley": 8975, - "palestine": 8976, - "##nis": 8977, - "##aki": 8978, - "proceeded": 8979, - "recover": 8980, - "bradley": 8981, - "grain": 8982, - "patch": 8983, - "afford": 8984, - "infection": 8985, - "nationalist": 8986, - "legendary": 8987, - "##ath": 8988, - "interchange": 8989, - "virtually": 8990, - "gen": 8991, - "gravity": 8992, - "exploration": 8993, - "amber": 8994, - "vital": 8995, - "wishes": 8996, - "powell": 8997, - "doctrine": 8998, - "elbow": 8999, - "screenplay": 9000, - "##bird": 9001, - "contribute": 9002, - "indonesian": 9003, - "pet": 9004, - "creates": 9005, - "##com": 9006, - "enzyme": 9007, - "kylie": 9008, - "discipline": 9009, - "drops": 9010, - "manila": 9011, - "hunger": 9012, - "##ien": 9013, - "layers": 9014, - "suffer": 9015, - "fever": 9016, - "bits": 9017, - "monica": 9018, - "keyboard": 9019, - "manages": 9020, - "##hood": 9021, - "searched": 9022, - "appeals": 9023, - "##bad": 9024, - "testament": 9025, - "grande": 9026, - "reid": 9027, - "##war": 9028, - "beliefs": 9029, - "congo": 9030, - "##ification": 9031, - "##dia": 9032, - "si": 9033, - "requiring": 9034, - "##via": 9035, - "casey": 9036, - "1849": 9037, - "regret": 9038, - "streak": 9039, - "rape": 9040, - "depends": 9041, - "syrian": 9042, - "sprint": 9043, - "pound": 9044, - "tourists": 9045, - "upcoming": 9046, - "pub": 9047, - "##xi": 9048, - "tense": 9049, - "##els": 9050, - "practiced": 9051, - "echo": 9052, - "nationwide": 9053, - "guild": 9054, - "motorcycle": 9055, - "liz": 9056, - "##zar": 9057, - "chiefs": 9058, - "desired": 9059, - "elena": 9060, - "bye": 9061, - "precious": 9062, - "absorbed": 9063, - "relatives": 9064, - "booth": 9065, - "pianist": 9066, - "##mal": 9067, - "citizenship": 9068, - "exhausted": 9069, - "wilhelm": 9070, - "##ceae": 9071, - "##hed": 9072, - "noting": 9073, - "quarterback": 9074, - "urge": 9075, - "hectares": 9076, - "##gue": 9077, - "ace": 9078, - "holly": 9079, - "##tal": 9080, - "blonde": 9081, - "davies": 9082, - "parked": 9083, - "sustainable": 9084, - "stepping": 9085, - "twentieth": 9086, - "airfield": 9087, - "galaxy": 9088, - "nest": 9089, - "chip": 9090, - "##nell": 9091, - "tan": 9092, - "shaft": 9093, - "paulo": 9094, - "requirement": 9095, - "##zy": 9096, - "paradise": 9097, - "tobacco": 9098, - "trans": 9099, - "renewed": 9100, - "vietnamese": 9101, - "##cker": 9102, - "##ju": 9103, - "suggesting": 9104, - "catching": 9105, - "holmes": 9106, - "enjoying": 9107, - "md": 9108, - "trips": 9109, - "colt": 9110, - "holder": 9111, - "butterfly": 9112, - "nerve": 9113, - "reformed": 9114, - "cherry": 9115, - "bowling": 9116, - "trailer": 9117, - "carriage": 9118, - "goodbye": 9119, - "appreciate": 9120, - "toy": 9121, - "joshua": 9122, - "interactive": 9123, - "enabled": 9124, - "involve": 9125, - "##kan": 9126, - "collar": 9127, - "determination": 9128, - "bunch": 9129, - "facebook": 9130, - "recall": 9131, - "shorts": 9132, - "superintendent": 9133, - "episcopal": 9134, - "frustration": 9135, - "giovanni": 9136, - "nineteenth": 9137, - "laser": 9138, - "privately": 9139, - "array": 9140, - "circulation": 9141, - "##ovic": 9142, - "armstrong": 9143, - "deals": 9144, - "painful": 9145, - "permit": 9146, - "discrimination": 9147, - "##wi": 9148, - "aires": 9149, - "retiring": 9150, - "cottage": 9151, - "ni": 9152, - "##sta": 9153, - "horizon": 9154, - "ellen": 9155, - "jamaica": 9156, - "ripped": 9157, - "fernando": 9158, - "chapters": 9159, - "playstation": 9160, - "patron": 9161, - "lecturer": 9162, - "navigation": 9163, - "behaviour": 9164, - "genes": 9165, - "georgian": 9166, - "export": 9167, - "solomon": 9168, - "rivals": 9169, - "swift": 9170, - "seventeen": 9171, - "rodriguez": 9172, - "princeton": 9173, - "independently": 9174, - "sox": 9175, - "1847": 9176, - "arguing": 9177, - "entity": 9178, - "casting": 9179, - "hank": 9180, - "criteria": 9181, - "oakland": 9182, - "geographic": 9183, - "milwaukee": 9184, - "reflection": 9185, - "expanding": 9186, - "conquest": 9187, - "dubbed": 9188, - "##tv": 9189, - "halt": 9190, - "brave": 9191, - "brunswick": 9192, - "doi": 9193, - "arched": 9194, - "curtis": 9195, - "divorced": 9196, - "predominantly": 9197, - "somerset": 9198, - "streams": 9199, - "ugly": 9200, - "zoo": 9201, - "horrible": 9202, - "curved": 9203, - "buenos": 9204, - "fierce": 9205, - "dictionary": 9206, - "vector": 9207, - "theological": 9208, - "unions": 9209, - "handful": 9210, - "stability": 9211, - "chan": 9212, - "punjab": 9213, - "segments": 9214, - "##lly": 9215, - "altar": 9216, - "ignoring": 9217, - "gesture": 9218, - "monsters": 9219, - "pastor": 9220, - "##stone": 9221, - "thighs": 9222, - "unexpected": 9223, - "operators": 9224, - "abruptly": 9225, - "coin": 9226, - "compiled": 9227, - "associates": 9228, - "improving": 9229, - "migration": 9230, - "pin": 9231, - "##ose": 9232, - "compact": 9233, - "collegiate": 9234, - "reserved": 9235, - "##urs": 9236, - "quarterfinals": 9237, - "roster": 9238, - "restore": 9239, - "assembled": 9240, - "hurry": 9241, - "oval": 9242, - "##cies": 9243, - "1846": 9244, - "flags": 9245, - "martha": 9246, - "##del": 9247, - "victories": 9248, - "sharply": 9249, - "##rated": 9250, - "argues": 9251, - "deadly": 9252, - "neo": 9253, - "drawings": 9254, - "symbols": 9255, - "performer": 9256, - "##iel": 9257, - "griffin": 9258, - "restrictions": 9259, - "editing": 9260, - "andrews": 9261, - "java": 9262, - "journals": 9263, - "arabia": 9264, - "compositions": 9265, - "dee": 9266, - "pierce": 9267, - "removing": 9268, - "hindi": 9269, - "casino": 9270, - "runway": 9271, - "civilians": 9272, - "minds": 9273, - "nasa": 9274, - "hotels": 9275, - "##zation": 9276, - "refuge": 9277, - "rent": 9278, - "retain": 9279, - "potentially": 9280, - "conferences": 9281, - "suburban": 9282, - "conducting": 9283, - "##tto": 9284, - "##tions": 9285, - "##tle": 9286, - "descended": 9287, - "massacre": 9288, - "##cal": 9289, - "ammunition": 9290, - "terrain": 9291, - "fork": 9292, - "souls": 9293, - "counts": 9294, - "chelsea": 9295, - "durham": 9296, - "drives": 9297, - "cab": 9298, - "##bank": 9299, - "perth": 9300, - "realizing": 9301, - "palestinian": 9302, - "finn": 9303, - "simpson": 9304, - "##dal": 9305, - "betty": 9306, - "##ule": 9307, - "moreover": 9308, - "particles": 9309, - "cardinals": 9310, - "tent": 9311, - "evaluation": 9312, - "extraordinary": 9313, - "##oid": 9314, - "inscription": 9315, - "##works": 9316, - "wednesday": 9317, - "chloe": 9318, - "maintains": 9319, - "panels": 9320, - "ashley": 9321, - "trucks": 9322, - "##nation": 9323, - "cluster": 9324, - "sunlight": 9325, - "strikes": 9326, - "zhang": 9327, - "##wing": 9328, - "dialect": 9329, - "canon": 9330, - "##ap": 9331, - "tucked": 9332, - "##ws": 9333, - "collecting": 9334, - "##mas": 9335, - "##can": 9336, - "##sville": 9337, - "maker": 9338, - "quoted": 9339, - "evan": 9340, - "franco": 9341, - "aria": 9342, - "buying": 9343, - "cleaning": 9344, - "eva": 9345, - "closet": 9346, - "provision": 9347, - "apollo": 9348, - "clinic": 9349, - "rat": 9350, - "##ez": 9351, - "necessarily": 9352, - "ac": 9353, - "##gle": 9354, - "##ising": 9355, - "venues": 9356, - "flipped": 9357, - "cent": 9358, - "spreading": 9359, - "trustees": 9360, - "checking": 9361, - "authorized": 9362, - "##sco": 9363, - "disappointed": 9364, - "##ado": 9365, - "notion": 9366, - "duration": 9367, - "trumpet": 9368, - "hesitated": 9369, - "topped": 9370, - "brussels": 9371, - "rolls": 9372, - "theoretical": 9373, - "hint": 9374, - "define": 9375, - "aggressive": 9376, - "repeat": 9377, - "wash": 9378, - "peaceful": 9379, - "optical": 9380, - "width": 9381, - "allegedly": 9382, - "mcdonald": 9383, - "strict": 9384, - "copyright": 9385, - "##illa": 9386, - "investors": 9387, - "mar": 9388, - "jam": 9389, - "witnesses": 9390, - "sounding": 9391, - "miranda": 9392, - "michelle": 9393, - "privacy": 9394, - "hugo": 9395, - "harmony": 9396, - "##pp": 9397, - "valid": 9398, - "lynn": 9399, - "glared": 9400, - "nina": 9401, - "102": 9402, - "headquartered": 9403, - "diving": 9404, - "boarding": 9405, - "gibson": 9406, - "##ncy": 9407, - "albanian": 9408, - "marsh": 9409, - "routine": 9410, - "dealt": 9411, - "enhanced": 9412, - "er": 9413, - "intelligent": 9414, - "substance": 9415, - "targeted": 9416, - "enlisted": 9417, - "discovers": 9418, - "spinning": 9419, - "observations": 9420, - "pissed": 9421, - "smoking": 9422, - "rebecca": 9423, - "capitol": 9424, - "visa": 9425, - "varied": 9426, - "costume": 9427, - "seemingly": 9428, - "indies": 9429, - "compensation": 9430, - "surgeon": 9431, - "thursday": 9432, - "arsenal": 9433, - "westminster": 9434, - "suburbs": 9435, - "rid": 9436, - "anglican": 9437, - "##ridge": 9438, - "knots": 9439, - "foods": 9440, - "alumni": 9441, - "lighter": 9442, - "fraser": 9443, - "whoever": 9444, - "portal": 9445, - "scandal": 9446, - "##ray": 9447, - "gavin": 9448, - "advised": 9449, - "instructor": 9450, - "flooding": 9451, - "terrorist": 9452, - "##ale": 9453, - "teenage": 9454, - "interim": 9455, - "senses": 9456, - "duck": 9457, - "teen": 9458, - "thesis": 9459, - "abby": 9460, - "eager": 9461, - "overcome": 9462, - "##ile": 9463, - "newport": 9464, - "glenn": 9465, - "rises": 9466, - "shame": 9467, - "##cc": 9468, - "prompted": 9469, - "priority": 9470, - "forgot": 9471, - "bomber": 9472, - "nicolas": 9473, - "protective": 9474, - "360": 9475, - "cartoon": 9476, - "katherine": 9477, - "breeze": 9478, - "lonely": 9479, - "trusted": 9480, - "henderson": 9481, - "richardson": 9482, - "relax": 9483, - "banner": 9484, - "candy": 9485, - "palms": 9486, - "remarkable": 9487, - "##rio": 9488, - "legends": 9489, - "cricketer": 9490, - "essay": 9491, - "ordained": 9492, - "edmund": 9493, - "rifles": 9494, - "trigger": 9495, - "##uri": 9496, - "##away": 9497, - "sail": 9498, - "alert": 9499, - "1830": 9500, - "audiences": 9501, - "penn": 9502, - "sussex": 9503, - "siblings": 9504, - "pursued": 9505, - "indianapolis": 9506, - "resist": 9507, - "rosa": 9508, - "consequence": 9509, - "succeed": 9510, - "avoided": 9511, - "1845": 9512, - "##ulation": 9513, - "inland": 9514, - "##tie": 9515, - "##nna": 9516, - "counsel": 9517, - "profession": 9518, - "chronicle": 9519, - "hurried": 9520, - "##una": 9521, - "eyebrow": 9522, - "eventual": 9523, - "bleeding": 9524, - "innovative": 9525, - "cure": 9526, - "##dom": 9527, - "committees": 9528, - "accounting": 9529, - "con": 9530, - "scope": 9531, - "hardy": 9532, - "heather": 9533, - "tenor": 9534, - "gut": 9535, - "herald": 9536, - "codes": 9537, - "tore": 9538, - "scales": 9539, - "wagon": 9540, - "##oo": 9541, - "luxury": 9542, - "tin": 9543, - "prefer": 9544, - "fountain": 9545, - "triangle": 9546, - "bonds": 9547, - "darling": 9548, - "convoy": 9549, - "dried": 9550, - "traced": 9551, - "beings": 9552, - "troy": 9553, - "accidentally": 9554, - "slam": 9555, - "findings": 9556, - "smelled": 9557, - "joey": 9558, - "lawyers": 9559, - "outcome": 9560, - "steep": 9561, - "bosnia": 9562, - "configuration": 9563, - "shifting": 9564, - "toll": 9565, - "brook": 9566, - "performers": 9567, - "lobby": 9568, - "philosophical": 9569, - "construct": 9570, - "shrine": 9571, - "aggregate": 9572, - "boot": 9573, - "cox": 9574, - "phenomenon": 9575, - "savage": 9576, - "insane": 9577, - "solely": 9578, - "reynolds": 9579, - "lifestyle": 9580, - "##ima": 9581, - "nationally": 9582, - "holdings": 9583, - "consideration": 9584, - "enable": 9585, - "edgar": 9586, - "mo": 9587, - "mama": 9588, - "##tein": 9589, - "fights": 9590, - "relegation": 9591, - "chances": 9592, - "atomic": 9593, - "hub": 9594, - "conjunction": 9595, - "awkward": 9596, - "reactions": 9597, - "currency": 9598, - "finale": 9599, - "kumar": 9600, - "underwent": 9601, - "steering": 9602, - "elaborate": 9603, - "gifts": 9604, - "comprising": 9605, - "melissa": 9606, - "veins": 9607, - "reasonable": 9608, - "sunshine": 9609, - "chi": 9610, - "solve": 9611, - "trails": 9612, - "inhabited": 9613, - "elimination": 9614, - "ethics": 9615, - "huh": 9616, - "ana": 9617, - "molly": 9618, - "consent": 9619, - "apartments": 9620, - "layout": 9621, - "marines": 9622, - "##ces": 9623, - "hunters": 9624, - "bulk": 9625, - "##oma": 9626, - "hometown": 9627, - "##wall": 9628, - "##mont": 9629, - "cracked": 9630, - "reads": 9631, - "neighbouring": 9632, - "withdrawn": 9633, - "admission": 9634, - "wingspan": 9635, - "damned": 9636, - "anthology": 9637, - "lancashire": 9638, - "brands": 9639, - "batting": 9640, - "forgive": 9641, - "cuban": 9642, - "awful": 9643, - "##lyn": 9644, - "104": 9645, - "dimensions": 9646, - "imagination": 9647, - "##ade": 9648, - "dante": 9649, - "##ship": 9650, - "tracking": 9651, - "desperately": 9652, - "goalkeeper": 9653, - "##yne": 9654, - "groaned": 9655, - "workshops": 9656, - "confident": 9657, - "burton": 9658, - "gerald": 9659, - "milton": 9660, - "circus": 9661, - "uncertain": 9662, - "slope": 9663, - "copenhagen": 9664, - "sophia": 9665, - "fog": 9666, - "philosopher": 9667, - "portraits": 9668, - "accent": 9669, - "cycling": 9670, - "varying": 9671, - "gripped": 9672, - "larvae": 9673, - "garrett": 9674, - "specified": 9675, - "scotia": 9676, - "mature": 9677, - "luther": 9678, - "kurt": 9679, - "rap": 9680, - "##kes": 9681, - "aerial": 9682, - "750": 9683, - "ferdinand": 9684, - "heated": 9685, - "es": 9686, - "transported": 9687, - "##shan": 9688, - "safely": 9689, - "nonetheless": 9690, - "##orn": 9691, - "##gal": 9692, - "motors": 9693, - "demanding": 9694, - "##sburg": 9695, - "startled": 9696, - "##brook": 9697, - "ally": 9698, - "generate": 9699, - "caps": 9700, - "ghana": 9701, - "stained": 9702, - "demo": 9703, - "mentions": 9704, - "beds": 9705, - "ap": 9706, - "afterward": 9707, - "diary": 9708, - "##bling": 9709, - "utility": 9710, - "##iro": 9711, - "richards": 9712, - "1837": 9713, - "conspiracy": 9714, - "conscious": 9715, - "shining": 9716, - "footsteps": 9717, - "observer": 9718, - "cyprus": 9719, - "urged": 9720, - "loyalty": 9721, - "developer": 9722, - "probability": 9723, - "olive": 9724, - "upgraded": 9725, - "gym": 9726, - "miracle": 9727, - "insects": 9728, - "graves": 9729, - "1844": 9730, - "ourselves": 9731, - "hydrogen": 9732, - "amazon": 9733, - "katie": 9734, - "tickets": 9735, - "poets": 9736, - "##pm": 9737, - "planes": 9738, - "##pan": 9739, - "prevention": 9740, - "witnessed": 9741, - "dense": 9742, - "jin": 9743, - "randy": 9744, - "tang": 9745, - "warehouse": 9746, - "monroe": 9747, - "bang": 9748, - "archived": 9749, - "elderly": 9750, - "investigations": 9751, - "alec": 9752, - "granite": 9753, - "mineral": 9754, - "conflicts": 9755, - "controlling": 9756, - "aboriginal": 9757, - "carlo": 9758, - "##zu": 9759, - "mechanics": 9760, - "stan": 9761, - "stark": 9762, - "rhode": 9763, - "skirt": 9764, - "est": 9765, - "##berry": 9766, - "bombs": 9767, - "respected": 9768, - "##horn": 9769, - "imposed": 9770, - "limestone": 9771, - "deny": 9772, - "nominee": 9773, - "memphis": 9774, - "grabbing": 9775, - "disabled": 9776, - "##als": 9777, - "amusement": 9778, - "aa": 9779, - "frankfurt": 9780, - "corn": 9781, - "referendum": 9782, - "varies": 9783, - "slowed": 9784, - "disk": 9785, - "firms": 9786, - "unconscious": 9787, - "incredible": 9788, - "clue": 9789, - "sue": 9790, - "##zhou": 9791, - "twist": 9792, - "##cio": 9793, - "joins": 9794, - "idaho": 9795, - "chad": 9796, - "developers": 9797, - "computing": 9798, - "destroyer": 9799, - "103": 9800, - "mortal": 9801, - "tucker": 9802, - "kingston": 9803, - "choices": 9804, - "yu": 9805, - "carson": 9806, - "1800": 9807, - "os": 9808, - "whitney": 9809, - "geneva": 9810, - "pretend": 9811, - "dimension": 9812, - "staged": 9813, - "plateau": 9814, - "maya": 9815, - "##une": 9816, - "freestyle": 9817, - "##bc": 9818, - "rovers": 9819, - "hiv": 9820, - "##ids": 9821, - "tristan": 9822, - "classroom": 9823, - "prospect": 9824, - "##hus": 9825, - "honestly": 9826, - "diploma": 9827, - "lied": 9828, - "thermal": 9829, - "auxiliary": 9830, - "feast": 9831, - "unlikely": 9832, - "iata": 9833, - "##tel": 9834, - "morocco": 9835, - "pounding": 9836, - "treasury": 9837, - "lithuania": 9838, - "considerably": 9839, - "1841": 9840, - "dish": 9841, - "1812": 9842, - "geological": 9843, - "matching": 9844, - "stumbled": 9845, - "destroying": 9846, - "marched": 9847, - "brien": 9848, - "advances": 9849, - "cake": 9850, - "nicole": 9851, - "belle": 9852, - "settling": 9853, - "measuring": 9854, - "directing": 9855, - "##mie": 9856, - "tuesday": 9857, - "bassist": 9858, - "capabilities": 9859, - "stunned": 9860, - "fraud": 9861, - "torpedo": 9862, - "##list": 9863, - "##phone": 9864, - "anton": 9865, - "wisdom": 9866, - "surveillance": 9867, - "ruined": 9868, - "##ulate": 9869, - "lawsuit": 9870, - "healthcare": 9871, - "theorem": 9872, - "halls": 9873, - "trend": 9874, - "aka": 9875, - "horizontal": 9876, - "dozens": 9877, - "acquire": 9878, - "lasting": 9879, - "swim": 9880, - "hawk": 9881, - "gorgeous": 9882, - "fees": 9883, - "vicinity": 9884, - "decrease": 9885, - "adoption": 9886, - "tactics": 9887, - "##ography": 9888, - "pakistani": 9889, - "##ole": 9890, - "draws": 9891, - "##hall": 9892, - "willie": 9893, - "burke": 9894, - "heath": 9895, - "algorithm": 9896, - "integral": 9897, - "powder": 9898, - "elliott": 9899, - "brigadier": 9900, - "jackie": 9901, - "tate": 9902, - "varieties": 9903, - "darker": 9904, - "##cho": 9905, - "lately": 9906, - "cigarette": 9907, - "specimens": 9908, - "adds": 9909, - "##ree": 9910, - "##ensis": 9911, - "##inger": 9912, - "exploded": 9913, - "finalist": 9914, - "cia": 9915, - "murders": 9916, - "wilderness": 9917, - "arguments": 9918, - "nicknamed": 9919, - "acceptance": 9920, - "onwards": 9921, - "manufacture": 9922, - "robertson": 9923, - "jets": 9924, - "tampa": 9925, - "enterprises": 9926, - "blog": 9927, - "loudly": 9928, - "composers": 9929, - "nominations": 9930, - "1838": 9931, - "ai": 9932, - "malta": 9933, - "inquiry": 9934, - "automobile": 9935, - "hosting": 9936, - "viii": 9937, - "rays": 9938, - "tilted": 9939, - "grief": 9940, - "museums": 9941, - "strategies": 9942, - "furious": 9943, - "euro": 9944, - "equality": 9945, - "cohen": 9946, - "poison": 9947, - "surrey": 9948, - "wireless": 9949, - "governed": 9950, - "ridiculous": 9951, - "moses": 9952, - "##esh": 9953, - "##room": 9954, - "vanished": 9955, - "##ito": 9956, - "barnes": 9957, - "attract": 9958, - "morrison": 9959, - "istanbul": 9960, - "##iness": 9961, - "absent": 9962, - "rotation": 9963, - "petition": 9964, - "janet": 9965, - "##logical": 9966, - "satisfaction": 9967, - "custody": 9968, - "deliberately": 9969, - "observatory": 9970, - "comedian": 9971, - "surfaces": 9972, - "pinyin": 9973, - "novelist": 9974, - "strictly": 9975, - "canterbury": 9976, - "oslo": 9977, - "monks": 9978, - "embrace": 9979, - "ibm": 9980, - "jealous": 9981, - "photograph": 9982, - "continent": 9983, - "dorothy": 9984, - "marina": 9985, - "doc": 9986, - "excess": 9987, - "holden": 9988, - "allegations": 9989, - "explaining": 9990, - "stack": 9991, - "avoiding": 9992, - "lance": 9993, - "storyline": 9994, - "majesty": 9995, - "poorly": 9996, - "spike": 9997, - "dos": 9998, - "bradford": 9999, - "raven": 10000, - "travis": 10001, - "classics": 10002, - "proven": 10003, - "voltage": 10004, - "pillow": 10005, - "fists": 10006, - "butt": 10007, - "1842": 10008, - "interpreted": 10009, - "##car": 10010, - "1839": 10011, - "gage": 10012, - "telegraph": 10013, - "lens": 10014, - "promising": 10015, - "expelled": 10016, - "casual": 10017, - "collector": 10018, - "zones": 10019, - "##min": 10020, - "silly": 10021, - "nintendo": 10022, - "##kh": 10023, - "##bra": 10024, - "downstairs": 10025, - "chef": 10026, - "suspicious": 10027, - "afl": 10028, - "flies": 10029, - "vacant": 10030, - "uganda": 10031, - "pregnancy": 10032, - "condemned": 10033, - "lutheran": 10034, - "estimates": 10035, - "cheap": 10036, - "decree": 10037, - "saxon": 10038, - "proximity": 10039, - "stripped": 10040, - "idiot": 10041, - "deposits": 10042, - "contrary": 10043, - "presenter": 10044, - "magnus": 10045, - "glacier": 10046, - "im": 10047, - "offense": 10048, - "edwin": 10049, - "##ori": 10050, - "upright": 10051, - "##long": 10052, - "bolt": 10053, - "##ois": 10054, - "toss": 10055, - "geographical": 10056, - "##izes": 10057, - "environments": 10058, - "delicate": 10059, - "marking": 10060, - "abstract": 10061, - "xavier": 10062, - "nails": 10063, - "windsor": 10064, - "plantation": 10065, - "occurring": 10066, - "equity": 10067, - "saskatchewan": 10068, - "fears": 10069, - "drifted": 10070, - "sequences": 10071, - "vegetation": 10072, - "revolt": 10073, - "##stic": 10074, - "1843": 10075, - "sooner": 10076, - "fusion": 10077, - "opposing": 10078, - "nato": 10079, - "skating": 10080, - "1836": 10081, - "secretly": 10082, - "ruin": 10083, - "lease": 10084, - "##oc": 10085, - "edit": 10086, - "##nne": 10087, - "flora": 10088, - "anxiety": 10089, - "ruby": 10090, - "##ological": 10091, - "##mia": 10092, - "tel": 10093, - "bout": 10094, - "taxi": 10095, - "emmy": 10096, - "frost": 10097, - "rainbow": 10098, - "compounds": 10099, - "foundations": 10100, - "rainfall": 10101, - "assassination": 10102, - "nightmare": 10103, - "dominican": 10104, - "##win": 10105, - "achievements": 10106, - "deserve": 10107, - "orlando": 10108, - "intact": 10109, - "armenia": 10110, - "##nte": 10111, - "calgary": 10112, - "valentine": 10113, - "106": 10114, - "marion": 10115, - "proclaimed": 10116, - "theodore": 10117, - "bells": 10118, - "courtyard": 10119, - "thigh": 10120, - "gonzalez": 10121, - "console": 10122, - "troop": 10123, - "minimal": 10124, - "monte": 10125, - "everyday": 10126, - "##ence": 10127, - "##if": 10128, - "supporter": 10129, - "terrorism": 10130, - "buck": 10131, - "openly": 10132, - "presbyterian": 10133, - "activists": 10134, - "carpet": 10135, - "##iers": 10136, - "rubbing": 10137, - "uprising": 10138, - "##yi": 10139, - "cute": 10140, - "conceived": 10141, - "legally": 10142, - "##cht": 10143, - "millennium": 10144, - "cello": 10145, - "velocity": 10146, - "ji": 10147, - "rescued": 10148, - "cardiff": 10149, - "1835": 10150, - "rex": 10151, - "concentrate": 10152, - "senators": 10153, - "beard": 10154, - "rendered": 10155, - "glowing": 10156, - "battalions": 10157, - "scouts": 10158, - "competitors": 10159, - "sculptor": 10160, - "catalogue": 10161, - "arctic": 10162, - "ion": 10163, - "raja": 10164, - "bicycle": 10165, - "wow": 10166, - "glancing": 10167, - "lawn": 10168, - "##woman": 10169, - "gentleman": 10170, - "lighthouse": 10171, - "publish": 10172, - "predicted": 10173, - "calculated": 10174, - "##val": 10175, - "variants": 10176, - "##gne": 10177, - "strain": 10178, - "##ui": 10179, - "winston": 10180, - "deceased": 10181, - "##nus": 10182, - "touchdowns": 10183, - "brady": 10184, - "caleb": 10185, - "sinking": 10186, - "echoed": 10187, - "crush": 10188, - "hon": 10189, - "blessed": 10190, - "protagonist": 10191, - "hayes": 10192, - "endangered": 10193, - "magnitude": 10194, - "editors": 10195, - "##tine": 10196, - "estimate": 10197, - "responsibilities": 10198, - "##mel": 10199, - "backup": 10200, - "laying": 10201, - "consumed": 10202, - "sealed": 10203, - "zurich": 10204, - "lovers": 10205, - "frustrated": 10206, - "##eau": 10207, - "ahmed": 10208, - "kicking": 10209, - "mit": 10210, - "treasurer": 10211, - "1832": 10212, - "biblical": 10213, - "refuse": 10214, - "terrified": 10215, - "pump": 10216, - "agrees": 10217, - "genuine": 10218, - "imprisonment": 10219, - "refuses": 10220, - "plymouth": 10221, - "##hen": 10222, - "lou": 10223, - "##nen": 10224, - "tara": 10225, - "trembling": 10226, - "antarctic": 10227, - "ton": 10228, - "learns": 10229, - "##tas": 10230, - "crap": 10231, - "crucial": 10232, - "faction": 10233, - "atop": 10234, - "##borough": 10235, - "wrap": 10236, - "lancaster": 10237, - "odds": 10238, - "hopkins": 10239, - "erik": 10240, - "lyon": 10241, - "##eon": 10242, - "bros": 10243, - "##ode": 10244, - "snap": 10245, - "locality": 10246, - "tips": 10247, - "empress": 10248, - "crowned": 10249, - "cal": 10250, - "acclaimed": 10251, - "chuckled": 10252, - "##ory": 10253, - "clara": 10254, - "sends": 10255, - "mild": 10256, - "towel": 10257, - "##fl": 10258, - "##day": 10259, - "##а": 10260, - "wishing": 10261, - "assuming": 10262, - "interviewed": 10263, - "##bal": 10264, - "##die": 10265, - "interactions": 10266, - "eden": 10267, - "cups": 10268, - "helena": 10269, - "##lf": 10270, - "indie": 10271, - "beck": 10272, - "##fire": 10273, - "batteries": 10274, - "filipino": 10275, - "wizard": 10276, - "parted": 10277, - "##lam": 10278, - "traces": 10279, - "##born": 10280, - "rows": 10281, - "idol": 10282, - "albany": 10283, - "delegates": 10284, - "##ees": 10285, - "##sar": 10286, - "discussions": 10287, - "##ex": 10288, - "notre": 10289, - "instructed": 10290, - "belgrade": 10291, - "highways": 10292, - "suggestion": 10293, - "lauren": 10294, - "possess": 10295, - "orientation": 10296, - "alexandria": 10297, - "abdul": 10298, - "beats": 10299, - "salary": 10300, - "reunion": 10301, - "ludwig": 10302, - "alright": 10303, - "wagner": 10304, - "intimate": 10305, - "pockets": 10306, - "slovenia": 10307, - "hugged": 10308, - "brighton": 10309, - "merchants": 10310, - "cruel": 10311, - "stole": 10312, - "trek": 10313, - "slopes": 10314, - "repairs": 10315, - "enrollment": 10316, - "politically": 10317, - "underlying": 10318, - "promotional": 10319, - "counting": 10320, - "boeing": 10321, - "##bb": 10322, - "isabella": 10323, - "naming": 10324, - "##и": 10325, - "keen": 10326, - "bacteria": 10327, - "listing": 10328, - "separately": 10329, - "belfast": 10330, - "ussr": 10331, - "450": 10332, - "lithuanian": 10333, - "anybody": 10334, - "ribs": 10335, - "sphere": 10336, - "martinez": 10337, - "cock": 10338, - "embarrassed": 10339, - "proposals": 10340, - "fragments": 10341, - "nationals": 10342, - "##fs": 10343, - "##wski": 10344, - "premises": 10345, - "fin": 10346, - "1500": 10347, - "alpine": 10348, - "matched": 10349, - "freely": 10350, - "bounded": 10351, - "jace": 10352, - "sleeve": 10353, - "##af": 10354, - "gaming": 10355, - "pier": 10356, - "populated": 10357, - "evident": 10358, - "##like": 10359, - "frances": 10360, - "flooded": 10361, - "##dle": 10362, - "frightened": 10363, - "pour": 10364, - "trainer": 10365, - "framed": 10366, - "visitor": 10367, - "challenging": 10368, - "pig": 10369, - "wickets": 10370, - "##fold": 10371, - "infected": 10372, - "email": 10373, - "##pes": 10374, - "arose": 10375, - "##aw": 10376, - "reward": 10377, - "ecuador": 10378, - "oblast": 10379, - "vale": 10380, - "ch": 10381, - "shuttle": 10382, - "##usa": 10383, - "bach": 10384, - "rankings": 10385, - "forbidden": 10386, - "cornwall": 10387, - "accordance": 10388, - "salem": 10389, - "consumers": 10390, - "bruno": 10391, - "fantastic": 10392, - "toes": 10393, - "machinery": 10394, - "resolved": 10395, - "julius": 10396, - "remembering": 10397, - "propaganda": 10398, - "iceland": 10399, - "bombardment": 10400, - "tide": 10401, - "contacts": 10402, - "wives": 10403, - "##rah": 10404, - "concerto": 10405, - "macdonald": 10406, - "albania": 10407, - "implement": 10408, - "daisy": 10409, - "tapped": 10410, - "sudan": 10411, - "helmet": 10412, - "angela": 10413, - "mistress": 10414, - "##lic": 10415, - "crop": 10416, - "sunk": 10417, - "finest": 10418, - "##craft": 10419, - "hostile": 10420, - "##ute": 10421, - "##tsu": 10422, - "boxer": 10423, - "fr": 10424, - "paths": 10425, - "adjusted": 10426, - "habit": 10427, - "ballot": 10428, - "supervision": 10429, - "soprano": 10430, - "##zen": 10431, - "bullets": 10432, - "wicked": 10433, - "sunset": 10434, - "regiments": 10435, - "disappear": 10436, - "lamp": 10437, - "performs": 10438, - "app": 10439, - "##gia": 10440, - "##oa": 10441, - "rabbit": 10442, - "digging": 10443, - "incidents": 10444, - "entries": 10445, - "##cion": 10446, - "dishes": 10447, - "##oi": 10448, - "introducing": 10449, - "##ati": 10450, - "##fied": 10451, - "freshman": 10452, - "slot": 10453, - "jill": 10454, - "tackles": 10455, - "baroque": 10456, - "backs": 10457, - "##iest": 10458, - "lone": 10459, - "sponsor": 10460, - "destiny": 10461, - "altogether": 10462, - "convert": 10463, - "##aro": 10464, - "consensus": 10465, - "shapes": 10466, - "demonstration": 10467, - "basically": 10468, - "feminist": 10469, - "auction": 10470, - "artifacts": 10471, - "##bing": 10472, - "strongest": 10473, - "twitter": 10474, - "halifax": 10475, - "2019": 10476, - "allmusic": 10477, - "mighty": 10478, - "smallest": 10479, - "precise": 10480, - "alexandra": 10481, - "viola": 10482, - "##los": 10483, - "##ille": 10484, - "manuscripts": 10485, - "##illo": 10486, - "dancers": 10487, - "ari": 10488, - "managers": 10489, - "monuments": 10490, - "blades": 10491, - "barracks": 10492, - "springfield": 10493, - "maiden": 10494, - "consolidated": 10495, - "electron": 10496, - "##end": 10497, - "berry": 10498, - "airing": 10499, - "wheat": 10500, - "nobel": 10501, - "inclusion": 10502, - "blair": 10503, - "payments": 10504, - "geography": 10505, - "bee": 10506, - "cc": 10507, - "eleanor": 10508, - "react": 10509, - "##hurst": 10510, - "afc": 10511, - "manitoba": 10512, - "##yu": 10513, - "su": 10514, - "lineup": 10515, - "fitness": 10516, - "recreational": 10517, - "investments": 10518, - "airborne": 10519, - "disappointment": 10520, - "##dis": 10521, - "edmonton": 10522, - "viewing": 10523, - "##row": 10524, - "renovation": 10525, - "##cast": 10526, - "infant": 10527, - "bankruptcy": 10528, - "roses": 10529, - "aftermath": 10530, - "pavilion": 10531, - "##yer": 10532, - "carpenter": 10533, - "withdrawal": 10534, - "ladder": 10535, - "##hy": 10536, - "discussing": 10537, - "popped": 10538, - "reliable": 10539, - "agreements": 10540, - "rochester": 10541, - "##abad": 10542, - "curves": 10543, - "bombers": 10544, - "220": 10545, - "rao": 10546, - "reverend": 10547, - "decreased": 10548, - "choosing": 10549, - "107": 10550, - "stiff": 10551, - "consulting": 10552, - "naples": 10553, - "crawford": 10554, - "tracy": 10555, - "ka": 10556, - "ribbon": 10557, - "cops": 10558, - "##lee": 10559, - "crushed": 10560, - "deciding": 10561, - "unified": 10562, - "teenager": 10563, - "accepting": 10564, - "flagship": 10565, - "explorer": 10566, - "poles": 10567, - "sanchez": 10568, - "inspection": 10569, - "revived": 10570, - "skilled": 10571, - "induced": 10572, - "exchanged": 10573, - "flee": 10574, - "locals": 10575, - "tragedy": 10576, - "swallow": 10577, - "loading": 10578, - "hanna": 10579, - "demonstrate": 10580, - "##ela": 10581, - "salvador": 10582, - "flown": 10583, - "contestants": 10584, - "civilization": 10585, - "##ines": 10586, - "wanna": 10587, - "rhodes": 10588, - "fletcher": 10589, - "hector": 10590, - "knocking": 10591, - "considers": 10592, - "##ough": 10593, - "nash": 10594, - "mechanisms": 10595, - "sensed": 10596, - "mentally": 10597, - "walt": 10598, - "unclear": 10599, - "##eus": 10600, - "renovated": 10601, - "madame": 10602, - "##cks": 10603, - "crews": 10604, - "governmental": 10605, - "##hin": 10606, - "undertaken": 10607, - "monkey": 10608, - "##ben": 10609, - "##ato": 10610, - "fatal": 10611, - "armored": 10612, - "copa": 10613, - "caves": 10614, - "governance": 10615, - "grasp": 10616, - "perception": 10617, - "certification": 10618, - "froze": 10619, - "damp": 10620, - "tugged": 10621, - "wyoming": 10622, - "##rg": 10623, - "##ero": 10624, - "newman": 10625, - "##lor": 10626, - "nerves": 10627, - "curiosity": 10628, - "graph": 10629, - "115": 10630, - "##ami": 10631, - "withdraw": 10632, - "tunnels": 10633, - "dull": 10634, - "meredith": 10635, - "moss": 10636, - "exhibits": 10637, - "neighbors": 10638, - "communicate": 10639, - "accuracy": 10640, - "explored": 10641, - "raiders": 10642, - "republicans": 10643, - "secular": 10644, - "kat": 10645, - "superman": 10646, - "penny": 10647, - "criticised": 10648, - "##tch": 10649, - "freed": 10650, - "update": 10651, - "conviction": 10652, - "wade": 10653, - "ham": 10654, - "likewise": 10655, - "delegation": 10656, - "gotta": 10657, - "doll": 10658, - "promises": 10659, - "technological": 10660, - "myth": 10661, - "nationality": 10662, - "resolve": 10663, - "convent": 10664, - "##mark": 10665, - "sharon": 10666, - "dig": 10667, - "sip": 10668, - "coordinator": 10669, - "entrepreneur": 10670, - "fold": 10671, - "##dine": 10672, - "capability": 10673, - "councillor": 10674, - "synonym": 10675, - "blown": 10676, - "swan": 10677, - "cursed": 10678, - "1815": 10679, - "jonas": 10680, - "haired": 10681, - "sofa": 10682, - "canvas": 10683, - "keeper": 10684, - "rivalry": 10685, - "##hart": 10686, - "rapper": 10687, - "speedway": 10688, - "swords": 10689, - "postal": 10690, - "maxwell": 10691, - "estonia": 10692, - "potter": 10693, - "recurring": 10694, - "##nn": 10695, - "##ave": 10696, - "errors": 10697, - "##oni": 10698, - "cognitive": 10699, - "1834": 10700, - "##²": 10701, - "claws": 10702, - "nadu": 10703, - "roberto": 10704, - "bce": 10705, - "wrestler": 10706, - "ellie": 10707, - "##ations": 10708, - "infinite": 10709, - "ink": 10710, - "##tia": 10711, - "presumably": 10712, - "finite": 10713, - "staircase": 10714, - "108": 10715, - "noel": 10716, - "patricia": 10717, - "nacional": 10718, - "##cation": 10719, - "chill": 10720, - "eternal": 10721, - "tu": 10722, - "preventing": 10723, - "prussia": 10724, - "fossil": 10725, - "limbs": 10726, - "##logist": 10727, - "ernst": 10728, - "frog": 10729, - "perez": 10730, - "rene": 10731, - "##ace": 10732, - "pizza": 10733, - "prussian": 10734, - "##ios": 10735, - "##vy": 10736, - "molecules": 10737, - "regulatory": 10738, - "answering": 10739, - "opinions": 10740, - "sworn": 10741, - "lengths": 10742, - "supposedly": 10743, - "hypothesis": 10744, - "upward": 10745, - "habitats": 10746, - "seating": 10747, - "ancestors": 10748, - "drank": 10749, - "yield": 10750, - "hd": 10751, - "synthesis": 10752, - "researcher": 10753, - "modest": 10754, - "##var": 10755, - "mothers": 10756, - "peered": 10757, - "voluntary": 10758, - "homeland": 10759, - "##the": 10760, - "acclaim": 10761, - "##igan": 10762, - "static": 10763, - "valve": 10764, - "luxembourg": 10765, - "alto": 10766, - "carroll": 10767, - "fe": 10768, - "receptor": 10769, - "norton": 10770, - "ambulance": 10771, - "##tian": 10772, - "johnston": 10773, - "catholics": 10774, - "depicting": 10775, - "jointly": 10776, - "elephant": 10777, - "gloria": 10778, - "mentor": 10779, - "badge": 10780, - "ahmad": 10781, - "distinguish": 10782, - "remarked": 10783, - "councils": 10784, - "precisely": 10785, - "allison": 10786, - "advancing": 10787, - "detection": 10788, - "crowded": 10789, - "##10": 10790, - "cooperative": 10791, - "ankle": 10792, - "mercedes": 10793, - "dagger": 10794, - "surrendered": 10795, - "pollution": 10796, - "commit": 10797, - "subway": 10798, - "jeffrey": 10799, - "lesson": 10800, - "sculptures": 10801, - "provider": 10802, - "##fication": 10803, - "membrane": 10804, - "timothy": 10805, - "rectangular": 10806, - "fiscal": 10807, - "heating": 10808, - "teammate": 10809, - "basket": 10810, - "particle": 10811, - "anonymous": 10812, - "deployment": 10813, - "##ple": 10814, - "missiles": 10815, - "courthouse": 10816, - "proportion": 10817, - "shoe": 10818, - "sec": 10819, - "##ller": 10820, - "complaints": 10821, - "forbes": 10822, - "blacks": 10823, - "abandon": 10824, - "remind": 10825, - "sizes": 10826, - "overwhelming": 10827, - "autobiography": 10828, - "natalie": 10829, - "##awa": 10830, - "risks": 10831, - "contestant": 10832, - "countryside": 10833, - "babies": 10834, - "scorer": 10835, - "invaded": 10836, - "enclosed": 10837, - "proceed": 10838, - "hurling": 10839, - "disorders": 10840, - "##cu": 10841, - "reflecting": 10842, - "continuously": 10843, - "cruiser": 10844, - "graduates": 10845, - "freeway": 10846, - "investigated": 10847, - "ore": 10848, - "deserved": 10849, - "maid": 10850, - "blocking": 10851, - "phillip": 10852, - "jorge": 10853, - "shakes": 10854, - "dove": 10855, - "mann": 10856, - "variables": 10857, - "lacked": 10858, - "burden": 10859, - "accompanying": 10860, - "que": 10861, - "consistently": 10862, - "organizing": 10863, - "provisional": 10864, - "complained": 10865, - "endless": 10866, - "##rm": 10867, - "tubes": 10868, - "juice": 10869, - "georges": 10870, - "krishna": 10871, - "mick": 10872, - "labels": 10873, - "thriller": 10874, - "##uch": 10875, - "laps": 10876, - "arcade": 10877, - "sage": 10878, - "snail": 10879, - "##table": 10880, - "shannon": 10881, - "fi": 10882, - "laurence": 10883, - "seoul": 10884, - "vacation": 10885, - "presenting": 10886, - "hire": 10887, - "churchill": 10888, - "surprisingly": 10889, - "prohibited": 10890, - "savannah": 10891, - "technically": 10892, - "##oli": 10893, - "170": 10894, - "##lessly": 10895, - "testimony": 10896, - "suited": 10897, - "speeds": 10898, - "toys": 10899, - "romans": 10900, - "mlb": 10901, - "flowering": 10902, - "measurement": 10903, - "talented": 10904, - "kay": 10905, - "settings": 10906, - "charleston": 10907, - "expectations": 10908, - "shattered": 10909, - "achieving": 10910, - "triumph": 10911, - "ceremonies": 10912, - "portsmouth": 10913, - "lanes": 10914, - "mandatory": 10915, - "loser": 10916, - "stretching": 10917, - "cologne": 10918, - "realizes": 10919, - "seventy": 10920, - "cornell": 10921, - "careers": 10922, - "webb": 10923, - "##ulating": 10924, - "americas": 10925, - "budapest": 10926, - "ava": 10927, - "suspicion": 10928, - "##ison": 10929, - "yo": 10930, - "conrad": 10931, - "##hai": 10932, - "sterling": 10933, - "jessie": 10934, - "rector": 10935, - "##az": 10936, - "1831": 10937, - "transform": 10938, - "organize": 10939, - "loans": 10940, - "christine": 10941, - "volcanic": 10942, - "warrant": 10943, - "slender": 10944, - "summers": 10945, - "subfamily": 10946, - "newer": 10947, - "danced": 10948, - "dynamics": 10949, - "rhine": 10950, - "proceeds": 10951, - "heinrich": 10952, - "gastropod": 10953, - "commands": 10954, - "sings": 10955, - "facilitate": 10956, - "easter": 10957, - "ra": 10958, - "positioned": 10959, - "responses": 10960, - "expense": 10961, - "fruits": 10962, - "yanked": 10963, - "imported": 10964, - "25th": 10965, - "velvet": 10966, - "vic": 10967, - "primitive": 10968, - "tribune": 10969, - "baldwin": 10970, - "neighbourhood": 10971, - "donna": 10972, - "rip": 10973, - "hay": 10974, - "pr": 10975, - "##uro": 10976, - "1814": 10977, - "espn": 10978, - "welcomed": 10979, - "##aria": 10980, - "qualifier": 10981, - "glare": 10982, - "highland": 10983, - "timing": 10984, - "##cted": 10985, - "shells": 10986, - "eased": 10987, - "geometry": 10988, - "louder": 10989, - "exciting": 10990, - "slovakia": 10991, - "##sion": 10992, - "##iz": 10993, - "##lot": 10994, - "savings": 10995, - "prairie": 10996, - "##ques": 10997, - "marching": 10998, - "rafael": 10999, - "tonnes": 11000, - "##lled": 11001, - "curtain": 11002, - "preceding": 11003, - "shy": 11004, - "heal": 11005, - "greene": 11006, - "worthy": 11007, - "##pot": 11008, - "detachment": 11009, - "bury": 11010, - "sherman": 11011, - "##eck": 11012, - "reinforced": 11013, - "seeks": 11014, - "bottles": 11015, - "contracted": 11016, - "duchess": 11017, - "outfit": 11018, - "walsh": 11019, - "##sc": 11020, - "mickey": 11021, - "##ase": 11022, - "geoffrey": 11023, - "archer": 11024, - "squeeze": 11025, - "dawson": 11026, - "eliminate": 11027, - "invention": 11028, - "##enberg": 11029, - "neal": 11030, - "##eth": 11031, - "stance": 11032, - "dealer": 11033, - "coral": 11034, - "maple": 11035, - "retire": 11036, - "polo": 11037, - "simplified": 11038, - "##ht": 11039, - "1833": 11040, - "hid": 11041, - "watts": 11042, - "backwards": 11043, - "jules": 11044, - "##oke": 11045, - "genesis": 11046, - "mt": 11047, - "frames": 11048, - "rebounds": 11049, - "burma": 11050, - "woodland": 11051, - "moist": 11052, - "santos": 11053, - "whispers": 11054, - "drained": 11055, - "subspecies": 11056, - "##aa": 11057, - "streaming": 11058, - "ulster": 11059, - "burnt": 11060, - "correspondence": 11061, - "maternal": 11062, - "gerard": 11063, - "denis": 11064, - "stealing": 11065, - "##load": 11066, - "genius": 11067, - "duchy": 11068, - "##oria": 11069, - "inaugurated": 11070, - "momentum": 11071, - "suits": 11072, - "placement": 11073, - "sovereign": 11074, - "clause": 11075, - "thames": 11076, - "##hara": 11077, - "confederation": 11078, - "reservation": 11079, - "sketch": 11080, - "yankees": 11081, - "lets": 11082, - "rotten": 11083, - "charm": 11084, - "hal": 11085, - "verses": 11086, - "ultra": 11087, - "commercially": 11088, - "dot": 11089, - "salon": 11090, - "citation": 11091, - "adopt": 11092, - "winnipeg": 11093, - "mist": 11094, - "allocated": 11095, - "cairo": 11096, - "##boy": 11097, - "jenkins": 11098, - "interference": 11099, - "objectives": 11100, - "##wind": 11101, - "1820": 11102, - "portfolio": 11103, - "armoured": 11104, - "sectors": 11105, - "##eh": 11106, - "initiatives": 11107, - "##world": 11108, - "integrity": 11109, - "exercises": 11110, - "robe": 11111, - "tap": 11112, - "ab": 11113, - "gazed": 11114, - "##tones": 11115, - "distracted": 11116, - "rulers": 11117, - "111": 11118, - "favorable": 11119, - "jerome": 11120, - "tended": 11121, - "cart": 11122, - "factories": 11123, - "##eri": 11124, - "diplomat": 11125, - "valued": 11126, - "gravel": 11127, - "charitable": 11128, - "##try": 11129, - "calvin": 11130, - "exploring": 11131, - "chang": 11132, - "shepherd": 11133, - "terrace": 11134, - "pdf": 11135, - "pupil": 11136, - "##ural": 11137, - "reflects": 11138, - "ups": 11139, - "##rch": 11140, - "governors": 11141, - "shelf": 11142, - "depths": 11143, - "##nberg": 11144, - "trailed": 11145, - "crest": 11146, - "tackle": 11147, - "##nian": 11148, - "##ats": 11149, - "hatred": 11150, - "##kai": 11151, - "clare": 11152, - "makers": 11153, - "ethiopia": 11154, - "longtime": 11155, - "detected": 11156, - "embedded": 11157, - "lacking": 11158, - "slapped": 11159, - "rely": 11160, - "thomson": 11161, - "anticipation": 11162, - "iso": 11163, - "morton": 11164, - "successive": 11165, - "agnes": 11166, - "screenwriter": 11167, - "straightened": 11168, - "philippe": 11169, - "playwright": 11170, - "haunted": 11171, - "licence": 11172, - "iris": 11173, - "intentions": 11174, - "sutton": 11175, - "112": 11176, - "logical": 11177, - "correctly": 11178, - "##weight": 11179, - "branded": 11180, - "licked": 11181, - "tipped": 11182, - "silva": 11183, - "ricky": 11184, - "narrator": 11185, - "requests": 11186, - "##ents": 11187, - "greeted": 11188, - "supernatural": 11189, - "cow": 11190, - "##wald": 11191, - "lung": 11192, - "refusing": 11193, - "employer": 11194, - "strait": 11195, - "gaelic": 11196, - "liner": 11197, - "##piece": 11198, - "zoe": 11199, - "sabha": 11200, - "##mba": 11201, - "driveway": 11202, - "harvest": 11203, - "prints": 11204, - "bates": 11205, - "reluctantly": 11206, - "threshold": 11207, - "algebra": 11208, - "ira": 11209, - "wherever": 11210, - "coupled": 11211, - "240": 11212, - "assumption": 11213, - "picks": 11214, - "##air": 11215, - "designers": 11216, - "raids": 11217, - "gentlemen": 11218, - "##ean": 11219, - "roller": 11220, - "blowing": 11221, - "leipzig": 11222, - "locks": 11223, - "screw": 11224, - "dressing": 11225, - "strand": 11226, - "##lings": 11227, - "scar": 11228, - "dwarf": 11229, - "depicts": 11230, - "##nu": 11231, - "nods": 11232, - "##mine": 11233, - "differ": 11234, - "boris": 11235, - "##eur": 11236, - "yuan": 11237, - "flip": 11238, - "##gie": 11239, - "mob": 11240, - "invested": 11241, - "questioning": 11242, - "applying": 11243, - "##ture": 11244, - "shout": 11245, - "##sel": 11246, - "gameplay": 11247, - "blamed": 11248, - "illustrations": 11249, - "bothered": 11250, - "weakness": 11251, - "rehabilitation": 11252, - "##of": 11253, - "##zes": 11254, - "envelope": 11255, - "rumors": 11256, - "miners": 11257, - "leicester": 11258, - "subtle": 11259, - "kerry": 11260, - "##ico": 11261, - "ferguson": 11262, - "##fu": 11263, - "premiership": 11264, - "ne": 11265, - "##cat": 11266, - "bengali": 11267, - "prof": 11268, - "catches": 11269, - "remnants": 11270, - "dana": 11271, - "##rily": 11272, - "shouting": 11273, - "presidents": 11274, - "baltic": 11275, - "ought": 11276, - "ghosts": 11277, - "dances": 11278, - "sailors": 11279, - "shirley": 11280, - "fancy": 11281, - "dominic": 11282, - "##bie": 11283, - "madonna": 11284, - "##rick": 11285, - "bark": 11286, - "buttons": 11287, - "gymnasium": 11288, - "ashes": 11289, - "liver": 11290, - "toby": 11291, - "oath": 11292, - "providence": 11293, - "doyle": 11294, - "evangelical": 11295, - "nixon": 11296, - "cement": 11297, - "carnegie": 11298, - "embarked": 11299, - "hatch": 11300, - "surroundings": 11301, - "guarantee": 11302, - "needing": 11303, - "pirate": 11304, - "essence": 11305, - "##bee": 11306, - "filter": 11307, - "crane": 11308, - "hammond": 11309, - "projected": 11310, - "immune": 11311, - "percy": 11312, - "twelfth": 11313, - "##ult": 11314, - "regent": 11315, - "doctoral": 11316, - "damon": 11317, - "mikhail": 11318, - "##ichi": 11319, - "lu": 11320, - "critically": 11321, - "elect": 11322, - "realised": 11323, - "abortion": 11324, - "acute": 11325, - "screening": 11326, - "mythology": 11327, - "steadily": 11328, - "##fc": 11329, - "frown": 11330, - "nottingham": 11331, - "kirk": 11332, - "wa": 11333, - "minneapolis": 11334, - "##rra": 11335, - "module": 11336, - "algeria": 11337, - "mc": 11338, - "nautical": 11339, - "encounters": 11340, - "surprising": 11341, - "statues": 11342, - "availability": 11343, - "shirts": 11344, - "pie": 11345, - "alma": 11346, - "brows": 11347, - "munster": 11348, - "mack": 11349, - "soup": 11350, - "crater": 11351, - "tornado": 11352, - "sanskrit": 11353, - "cedar": 11354, - "explosive": 11355, - "bordered": 11356, - "dixon": 11357, - "planets": 11358, - "stamp": 11359, - "exam": 11360, - "happily": 11361, - "##bble": 11362, - "carriers": 11363, - "kidnapped": 11364, - "##vis": 11365, - "accommodation": 11366, - "emigrated": 11367, - "##met": 11368, - "knockout": 11369, - "correspondent": 11370, - "violation": 11371, - "profits": 11372, - "peaks": 11373, - "lang": 11374, - "specimen": 11375, - "agenda": 11376, - "ancestry": 11377, - "pottery": 11378, - "spelling": 11379, - "equations": 11380, - "obtaining": 11381, - "ki": 11382, - "linking": 11383, - "1825": 11384, - "debris": 11385, - "asylum": 11386, - "##20": 11387, - "buddhism": 11388, - "teddy": 11389, - "##ants": 11390, - "gazette": 11391, - "##nger": 11392, - "##sse": 11393, - "dental": 11394, - "eligibility": 11395, - "utc": 11396, - "fathers": 11397, - "averaged": 11398, - "zimbabwe": 11399, - "francesco": 11400, - "coloured": 11401, - "hissed": 11402, - "translator": 11403, - "lynch": 11404, - "mandate": 11405, - "humanities": 11406, - "mackenzie": 11407, - "uniforms": 11408, - "lin": 11409, - "##iana": 11410, - "##gio": 11411, - "asset": 11412, - "mhz": 11413, - "fitting": 11414, - "samantha": 11415, - "genera": 11416, - "wei": 11417, - "rim": 11418, - "beloved": 11419, - "shark": 11420, - "riot": 11421, - "entities": 11422, - "expressions": 11423, - "indo": 11424, - "carmen": 11425, - "slipping": 11426, - "owing": 11427, - "abbot": 11428, - "neighbor": 11429, - "sidney": 11430, - "##av": 11431, - "rats": 11432, - "recommendations": 11433, - "encouraging": 11434, - "squadrons": 11435, - "anticipated": 11436, - "commanders": 11437, - "conquered": 11438, - "##oto": 11439, - "donations": 11440, - "diagnosed": 11441, - "##mond": 11442, - "divide": 11443, - "##iva": 11444, - "guessed": 11445, - "decoration": 11446, - "vernon": 11447, - "auditorium": 11448, - "revelation": 11449, - "conversations": 11450, - "##kers": 11451, - "##power": 11452, - "herzegovina": 11453, - "dash": 11454, - "alike": 11455, - "protested": 11456, - "lateral": 11457, - "herman": 11458, - "accredited": 11459, - "mg": 11460, - "##gent": 11461, - "freeman": 11462, - "mel": 11463, - "fiji": 11464, - "crow": 11465, - "crimson": 11466, - "##rine": 11467, - "livestock": 11468, - "##pped": 11469, - "humanitarian": 11470, - "bored": 11471, - "oz": 11472, - "whip": 11473, - "##lene": 11474, - "##ali": 11475, - "legitimate": 11476, - "alter": 11477, - "grinning": 11478, - "spelled": 11479, - "anxious": 11480, - "oriental": 11481, - "wesley": 11482, - "##nin": 11483, - "##hole": 11484, - "carnival": 11485, - "controller": 11486, - "detect": 11487, - "##ssa": 11488, - "bowed": 11489, - "educator": 11490, - "kosovo": 11491, - "macedonia": 11492, - "##sin": 11493, - "occupy": 11494, - "mastering": 11495, - "stephanie": 11496, - "janeiro": 11497, - "para": 11498, - "unaware": 11499, - "nurses": 11500, - "noon": 11501, - "135": 11502, - "cam": 11503, - "hopefully": 11504, - "ranger": 11505, - "combine": 11506, - "sociology": 11507, - "polar": 11508, - "rica": 11509, - "##eer": 11510, - "neill": 11511, - "##sman": 11512, - "holocaust": 11513, - "##ip": 11514, - "doubled": 11515, - "lust": 11516, - "1828": 11517, - "109": 11518, - "decent": 11519, - "cooling": 11520, - "unveiled": 11521, - "##card": 11522, - "1829": 11523, - "nsw": 11524, - "homer": 11525, - "chapman": 11526, - "meyer": 11527, - "##gin": 11528, - "dive": 11529, - "mae": 11530, - "reagan": 11531, - "expertise": 11532, - "##gled": 11533, - "darwin": 11534, - "brooke": 11535, - "sided": 11536, - "prosecution": 11537, - "investigating": 11538, - "comprised": 11539, - "petroleum": 11540, - "genres": 11541, - "reluctant": 11542, - "differently": 11543, - "trilogy": 11544, - "johns": 11545, - "vegetables": 11546, - "corpse": 11547, - "highlighted": 11548, - "lounge": 11549, - "pension": 11550, - "unsuccessfully": 11551, - "elegant": 11552, - "aided": 11553, - "ivory": 11554, - "beatles": 11555, - "amelia": 11556, - "cain": 11557, - "dubai": 11558, - "sunny": 11559, - "immigrant": 11560, - "babe": 11561, - "click": 11562, - "##nder": 11563, - "underwater": 11564, - "pepper": 11565, - "combining": 11566, - "mumbled": 11567, - "atlas": 11568, - "horns": 11569, - "accessed": 11570, - "ballad": 11571, - "physicians": 11572, - "homeless": 11573, - "gestured": 11574, - "rpm": 11575, - "freak": 11576, - "louisville": 11577, - "corporations": 11578, - "patriots": 11579, - "prizes": 11580, - "rational": 11581, - "warn": 11582, - "modes": 11583, - "decorative": 11584, - "overnight": 11585, - "din": 11586, - "troubled": 11587, - "phantom": 11588, - "##ort": 11589, - "monarch": 11590, - "sheer": 11591, - "##dorf": 11592, - "generals": 11593, - "guidelines": 11594, - "organs": 11595, - "addresses": 11596, - "##zon": 11597, - "enhance": 11598, - "curling": 11599, - "parishes": 11600, - "cord": 11601, - "##kie": 11602, - "linux": 11603, - "caesar": 11604, - "deutsche": 11605, - "bavaria": 11606, - "##bia": 11607, - "coleman": 11608, - "cyclone": 11609, - "##eria": 11610, - "bacon": 11611, - "petty": 11612, - "##yama": 11613, - "##old": 11614, - "hampton": 11615, - "diagnosis": 11616, - "1824": 11617, - "throws": 11618, - "complexity": 11619, - "rita": 11620, - "disputed": 11621, - "##₃": 11622, - "pablo": 11623, - "##sch": 11624, - "marketed": 11625, - "trafficking": 11626, - "##ulus": 11627, - "examine": 11628, - "plague": 11629, - "formats": 11630, - "##oh": 11631, - "vault": 11632, - "faithful": 11633, - "##bourne": 11634, - "webster": 11635, - "##ox": 11636, - "highlights": 11637, - "##ient": 11638, - "##ann": 11639, - "phones": 11640, - "vacuum": 11641, - "sandwich": 11642, - "modeling": 11643, - "##gated": 11644, - "bolivia": 11645, - "clergy": 11646, - "qualities": 11647, - "isabel": 11648, - "##nas": 11649, - "##ars": 11650, - "wears": 11651, - "screams": 11652, - "reunited": 11653, - "annoyed": 11654, - "bra": 11655, - "##ancy": 11656, - "##rate": 11657, - "differential": 11658, - "transmitter": 11659, - "tattoo": 11660, - "container": 11661, - "poker": 11662, - "##och": 11663, - "excessive": 11664, - "resides": 11665, - "cowboys": 11666, - "##tum": 11667, - "augustus": 11668, - "trash": 11669, - "providers": 11670, - "statute": 11671, - "retreated": 11672, - "balcony": 11673, - "reversed": 11674, - "void": 11675, - "storey": 11676, - "preceded": 11677, - "masses": 11678, - "leap": 11679, - "laughs": 11680, - "neighborhoods": 11681, - "wards": 11682, - "schemes": 11683, - "falcon": 11684, - "santo": 11685, - "battlefield": 11686, - "pad": 11687, - "ronnie": 11688, - "thread": 11689, - "lesbian": 11690, - "venus": 11691, - "##dian": 11692, - "beg": 11693, - "sandstone": 11694, - "daylight": 11695, - "punched": 11696, - "gwen": 11697, - "analog": 11698, - "stroked": 11699, - "wwe": 11700, - "acceptable": 11701, - "measurements": 11702, - "dec": 11703, - "toxic": 11704, - "##kel": 11705, - "adequate": 11706, - "surgical": 11707, - "economist": 11708, - "parameters": 11709, - "varsity": 11710, - "##sberg": 11711, - "quantity": 11712, - "ella": 11713, - "##chy": 11714, - "##rton": 11715, - "countess": 11716, - "generating": 11717, - "precision": 11718, - "diamonds": 11719, - "expressway": 11720, - "ga": 11721, - "##ı": 11722, - "1821": 11723, - "uruguay": 11724, - "talents": 11725, - "galleries": 11726, - "expenses": 11727, - "scanned": 11728, - "colleague": 11729, - "outlets": 11730, - "ryder": 11731, - "lucien": 11732, - "##ila": 11733, - "paramount": 11734, - "##bon": 11735, - "syracuse": 11736, - "dim": 11737, - "fangs": 11738, - "gown": 11739, - "sweep": 11740, - "##sie": 11741, - "toyota": 11742, - "missionaries": 11743, - "websites": 11744, - "##nsis": 11745, - "sentences": 11746, - "adviser": 11747, - "val": 11748, - "trademark": 11749, - "spells": 11750, - "##plane": 11751, - "patience": 11752, - "starter": 11753, - "slim": 11754, - "##borg": 11755, - "toe": 11756, - "incredibly": 11757, - "shoots": 11758, - "elliot": 11759, - "nobility": 11760, - "##wyn": 11761, - "cowboy": 11762, - "endorsed": 11763, - "gardner": 11764, - "tendency": 11765, - "persuaded": 11766, - "organisms": 11767, - "emissions": 11768, - "kazakhstan": 11769, - "amused": 11770, - "boring": 11771, - "chips": 11772, - "themed": 11773, - "##hand": 11774, - "llc": 11775, - "constantinople": 11776, - "chasing": 11777, - "systematic": 11778, - "guatemala": 11779, - "borrowed": 11780, - "erin": 11781, - "carey": 11782, - "##hard": 11783, - "highlands": 11784, - "struggles": 11785, - "1810": 11786, - "##ifying": 11787, - "##ced": 11788, - "wong": 11789, - "exceptions": 11790, - "develops": 11791, - "enlarged": 11792, - "kindergarten": 11793, - "castro": 11794, - "##ern": 11795, - "##rina": 11796, - "leigh": 11797, - "zombie": 11798, - "juvenile": 11799, - "##most": 11800, - "consul": 11801, - "##nar": 11802, - "sailor": 11803, - "hyde": 11804, - "clarence": 11805, - "intensive": 11806, - "pinned": 11807, - "nasty": 11808, - "useless": 11809, - "jung": 11810, - "clayton": 11811, - "stuffed": 11812, - "exceptional": 11813, - "ix": 11814, - "apostolic": 11815, - "230": 11816, - "transactions": 11817, - "##dge": 11818, - "exempt": 11819, - "swinging": 11820, - "cove": 11821, - "religions": 11822, - "##ash": 11823, - "shields": 11824, - "dairy": 11825, - "bypass": 11826, - "190": 11827, - "pursuing": 11828, - "bug": 11829, - "joyce": 11830, - "bombay": 11831, - "chassis": 11832, - "southampton": 11833, - "chat": 11834, - "interact": 11835, - "redesignated": 11836, - "##pen": 11837, - "nascar": 11838, - "pray": 11839, - "salmon": 11840, - "rigid": 11841, - "regained": 11842, - "malaysian": 11843, - "grim": 11844, - "publicity": 11845, - "constituted": 11846, - "capturing": 11847, - "toilet": 11848, - "delegate": 11849, - "purely": 11850, - "tray": 11851, - "drift": 11852, - "loosely": 11853, - "striker": 11854, - "weakened": 11855, - "trinidad": 11856, - "mitch": 11857, - "itv": 11858, - "defines": 11859, - "transmitted": 11860, - "ming": 11861, - "scarlet": 11862, - "nodding": 11863, - "fitzgerald": 11864, - "fu": 11865, - "narrowly": 11866, - "sp": 11867, - "tooth": 11868, - "standings": 11869, - "virtue": 11870, - "##₁": 11871, - "##wara": 11872, - "##cting": 11873, - "chateau": 11874, - "gloves": 11875, - "lid": 11876, - "##nel": 11877, - "hurting": 11878, - "conservatory": 11879, - "##pel": 11880, - "sinclair": 11881, - "reopened": 11882, - "sympathy": 11883, - "nigerian": 11884, - "strode": 11885, - "advocated": 11886, - "optional": 11887, - "chronic": 11888, - "discharge": 11889, - "##rc": 11890, - "suck": 11891, - "compatible": 11892, - "laurel": 11893, - "stella": 11894, - "shi": 11895, - "fails": 11896, - "wage": 11897, - "dodge": 11898, - "128": 11899, - "informal": 11900, - "sorts": 11901, - "levi": 11902, - "buddha": 11903, - "villagers": 11904, - "##aka": 11905, - "chronicles": 11906, - "heavier": 11907, - "summoned": 11908, - "gateway": 11909, - "3000": 11910, - "eleventh": 11911, - "jewelry": 11912, - "translations": 11913, - "accordingly": 11914, - "seas": 11915, - "##ency": 11916, - "fiber": 11917, - "pyramid": 11918, - "cubic": 11919, - "dragging": 11920, - "##ista": 11921, - "caring": 11922, - "##ops": 11923, - "android": 11924, - "contacted": 11925, - "lunar": 11926, - "##dt": 11927, - "kai": 11928, - "lisbon": 11929, - "patted": 11930, - "1826": 11931, - "sacramento": 11932, - "theft": 11933, - "madagascar": 11934, - "subtropical": 11935, - "disputes": 11936, - "ta": 11937, - "holidays": 11938, - "piper": 11939, - "willow": 11940, - "mare": 11941, - "cane": 11942, - "itunes": 11943, - "newfoundland": 11944, - "benny": 11945, - "companions": 11946, - "dong": 11947, - "raj": 11948, - "observe": 11949, - "roar": 11950, - "charming": 11951, - "plaque": 11952, - "tibetan": 11953, - "fossils": 11954, - "enacted": 11955, - "manning": 11956, - "bubble": 11957, - "tina": 11958, - "tanzania": 11959, - "##eda": 11960, - "##hir": 11961, - "funk": 11962, - "swamp": 11963, - "deputies": 11964, - "cloak": 11965, - "ufc": 11966, - "scenario": 11967, - "par": 11968, - "scratch": 11969, - "metals": 11970, - "anthem": 11971, - "guru": 11972, - "engaging": 11973, - "specially": 11974, - "##boat": 11975, - "dialects": 11976, - "nineteen": 11977, - "cecil": 11978, - "duet": 11979, - "disability": 11980, - "messenger": 11981, - "unofficial": 11982, - "##lies": 11983, - "defunct": 11984, - "eds": 11985, - "moonlight": 11986, - "drainage": 11987, - "surname": 11988, - "puzzle": 11989, - "honda": 11990, - "switching": 11991, - "conservatives": 11992, - "mammals": 11993, - "knox": 11994, - "broadcaster": 11995, - "sidewalk": 11996, - "cope": 11997, - "##ried": 11998, - "benson": 11999, - "princes": 12000, - "peterson": 12001, - "##sal": 12002, - "bedford": 12003, - "sharks": 12004, - "eli": 12005, - "wreck": 12006, - "alberto": 12007, - "gasp": 12008, - "archaeology": 12009, - "lgbt": 12010, - "teaches": 12011, - "securities": 12012, - "madness": 12013, - "compromise": 12014, - "waving": 12015, - "coordination": 12016, - "davidson": 12017, - "visions": 12018, - "leased": 12019, - "possibilities": 12020, - "eighty": 12021, - "jun": 12022, - "fernandez": 12023, - "enthusiasm": 12024, - "assassin": 12025, - "sponsorship": 12026, - "reviewer": 12027, - "kingdoms": 12028, - "estonian": 12029, - "laboratories": 12030, - "##fy": 12031, - "##nal": 12032, - "applies": 12033, - "verb": 12034, - "celebrations": 12035, - "##zzo": 12036, - "rowing": 12037, - "lightweight": 12038, - "sadness": 12039, - "submit": 12040, - "mvp": 12041, - "balanced": 12042, - "dude": 12043, - "##vas": 12044, - "explicitly": 12045, - "metric": 12046, - "magnificent": 12047, - "mound": 12048, - "brett": 12049, - "mohammad": 12050, - "mistakes": 12051, - "irregular": 12052, - "##hing": 12053, - "##ass": 12054, - "sanders": 12055, - "betrayed": 12056, - "shipped": 12057, - "surge": 12058, - "##enburg": 12059, - "reporters": 12060, - "termed": 12061, - "georg": 12062, - "pity": 12063, - "verbal": 12064, - "bulls": 12065, - "abbreviated": 12066, - "enabling": 12067, - "appealed": 12068, - "##are": 12069, - "##atic": 12070, - "sicily": 12071, - "sting": 12072, - "heel": 12073, - "sweetheart": 12074, - "bart": 12075, - "spacecraft": 12076, - "brutal": 12077, - "monarchy": 12078, - "##tter": 12079, - "aberdeen": 12080, - "cameo": 12081, - "diane": 12082, - "##ub": 12083, - "survivor": 12084, - "clyde": 12085, - "##aries": 12086, - "complaint": 12087, - "##makers": 12088, - "clarinet": 12089, - "delicious": 12090, - "chilean": 12091, - "karnataka": 12092, - "coordinates": 12093, - "1818": 12094, - "panties": 12095, - "##rst": 12096, - "pretending": 12097, - "ar": 12098, - "dramatically": 12099, - "kiev": 12100, - "bella": 12101, - "tends": 12102, - "distances": 12103, - "113": 12104, - "catalog": 12105, - "launching": 12106, - "instances": 12107, - "telecommunications": 12108, - "portable": 12109, - "lindsay": 12110, - "vatican": 12111, - "##eim": 12112, - "angles": 12113, - "aliens": 12114, - "marker": 12115, - "stint": 12116, - "screens": 12117, - "bolton": 12118, - "##rne": 12119, - "judy": 12120, - "wool": 12121, - "benedict": 12122, - "plasma": 12123, - "europa": 12124, - "spark": 12125, - "imaging": 12126, - "filmmaker": 12127, - "swiftly": 12128, - "##een": 12129, - "contributor": 12130, - "##nor": 12131, - "opted": 12132, - "stamps": 12133, - "apologize": 12134, - "financing": 12135, - "butter": 12136, - "gideon": 12137, - "sophisticated": 12138, - "alignment": 12139, - "avery": 12140, - "chemicals": 12141, - "yearly": 12142, - "speculation": 12143, - "prominence": 12144, - "professionally": 12145, - "##ils": 12146, - "immortal": 12147, - "institutional": 12148, - "inception": 12149, - "wrists": 12150, - "identifying": 12151, - "tribunal": 12152, - "derives": 12153, - "gains": 12154, - "##wo": 12155, - "papal": 12156, - "preference": 12157, - "linguistic": 12158, - "vince": 12159, - "operative": 12160, - "brewery": 12161, - "##ont": 12162, - "unemployment": 12163, - "boyd": 12164, - "##ured": 12165, - "##outs": 12166, - "albeit": 12167, - "prophet": 12168, - "1813": 12169, - "bi": 12170, - "##rr": 12171, - "##face": 12172, - "##rad": 12173, - "quarterly": 12174, - "asteroid": 12175, - "cleaned": 12176, - "radius": 12177, - "temper": 12178, - "##llen": 12179, - "telugu": 12180, - "jerk": 12181, - "viscount": 12182, - "menu": 12183, - "##ote": 12184, - "glimpse": 12185, - "##aya": 12186, - "yacht": 12187, - "hawaiian": 12188, - "baden": 12189, - "##rl": 12190, - "laptop": 12191, - "readily": 12192, - "##gu": 12193, - "monetary": 12194, - "offshore": 12195, - "scots": 12196, - "watches": 12197, - "##yang": 12198, - "##arian": 12199, - "upgrade": 12200, - "needle": 12201, - "xbox": 12202, - "lea": 12203, - "encyclopedia": 12204, - "flank": 12205, - "fingertips": 12206, - "##pus": 12207, - "delight": 12208, - "teachings": 12209, - "confirm": 12210, - "roth": 12211, - "beaches": 12212, - "midway": 12213, - "winters": 12214, - "##iah": 12215, - "teasing": 12216, - "daytime": 12217, - "beverly": 12218, - "gambling": 12219, - "bonnie": 12220, - "##backs": 12221, - "regulated": 12222, - "clement": 12223, - "hermann": 12224, - "tricks": 12225, - "knot": 12226, - "##shing": 12227, - "##uring": 12228, - "##vre": 12229, - "detached": 12230, - "ecological": 12231, - "owed": 12232, - "specialty": 12233, - "byron": 12234, - "inventor": 12235, - "bats": 12236, - "stays": 12237, - "screened": 12238, - "unesco": 12239, - "midland": 12240, - "trim": 12241, - "affection": 12242, - "##ander": 12243, - "##rry": 12244, - "jess": 12245, - "thoroughly": 12246, - "feedback": 12247, - "##uma": 12248, - "chennai": 12249, - "strained": 12250, - "heartbeat": 12251, - "wrapping": 12252, - "overtime": 12253, - "pleaded": 12254, - "##sworth": 12255, - "mon": 12256, - "leisure": 12257, - "oclc": 12258, - "##tate": 12259, - "##ele": 12260, - "feathers": 12261, - "angelo": 12262, - "thirds": 12263, - "nuts": 12264, - "surveys": 12265, - "clever": 12266, - "gill": 12267, - "commentator": 12268, - "##dos": 12269, - "darren": 12270, - "rides": 12271, - "gibraltar": 12272, - "##nc": 12273, - "##mu": 12274, - "dissolution": 12275, - "dedication": 12276, - "shin": 12277, - "meals": 12278, - "saddle": 12279, - "elvis": 12280, - "reds": 12281, - "chaired": 12282, - "taller": 12283, - "appreciation": 12284, - "functioning": 12285, - "niece": 12286, - "favored": 12287, - "advocacy": 12288, - "robbie": 12289, - "criminals": 12290, - "suffolk": 12291, - "yugoslav": 12292, - "passport": 12293, - "constable": 12294, - "congressman": 12295, - "hastings": 12296, - "vera": 12297, - "##rov": 12298, - "consecrated": 12299, - "sparks": 12300, - "ecclesiastical": 12301, - "confined": 12302, - "##ovich": 12303, - "muller": 12304, - "floyd": 12305, - "nora": 12306, - "1822": 12307, - "paved": 12308, - "1827": 12309, - "cumberland": 12310, - "ned": 12311, - "saga": 12312, - "spiral": 12313, - "##flow": 12314, - "appreciated": 12315, - "yi": 12316, - "collaborative": 12317, - "treating": 12318, - "similarities": 12319, - "feminine": 12320, - "finishes": 12321, - "##ib": 12322, - "jade": 12323, - "import": 12324, - "##nse": 12325, - "##hot": 12326, - "champagne": 12327, - "mice": 12328, - "securing": 12329, - "celebrities": 12330, - "helsinki": 12331, - "attributes": 12332, - "##gos": 12333, - "cousins": 12334, - "phases": 12335, - "ache": 12336, - "lucia": 12337, - "gandhi": 12338, - "submission": 12339, - "vicar": 12340, - "spear": 12341, - "shine": 12342, - "tasmania": 12343, - "biting": 12344, - "detention": 12345, - "constitute": 12346, - "tighter": 12347, - "seasonal": 12348, - "##gus": 12349, - "terrestrial": 12350, - "matthews": 12351, - "##oka": 12352, - "effectiveness": 12353, - "parody": 12354, - "philharmonic": 12355, - "##onic": 12356, - "1816": 12357, - "strangers": 12358, - "encoded": 12359, - "consortium": 12360, - "guaranteed": 12361, - "regards": 12362, - "shifts": 12363, - "tortured": 12364, - "collision": 12365, - "supervisor": 12366, - "inform": 12367, - "broader": 12368, - "insight": 12369, - "theaters": 12370, - "armour": 12371, - "emeritus": 12372, - "blink": 12373, - "incorporates": 12374, - "mapping": 12375, - "##50": 12376, - "##ein": 12377, - "handball": 12378, - "flexible": 12379, - "##nta": 12380, - "substantially": 12381, - "generous": 12382, - "thief": 12383, - "##own": 12384, - "carr": 12385, - "loses": 12386, - "1793": 12387, - "prose": 12388, - "ucla": 12389, - "romeo": 12390, - "generic": 12391, - "metallic": 12392, - "realization": 12393, - "damages": 12394, - "mk": 12395, - "commissioners": 12396, - "zach": 12397, - "default": 12398, - "##ther": 12399, - "helicopters": 12400, - "lengthy": 12401, - "stems": 12402, - "spa": 12403, - "partnered": 12404, - "spectators": 12405, - "rogue": 12406, - "indication": 12407, - "penalties": 12408, - "teresa": 12409, - "1801": 12410, - "sen": 12411, - "##tric": 12412, - "dalton": 12413, - "##wich": 12414, - "irving": 12415, - "photographic": 12416, - "##vey": 12417, - "dell": 12418, - "deaf": 12419, - "peters": 12420, - "excluded": 12421, - "unsure": 12422, - "##vable": 12423, - "patterson": 12424, - "crawled": 12425, - "##zio": 12426, - "resided": 12427, - "whipped": 12428, - "latvia": 12429, - "slower": 12430, - "ecole": 12431, - "pipes": 12432, - "employers": 12433, - "maharashtra": 12434, - "comparable": 12435, - "va": 12436, - "textile": 12437, - "pageant": 12438, - "##gel": 12439, - "alphabet": 12440, - "binary": 12441, - "irrigation": 12442, - "chartered": 12443, - "choked": 12444, - "antoine": 12445, - "offs": 12446, - "waking": 12447, - "supplement": 12448, - "##wen": 12449, - "quantities": 12450, - "demolition": 12451, - "regain": 12452, - "locate": 12453, - "urdu": 12454, - "folks": 12455, - "alt": 12456, - "114": 12457, - "##mc": 12458, - "scary": 12459, - "andreas": 12460, - "whites": 12461, - "##ava": 12462, - "classrooms": 12463, - "mw": 12464, - "aesthetic": 12465, - "publishes": 12466, - "valleys": 12467, - "guides": 12468, - "cubs": 12469, - "johannes": 12470, - "bryant": 12471, - "conventions": 12472, - "affecting": 12473, - "##itt": 12474, - "drain": 12475, - "awesome": 12476, - "isolation": 12477, - "prosecutor": 12478, - "ambitious": 12479, - "apology": 12480, - "captive": 12481, - "downs": 12482, - "atmospheric": 12483, - "lorenzo": 12484, - "aisle": 12485, - "beef": 12486, - "foul": 12487, - "##onia": 12488, - "kidding": 12489, - "composite": 12490, - "disturbed": 12491, - "illusion": 12492, - "natives": 12493, - "##ffer": 12494, - "emi": 12495, - "rockets": 12496, - "riverside": 12497, - "wartime": 12498, - "painters": 12499, - "adolf": 12500, - "melted": 12501, - "##ail": 12502, - "uncertainty": 12503, - "simulation": 12504, - "hawks": 12505, - "progressed": 12506, - "meantime": 12507, - "builder": 12508, - "spray": 12509, - "breach": 12510, - "unhappy": 12511, - "regina": 12512, - "russians": 12513, - "##urg": 12514, - "determining": 12515, - "##tation": 12516, - "tram": 12517, - "1806": 12518, - "##quin": 12519, - "aging": 12520, - "##12": 12521, - "1823": 12522, - "garion": 12523, - "rented": 12524, - "mister": 12525, - "diaz": 12526, - "terminated": 12527, - "clip": 12528, - "1817": 12529, - "depend": 12530, - "nervously": 12531, - "disco": 12532, - "owe": 12533, - "defenders": 12534, - "shiva": 12535, - "notorious": 12536, - "disbelief": 12537, - "shiny": 12538, - "worcester": 12539, - "##gation": 12540, - "##yr": 12541, - "trailing": 12542, - "undertook": 12543, - "islander": 12544, - "belarus": 12545, - "limitations": 12546, - "watershed": 12547, - "fuller": 12548, - "overlooking": 12549, - "utilized": 12550, - "raphael": 12551, - "1819": 12552, - "synthetic": 12553, - "breakdown": 12554, - "klein": 12555, - "##nate": 12556, - "moaned": 12557, - "memoir": 12558, - "lamb": 12559, - "practicing": 12560, - "##erly": 12561, - "cellular": 12562, - "arrows": 12563, - "exotic": 12564, - "##graphy": 12565, - "witches": 12566, - "117": 12567, - "charted": 12568, - "rey": 12569, - "hut": 12570, - "hierarchy": 12571, - "subdivision": 12572, - "freshwater": 12573, - "giuseppe": 12574, - "aloud": 12575, - "reyes": 12576, - "qatar": 12577, - "marty": 12578, - "sideways": 12579, - "utterly": 12580, - "sexually": 12581, - "jude": 12582, - "prayers": 12583, - "mccarthy": 12584, - "softball": 12585, - "blend": 12586, - "damien": 12587, - "##gging": 12588, - "##metric": 12589, - "wholly": 12590, - "erupted": 12591, - "lebanese": 12592, - "negro": 12593, - "revenues": 12594, - "tasted": 12595, - "comparative": 12596, - "teamed": 12597, - "transaction": 12598, - "labeled": 12599, - "maori": 12600, - "sovereignty": 12601, - "parkway": 12602, - "trauma": 12603, - "gran": 12604, - "malay": 12605, - "121": 12606, - "advancement": 12607, - "descendant": 12608, - "2020": 12609, - "buzz": 12610, - "salvation": 12611, - "inventory": 12612, - "symbolic": 12613, - "##making": 12614, - "antarctica": 12615, - "mps": 12616, - "##gas": 12617, - "##bro": 12618, - "mohammed": 12619, - "myanmar": 12620, - "holt": 12621, - "submarines": 12622, - "tones": 12623, - "##lman": 12624, - "locker": 12625, - "patriarch": 12626, - "bangkok": 12627, - "emerson": 12628, - "remarks": 12629, - "predators": 12630, - "kin": 12631, - "afghan": 12632, - "confession": 12633, - "norwich": 12634, - "rental": 12635, - "emerge": 12636, - "advantages": 12637, - "##zel": 12638, - "rca": 12639, - "##hold": 12640, - "shortened": 12641, - "storms": 12642, - "aidan": 12643, - "##matic": 12644, - "autonomy": 12645, - "compliance": 12646, - "##quet": 12647, - "dudley": 12648, - "atp": 12649, - "##osis": 12650, - "1803": 12651, - "motto": 12652, - "documentation": 12653, - "summary": 12654, - "professors": 12655, - "spectacular": 12656, - "christina": 12657, - "archdiocese": 12658, - "flashing": 12659, - "innocence": 12660, - "remake": 12661, - "##dell": 12662, - "psychic": 12663, - "reef": 12664, - "scare": 12665, - "employ": 12666, - "rs": 12667, - "sticks": 12668, - "meg": 12669, - "gus": 12670, - "leans": 12671, - "##ude": 12672, - "accompany": 12673, - "bergen": 12674, - "tomas": 12675, - "##iko": 12676, - "doom": 12677, - "wages": 12678, - "pools": 12679, - "##nch": 12680, - "##bes": 12681, - "breasts": 12682, - "scholarly": 12683, - "alison": 12684, - "outline": 12685, - "brittany": 12686, - "breakthrough": 12687, - "willis": 12688, - "realistic": 12689, - "##cut": 12690, - "##boro": 12691, - "competitor": 12692, - "##stan": 12693, - "pike": 12694, - "picnic": 12695, - "icon": 12696, - "designing": 12697, - "commercials": 12698, - "washing": 12699, - "villain": 12700, - "skiing": 12701, - "micro": 12702, - "costumes": 12703, - "auburn": 12704, - "halted": 12705, - "executives": 12706, - "##hat": 12707, - "logistics": 12708, - "cycles": 12709, - "vowel": 12710, - "applicable": 12711, - "barrett": 12712, - "exclaimed": 12713, - "eurovision": 12714, - "eternity": 12715, - "ramon": 12716, - "##umi": 12717, - "##lls": 12718, - "modifications": 12719, - "sweeping": 12720, - "disgust": 12721, - "##uck": 12722, - "torch": 12723, - "aviv": 12724, - "ensuring": 12725, - "rude": 12726, - "dusty": 12727, - "sonic": 12728, - "donovan": 12729, - "outskirts": 12730, - "cu": 12731, - "pathway": 12732, - "##band": 12733, - "##gun": 12734, - "##lines": 12735, - "disciplines": 12736, - "acids": 12737, - "cadet": 12738, - "paired": 12739, - "##40": 12740, - "sketches": 12741, - "##sive": 12742, - "marriages": 12743, - "##⁺": 12744, - "folding": 12745, - "peers": 12746, - "slovak": 12747, - "implies": 12748, - "admired": 12749, - "##beck": 12750, - "1880s": 12751, - "leopold": 12752, - "instinct": 12753, - "attained": 12754, - "weston": 12755, - "megan": 12756, - "horace": 12757, - "##ination": 12758, - "dorsal": 12759, - "ingredients": 12760, - "evolutionary": 12761, - "##its": 12762, - "complications": 12763, - "deity": 12764, - "lethal": 12765, - "brushing": 12766, - "levy": 12767, - "deserted": 12768, - "institutes": 12769, - "posthumously": 12770, - "delivering": 12771, - "telescope": 12772, - "coronation": 12773, - "motivated": 12774, - "rapids": 12775, - "luc": 12776, - "flicked": 12777, - "pays": 12778, - "volcano": 12779, - "tanner": 12780, - "weighed": 12781, - "##nica": 12782, - "crowds": 12783, - "frankie": 12784, - "gifted": 12785, - "addressing": 12786, - "granddaughter": 12787, - "winding": 12788, - "##rna": 12789, - "constantine": 12790, - "gomez": 12791, - "##front": 12792, - "landscapes": 12793, - "rudolf": 12794, - "anthropology": 12795, - "slate": 12796, - "werewolf": 12797, - "##lio": 12798, - "astronomy": 12799, - "circa": 12800, - "rouge": 12801, - "dreaming": 12802, - "sack": 12803, - "knelt": 12804, - "drowned": 12805, - "naomi": 12806, - "prolific": 12807, - "tracked": 12808, - "freezing": 12809, - "herb": 12810, - "##dium": 12811, - "agony": 12812, - "randall": 12813, - "twisting": 12814, - "wendy": 12815, - "deposit": 12816, - "touches": 12817, - "vein": 12818, - "wheeler": 12819, - "##bbled": 12820, - "##bor": 12821, - "batted": 12822, - "retaining": 12823, - "tire": 12824, - "presently": 12825, - "compare": 12826, - "specification": 12827, - "daemon": 12828, - "nigel": 12829, - "##grave": 12830, - "merry": 12831, - "recommendation": 12832, - "czechoslovakia": 12833, - "sandra": 12834, - "ng": 12835, - "roma": 12836, - "##sts": 12837, - "lambert": 12838, - "inheritance": 12839, - "sheikh": 12840, - "winchester": 12841, - "cries": 12842, - "examining": 12843, - "##yle": 12844, - "comeback": 12845, - "cuisine": 12846, - "nave": 12847, - "##iv": 12848, - "ko": 12849, - "retrieve": 12850, - "tomatoes": 12851, - "barker": 12852, - "polished": 12853, - "defining": 12854, - "irene": 12855, - "lantern": 12856, - "personalities": 12857, - "begging": 12858, - "tract": 12859, - "swore": 12860, - "1809": 12861, - "175": 12862, - "##gic": 12863, - "omaha": 12864, - "brotherhood": 12865, - "##rley": 12866, - "haiti": 12867, - "##ots": 12868, - "exeter": 12869, - "##ete": 12870, - "##zia": 12871, - "steele": 12872, - "dumb": 12873, - "pearson": 12874, - "210": 12875, - "surveyed": 12876, - "elisabeth": 12877, - "trends": 12878, - "##ef": 12879, - "fritz": 12880, - "##rf": 12881, - "premium": 12882, - "bugs": 12883, - "fraction": 12884, - "calmly": 12885, - "viking": 12886, - "##birds": 12887, - "tug": 12888, - "inserted": 12889, - "unusually": 12890, - "##ield": 12891, - "confronted": 12892, - "distress": 12893, - "crashing": 12894, - "brent": 12895, - "turks": 12896, - "resign": 12897, - "##olo": 12898, - "cambodia": 12899, - "gabe": 12900, - "sauce": 12901, - "##kal": 12902, - "evelyn": 12903, - "116": 12904, - "extant": 12905, - "clusters": 12906, - "quarry": 12907, - "teenagers": 12908, - "luna": 12909, - "##lers": 12910, - "##ister": 12911, - "affiliation": 12912, - "drill": 12913, - "##ashi": 12914, - "panthers": 12915, - "scenic": 12916, - "libya": 12917, - "anita": 12918, - "strengthen": 12919, - "inscriptions": 12920, - "##cated": 12921, - "lace": 12922, - "sued": 12923, - "judith": 12924, - "riots": 12925, - "##uted": 12926, - "mint": 12927, - "##eta": 12928, - "preparations": 12929, - "midst": 12930, - "dub": 12931, - "challenger": 12932, - "##vich": 12933, - "mock": 12934, - "cf": 12935, - "displaced": 12936, - "wicket": 12937, - "breaths": 12938, - "enables": 12939, - "schmidt": 12940, - "analyst": 12941, - "##lum": 12942, - "ag": 12943, - "highlight": 12944, - "automotive": 12945, - "axe": 12946, - "josef": 12947, - "newark": 12948, - "sufficiently": 12949, - "resembles": 12950, - "50th": 12951, - "##pal": 12952, - "flushed": 12953, - "mum": 12954, - "traits": 12955, - "##ante": 12956, - "commodore": 12957, - "incomplete": 12958, - "warming": 12959, - "titular": 12960, - "ceremonial": 12961, - "ethical": 12962, - "118": 12963, - "celebrating": 12964, - "eighteenth": 12965, - "cao": 12966, - "lima": 12967, - "medalist": 12968, - "mobility": 12969, - "strips": 12970, - "snakes": 12971, - "##city": 12972, - "miniature": 12973, - "zagreb": 12974, - "barton": 12975, - "escapes": 12976, - "umbrella": 12977, - "automated": 12978, - "doubted": 12979, - "differs": 12980, - "cooled": 12981, - "georgetown": 12982, - "dresden": 12983, - "cooked": 12984, - "fade": 12985, - "wyatt": 12986, - "rna": 12987, - "jacobs": 12988, - "carlton": 12989, - "abundant": 12990, - "stereo": 12991, - "boost": 12992, - "madras": 12993, - "inning": 12994, - "##hia": 12995, - "spur": 12996, - "ip": 12997, - "malayalam": 12998, - "begged": 12999, - "osaka": 13000, - "groan": 13001, - "escaping": 13002, - "charging": 13003, - "dose": 13004, - "vista": 13005, - "##aj": 13006, - "bud": 13007, - "papa": 13008, - "communists": 13009, - "advocates": 13010, - "edged": 13011, - "tri": 13012, - "##cent": 13013, - "resemble": 13014, - "peaking": 13015, - "necklace": 13016, - "fried": 13017, - "montenegro": 13018, - "saxony": 13019, - "goose": 13020, - "glances": 13021, - "stuttgart": 13022, - "curator": 13023, - "recruit": 13024, - "grocery": 13025, - "sympathetic": 13026, - "##tting": 13027, - "##fort": 13028, - "127": 13029, - "lotus": 13030, - "randolph": 13031, - "ancestor": 13032, - "##rand": 13033, - "succeeding": 13034, - "jupiter": 13035, - "1798": 13036, - "macedonian": 13037, - "##heads": 13038, - "hiking": 13039, - "1808": 13040, - "handing": 13041, - "fischer": 13042, - "##itive": 13043, - "garbage": 13044, - "node": 13045, - "##pies": 13046, - "prone": 13047, - "singular": 13048, - "papua": 13049, - "inclined": 13050, - "attractions": 13051, - "italia": 13052, - "pouring": 13053, - "motioned": 13054, - "grandma": 13055, - "garnered": 13056, - "jacksonville": 13057, - "corp": 13058, - "ego": 13059, - "ringing": 13060, - "aluminum": 13061, - "##hausen": 13062, - "ordering": 13063, - "##foot": 13064, - "drawer": 13065, - "traders": 13066, - "synagogue": 13067, - "##play": 13068, - "##kawa": 13069, - "resistant": 13070, - "wandering": 13071, - "fragile": 13072, - "fiona": 13073, - "teased": 13074, - "var": 13075, - "hardcore": 13076, - "soaked": 13077, - "jubilee": 13078, - "decisive": 13079, - "exposition": 13080, - "mercer": 13081, - "poster": 13082, - "valencia": 13083, - "hale": 13084, - "kuwait": 13085, - "1811": 13086, - "##ises": 13087, - "##wr": 13088, - "##eed": 13089, - "tavern": 13090, - "gamma": 13091, - "122": 13092, - "johan": 13093, - "##uer": 13094, - "airways": 13095, - "amino": 13096, - "gil": 13097, - "##ury": 13098, - "vocational": 13099, - "domains": 13100, - "torres": 13101, - "##sp": 13102, - "generator": 13103, - "folklore": 13104, - "outcomes": 13105, - "##keeper": 13106, - "canberra": 13107, - "shooter": 13108, - "fl": 13109, - "beams": 13110, - "confrontation": 13111, - "##lling": 13112, - "##gram": 13113, - "feb": 13114, - "aligned": 13115, - "forestry": 13116, - "pipeline": 13117, - "jax": 13118, - "motorway": 13119, - "conception": 13120, - "decay": 13121, - "##tos": 13122, - "coffin": 13123, - "##cott": 13124, - "stalin": 13125, - "1805": 13126, - "escorted": 13127, - "minded": 13128, - "##nam": 13129, - "sitcom": 13130, - "purchasing": 13131, - "twilight": 13132, - "veronica": 13133, - "additions": 13134, - "passive": 13135, - "tensions": 13136, - "straw": 13137, - "123": 13138, - "frequencies": 13139, - "1804": 13140, - "refugee": 13141, - "cultivation": 13142, - "##iate": 13143, - "christie": 13144, - "clary": 13145, - "bulletin": 13146, - "crept": 13147, - "disposal": 13148, - "##rich": 13149, - "##zong": 13150, - "processor": 13151, - "crescent": 13152, - "##rol": 13153, - "bmw": 13154, - "emphasized": 13155, - "whale": 13156, - "nazis": 13157, - "aurora": 13158, - "##eng": 13159, - "dwelling": 13160, - "hauled": 13161, - "sponsors": 13162, - "toledo": 13163, - "mega": 13164, - "ideology": 13165, - "theatres": 13166, - "tessa": 13167, - "cerambycidae": 13168, - "saves": 13169, - "turtle": 13170, - "cone": 13171, - "suspects": 13172, - "kara": 13173, - "rusty": 13174, - "yelling": 13175, - "greeks": 13176, - "mozart": 13177, - "shades": 13178, - "cocked": 13179, - "participant": 13180, - "##tro": 13181, - "shire": 13182, - "spit": 13183, - "freeze": 13184, - "necessity": 13185, - "##cos": 13186, - "inmates": 13187, - "nielsen": 13188, - "councillors": 13189, - "loaned": 13190, - "uncommon": 13191, - "omar": 13192, - "peasants": 13193, - "botanical": 13194, - "offspring": 13195, - "daniels": 13196, - "formations": 13197, - "jokes": 13198, - "1794": 13199, - "pioneers": 13200, - "sigma": 13201, - "licensing": 13202, - "##sus": 13203, - "wheelchair": 13204, - "polite": 13205, - "1807": 13206, - "liquor": 13207, - "pratt": 13208, - "trustee": 13209, - "##uta": 13210, - "forewings": 13211, - "balloon": 13212, - "##zz": 13213, - "kilometre": 13214, - "camping": 13215, - "explicit": 13216, - "casually": 13217, - "shawn": 13218, - "foolish": 13219, - "teammates": 13220, - "nm": 13221, - "hassan": 13222, - "carrie": 13223, - "judged": 13224, - "satisfy": 13225, - "vanessa": 13226, - "knives": 13227, - "selective": 13228, - "cnn": 13229, - "flowed": 13230, - "##lice": 13231, - "eclipse": 13232, - "stressed": 13233, - "eliza": 13234, - "mathematician": 13235, - "cease": 13236, - "cultivated": 13237, - "##roy": 13238, - "commissions": 13239, - "browns": 13240, - "##ania": 13241, - "destroyers": 13242, - "sheridan": 13243, - "meadow": 13244, - "##rius": 13245, - "minerals": 13246, - "##cial": 13247, - "downstream": 13248, - "clash": 13249, - "gram": 13250, - "memoirs": 13251, - "ventures": 13252, - "baha": 13253, - "seymour": 13254, - "archie": 13255, - "midlands": 13256, - "edith": 13257, - "fare": 13258, - "flynn": 13259, - "invite": 13260, - "canceled": 13261, - "tiles": 13262, - "stabbed": 13263, - "boulder": 13264, - "incorporate": 13265, - "amended": 13266, - "camden": 13267, - "facial": 13268, - "mollusk": 13269, - "unreleased": 13270, - "descriptions": 13271, - "yoga": 13272, - "grabs": 13273, - "550": 13274, - "raises": 13275, - "ramp": 13276, - "shiver": 13277, - "##rose": 13278, - "coined": 13279, - "pioneering": 13280, - "tunes": 13281, - "qing": 13282, - "warwick": 13283, - "tops": 13284, - "119": 13285, - "melanie": 13286, - "giles": 13287, - "##rous": 13288, - "wandered": 13289, - "##inal": 13290, - "annexed": 13291, - "nov": 13292, - "30th": 13293, - "unnamed": 13294, - "##ished": 13295, - "organizational": 13296, - "airplane": 13297, - "normandy": 13298, - "stoke": 13299, - "whistle": 13300, - "blessing": 13301, - "violations": 13302, - "chased": 13303, - "holders": 13304, - "shotgun": 13305, - "##ctic": 13306, - "outlet": 13307, - "reactor": 13308, - "##vik": 13309, - "tires": 13310, - "tearing": 13311, - "shores": 13312, - "fortified": 13313, - "mascot": 13314, - "constituencies": 13315, - "nc": 13316, - "columnist": 13317, - "productive": 13318, - "tibet": 13319, - "##rta": 13320, - "lineage": 13321, - "hooked": 13322, - "oct": 13323, - "tapes": 13324, - "judging": 13325, - "cody": 13326, - "##gger": 13327, - "hansen": 13328, - "kashmir": 13329, - "triggered": 13330, - "##eva": 13331, - "solved": 13332, - "cliffs": 13333, - "##tree": 13334, - "resisted": 13335, - "anatomy": 13336, - "protesters": 13337, - "transparent": 13338, - "implied": 13339, - "##iga": 13340, - "injection": 13341, - "mattress": 13342, - "excluding": 13343, - "##mbo": 13344, - "defenses": 13345, - "helpless": 13346, - "devotion": 13347, - "##elli": 13348, - "growl": 13349, - "liberals": 13350, - "weber": 13351, - "phenomena": 13352, - "atoms": 13353, - "plug": 13354, - "##iff": 13355, - "mortality": 13356, - "apprentice": 13357, - "howe": 13358, - "convincing": 13359, - "aaa": 13360, - "swimmer": 13361, - "barber": 13362, - "leone": 13363, - "promptly": 13364, - "sodium": 13365, - "def": 13366, - "nowadays": 13367, - "arise": 13368, - "##oning": 13369, - "gloucester": 13370, - "corrected": 13371, - "dignity": 13372, - "norm": 13373, - "erie": 13374, - "##ders": 13375, - "elders": 13376, - "evacuated": 13377, - "sylvia": 13378, - "compression": 13379, - "##yar": 13380, - "hartford": 13381, - "pose": 13382, - "backpack": 13383, - "reasoning": 13384, - "accepts": 13385, - "24th": 13386, - "wipe": 13387, - "millimetres": 13388, - "marcel": 13389, - "##oda": 13390, - "dodgers": 13391, - "albion": 13392, - "1790": 13393, - "overwhelmed": 13394, - "aerospace": 13395, - "oaks": 13396, - "1795": 13397, - "showcase": 13398, - "acknowledge": 13399, - "recovering": 13400, - "nolan": 13401, - "ashe": 13402, - "hurts": 13403, - "geology": 13404, - "fashioned": 13405, - "disappearance": 13406, - "farewell": 13407, - "swollen": 13408, - "shrug": 13409, - "marquis": 13410, - "wimbledon": 13411, - "124": 13412, - "rue": 13413, - "1792": 13414, - "commemorate": 13415, - "reduces": 13416, - "experiencing": 13417, - "inevitable": 13418, - "calcutta": 13419, - "intel": 13420, - "##court": 13421, - "murderer": 13422, - "sticking": 13423, - "fisheries": 13424, - "imagery": 13425, - "bloom": 13426, - "280": 13427, - "brake": 13428, - "##inus": 13429, - "gustav": 13430, - "hesitation": 13431, - "memorable": 13432, - "po": 13433, - "viral": 13434, - "beans": 13435, - "accidents": 13436, - "tunisia": 13437, - "antenna": 13438, - "spilled": 13439, - "consort": 13440, - "treatments": 13441, - "aye": 13442, - "perimeter": 13443, - "##gard": 13444, - "donation": 13445, - "hostage": 13446, - "migrated": 13447, - "banker": 13448, - "addiction": 13449, - "apex": 13450, - "lil": 13451, - "trout": 13452, - "##ously": 13453, - "conscience": 13454, - "##nova": 13455, - "rams": 13456, - "sands": 13457, - "genome": 13458, - "passionate": 13459, - "troubles": 13460, - "##lets": 13461, - "##set": 13462, - "amid": 13463, - "##ibility": 13464, - "##ret": 13465, - "higgins": 13466, - "exceed": 13467, - "vikings": 13468, - "##vie": 13469, - "payne": 13470, - "##zan": 13471, - "muscular": 13472, - "##ste": 13473, - "defendant": 13474, - "sucking": 13475, - "##wal": 13476, - "ibrahim": 13477, - "fuselage": 13478, - "claudia": 13479, - "vfl": 13480, - "europeans": 13481, - "snails": 13482, - "interval": 13483, - "##garh": 13484, - "preparatory": 13485, - "statewide": 13486, - "tasked": 13487, - "lacrosse": 13488, - "viktor": 13489, - "##lation": 13490, - "angola": 13491, - "##hra": 13492, - "flint": 13493, - "implications": 13494, - "employs": 13495, - "teens": 13496, - "patrons": 13497, - "stall": 13498, - "weekends": 13499, - "barriers": 13500, - "scrambled": 13501, - "nucleus": 13502, - "tehran": 13503, - "jenna": 13504, - "parsons": 13505, - "lifelong": 13506, - "robots": 13507, - "displacement": 13508, - "5000": 13509, - "##bles": 13510, - "precipitation": 13511, - "##gt": 13512, - "knuckles": 13513, - "clutched": 13514, - "1802": 13515, - "marrying": 13516, - "ecology": 13517, - "marx": 13518, - "accusations": 13519, - "declare": 13520, - "scars": 13521, - "kolkata": 13522, - "mat": 13523, - "meadows": 13524, - "bermuda": 13525, - "skeleton": 13526, - "finalists": 13527, - "vintage": 13528, - "crawl": 13529, - "coordinate": 13530, - "affects": 13531, - "subjected": 13532, - "orchestral": 13533, - "mistaken": 13534, - "##tc": 13535, - "mirrors": 13536, - "dipped": 13537, - "relied": 13538, - "260": 13539, - "arches": 13540, - "candle": 13541, - "##nick": 13542, - "incorporating": 13543, - "wildly": 13544, - "fond": 13545, - "basilica": 13546, - "owl": 13547, - "fringe": 13548, - "rituals": 13549, - "whispering": 13550, - "stirred": 13551, - "feud": 13552, - "tertiary": 13553, - "slick": 13554, - "goat": 13555, - "honorable": 13556, - "whereby": 13557, - "skip": 13558, - "ricardo": 13559, - "stripes": 13560, - "parachute": 13561, - "adjoining": 13562, - "submerged": 13563, - "synthesizer": 13564, - "##gren": 13565, - "intend": 13566, - "positively": 13567, - "ninety": 13568, - "phi": 13569, - "beaver": 13570, - "partition": 13571, - "fellows": 13572, - "alexis": 13573, - "prohibition": 13574, - "carlisle": 13575, - "bizarre": 13576, - "fraternity": 13577, - "##bre": 13578, - "doubts": 13579, - "icy": 13580, - "cbc": 13581, - "aquatic": 13582, - "sneak": 13583, - "sonny": 13584, - "combines": 13585, - "airports": 13586, - "crude": 13587, - "supervised": 13588, - "spatial": 13589, - "merge": 13590, - "alfonso": 13591, - "##bic": 13592, - "corrupt": 13593, - "scan": 13594, - "undergo": 13595, - "##ams": 13596, - "disabilities": 13597, - "colombian": 13598, - "comparing": 13599, - "dolphins": 13600, - "perkins": 13601, - "##lish": 13602, - "reprinted": 13603, - "unanimous": 13604, - "bounced": 13605, - "hairs": 13606, - "underworld": 13607, - "midwest": 13608, - "semester": 13609, - "bucket": 13610, - "paperback": 13611, - "miniseries": 13612, - "coventry": 13613, - "demise": 13614, - "##leigh": 13615, - "demonstrations": 13616, - "sensor": 13617, - "rotating": 13618, - "yan": 13619, - "##hler": 13620, - "arrange": 13621, - "soils": 13622, - "##idge": 13623, - "hyderabad": 13624, - "labs": 13625, - "##dr": 13626, - "brakes": 13627, - "grandchildren": 13628, - "##nde": 13629, - "negotiated": 13630, - "rover": 13631, - "ferrari": 13632, - "continuation": 13633, - "directorate": 13634, - "augusta": 13635, - "stevenson": 13636, - "counterpart": 13637, - "gore": 13638, - "##rda": 13639, - "nursery": 13640, - "rican": 13641, - "ave": 13642, - "collectively": 13643, - "broadly": 13644, - "pastoral": 13645, - "repertoire": 13646, - "asserted": 13647, - "discovering": 13648, - "nordic": 13649, - "styled": 13650, - "fiba": 13651, - "cunningham": 13652, - "harley": 13653, - "middlesex": 13654, - "survives": 13655, - "tumor": 13656, - "tempo": 13657, - "zack": 13658, - "aiming": 13659, - "lok": 13660, - "urgent": 13661, - "##rade": 13662, - "##nto": 13663, - "devils": 13664, - "##ement": 13665, - "contractor": 13666, - "turin": 13667, - "##wl": 13668, - "##ool": 13669, - "bliss": 13670, - "repaired": 13671, - "simmons": 13672, - "moan": 13673, - "astronomical": 13674, - "cr": 13675, - "negotiate": 13676, - "lyric": 13677, - "1890s": 13678, - "lara": 13679, - "bred": 13680, - "clad": 13681, - "angus": 13682, - "pbs": 13683, - "##ience": 13684, - "engineered": 13685, - "posed": 13686, - "##lk": 13687, - "hernandez": 13688, - "possessions": 13689, - "elbows": 13690, - "psychiatric": 13691, - "strokes": 13692, - "confluence": 13693, - "electorate": 13694, - "lifts": 13695, - "campuses": 13696, - "lava": 13697, - "alps": 13698, - "##ep": 13699, - "##ution": 13700, - "##date": 13701, - "physicist": 13702, - "woody": 13703, - "##page": 13704, - "##ographic": 13705, - "##itis": 13706, - "juliet": 13707, - "reformation": 13708, - "sparhawk": 13709, - "320": 13710, - "complement": 13711, - "suppressed": 13712, - "jewel": 13713, - "##½": 13714, - "floated": 13715, - "##kas": 13716, - "continuity": 13717, - "sadly": 13718, - "##ische": 13719, - "inability": 13720, - "melting": 13721, - "scanning": 13722, - "paula": 13723, - "flour": 13724, - "judaism": 13725, - "safer": 13726, - "vague": 13727, - "##lm": 13728, - "solving": 13729, - "curb": 13730, - "##stown": 13731, - "financially": 13732, - "gable": 13733, - "bees": 13734, - "expired": 13735, - "miserable": 13736, - "cassidy": 13737, - "dominion": 13738, - "1789": 13739, - "cupped": 13740, - "145": 13741, - "robbery": 13742, - "facto": 13743, - "amos": 13744, - "warden": 13745, - "resume": 13746, - "tallest": 13747, - "marvin": 13748, - "ing": 13749, - "pounded": 13750, - "usd": 13751, - "declaring": 13752, - "gasoline": 13753, - "##aux": 13754, - "darkened": 13755, - "270": 13756, - "650": 13757, - "sophomore": 13758, - "##mere": 13759, - "erection": 13760, - "gossip": 13761, - "televised": 13762, - "risen": 13763, - "dial": 13764, - "##eu": 13765, - "pillars": 13766, - "##link": 13767, - "passages": 13768, - "profound": 13769, - "##tina": 13770, - "arabian": 13771, - "ashton": 13772, - "silicon": 13773, - "nail": 13774, - "##ead": 13775, - "##lated": 13776, - "##wer": 13777, - "##hardt": 13778, - "fleming": 13779, - "firearms": 13780, - "ducked": 13781, - "circuits": 13782, - "blows": 13783, - "waterloo": 13784, - "titans": 13785, - "##lina": 13786, - "atom": 13787, - "fireplace": 13788, - "cheshire": 13789, - "financed": 13790, - "activation": 13791, - "algorithms": 13792, - "##zzi": 13793, - "constituent": 13794, - "catcher": 13795, - "cherokee": 13796, - "partnerships": 13797, - "sexuality": 13798, - "platoon": 13799, - "tragic": 13800, - "vivian": 13801, - "guarded": 13802, - "whiskey": 13803, - "meditation": 13804, - "poetic": 13805, - "##late": 13806, - "##nga": 13807, - "##ake": 13808, - "porto": 13809, - "listeners": 13810, - "dominance": 13811, - "kendra": 13812, - "mona": 13813, - "chandler": 13814, - "factions": 13815, - "22nd": 13816, - "salisbury": 13817, - "attitudes": 13818, - "derivative": 13819, - "##ido": 13820, - "##haus": 13821, - "intake": 13822, - "paced": 13823, - "javier": 13824, - "illustrator": 13825, - "barrels": 13826, - "bias": 13827, - "cockpit": 13828, - "burnett": 13829, - "dreamed": 13830, - "ensuing": 13831, - "##anda": 13832, - "receptors": 13833, - "someday": 13834, - "hawkins": 13835, - "mattered": 13836, - "##lal": 13837, - "slavic": 13838, - "1799": 13839, - "jesuit": 13840, - "cameroon": 13841, - "wasted": 13842, - "tai": 13843, - "wax": 13844, - "lowering": 13845, - "victorious": 13846, - "freaking": 13847, - "outright": 13848, - "hancock": 13849, - "librarian": 13850, - "sensing": 13851, - "bald": 13852, - "calcium": 13853, - "myers": 13854, - "tablet": 13855, - "announcing": 13856, - "barack": 13857, - "shipyard": 13858, - "pharmaceutical": 13859, - "##uan": 13860, - "greenwich": 13861, - "flush": 13862, - "medley": 13863, - "patches": 13864, - "wolfgang": 13865, - "pt": 13866, - "speeches": 13867, - "acquiring": 13868, - "exams": 13869, - "nikolai": 13870, - "##gg": 13871, - "hayden": 13872, - "kannada": 13873, - "##type": 13874, - "reilly": 13875, - "##pt": 13876, - "waitress": 13877, - "abdomen": 13878, - "devastated": 13879, - "capped": 13880, - "pseudonym": 13881, - "pharmacy": 13882, - "fulfill": 13883, - "paraguay": 13884, - "1796": 13885, - "clicked": 13886, - "##trom": 13887, - "archipelago": 13888, - "syndicated": 13889, - "##hman": 13890, - "lumber": 13891, - "orgasm": 13892, - "rejection": 13893, - "clifford": 13894, - "lorraine": 13895, - "advent": 13896, - "mafia": 13897, - "rodney": 13898, - "brock": 13899, - "##ght": 13900, - "##used": 13901, - "##elia": 13902, - "cassette": 13903, - "chamberlain": 13904, - "despair": 13905, - "mongolia": 13906, - "sensors": 13907, - "developmental": 13908, - "upstream": 13909, - "##eg": 13910, - "##alis": 13911, - "spanning": 13912, - "165": 13913, - "trombone": 13914, - "basque": 13915, - "seeded": 13916, - "interred": 13917, - "renewable": 13918, - "rhys": 13919, - "leapt": 13920, - "revision": 13921, - "molecule": 13922, - "##ages": 13923, - "chord": 13924, - "vicious": 13925, - "nord": 13926, - "shivered": 13927, - "23rd": 13928, - "arlington": 13929, - "debts": 13930, - "corpus": 13931, - "sunrise": 13932, - "bays": 13933, - "blackburn": 13934, - "centimetres": 13935, - "##uded": 13936, - "shuddered": 13937, - "gm": 13938, - "strangely": 13939, - "gripping": 13940, - "cartoons": 13941, - "isabelle": 13942, - "orbital": 13943, - "##ppa": 13944, - "seals": 13945, - "proving": 13946, - "##lton": 13947, - "refusal": 13948, - "strengthened": 13949, - "bust": 13950, - "assisting": 13951, - "baghdad": 13952, - "batsman": 13953, - "portrayal": 13954, - "mara": 13955, - "pushes": 13956, - "spears": 13957, - "og": 13958, - "##cock": 13959, - "reside": 13960, - "nathaniel": 13961, - "brennan": 13962, - "1776": 13963, - "confirmation": 13964, - "caucus": 13965, - "##worthy": 13966, - "markings": 13967, - "yemen": 13968, - "nobles": 13969, - "ku": 13970, - "lazy": 13971, - "viewer": 13972, - "catalan": 13973, - "encompasses": 13974, - "sawyer": 13975, - "##fall": 13976, - "sparked": 13977, - "substances": 13978, - "patents": 13979, - "braves": 13980, - "arranger": 13981, - "evacuation": 13982, - "sergio": 13983, - "persuade": 13984, - "dover": 13985, - "tolerance": 13986, - "penguin": 13987, - "cum": 13988, - "jockey": 13989, - "insufficient": 13990, - "townships": 13991, - "occupying": 13992, - "declining": 13993, - "plural": 13994, - "processed": 13995, - "projection": 13996, - "puppet": 13997, - "flanders": 13998, - "introduces": 13999, - "liability": 14000, - "##yon": 14001, - "gymnastics": 14002, - "antwerp": 14003, - "taipei": 14004, - "hobart": 14005, - "candles": 14006, - "jeep": 14007, - "wes": 14008, - "observers": 14009, - "126": 14010, - "chaplain": 14011, - "bundle": 14012, - "glorious": 14013, - "##hine": 14014, - "hazel": 14015, - "flung": 14016, - "sol": 14017, - "excavations": 14018, - "dumped": 14019, - "stares": 14020, - "sh": 14021, - "bangalore": 14022, - "triangular": 14023, - "icelandic": 14024, - "intervals": 14025, - "expressing": 14026, - "turbine": 14027, - "##vers": 14028, - "songwriting": 14029, - "crafts": 14030, - "##igo": 14031, - "jasmine": 14032, - "ditch": 14033, - "rite": 14034, - "##ways": 14035, - "entertaining": 14036, - "comply": 14037, - "sorrow": 14038, - "wrestlers": 14039, - "basel": 14040, - "emirates": 14041, - "marian": 14042, - "rivera": 14043, - "helpful": 14044, - "##some": 14045, - "caution": 14046, - "downward": 14047, - "networking": 14048, - "##atory": 14049, - "##tered": 14050, - "darted": 14051, - "genocide": 14052, - "emergence": 14053, - "replies": 14054, - "specializing": 14055, - "spokesman": 14056, - "convenient": 14057, - "unlocked": 14058, - "fading": 14059, - "augustine": 14060, - "concentrations": 14061, - "resemblance": 14062, - "elijah": 14063, - "investigator": 14064, - "andhra": 14065, - "##uda": 14066, - "promotes": 14067, - "bean": 14068, - "##rrell": 14069, - "fleeing": 14070, - "wan": 14071, - "simone": 14072, - "announcer": 14073, - "##ame": 14074, - "##bby": 14075, - "lydia": 14076, - "weaver": 14077, - "132": 14078, - "residency": 14079, - "modification": 14080, - "##fest": 14081, - "stretches": 14082, - "##ast": 14083, - "alternatively": 14084, - "nat": 14085, - "lowe": 14086, - "lacks": 14087, - "##ented": 14088, - "pam": 14089, - "tile": 14090, - "concealed": 14091, - "inferior": 14092, - "abdullah": 14093, - "residences": 14094, - "tissues": 14095, - "vengeance": 14096, - "##ided": 14097, - "moisture": 14098, - "peculiar": 14099, - "groove": 14100, - "zip": 14101, - "bologna": 14102, - "jennings": 14103, - "ninja": 14104, - "oversaw": 14105, - "zombies": 14106, - "pumping": 14107, - "batch": 14108, - "livingston": 14109, - "emerald": 14110, - "installations": 14111, - "1797": 14112, - "peel": 14113, - "nitrogen": 14114, - "rama": 14115, - "##fying": 14116, - "##star": 14117, - "schooling": 14118, - "strands": 14119, - "responding": 14120, - "werner": 14121, - "##ost": 14122, - "lime": 14123, - "casa": 14124, - "accurately": 14125, - "targeting": 14126, - "##rod": 14127, - "underway": 14128, - "##uru": 14129, - "hemisphere": 14130, - "lester": 14131, - "##yard": 14132, - "occupies": 14133, - "2d": 14134, - "griffith": 14135, - "angrily": 14136, - "reorganized": 14137, - "##owing": 14138, - "courtney": 14139, - "deposited": 14140, - "##dd": 14141, - "##30": 14142, - "estadio": 14143, - "##ifies": 14144, - "dunn": 14145, - "exiled": 14146, - "##ying": 14147, - "checks": 14148, - "##combe": 14149, - "##о": 14150, - "##fly": 14151, - "successes": 14152, - "unexpectedly": 14153, - "blu": 14154, - "assessed": 14155, - "##flower": 14156, - "##ه": 14157, - "observing": 14158, - "sacked": 14159, - "spiders": 14160, - "kn": 14161, - "##tail": 14162, - "mu": 14163, - "nodes": 14164, - "prosperity": 14165, - "audrey": 14166, - "divisional": 14167, - "155": 14168, - "broncos": 14169, - "tangled": 14170, - "adjust": 14171, - "feeds": 14172, - "erosion": 14173, - "paolo": 14174, - "surf": 14175, - "directory": 14176, - "snatched": 14177, - "humid": 14178, - "admiralty": 14179, - "screwed": 14180, - "gt": 14181, - "reddish": 14182, - "##nese": 14183, - "modules": 14184, - "trench": 14185, - "lamps": 14186, - "bind": 14187, - "leah": 14188, - "bucks": 14189, - "competes": 14190, - "##nz": 14191, - "##form": 14192, - "transcription": 14193, - "##uc": 14194, - "isles": 14195, - "violently": 14196, - "clutching": 14197, - "pga": 14198, - "cyclist": 14199, - "inflation": 14200, - "flats": 14201, - "ragged": 14202, - "unnecessary": 14203, - "##hian": 14204, - "stubborn": 14205, - "coordinated": 14206, - "harriet": 14207, - "baba": 14208, - "disqualified": 14209, - "330": 14210, - "insect": 14211, - "wolfe": 14212, - "##fies": 14213, - "reinforcements": 14214, - "rocked": 14215, - "duel": 14216, - "winked": 14217, - "embraced": 14218, - "bricks": 14219, - "##raj": 14220, - "hiatus": 14221, - "defeats": 14222, - "pending": 14223, - "brightly": 14224, - "jealousy": 14225, - "##xton": 14226, - "##hm": 14227, - "##uki": 14228, - "lena": 14229, - "gdp": 14230, - "colorful": 14231, - "##dley": 14232, - "stein": 14233, - "kidney": 14234, - "##shu": 14235, - "underwear": 14236, - "wanderers": 14237, - "##haw": 14238, - "##icus": 14239, - "guardians": 14240, - "m³": 14241, - "roared": 14242, - "habits": 14243, - "##wise": 14244, - "permits": 14245, - "gp": 14246, - "uranium": 14247, - "punished": 14248, - "disguise": 14249, - "bundesliga": 14250, - "elise": 14251, - "dundee": 14252, - "erotic": 14253, - "partisan": 14254, - "pi": 14255, - "collectors": 14256, - "float": 14257, - "individually": 14258, - "rendering": 14259, - "behavioral": 14260, - "bucharest": 14261, - "ser": 14262, - "hare": 14263, - "valerie": 14264, - "corporal": 14265, - "nutrition": 14266, - "proportional": 14267, - "##isa": 14268, - "immense": 14269, - "##kis": 14270, - "pavement": 14271, - "##zie": 14272, - "##eld": 14273, - "sutherland": 14274, - "crouched": 14275, - "1775": 14276, - "##lp": 14277, - "suzuki": 14278, - "trades": 14279, - "endurance": 14280, - "operas": 14281, - "crosby": 14282, - "prayed": 14283, - "priory": 14284, - "rory": 14285, - "socially": 14286, - "##urn": 14287, - "gujarat": 14288, - "##pu": 14289, - "walton": 14290, - "cube": 14291, - "pasha": 14292, - "privilege": 14293, - "lennon": 14294, - "floods": 14295, - "thorne": 14296, - "waterfall": 14297, - "nipple": 14298, - "scouting": 14299, - "approve": 14300, - "##lov": 14301, - "minorities": 14302, - "voter": 14303, - "dwight": 14304, - "extensions": 14305, - "assure": 14306, - "ballroom": 14307, - "slap": 14308, - "dripping": 14309, - "privileges": 14310, - "rejoined": 14311, - "confessed": 14312, - "demonstrating": 14313, - "patriotic": 14314, - "yell": 14315, - "investor": 14316, - "##uth": 14317, - "pagan": 14318, - "slumped": 14319, - "squares": 14320, - "##cle": 14321, - "##kins": 14322, - "confront": 14323, - "bert": 14324, - "embarrassment": 14325, - "##aid": 14326, - "aston": 14327, - "urging": 14328, - "sweater": 14329, - "starr": 14330, - "yuri": 14331, - "brains": 14332, - "williamson": 14333, - "commuter": 14334, - "mortar": 14335, - "structured": 14336, - "selfish": 14337, - "exports": 14338, - "##jon": 14339, - "cds": 14340, - "##him": 14341, - "unfinished": 14342, - "##rre": 14343, - "mortgage": 14344, - "destinations": 14345, - "##nagar": 14346, - "canoe": 14347, - "solitary": 14348, - "buchanan": 14349, - "delays": 14350, - "magistrate": 14351, - "fk": 14352, - "##pling": 14353, - "motivation": 14354, - "##lier": 14355, - "##vier": 14356, - "recruiting": 14357, - "assess": 14358, - "##mouth": 14359, - "malik": 14360, - "antique": 14361, - "1791": 14362, - "pius": 14363, - "rahman": 14364, - "reich": 14365, - "tub": 14366, - "zhou": 14367, - "smashed": 14368, - "airs": 14369, - "galway": 14370, - "xii": 14371, - "conditioning": 14372, - "honduras": 14373, - "discharged": 14374, - "dexter": 14375, - "##pf": 14376, - "lionel": 14377, - "129": 14378, - "debates": 14379, - "lemon": 14380, - "tiffany": 14381, - "volunteered": 14382, - "dom": 14383, - "dioxide": 14384, - "procession": 14385, - "devi": 14386, - "sic": 14387, - "tremendous": 14388, - "advertisements": 14389, - "colts": 14390, - "transferring": 14391, - "verdict": 14392, - "hanover": 14393, - "decommissioned": 14394, - "utter": 14395, - "relate": 14396, - "pac": 14397, - "racism": 14398, - "##top": 14399, - "beacon": 14400, - "limp": 14401, - "similarity": 14402, - "terra": 14403, - "occurrence": 14404, - "ant": 14405, - "##how": 14406, - "becky": 14407, - "capt": 14408, - "updates": 14409, - "armament": 14410, - "richie": 14411, - "pal": 14412, - "##graph": 14413, - "halloween": 14414, - "mayo": 14415, - "##ssen": 14416, - "##bone": 14417, - "cara": 14418, - "serena": 14419, - "fcc": 14420, - "dolls": 14421, - "obligations": 14422, - "##dling": 14423, - "violated": 14424, - "lafayette": 14425, - "jakarta": 14426, - "exploitation": 14427, - "##ime": 14428, - "infamous": 14429, - "iconic": 14430, - "##lah": 14431, - "##park": 14432, - "kitty": 14433, - "moody": 14434, - "reginald": 14435, - "dread": 14436, - "spill": 14437, - "crystals": 14438, - "olivier": 14439, - "modeled": 14440, - "bluff": 14441, - "equilibrium": 14442, - "separating": 14443, - "notices": 14444, - "ordnance": 14445, - "extinction": 14446, - "onset": 14447, - "cosmic": 14448, - "attachment": 14449, - "sammy": 14450, - "expose": 14451, - "privy": 14452, - "anchored": 14453, - "##bil": 14454, - "abbott": 14455, - "admits": 14456, - "bending": 14457, - "baritone": 14458, - "emmanuel": 14459, - "policeman": 14460, - "vaughan": 14461, - "winged": 14462, - "climax": 14463, - "dresses": 14464, - "denny": 14465, - "polytechnic": 14466, - "mohamed": 14467, - "burmese": 14468, - "authentic": 14469, - "nikki": 14470, - "genetics": 14471, - "grandparents": 14472, - "homestead": 14473, - "gaza": 14474, - "postponed": 14475, - "metacritic": 14476, - "una": 14477, - "##sby": 14478, - "##bat": 14479, - "unstable": 14480, - "dissertation": 14481, - "##rial": 14482, - "##cian": 14483, - "curls": 14484, - "obscure": 14485, - "uncovered": 14486, - "bronx": 14487, - "praying": 14488, - "disappearing": 14489, - "##hoe": 14490, - "prehistoric": 14491, - "coke": 14492, - "turret": 14493, - "mutations": 14494, - "nonprofit": 14495, - "pits": 14496, - "monaco": 14497, - "##ي": 14498, - "##usion": 14499, - "prominently": 14500, - "dispatched": 14501, - "podium": 14502, - "##mir": 14503, - "uci": 14504, - "##uation": 14505, - "133": 14506, - "fortifications": 14507, - "birthplace": 14508, - "kendall": 14509, - "##lby": 14510, - "##oll": 14511, - "preacher": 14512, - "rack": 14513, - "goodman": 14514, - "##rman": 14515, - "persistent": 14516, - "##ott": 14517, - "countless": 14518, - "jaime": 14519, - "recorder": 14520, - "lexington": 14521, - "persecution": 14522, - "jumps": 14523, - "renewal": 14524, - "wagons": 14525, - "##11": 14526, - "crushing": 14527, - "##holder": 14528, - "decorations": 14529, - "##lake": 14530, - "abundance": 14531, - "wrath": 14532, - "laundry": 14533, - "£1": 14534, - "garde": 14535, - "##rp": 14536, - "jeanne": 14537, - "beetles": 14538, - "peasant": 14539, - "##sl": 14540, - "splitting": 14541, - "caste": 14542, - "sergei": 14543, - "##rer": 14544, - "##ema": 14545, - "scripts": 14546, - "##ively": 14547, - "rub": 14548, - "satellites": 14549, - "##vor": 14550, - "inscribed": 14551, - "verlag": 14552, - "scrapped": 14553, - "gale": 14554, - "packages": 14555, - "chick": 14556, - "potato": 14557, - "slogan": 14558, - "kathleen": 14559, - "arabs": 14560, - "##culture": 14561, - "counterparts": 14562, - "reminiscent": 14563, - "choral": 14564, - "##tead": 14565, - "rand": 14566, - "retains": 14567, - "bushes": 14568, - "dane": 14569, - "accomplish": 14570, - "courtesy": 14571, - "closes": 14572, - "##oth": 14573, - "slaughter": 14574, - "hague": 14575, - "krakow": 14576, - "lawson": 14577, - "tailed": 14578, - "elias": 14579, - "ginger": 14580, - "##ttes": 14581, - "canopy": 14582, - "betrayal": 14583, - "rebuilding": 14584, - "turf": 14585, - "##hof": 14586, - "frowning": 14587, - "allegiance": 14588, - "brigades": 14589, - "kicks": 14590, - "rebuild": 14591, - "polls": 14592, - "alias": 14593, - "nationalism": 14594, - "td": 14595, - "rowan": 14596, - "audition": 14597, - "bowie": 14598, - "fortunately": 14599, - "recognizes": 14600, - "harp": 14601, - "dillon": 14602, - "horrified": 14603, - "##oro": 14604, - "renault": 14605, - "##tics": 14606, - "ropes": 14607, - "##α": 14608, - "presumed": 14609, - "rewarded": 14610, - "infrared": 14611, - "wiping": 14612, - "accelerated": 14613, - "illustration": 14614, - "##rid": 14615, - "presses": 14616, - "practitioners": 14617, - "badminton": 14618, - "##iard": 14619, - "detained": 14620, - "##tera": 14621, - "recognizing": 14622, - "relates": 14623, - "misery": 14624, - "##sies": 14625, - "##tly": 14626, - "reproduction": 14627, - "piercing": 14628, - "potatoes": 14629, - "thornton": 14630, - "esther": 14631, - "manners": 14632, - "hbo": 14633, - "##aan": 14634, - "ours": 14635, - "bullshit": 14636, - "ernie": 14637, - "perennial": 14638, - "sensitivity": 14639, - "illuminated": 14640, - "rupert": 14641, - "##jin": 14642, - "##iss": 14643, - "##ear": 14644, - "rfc": 14645, - "nassau": 14646, - "##dock": 14647, - "staggered": 14648, - "socialism": 14649, - "##haven": 14650, - "appointments": 14651, - "nonsense": 14652, - "prestige": 14653, - "sharma": 14654, - "haul": 14655, - "##tical": 14656, - "solidarity": 14657, - "gps": 14658, - "##ook": 14659, - "##rata": 14660, - "igor": 14661, - "pedestrian": 14662, - "##uit": 14663, - "baxter": 14664, - "tenants": 14665, - "wires": 14666, - "medication": 14667, - "unlimited": 14668, - "guiding": 14669, - "impacts": 14670, - "diabetes": 14671, - "##rama": 14672, - "sasha": 14673, - "pas": 14674, - "clive": 14675, - "extraction": 14676, - "131": 14677, - "continually": 14678, - "constraints": 14679, - "##bilities": 14680, - "sonata": 14681, - "hunted": 14682, - "sixteenth": 14683, - "chu": 14684, - "planting": 14685, - "quote": 14686, - "mayer": 14687, - "pretended": 14688, - "abs": 14689, - "spat": 14690, - "##hua": 14691, - "ceramic": 14692, - "##cci": 14693, - "curtains": 14694, - "pigs": 14695, - "pitching": 14696, - "##dad": 14697, - "latvian": 14698, - "sore": 14699, - "dayton": 14700, - "##sted": 14701, - "##qi": 14702, - "patrols": 14703, - "slice": 14704, - "playground": 14705, - "##nted": 14706, - "shone": 14707, - "stool": 14708, - "apparatus": 14709, - "inadequate": 14710, - "mates": 14711, - "treason": 14712, - "##ija": 14713, - "desires": 14714, - "##liga": 14715, - "##croft": 14716, - "somalia": 14717, - "laurent": 14718, - "mir": 14719, - "leonardo": 14720, - "oracle": 14721, - "grape": 14722, - "obliged": 14723, - "chevrolet": 14724, - "thirteenth": 14725, - "stunning": 14726, - "enthusiastic": 14727, - "##ede": 14728, - "accounted": 14729, - "concludes": 14730, - "currents": 14731, - "basil": 14732, - "##kovic": 14733, - "drought": 14734, - "##rica": 14735, - "mai": 14736, - "##aire": 14737, - "shove": 14738, - "posting": 14739, - "##shed": 14740, - "pilgrimage": 14741, - "humorous": 14742, - "packing": 14743, - "fry": 14744, - "pencil": 14745, - "wines": 14746, - "smells": 14747, - "144": 14748, - "marilyn": 14749, - "aching": 14750, - "newest": 14751, - "clung": 14752, - "bon": 14753, - "neighbours": 14754, - "sanctioned": 14755, - "##pie": 14756, - "mug": 14757, - "##stock": 14758, - "drowning": 14759, - "##mma": 14760, - "hydraulic": 14761, - "##vil": 14762, - "hiring": 14763, - "reminder": 14764, - "lilly": 14765, - "investigators": 14766, - "##ncies": 14767, - "sour": 14768, - "##eous": 14769, - "compulsory": 14770, - "packet": 14771, - "##rion": 14772, - "##graphic": 14773, - "##elle": 14774, - "cannes": 14775, - "##inate": 14776, - "depressed": 14777, - "##rit": 14778, - "heroic": 14779, - "importantly": 14780, - "theresa": 14781, - "##tled": 14782, - "conway": 14783, - "saturn": 14784, - "marginal": 14785, - "rae": 14786, - "##xia": 14787, - "corresponds": 14788, - "royce": 14789, - "pact": 14790, - "jasper": 14791, - "explosives": 14792, - "packaging": 14793, - "aluminium": 14794, - "##ttered": 14795, - "denotes": 14796, - "rhythmic": 14797, - "spans": 14798, - "assignments": 14799, - "hereditary": 14800, - "outlined": 14801, - "originating": 14802, - "sundays": 14803, - "lad": 14804, - "reissued": 14805, - "greeting": 14806, - "beatrice": 14807, - "##dic": 14808, - "pillar": 14809, - "marcos": 14810, - "plots": 14811, - "handbook": 14812, - "alcoholic": 14813, - "judiciary": 14814, - "avant": 14815, - "slides": 14816, - "extract": 14817, - "masculine": 14818, - "blur": 14819, - "##eum": 14820, - "##force": 14821, - "homage": 14822, - "trembled": 14823, - "owens": 14824, - "hymn": 14825, - "trey": 14826, - "omega": 14827, - "signaling": 14828, - "socks": 14829, - "accumulated": 14830, - "reacted": 14831, - "attic": 14832, - "theo": 14833, - "lining": 14834, - "angie": 14835, - "distraction": 14836, - "primera": 14837, - "talbot": 14838, - "##key": 14839, - "1200": 14840, - "ti": 14841, - "creativity": 14842, - "billed": 14843, - "##hey": 14844, - "deacon": 14845, - "eduardo": 14846, - "identifies": 14847, - "proposition": 14848, - "dizzy": 14849, - "gunner": 14850, - "hogan": 14851, - "##yam": 14852, - "##pping": 14853, - "##hol": 14854, - "ja": 14855, - "##chan": 14856, - "jensen": 14857, - "reconstructed": 14858, - "##berger": 14859, - "clearance": 14860, - "darius": 14861, - "##nier": 14862, - "abe": 14863, - "harlem": 14864, - "plea": 14865, - "dei": 14866, - "circled": 14867, - "emotionally": 14868, - "notation": 14869, - "fascist": 14870, - "neville": 14871, - "exceeded": 14872, - "upwards": 14873, - "viable": 14874, - "ducks": 14875, - "##fo": 14876, - "workforce": 14877, - "racer": 14878, - "limiting": 14879, - "shri": 14880, - "##lson": 14881, - "possesses": 14882, - "1600": 14883, - "kerr": 14884, - "moths": 14885, - "devastating": 14886, - "laden": 14887, - "disturbing": 14888, - "locking": 14889, - "##cture": 14890, - "gal": 14891, - "fearing": 14892, - "accreditation": 14893, - "flavor": 14894, - "aide": 14895, - "1870s": 14896, - "mountainous": 14897, - "##baum": 14898, - "melt": 14899, - "##ures": 14900, - "motel": 14901, - "texture": 14902, - "servers": 14903, - "soda": 14904, - "##mb": 14905, - "herd": 14906, - "##nium": 14907, - "erect": 14908, - "puzzled": 14909, - "hum": 14910, - "peggy": 14911, - "examinations": 14912, - "gould": 14913, - "testified": 14914, - "geoff": 14915, - "ren": 14916, - "devised": 14917, - "sacks": 14918, - "##law": 14919, - "denial": 14920, - "posters": 14921, - "grunted": 14922, - "cesar": 14923, - "tutor": 14924, - "ec": 14925, - "gerry": 14926, - "offerings": 14927, - "byrne": 14928, - "falcons": 14929, - "combinations": 14930, - "ct": 14931, - "incoming": 14932, - "pardon": 14933, - "rocking": 14934, - "26th": 14935, - "avengers": 14936, - "flared": 14937, - "mankind": 14938, - "seller": 14939, - "uttar": 14940, - "loch": 14941, - "nadia": 14942, - "stroking": 14943, - "exposing": 14944, - "##hd": 14945, - "fertile": 14946, - "ancestral": 14947, - "instituted": 14948, - "##has": 14949, - "noises": 14950, - "prophecy": 14951, - "taxation": 14952, - "eminent": 14953, - "vivid": 14954, - "pol": 14955, - "##bol": 14956, - "dart": 14957, - "indirect": 14958, - "multimedia": 14959, - "notebook": 14960, - "upside": 14961, - "displaying": 14962, - "adrenaline": 14963, - "referenced": 14964, - "geometric": 14965, - "##iving": 14966, - "progression": 14967, - "##ddy": 14968, - "blunt": 14969, - "announce": 14970, - "##far": 14971, - "implementing": 14972, - "##lav": 14973, - "aggression": 14974, - "liaison": 14975, - "cooler": 14976, - "cares": 14977, - "headache": 14978, - "plantations": 14979, - "gorge": 14980, - "dots": 14981, - "impulse": 14982, - "thickness": 14983, - "ashamed": 14984, - "averaging": 14985, - "kathy": 14986, - "obligation": 14987, - "precursor": 14988, - "137": 14989, - "fowler": 14990, - "symmetry": 14991, - "thee": 14992, - "225": 14993, - "hears": 14994, - "##rai": 14995, - "undergoing": 14996, - "ads": 14997, - "butcher": 14998, - "bowler": 14999, - "##lip": 15000, - "cigarettes": 15001, - "subscription": 15002, - "goodness": 15003, - "##ically": 15004, - "browne": 15005, - "##hos": 15006, - "##tech": 15007, - "kyoto": 15008, - "donor": 15009, - "##erty": 15010, - "damaging": 15011, - "friction": 15012, - "drifting": 15013, - "expeditions": 15014, - "hardened": 15015, - "prostitution": 15016, - "152": 15017, - "fauna": 15018, - "blankets": 15019, - "claw": 15020, - "tossing": 15021, - "snarled": 15022, - "butterflies": 15023, - "recruits": 15024, - "investigative": 15025, - "coated": 15026, - "healed": 15027, - "138": 15028, - "communal": 15029, - "hai": 15030, - "xiii": 15031, - "academics": 15032, - "boone": 15033, - "psychologist": 15034, - "restless": 15035, - "lahore": 15036, - "stephens": 15037, - "mba": 15038, - "brendan": 15039, - "foreigners": 15040, - "printer": 15041, - "##pc": 15042, - "ached": 15043, - "explode": 15044, - "27th": 15045, - "deed": 15046, - "scratched": 15047, - "dared": 15048, - "##pole": 15049, - "cardiac": 15050, - "1780": 15051, - "okinawa": 15052, - "proto": 15053, - "commando": 15054, - "compelled": 15055, - "oddly": 15056, - "electrons": 15057, - "##base": 15058, - "replica": 15059, - "thanksgiving": 15060, - "##rist": 15061, - "sheila": 15062, - "deliberate": 15063, - "stafford": 15064, - "tidal": 15065, - "representations": 15066, - "hercules": 15067, - "ou": 15068, - "##path": 15069, - "##iated": 15070, - "kidnapping": 15071, - "lenses": 15072, - "##tling": 15073, - "deficit": 15074, - "samoa": 15075, - "mouths": 15076, - "consuming": 15077, - "computational": 15078, - "maze": 15079, - "granting": 15080, - "smirk": 15081, - "razor": 15082, - "fixture": 15083, - "ideals": 15084, - "inviting": 15085, - "aiden": 15086, - "nominal": 15087, - "##vs": 15088, - "issuing": 15089, - "julio": 15090, - "pitt": 15091, - "ramsey": 15092, - "docks": 15093, - "##oss": 15094, - "exhaust": 15095, - "##owed": 15096, - "bavarian": 15097, - "draped": 15098, - "anterior": 15099, - "mating": 15100, - "ethiopian": 15101, - "explores": 15102, - "noticing": 15103, - "##nton": 15104, - "discarded": 15105, - "convenience": 15106, - "hoffman": 15107, - "endowment": 15108, - "beasts": 15109, - "cartridge": 15110, - "mormon": 15111, - "paternal": 15112, - "probe": 15113, - "sleeves": 15114, - "interfere": 15115, - "lump": 15116, - "deadline": 15117, - "##rail": 15118, - "jenks": 15119, - "bulldogs": 15120, - "scrap": 15121, - "alternating": 15122, - "justified": 15123, - "reproductive": 15124, - "nam": 15125, - "seize": 15126, - "descending": 15127, - "secretariat": 15128, - "kirby": 15129, - "coupe": 15130, - "grouped": 15131, - "smash": 15132, - "panther": 15133, - "sedan": 15134, - "tapping": 15135, - "##18": 15136, - "lola": 15137, - "cheer": 15138, - "germanic": 15139, - "unfortunate": 15140, - "##eter": 15141, - "unrelated": 15142, - "##fan": 15143, - "subordinate": 15144, - "##sdale": 15145, - "suzanne": 15146, - "advertisement": 15147, - "##ility": 15148, - "horsepower": 15149, - "##lda": 15150, - "cautiously": 15151, - "discourse": 15152, - "luigi": 15153, - "##mans": 15154, - "##fields": 15155, - "noun": 15156, - "prevalent": 15157, - "mao": 15158, - "schneider": 15159, - "everett": 15160, - "surround": 15161, - "governorate": 15162, - "kira": 15163, - "##avia": 15164, - "westward": 15165, - "##take": 15166, - "misty": 15167, - "rails": 15168, - "sustainability": 15169, - "134": 15170, - "unused": 15171, - "##rating": 15172, - "packs": 15173, - "toast": 15174, - "unwilling": 15175, - "regulate": 15176, - "thy": 15177, - "suffrage": 15178, - "nile": 15179, - "awe": 15180, - "assam": 15181, - "definitions": 15182, - "travelers": 15183, - "affordable": 15184, - "##rb": 15185, - "conferred": 15186, - "sells": 15187, - "undefeated": 15188, - "beneficial": 15189, - "torso": 15190, - "basal": 15191, - "repeating": 15192, - "remixes": 15193, - "##pass": 15194, - "bahrain": 15195, - "cables": 15196, - "fang": 15197, - "##itated": 15198, - "excavated": 15199, - "numbering": 15200, - "statutory": 15201, - "##rey": 15202, - "deluxe": 15203, - "##lian": 15204, - "forested": 15205, - "ramirez": 15206, - "derbyshire": 15207, - "zeus": 15208, - "slamming": 15209, - "transfers": 15210, - "astronomer": 15211, - "banana": 15212, - "lottery": 15213, - "berg": 15214, - "histories": 15215, - "bamboo": 15216, - "##uchi": 15217, - "resurrection": 15218, - "posterior": 15219, - "bowls": 15220, - "vaguely": 15221, - "##thi": 15222, - "thou": 15223, - "preserving": 15224, - "tensed": 15225, - "offence": 15226, - "##inas": 15227, - "meyrick": 15228, - "callum": 15229, - "ridden": 15230, - "watt": 15231, - "langdon": 15232, - "tying": 15233, - "lowland": 15234, - "snorted": 15235, - "daring": 15236, - "truman": 15237, - "##hale": 15238, - "##girl": 15239, - "aura": 15240, - "overly": 15241, - "filing": 15242, - "weighing": 15243, - "goa": 15244, - "infections": 15245, - "philanthropist": 15246, - "saunders": 15247, - "eponymous": 15248, - "##owski": 15249, - "latitude": 15250, - "perspectives": 15251, - "reviewing": 15252, - "mets": 15253, - "commandant": 15254, - "radial": 15255, - "##kha": 15256, - "flashlight": 15257, - "reliability": 15258, - "koch": 15259, - "vowels": 15260, - "amazed": 15261, - "ada": 15262, - "elaine": 15263, - "supper": 15264, - "##rth": 15265, - "##encies": 15266, - "predator": 15267, - "debated": 15268, - "soviets": 15269, - "cola": 15270, - "##boards": 15271, - "##nah": 15272, - "compartment": 15273, - "crooked": 15274, - "arbitrary": 15275, - "fourteenth": 15276, - "##ctive": 15277, - "havana": 15278, - "majors": 15279, - "steelers": 15280, - "clips": 15281, - "profitable": 15282, - "ambush": 15283, - "exited": 15284, - "packers": 15285, - "##tile": 15286, - "nude": 15287, - "cracks": 15288, - "fungi": 15289, - "##е": 15290, - "limb": 15291, - "trousers": 15292, - "josie": 15293, - "shelby": 15294, - "tens": 15295, - "frederic": 15296, - "##ος": 15297, - "definite": 15298, - "smoothly": 15299, - "constellation": 15300, - "insult": 15301, - "baton": 15302, - "discs": 15303, - "lingering": 15304, - "##nco": 15305, - "conclusions": 15306, - "lent": 15307, - "staging": 15308, - "becker": 15309, - "grandpa": 15310, - "shaky": 15311, - "##tron": 15312, - "einstein": 15313, - "obstacles": 15314, - "sk": 15315, - "adverse": 15316, - "elle": 15317, - "economically": 15318, - "##moto": 15319, - "mccartney": 15320, - "thor": 15321, - "dismissal": 15322, - "motions": 15323, - "readings": 15324, - "nostrils": 15325, - "treatise": 15326, - "##pace": 15327, - "squeezing": 15328, - "evidently": 15329, - "prolonged": 15330, - "1783": 15331, - "venezuelan": 15332, - "je": 15333, - "marguerite": 15334, - "beirut": 15335, - "takeover": 15336, - "shareholders": 15337, - "##vent": 15338, - "denise": 15339, - "digit": 15340, - "airplay": 15341, - "norse": 15342, - "##bbling": 15343, - "imaginary": 15344, - "pills": 15345, - "hubert": 15346, - "blaze": 15347, - "vacated": 15348, - "eliminating": 15349, - "##ello": 15350, - "vine": 15351, - "mansfield": 15352, - "##tty": 15353, - "retrospective": 15354, - "barrow": 15355, - "borne": 15356, - "clutch": 15357, - "bail": 15358, - "forensic": 15359, - "weaving": 15360, - "##nett": 15361, - "##witz": 15362, - "desktop": 15363, - "citadel": 15364, - "promotions": 15365, - "worrying": 15366, - "dorset": 15367, - "ieee": 15368, - "subdivided": 15369, - "##iating": 15370, - "manned": 15371, - "expeditionary": 15372, - "pickup": 15373, - "synod": 15374, - "chuckle": 15375, - "185": 15376, - "barney": 15377, - "##rz": 15378, - "##ffin": 15379, - "functionality": 15380, - "karachi": 15381, - "litigation": 15382, - "meanings": 15383, - "uc": 15384, - "lick": 15385, - "turbo": 15386, - "anders": 15387, - "##ffed": 15388, - "execute": 15389, - "curl": 15390, - "oppose": 15391, - "ankles": 15392, - "typhoon": 15393, - "##د": 15394, - "##ache": 15395, - "##asia": 15396, - "linguistics": 15397, - "compassion": 15398, - "pressures": 15399, - "grazing": 15400, - "perfection": 15401, - "##iting": 15402, - "immunity": 15403, - "monopoly": 15404, - "muddy": 15405, - "backgrounds": 15406, - "136": 15407, - "namibia": 15408, - "francesca": 15409, - "monitors": 15410, - "attracting": 15411, - "stunt": 15412, - "tuition": 15413, - "##ии": 15414, - "vegetable": 15415, - "##mates": 15416, - "##quent": 15417, - "mgm": 15418, - "jen": 15419, - "complexes": 15420, - "forts": 15421, - "##ond": 15422, - "cellar": 15423, - "bites": 15424, - "seventeenth": 15425, - "royals": 15426, - "flemish": 15427, - "failures": 15428, - "mast": 15429, - "charities": 15430, - "##cular": 15431, - "peruvian": 15432, - "capitals": 15433, - "macmillan": 15434, - "ipswich": 15435, - "outward": 15436, - "frigate": 15437, - "postgraduate": 15438, - "folds": 15439, - "employing": 15440, - "##ouse": 15441, - "concurrently": 15442, - "fiery": 15443, - "##tai": 15444, - "contingent": 15445, - "nightmares": 15446, - "monumental": 15447, - "nicaragua": 15448, - "##kowski": 15449, - "lizard": 15450, - "mal": 15451, - "fielding": 15452, - "gig": 15453, - "reject": 15454, - "##pad": 15455, - "harding": 15456, - "##ipe": 15457, - "coastline": 15458, - "##cin": 15459, - "##nos": 15460, - "beethoven": 15461, - "humphrey": 15462, - "innovations": 15463, - "##tam": 15464, - "##nge": 15465, - "norris": 15466, - "doris": 15467, - "solicitor": 15468, - "huang": 15469, - "obey": 15470, - "141": 15471, - "##lc": 15472, - "niagara": 15473, - "##tton": 15474, - "shelves": 15475, - "aug": 15476, - "bourbon": 15477, - "curry": 15478, - "nightclub": 15479, - "specifications": 15480, - "hilton": 15481, - "##ndo": 15482, - "centennial": 15483, - "dispersed": 15484, - "worm": 15485, - "neglected": 15486, - "briggs": 15487, - "sm": 15488, - "font": 15489, - "kuala": 15490, - "uneasy": 15491, - "plc": 15492, - "##nstein": 15493, - "##bound": 15494, - "##aking": 15495, - "##burgh": 15496, - "awaiting": 15497, - "pronunciation": 15498, - "##bbed": 15499, - "##quest": 15500, - "eh": 15501, - "optimal": 15502, - "zhu": 15503, - "raped": 15504, - "greens": 15505, - "presided": 15506, - "brenda": 15507, - "worries": 15508, - "##life": 15509, - "venetian": 15510, - "marxist": 15511, - "turnout": 15512, - "##lius": 15513, - "refined": 15514, - "braced": 15515, - "sins": 15516, - "grasped": 15517, - "sunderland": 15518, - "nickel": 15519, - "speculated": 15520, - "lowell": 15521, - "cyrillic": 15522, - "communism": 15523, - "fundraising": 15524, - "resembling": 15525, - "colonists": 15526, - "mutant": 15527, - "freddie": 15528, - "usc": 15529, - "##mos": 15530, - "gratitude": 15531, - "##run": 15532, - "mural": 15533, - "##lous": 15534, - "chemist": 15535, - "wi": 15536, - "reminds": 15537, - "28th": 15538, - "steals": 15539, - "tess": 15540, - "pietro": 15541, - "##ingen": 15542, - "promoter": 15543, - "ri": 15544, - "microphone": 15545, - "honoured": 15546, - "rai": 15547, - "sant": 15548, - "##qui": 15549, - "feather": 15550, - "##nson": 15551, - "burlington": 15552, - "kurdish": 15553, - "terrorists": 15554, - "deborah": 15555, - "sickness": 15556, - "##wed": 15557, - "##eet": 15558, - "hazard": 15559, - "irritated": 15560, - "desperation": 15561, - "veil": 15562, - "clarity": 15563, - "##rik": 15564, - "jewels": 15565, - "xv": 15566, - "##gged": 15567, - "##ows": 15568, - "##cup": 15569, - "berkshire": 15570, - "unfair": 15571, - "mysteries": 15572, - "orchid": 15573, - "winced": 15574, - "exhaustion": 15575, - "renovations": 15576, - "stranded": 15577, - "obe": 15578, - "infinity": 15579, - "##nies": 15580, - "adapt": 15581, - "redevelopment": 15582, - "thanked": 15583, - "registry": 15584, - "olga": 15585, - "domingo": 15586, - "noir": 15587, - "tudor": 15588, - "ole": 15589, - "##atus": 15590, - "commenting": 15591, - "behaviors": 15592, - "##ais": 15593, - "crisp": 15594, - "pauline": 15595, - "probable": 15596, - "stirling": 15597, - "wigan": 15598, - "##bian": 15599, - "paralympics": 15600, - "panting": 15601, - "surpassed": 15602, - "##rew": 15603, - "luca": 15604, - "barred": 15605, - "pony": 15606, - "famed": 15607, - "##sters": 15608, - "cassandra": 15609, - "waiter": 15610, - "carolyn": 15611, - "exported": 15612, - "##orted": 15613, - "andres": 15614, - "destructive": 15615, - "deeds": 15616, - "jonah": 15617, - "castles": 15618, - "vacancy": 15619, - "suv": 15620, - "##glass": 15621, - "1788": 15622, - "orchard": 15623, - "yep": 15624, - "famine": 15625, - "belarusian": 15626, - "sprang": 15627, - "##forth": 15628, - "skinny": 15629, - "##mis": 15630, - "administrators": 15631, - "rotterdam": 15632, - "zambia": 15633, - "zhao": 15634, - "boiler": 15635, - "discoveries": 15636, - "##ride": 15637, - "##physics": 15638, - "lucius": 15639, - "disappointing": 15640, - "outreach": 15641, - "spoon": 15642, - "##frame": 15643, - "qualifications": 15644, - "unanimously": 15645, - "enjoys": 15646, - "regency": 15647, - "##iidae": 15648, - "stade": 15649, - "realism": 15650, - "veterinary": 15651, - "rodgers": 15652, - "dump": 15653, - "alain": 15654, - "chestnut": 15655, - "castile": 15656, - "censorship": 15657, - "rumble": 15658, - "gibbs": 15659, - "##itor": 15660, - "communion": 15661, - "reggae": 15662, - "inactivated": 15663, - "logs": 15664, - "loads": 15665, - "##houses": 15666, - "homosexual": 15667, - "##iano": 15668, - "ale": 15669, - "informs": 15670, - "##cas": 15671, - "phrases": 15672, - "plaster": 15673, - "linebacker": 15674, - "ambrose": 15675, - "kaiser": 15676, - "fascinated": 15677, - "850": 15678, - "limerick": 15679, - "recruitment": 15680, - "forge": 15681, - "mastered": 15682, - "##nding": 15683, - "leinster": 15684, - "rooted": 15685, - "threaten": 15686, - "##strom": 15687, - "borneo": 15688, - "##hes": 15689, - "suggestions": 15690, - "scholarships": 15691, - "propeller": 15692, - "documentaries": 15693, - "patronage": 15694, - "coats": 15695, - "constructing": 15696, - "invest": 15697, - "neurons": 15698, - "comet": 15699, - "entirety": 15700, - "shouts": 15701, - "identities": 15702, - "annoying": 15703, - "unchanged": 15704, - "wary": 15705, - "##antly": 15706, - "##ogy": 15707, - "neat": 15708, - "oversight": 15709, - "##kos": 15710, - "phillies": 15711, - "replay": 15712, - "constance": 15713, - "##kka": 15714, - "incarnation": 15715, - "humble": 15716, - "skies": 15717, - "minus": 15718, - "##acy": 15719, - "smithsonian": 15720, - "##chel": 15721, - "guerrilla": 15722, - "jar": 15723, - "cadets": 15724, - "##plate": 15725, - "surplus": 15726, - "audit": 15727, - "##aru": 15728, - "cracking": 15729, - "joanna": 15730, - "louisa": 15731, - "pacing": 15732, - "##lights": 15733, - "intentionally": 15734, - "##iri": 15735, - "diner": 15736, - "nwa": 15737, - "imprint": 15738, - "australians": 15739, - "tong": 15740, - "unprecedented": 15741, - "bunker": 15742, - "naive": 15743, - "specialists": 15744, - "ark": 15745, - "nichols": 15746, - "railing": 15747, - "leaked": 15748, - "pedal": 15749, - "##uka": 15750, - "shrub": 15751, - "longing": 15752, - "roofs": 15753, - "v8": 15754, - "captains": 15755, - "neural": 15756, - "tuned": 15757, - "##ntal": 15758, - "##jet": 15759, - "emission": 15760, - "medina": 15761, - "frantic": 15762, - "codex": 15763, - "definitive": 15764, - "sid": 15765, - "abolition": 15766, - "intensified": 15767, - "stocks": 15768, - "enrique": 15769, - "sustain": 15770, - "genoa": 15771, - "oxide": 15772, - "##written": 15773, - "clues": 15774, - "cha": 15775, - "##gers": 15776, - "tributaries": 15777, - "fragment": 15778, - "venom": 15779, - "##rity": 15780, - "##ente": 15781, - "##sca": 15782, - "muffled": 15783, - "vain": 15784, - "sire": 15785, - "laos": 15786, - "##ingly": 15787, - "##hana": 15788, - "hastily": 15789, - "snapping": 15790, - "surfaced": 15791, - "sentiment": 15792, - "motive": 15793, - "##oft": 15794, - "contests": 15795, - "approximate": 15796, - "mesa": 15797, - "luckily": 15798, - "dinosaur": 15799, - "exchanges": 15800, - "propelled": 15801, - "accord": 15802, - "bourne": 15803, - "relieve": 15804, - "tow": 15805, - "masks": 15806, - "offended": 15807, - "##ues": 15808, - "cynthia": 15809, - "##mmer": 15810, - "rains": 15811, - "bartender": 15812, - "zinc": 15813, - "reviewers": 15814, - "lois": 15815, - "##sai": 15816, - "legged": 15817, - "arrogant": 15818, - "rafe": 15819, - "rosie": 15820, - "comprise": 15821, - "handicap": 15822, - "blockade": 15823, - "inlet": 15824, - "lagoon": 15825, - "copied": 15826, - "drilling": 15827, - "shelley": 15828, - "petals": 15829, - "##inian": 15830, - "mandarin": 15831, - "obsolete": 15832, - "##inated": 15833, - "onward": 15834, - "arguably": 15835, - "productivity": 15836, - "cindy": 15837, - "praising": 15838, - "seldom": 15839, - "busch": 15840, - "discusses": 15841, - "raleigh": 15842, - "shortage": 15843, - "ranged": 15844, - "stanton": 15845, - "encouragement": 15846, - "firstly": 15847, - "conceded": 15848, - "overs": 15849, - "temporal": 15850, - "##uke": 15851, - "cbe": 15852, - "##bos": 15853, - "woo": 15854, - "certainty": 15855, - "pumps": 15856, - "##pton": 15857, - "stalked": 15858, - "##uli": 15859, - "lizzie": 15860, - "periodic": 15861, - "thieves": 15862, - "weaker": 15863, - "##night": 15864, - "gases": 15865, - "shoving": 15866, - "chooses": 15867, - "wc": 15868, - "##chemical": 15869, - "prompting": 15870, - "weights": 15871, - "##kill": 15872, - "robust": 15873, - "flanked": 15874, - "sticky": 15875, - "hu": 15876, - "tuberculosis": 15877, - "##eb": 15878, - "##eal": 15879, - "christchurch": 15880, - "resembled": 15881, - "wallet": 15882, - "reese": 15883, - "inappropriate": 15884, - "pictured": 15885, - "distract": 15886, - "fixing": 15887, - "fiddle": 15888, - "giggled": 15889, - "burger": 15890, - "heirs": 15891, - "hairy": 15892, - "mechanic": 15893, - "torque": 15894, - "apache": 15895, - "obsessed": 15896, - "chiefly": 15897, - "cheng": 15898, - "logging": 15899, - "##tag": 15900, - "extracted": 15901, - "meaningful": 15902, - "numb": 15903, - "##vsky": 15904, - "gloucestershire": 15905, - "reminding": 15906, - "##bay": 15907, - "unite": 15908, - "##lit": 15909, - "breeds": 15910, - "diminished": 15911, - "clown": 15912, - "glove": 15913, - "1860s": 15914, - "##ن": 15915, - "##ug": 15916, - "archibald": 15917, - "focal": 15918, - "freelance": 15919, - "sliced": 15920, - "depiction": 15921, - "##yk": 15922, - "organism": 15923, - "switches": 15924, - "sights": 15925, - "stray": 15926, - "crawling": 15927, - "##ril": 15928, - "lever": 15929, - "leningrad": 15930, - "interpretations": 15931, - "loops": 15932, - "anytime": 15933, - "reel": 15934, - "alicia": 15935, - "delighted": 15936, - "##ech": 15937, - "inhaled": 15938, - "xiv": 15939, - "suitcase": 15940, - "bernie": 15941, - "vega": 15942, - "licenses": 15943, - "northampton": 15944, - "exclusion": 15945, - "induction": 15946, - "monasteries": 15947, - "racecourse": 15948, - "homosexuality": 15949, - "##right": 15950, - "##sfield": 15951, - "##rky": 15952, - "dimitri": 15953, - "michele": 15954, - "alternatives": 15955, - "ions": 15956, - "commentators": 15957, - "genuinely": 15958, - "objected": 15959, - "pork": 15960, - "hospitality": 15961, - "fencing": 15962, - "stephan": 15963, - "warships": 15964, - "peripheral": 15965, - "wit": 15966, - "drunken": 15967, - "wrinkled": 15968, - "quentin": 15969, - "spends": 15970, - "departing": 15971, - "chung": 15972, - "numerical": 15973, - "spokesperson": 15974, - "##zone": 15975, - "johannesburg": 15976, - "caliber": 15977, - "killers": 15978, - "##udge": 15979, - "assumes": 15980, - "neatly": 15981, - "demographic": 15982, - "abigail": 15983, - "bloc": 15984, - "##vel": 15985, - "mounting": 15986, - "##lain": 15987, - "bentley": 15988, - "slightest": 15989, - "xu": 15990, - "recipients": 15991, - "##jk": 15992, - "merlin": 15993, - "##writer": 15994, - "seniors": 15995, - "prisons": 15996, - "blinking": 15997, - "hindwings": 15998, - "flickered": 15999, - "kappa": 16000, - "##hel": 16001, - "80s": 16002, - "strengthening": 16003, - "appealing": 16004, - "brewing": 16005, - "gypsy": 16006, - "mali": 16007, - "lashes": 16008, - "hulk": 16009, - "unpleasant": 16010, - "harassment": 16011, - "bio": 16012, - "treaties": 16013, - "predict": 16014, - "instrumentation": 16015, - "pulp": 16016, - "troupe": 16017, - "boiling": 16018, - "mantle": 16019, - "##ffe": 16020, - "ins": 16021, - "##vn": 16022, - "dividing": 16023, - "handles": 16024, - "verbs": 16025, - "##onal": 16026, - "coconut": 16027, - "senegal": 16028, - "340": 16029, - "thorough": 16030, - "gum": 16031, - "momentarily": 16032, - "##sto": 16033, - "cocaine": 16034, - "panicked": 16035, - "destined": 16036, - "##turing": 16037, - "teatro": 16038, - "denying": 16039, - "weary": 16040, - "captained": 16041, - "mans": 16042, - "##hawks": 16043, - "##code": 16044, - "wakefield": 16045, - "bollywood": 16046, - "thankfully": 16047, - "##16": 16048, - "cyril": 16049, - "##wu": 16050, - "amendments": 16051, - "##bahn": 16052, - "consultation": 16053, - "stud": 16054, - "reflections": 16055, - "kindness": 16056, - "1787": 16057, - "internally": 16058, - "##ovo": 16059, - "tex": 16060, - "mosaic": 16061, - "distribute": 16062, - "paddy": 16063, - "seeming": 16064, - "143": 16065, - "##hic": 16066, - "piers": 16067, - "##15": 16068, - "##mura": 16069, - "##verse": 16070, - "popularly": 16071, - "winger": 16072, - "kang": 16073, - "sentinel": 16074, - "mccoy": 16075, - "##anza": 16076, - "covenant": 16077, - "##bag": 16078, - "verge": 16079, - "fireworks": 16080, - "suppress": 16081, - "thrilled": 16082, - "dominate": 16083, - "##jar": 16084, - "swansea": 16085, - "##60": 16086, - "142": 16087, - "reconciliation": 16088, - "##ndi": 16089, - "stiffened": 16090, - "cue": 16091, - "dorian": 16092, - "##uf": 16093, - "damascus": 16094, - "amor": 16095, - "ida": 16096, - "foremost": 16097, - "##aga": 16098, - "porsche": 16099, - "unseen": 16100, - "dir": 16101, - "##had": 16102, - "##azi": 16103, - "stony": 16104, - "lexi": 16105, - "melodies": 16106, - "##nko": 16107, - "angular": 16108, - "integer": 16109, - "podcast": 16110, - "ants": 16111, - "inherent": 16112, - "jaws": 16113, - "justify": 16114, - "persona": 16115, - "##olved": 16116, - "josephine": 16117, - "##nr": 16118, - "##ressed": 16119, - "customary": 16120, - "flashes": 16121, - "gala": 16122, - "cyrus": 16123, - "glaring": 16124, - "backyard": 16125, - "ariel": 16126, - "physiology": 16127, - "greenland": 16128, - "html": 16129, - "stir": 16130, - "avon": 16131, - "atletico": 16132, - "finch": 16133, - "methodology": 16134, - "ked": 16135, - "##lent": 16136, - "mas": 16137, - "catholicism": 16138, - "townsend": 16139, - "branding": 16140, - "quincy": 16141, - "fits": 16142, - "containers": 16143, - "1777": 16144, - "ashore": 16145, - "aragon": 16146, - "##19": 16147, - "forearm": 16148, - "poisoning": 16149, - "##sd": 16150, - "adopting": 16151, - "conquer": 16152, - "grinding": 16153, - "amnesty": 16154, - "keller": 16155, - "finances": 16156, - "evaluate": 16157, - "forged": 16158, - "lankan": 16159, - "instincts": 16160, - "##uto": 16161, - "guam": 16162, - "bosnian": 16163, - "photographed": 16164, - "workplace": 16165, - "desirable": 16166, - "protector": 16167, - "##dog": 16168, - "allocation": 16169, - "intently": 16170, - "encourages": 16171, - "willy": 16172, - "##sten": 16173, - "bodyguard": 16174, - "electro": 16175, - "brighter": 16176, - "##ν": 16177, - "bihar": 16178, - "##chev": 16179, - "lasts": 16180, - "opener": 16181, - "amphibious": 16182, - "sal": 16183, - "verde": 16184, - "arte": 16185, - "##cope": 16186, - "captivity": 16187, - "vocabulary": 16188, - "yields": 16189, - "##tted": 16190, - "agreeing": 16191, - "desmond": 16192, - "pioneered": 16193, - "##chus": 16194, - "strap": 16195, - "campaigned": 16196, - "railroads": 16197, - "##ович": 16198, - "emblem": 16199, - "##dre": 16200, - "stormed": 16201, - "501": 16202, - "##ulous": 16203, - "marijuana": 16204, - "northumberland": 16205, - "##gn": 16206, - "##nath": 16207, - "bowen": 16208, - "landmarks": 16209, - "beaumont": 16210, - "##qua": 16211, - "danube": 16212, - "##bler": 16213, - "attorneys": 16214, - "th": 16215, - "ge": 16216, - "flyers": 16217, - "critique": 16218, - "villains": 16219, - "cass": 16220, - "mutation": 16221, - "acc": 16222, - "##0s": 16223, - "colombo": 16224, - "mckay": 16225, - "motif": 16226, - "sampling": 16227, - "concluding": 16228, - "syndicate": 16229, - "##rell": 16230, - "neon": 16231, - "stables": 16232, - "ds": 16233, - "warnings": 16234, - "clint": 16235, - "mourning": 16236, - "wilkinson": 16237, - "##tated": 16238, - "merrill": 16239, - "leopard": 16240, - "evenings": 16241, - "exhaled": 16242, - "emil": 16243, - "sonia": 16244, - "ezra": 16245, - "discrete": 16246, - "stove": 16247, - "farrell": 16248, - "fifteenth": 16249, - "prescribed": 16250, - "superhero": 16251, - "##rier": 16252, - "worms": 16253, - "helm": 16254, - "wren": 16255, - "##duction": 16256, - "##hc": 16257, - "expo": 16258, - "##rator": 16259, - "hq": 16260, - "unfamiliar": 16261, - "antony": 16262, - "prevents": 16263, - "acceleration": 16264, - "fiercely": 16265, - "mari": 16266, - "painfully": 16267, - "calculations": 16268, - "cheaper": 16269, - "ign": 16270, - "clifton": 16271, - "irvine": 16272, - "davenport": 16273, - "mozambique": 16274, - "##np": 16275, - "pierced": 16276, - "##evich": 16277, - "wonders": 16278, - "##wig": 16279, - "##cate": 16280, - "##iling": 16281, - "crusade": 16282, - "ware": 16283, - "##uel": 16284, - "enzymes": 16285, - "reasonably": 16286, - "mls": 16287, - "##coe": 16288, - "mater": 16289, - "ambition": 16290, - "bunny": 16291, - "eliot": 16292, - "kernel": 16293, - "##fin": 16294, - "asphalt": 16295, - "headmaster": 16296, - "torah": 16297, - "aden": 16298, - "lush": 16299, - "pins": 16300, - "waived": 16301, - "##care": 16302, - "##yas": 16303, - "joao": 16304, - "substrate": 16305, - "enforce": 16306, - "##grad": 16307, - "##ules": 16308, - "alvarez": 16309, - "selections": 16310, - "epidemic": 16311, - "tempted": 16312, - "##bit": 16313, - "bremen": 16314, - "translates": 16315, - "ensured": 16316, - "waterfront": 16317, - "29th": 16318, - "forrest": 16319, - "manny": 16320, - "malone": 16321, - "kramer": 16322, - "reigning": 16323, - "cookies": 16324, - "simpler": 16325, - "absorption": 16326, - "205": 16327, - "engraved": 16328, - "##ffy": 16329, - "evaluated": 16330, - "1778": 16331, - "haze": 16332, - "146": 16333, - "comforting": 16334, - "crossover": 16335, - "##abe": 16336, - "thorn": 16337, - "##rift": 16338, - "##imo": 16339, - "##pop": 16340, - "suppression": 16341, - "fatigue": 16342, - "cutter": 16343, - "##tr": 16344, - "201": 16345, - "wurttemberg": 16346, - "##orf": 16347, - "enforced": 16348, - "hovering": 16349, - "proprietary": 16350, - "gb": 16351, - "samurai": 16352, - "syllable": 16353, - "ascent": 16354, - "lacey": 16355, - "tick": 16356, - "lars": 16357, - "tractor": 16358, - "merchandise": 16359, - "rep": 16360, - "bouncing": 16361, - "defendants": 16362, - "##yre": 16363, - "huntington": 16364, - "##ground": 16365, - "##oko": 16366, - "standardized": 16367, - "##hor": 16368, - "##hima": 16369, - "assassinated": 16370, - "nu": 16371, - "predecessors": 16372, - "rainy": 16373, - "liar": 16374, - "assurance": 16375, - "lyrical": 16376, - "##uga": 16377, - "secondly": 16378, - "flattened": 16379, - "ios": 16380, - "parameter": 16381, - "undercover": 16382, - "##mity": 16383, - "bordeaux": 16384, - "punish": 16385, - "ridges": 16386, - "markers": 16387, - "exodus": 16388, - "inactive": 16389, - "hesitate": 16390, - "debbie": 16391, - "nyc": 16392, - "pledge": 16393, - "savoy": 16394, - "nagar": 16395, - "offset": 16396, - "organist": 16397, - "##tium": 16398, - "hesse": 16399, - "marin": 16400, - "converting": 16401, - "##iver": 16402, - "diagram": 16403, - "propulsion": 16404, - "pu": 16405, - "validity": 16406, - "reverted": 16407, - "supportive": 16408, - "##dc": 16409, - "ministries": 16410, - "clans": 16411, - "responds": 16412, - "proclamation": 16413, - "##inae": 16414, - "##ø": 16415, - "##rea": 16416, - "ein": 16417, - "pleading": 16418, - "patriot": 16419, - "sf": 16420, - "birch": 16421, - "islanders": 16422, - "strauss": 16423, - "hates": 16424, - "##dh": 16425, - "brandenburg": 16426, - "concession": 16427, - "rd": 16428, - "##ob": 16429, - "1900s": 16430, - "killings": 16431, - "textbook": 16432, - "antiquity": 16433, - "cinematography": 16434, - "wharf": 16435, - "embarrassing": 16436, - "setup": 16437, - "creed": 16438, - "farmland": 16439, - "inequality": 16440, - "centred": 16441, - "signatures": 16442, - "fallon": 16443, - "370": 16444, - "##ingham": 16445, - "##uts": 16446, - "ceylon": 16447, - "gazing": 16448, - "directive": 16449, - "laurie": 16450, - "##tern": 16451, - "globally": 16452, - "##uated": 16453, - "##dent": 16454, - "allah": 16455, - "excavation": 16456, - "threads": 16457, - "##cross": 16458, - "148": 16459, - "frantically": 16460, - "icc": 16461, - "utilize": 16462, - "determines": 16463, - "respiratory": 16464, - "thoughtful": 16465, - "receptions": 16466, - "##dicate": 16467, - "merging": 16468, - "chandra": 16469, - "seine": 16470, - "147": 16471, - "builders": 16472, - "builds": 16473, - "diagnostic": 16474, - "dev": 16475, - "visibility": 16476, - "goddamn": 16477, - "analyses": 16478, - "dhaka": 16479, - "cho": 16480, - "proves": 16481, - "chancel": 16482, - "concurrent": 16483, - "curiously": 16484, - "canadians": 16485, - "pumped": 16486, - "restoring": 16487, - "1850s": 16488, - "turtles": 16489, - "jaguar": 16490, - "sinister": 16491, - "spinal": 16492, - "traction": 16493, - "declan": 16494, - "vows": 16495, - "1784": 16496, - "glowed": 16497, - "capitalism": 16498, - "swirling": 16499, - "install": 16500, - "universidad": 16501, - "##lder": 16502, - "##oat": 16503, - "soloist": 16504, - "##genic": 16505, - "##oor": 16506, - "coincidence": 16507, - "beginnings": 16508, - "nissan": 16509, - "dip": 16510, - "resorts": 16511, - "caucasus": 16512, - "combustion": 16513, - "infectious": 16514, - "##eno": 16515, - "pigeon": 16516, - "serpent": 16517, - "##itating": 16518, - "conclude": 16519, - "masked": 16520, - "salad": 16521, - "jew": 16522, - "##gr": 16523, - "surreal": 16524, - "toni": 16525, - "##wc": 16526, - "harmonica": 16527, - "151": 16528, - "##gins": 16529, - "##etic": 16530, - "##coat": 16531, - "fishermen": 16532, - "intending": 16533, - "bravery": 16534, - "##wave": 16535, - "klaus": 16536, - "titan": 16537, - "wembley": 16538, - "taiwanese": 16539, - "ransom": 16540, - "40th": 16541, - "incorrect": 16542, - "hussein": 16543, - "eyelids": 16544, - "jp": 16545, - "cooke": 16546, - "dramas": 16547, - "utilities": 16548, - "##etta": 16549, - "##print": 16550, - "eisenhower": 16551, - "principally": 16552, - "granada": 16553, - "lana": 16554, - "##rak": 16555, - "openings": 16556, - "concord": 16557, - "##bl": 16558, - "bethany": 16559, - "connie": 16560, - "morality": 16561, - "sega": 16562, - "##mons": 16563, - "##nard": 16564, - "earnings": 16565, - "##kara": 16566, - "##cine": 16567, - "wii": 16568, - "communes": 16569, - "##rel": 16570, - "coma": 16571, - "composing": 16572, - "softened": 16573, - "severed": 16574, - "grapes": 16575, - "##17": 16576, - "nguyen": 16577, - "analyzed": 16578, - "warlord": 16579, - "hubbard": 16580, - "heavenly": 16581, - "behave": 16582, - "slovenian": 16583, - "##hit": 16584, - "##ony": 16585, - "hailed": 16586, - "filmmakers": 16587, - "trance": 16588, - "caldwell": 16589, - "skye": 16590, - "unrest": 16591, - "coward": 16592, - "likelihood": 16593, - "##aging": 16594, - "bern": 16595, - "sci": 16596, - "taliban": 16597, - "honolulu": 16598, - "propose": 16599, - "##wang": 16600, - "1700": 16601, - "browser": 16602, - "imagining": 16603, - "cobra": 16604, - "contributes": 16605, - "dukes": 16606, - "instinctively": 16607, - "conan": 16608, - "violinist": 16609, - "##ores": 16610, - "accessories": 16611, - "gradual": 16612, - "##amp": 16613, - "quotes": 16614, - "sioux": 16615, - "##dating": 16616, - "undertake": 16617, - "intercepted": 16618, - "sparkling": 16619, - "compressed": 16620, - "139": 16621, - "fungus": 16622, - "tombs": 16623, - "haley": 16624, - "imposing": 16625, - "rests": 16626, - "degradation": 16627, - "lincolnshire": 16628, - "retailers": 16629, - "wetlands": 16630, - "tulsa": 16631, - "distributor": 16632, - "dungeon": 16633, - "nun": 16634, - "greenhouse": 16635, - "convey": 16636, - "atlantis": 16637, - "aft": 16638, - "exits": 16639, - "oman": 16640, - "dresser": 16641, - "lyons": 16642, - "##sti": 16643, - "joking": 16644, - "eddy": 16645, - "judgement": 16646, - "omitted": 16647, - "digits": 16648, - "##cts": 16649, - "##game": 16650, - "juniors": 16651, - "##rae": 16652, - "cents": 16653, - "stricken": 16654, - "une": 16655, - "##ngo": 16656, - "wizards": 16657, - "weir": 16658, - "breton": 16659, - "nan": 16660, - "technician": 16661, - "fibers": 16662, - "liking": 16663, - "royalty": 16664, - "##cca": 16665, - "154": 16666, - "persia": 16667, - "terribly": 16668, - "magician": 16669, - "##rable": 16670, - "##unt": 16671, - "vance": 16672, - "cafeteria": 16673, - "booker": 16674, - "camille": 16675, - "warmer": 16676, - "##static": 16677, - "consume": 16678, - "cavern": 16679, - "gaps": 16680, - "compass": 16681, - "contemporaries": 16682, - "foyer": 16683, - "soothing": 16684, - "graveyard": 16685, - "maj": 16686, - "plunged": 16687, - "blush": 16688, - "##wear": 16689, - "cascade": 16690, - "demonstrates": 16691, - "ordinance": 16692, - "##nov": 16693, - "boyle": 16694, - "##lana": 16695, - "rockefeller": 16696, - "shaken": 16697, - "banjo": 16698, - "izzy": 16699, - "##ense": 16700, - "breathless": 16701, - "vines": 16702, - "##32": 16703, - "##eman": 16704, - "alterations": 16705, - "chromosome": 16706, - "dwellings": 16707, - "feudal": 16708, - "mole": 16709, - "153": 16710, - "catalonia": 16711, - "relics": 16712, - "tenant": 16713, - "mandated": 16714, - "##fm": 16715, - "fridge": 16716, - "hats": 16717, - "honesty": 16718, - "patented": 16719, - "raul": 16720, - "heap": 16721, - "cruisers": 16722, - "accusing": 16723, - "enlightenment": 16724, - "infants": 16725, - "wherein": 16726, - "chatham": 16727, - "contractors": 16728, - "zen": 16729, - "affinity": 16730, - "hc": 16731, - "osborne": 16732, - "piston": 16733, - "156": 16734, - "traps": 16735, - "maturity": 16736, - "##rana": 16737, - "lagos": 16738, - "##zal": 16739, - "peering": 16740, - "##nay": 16741, - "attendant": 16742, - "dealers": 16743, - "protocols": 16744, - "subset": 16745, - "prospects": 16746, - "biographical": 16747, - "##cre": 16748, - "artery": 16749, - "##zers": 16750, - "insignia": 16751, - "nuns": 16752, - "endured": 16753, - "##eration": 16754, - "recommend": 16755, - "schwartz": 16756, - "serbs": 16757, - "berger": 16758, - "cromwell": 16759, - "crossroads": 16760, - "##ctor": 16761, - "enduring": 16762, - "clasped": 16763, - "grounded": 16764, - "##bine": 16765, - "marseille": 16766, - "twitched": 16767, - "abel": 16768, - "choke": 16769, - "https": 16770, - "catalyst": 16771, - "moldova": 16772, - "italians": 16773, - "##tist": 16774, - "disastrous": 16775, - "wee": 16776, - "##oured": 16777, - "##nti": 16778, - "wwf": 16779, - "nope": 16780, - "##piration": 16781, - "##asa": 16782, - "expresses": 16783, - "thumbs": 16784, - "167": 16785, - "##nza": 16786, - "coca": 16787, - "1781": 16788, - "cheating": 16789, - "##ption": 16790, - "skipped": 16791, - "sensory": 16792, - "heidelberg": 16793, - "spies": 16794, - "satan": 16795, - "dangers": 16796, - "semifinal": 16797, - "202": 16798, - "bohemia": 16799, - "whitish": 16800, - "confusing": 16801, - "shipbuilding": 16802, - "relies": 16803, - "surgeons": 16804, - "landings": 16805, - "ravi": 16806, - "baku": 16807, - "moor": 16808, - "suffix": 16809, - "alejandro": 16810, - "##yana": 16811, - "litre": 16812, - "upheld": 16813, - "##unk": 16814, - "rajasthan": 16815, - "##rek": 16816, - "coaster": 16817, - "insists": 16818, - "posture": 16819, - "scenarios": 16820, - "etienne": 16821, - "favoured": 16822, - "appoint": 16823, - "transgender": 16824, - "elephants": 16825, - "poked": 16826, - "greenwood": 16827, - "defences": 16828, - "fulfilled": 16829, - "militant": 16830, - "somali": 16831, - "1758": 16832, - "chalk": 16833, - "potent": 16834, - "##ucci": 16835, - "migrants": 16836, - "wink": 16837, - "assistants": 16838, - "nos": 16839, - "restriction": 16840, - "activism": 16841, - "niger": 16842, - "##ario": 16843, - "colon": 16844, - "shaun": 16845, - "##sat": 16846, - "daphne": 16847, - "##erated": 16848, - "swam": 16849, - "congregations": 16850, - "reprise": 16851, - "considerations": 16852, - "magnet": 16853, - "playable": 16854, - "xvi": 16855, - "##р": 16856, - "overthrow": 16857, - "tobias": 16858, - "knob": 16859, - "chavez": 16860, - "coding": 16861, - "##mers": 16862, - "propped": 16863, - "katrina": 16864, - "orient": 16865, - "newcomer": 16866, - "##suke": 16867, - "temperate": 16868, - "##pool": 16869, - "farmhouse": 16870, - "interrogation": 16871, - "##vd": 16872, - "committing": 16873, - "##vert": 16874, - "forthcoming": 16875, - "strawberry": 16876, - "joaquin": 16877, - "macau": 16878, - "ponds": 16879, - "shocking": 16880, - "siberia": 16881, - "##cellular": 16882, - "chant": 16883, - "contributors": 16884, - "##nant": 16885, - "##ologists": 16886, - "sped": 16887, - "absorb": 16888, - "hail": 16889, - "1782": 16890, - "spared": 16891, - "##hore": 16892, - "barbados": 16893, - "karate": 16894, - "opus": 16895, - "originates": 16896, - "saul": 16897, - "##xie": 16898, - "evergreen": 16899, - "leaped": 16900, - "##rock": 16901, - "correlation": 16902, - "exaggerated": 16903, - "weekday": 16904, - "unification": 16905, - "bump": 16906, - "tracing": 16907, - "brig": 16908, - "afb": 16909, - "pathways": 16910, - "utilizing": 16911, - "##ners": 16912, - "mod": 16913, - "mb": 16914, - "disturbance": 16915, - "kneeling": 16916, - "##stad": 16917, - "##guchi": 16918, - "100th": 16919, - "pune": 16920, - "##thy": 16921, - "decreasing": 16922, - "168": 16923, - "manipulation": 16924, - "miriam": 16925, - "academia": 16926, - "ecosystem": 16927, - "occupational": 16928, - "rbi": 16929, - "##lem": 16930, - "rift": 16931, - "##14": 16932, - "rotary": 16933, - "stacked": 16934, - "incorporation": 16935, - "awakening": 16936, - "generators": 16937, - "guerrero": 16938, - "racist": 16939, - "##omy": 16940, - "cyber": 16941, - "derivatives": 16942, - "culminated": 16943, - "allie": 16944, - "annals": 16945, - "panzer": 16946, - "sainte": 16947, - "wikipedia": 16948, - "pops": 16949, - "zu": 16950, - "austro": 16951, - "##vate": 16952, - "algerian": 16953, - "politely": 16954, - "nicholson": 16955, - "mornings": 16956, - "educate": 16957, - "tastes": 16958, - "thrill": 16959, - "dartmouth": 16960, - "##gating": 16961, - "db": 16962, - "##jee": 16963, - "regan": 16964, - "differing": 16965, - "concentrating": 16966, - "choreography": 16967, - "divinity": 16968, - "##media": 16969, - "pledged": 16970, - "alexandre": 16971, - "routing": 16972, - "gregor": 16973, - "madeline": 16974, - "##idal": 16975, - "apocalypse": 16976, - "##hora": 16977, - "gunfire": 16978, - "culminating": 16979, - "elves": 16980, - "fined": 16981, - "liang": 16982, - "lam": 16983, - "programmed": 16984, - "tar": 16985, - "guessing": 16986, - "transparency": 16987, - "gabrielle": 16988, - "##gna": 16989, - "cancellation": 16990, - "flexibility": 16991, - "##lining": 16992, - "accession": 16993, - "shea": 16994, - "stronghold": 16995, - "nets": 16996, - "specializes": 16997, - "##rgan": 16998, - "abused": 16999, - "hasan": 17000, - "sgt": 17001, - "ling": 17002, - "exceeding": 17003, - "##₄": 17004, - "admiration": 17005, - "supermarket": 17006, - "##ark": 17007, - "photographers": 17008, - "specialised": 17009, - "tilt": 17010, - "resonance": 17011, - "hmm": 17012, - "perfume": 17013, - "380": 17014, - "sami": 17015, - "threatens": 17016, - "garland": 17017, - "botany": 17018, - "guarding": 17019, - "boiled": 17020, - "greet": 17021, - "puppy": 17022, - "russo": 17023, - "supplier": 17024, - "wilmington": 17025, - "vibrant": 17026, - "vijay": 17027, - "##bius": 17028, - "paralympic": 17029, - "grumbled": 17030, - "paige": 17031, - "faa": 17032, - "licking": 17033, - "margins": 17034, - "hurricanes": 17035, - "##gong": 17036, - "fest": 17037, - "grenade": 17038, - "ripping": 17039, - "##uz": 17040, - "counseling": 17041, - "weigh": 17042, - "##sian": 17043, - "needles": 17044, - "wiltshire": 17045, - "edison": 17046, - "costly": 17047, - "##not": 17048, - "fulton": 17049, - "tramway": 17050, - "redesigned": 17051, - "staffordshire": 17052, - "cache": 17053, - "gasping": 17054, - "watkins": 17055, - "sleepy": 17056, - "candidacy": 17057, - "##group": 17058, - "monkeys": 17059, - "timeline": 17060, - "throbbing": 17061, - "##bid": 17062, - "##sos": 17063, - "berth": 17064, - "uzbekistan": 17065, - "vanderbilt": 17066, - "bothering": 17067, - "overturned": 17068, - "ballots": 17069, - "gem": 17070, - "##iger": 17071, - "sunglasses": 17072, - "subscribers": 17073, - "hooker": 17074, - "compelling": 17075, - "ang": 17076, - "exceptionally": 17077, - "saloon": 17078, - "stab": 17079, - "##rdi": 17080, - "carla": 17081, - "terrifying": 17082, - "rom": 17083, - "##vision": 17084, - "coil": 17085, - "##oids": 17086, - "satisfying": 17087, - "vendors": 17088, - "31st": 17089, - "mackay": 17090, - "deities": 17091, - "overlooked": 17092, - "ambient": 17093, - "bahamas": 17094, - "felipe": 17095, - "olympia": 17096, - "whirled": 17097, - "botanist": 17098, - "advertised": 17099, - "tugging": 17100, - "##dden": 17101, - "disciples": 17102, - "morales": 17103, - "unionist": 17104, - "rites": 17105, - "foley": 17106, - "morse": 17107, - "motives": 17108, - "creepy": 17109, - "##₀": 17110, - "soo": 17111, - "##sz": 17112, - "bargain": 17113, - "highness": 17114, - "frightening": 17115, - "turnpike": 17116, - "tory": 17117, - "reorganization": 17118, - "##cer": 17119, - "depict": 17120, - "biographer": 17121, - "##walk": 17122, - "unopposed": 17123, - "manifesto": 17124, - "##gles": 17125, - "institut": 17126, - "emile": 17127, - "accidental": 17128, - "kapoor": 17129, - "##dam": 17130, - "kilkenny": 17131, - "cortex": 17132, - "lively": 17133, - "##13": 17134, - "romanesque": 17135, - "jain": 17136, - "shan": 17137, - "cannons": 17138, - "##ood": 17139, - "##ske": 17140, - "petrol": 17141, - "echoing": 17142, - "amalgamated": 17143, - "disappears": 17144, - "cautious": 17145, - "proposes": 17146, - "sanctions": 17147, - "trenton": 17148, - "##ر": 17149, - "flotilla": 17150, - "aus": 17151, - "contempt": 17152, - "tor": 17153, - "canary": 17154, - "cote": 17155, - "theirs": 17156, - "##hun": 17157, - "conceptual": 17158, - "deleted": 17159, - "fascinating": 17160, - "paso": 17161, - "blazing": 17162, - "elf": 17163, - "honourable": 17164, - "hutchinson": 17165, - "##eiro": 17166, - "##outh": 17167, - "##zin": 17168, - "surveyor": 17169, - "tee": 17170, - "amidst": 17171, - "wooded": 17172, - "reissue": 17173, - "intro": 17174, - "##ono": 17175, - "cobb": 17176, - "shelters": 17177, - "newsletter": 17178, - "hanson": 17179, - "brace": 17180, - "encoding": 17181, - "confiscated": 17182, - "dem": 17183, - "caravan": 17184, - "marino": 17185, - "scroll": 17186, - "melodic": 17187, - "cows": 17188, - "imam": 17189, - "##adi": 17190, - "##aneous": 17191, - "northward": 17192, - "searches": 17193, - "biodiversity": 17194, - "cora": 17195, - "310": 17196, - "roaring": 17197, - "##bers": 17198, - "connell": 17199, - "theologian": 17200, - "halo": 17201, - "compose": 17202, - "pathetic": 17203, - "unmarried": 17204, - "dynamo": 17205, - "##oot": 17206, - "az": 17207, - "calculation": 17208, - "toulouse": 17209, - "deserves": 17210, - "humour": 17211, - "nr": 17212, - "forgiveness": 17213, - "tam": 17214, - "undergone": 17215, - "martyr": 17216, - "pamela": 17217, - "myths": 17218, - "whore": 17219, - "counselor": 17220, - "hicks": 17221, - "290": 17222, - "heavens": 17223, - "battleship": 17224, - "electromagnetic": 17225, - "##bbs": 17226, - "stellar": 17227, - "establishments": 17228, - "presley": 17229, - "hopped": 17230, - "##chin": 17231, - "temptation": 17232, - "90s": 17233, - "wills": 17234, - "nas": 17235, - "##yuan": 17236, - "nhs": 17237, - "##nya": 17238, - "seminars": 17239, - "##yev": 17240, - "adaptations": 17241, - "gong": 17242, - "asher": 17243, - "lex": 17244, - "indicator": 17245, - "sikh": 17246, - "tobago": 17247, - "cites": 17248, - "goin": 17249, - "##yte": 17250, - "satirical": 17251, - "##gies": 17252, - "characterised": 17253, - "correspond": 17254, - "bubbles": 17255, - "lure": 17256, - "participates": 17257, - "##vid": 17258, - "eruption": 17259, - "skate": 17260, - "therapeutic": 17261, - "1785": 17262, - "canals": 17263, - "wholesale": 17264, - "defaulted": 17265, - "sac": 17266, - "460": 17267, - "petit": 17268, - "##zzled": 17269, - "virgil": 17270, - "leak": 17271, - "ravens": 17272, - "256": 17273, - "portraying": 17274, - "##yx": 17275, - "ghetto": 17276, - "creators": 17277, - "dams": 17278, - "portray": 17279, - "vicente": 17280, - "##rington": 17281, - "fae": 17282, - "namesake": 17283, - "bounty": 17284, - "##arium": 17285, - "joachim": 17286, - "##ota": 17287, - "##iser": 17288, - "aforementioned": 17289, - "axle": 17290, - "snout": 17291, - "depended": 17292, - "dismantled": 17293, - "reuben": 17294, - "480": 17295, - "##ibly": 17296, - "gallagher": 17297, - "##lau": 17298, - "##pd": 17299, - "earnest": 17300, - "##ieu": 17301, - "##iary": 17302, - "inflicted": 17303, - "objections": 17304, - "##llar": 17305, - "asa": 17306, - "gritted": 17307, - "##athy": 17308, - "jericho": 17309, - "##sea": 17310, - "##was": 17311, - "flick": 17312, - "underside": 17313, - "ceramics": 17314, - "undead": 17315, - "substituted": 17316, - "195": 17317, - "eastward": 17318, - "undoubtedly": 17319, - "wheeled": 17320, - "chimney": 17321, - "##iche": 17322, - "guinness": 17323, - "cb": 17324, - "##ager": 17325, - "siding": 17326, - "##bell": 17327, - "traitor": 17328, - "baptiste": 17329, - "disguised": 17330, - "inauguration": 17331, - "149": 17332, - "tipperary": 17333, - "choreographer": 17334, - "perched": 17335, - "warmed": 17336, - "stationary": 17337, - "eco": 17338, - "##ike": 17339, - "##ntes": 17340, - "bacterial": 17341, - "##aurus": 17342, - "flores": 17343, - "phosphate": 17344, - "##core": 17345, - "attacker": 17346, - "invaders": 17347, - "alvin": 17348, - "intersects": 17349, - "a1": 17350, - "indirectly": 17351, - "immigrated": 17352, - "businessmen": 17353, - "cornelius": 17354, - "valves": 17355, - "narrated": 17356, - "pill": 17357, - "sober": 17358, - "ul": 17359, - "nationale": 17360, - "monastic": 17361, - "applicants": 17362, - "scenery": 17363, - "##jack": 17364, - "161": 17365, - "motifs": 17366, - "constitutes": 17367, - "cpu": 17368, - "##osh": 17369, - "jurisdictions": 17370, - "sd": 17371, - "tuning": 17372, - "irritation": 17373, - "woven": 17374, - "##uddin": 17375, - "fertility": 17376, - "gao": 17377, - "##erie": 17378, - "antagonist": 17379, - "impatient": 17380, - "glacial": 17381, - "hides": 17382, - "boarded": 17383, - "denominations": 17384, - "interception": 17385, - "##jas": 17386, - "cookie": 17387, - "nicola": 17388, - "##tee": 17389, - "algebraic": 17390, - "marquess": 17391, - "bahn": 17392, - "parole": 17393, - "buyers": 17394, - "bait": 17395, - "turbines": 17396, - "paperwork": 17397, - "bestowed": 17398, - "natasha": 17399, - "renee": 17400, - "oceans": 17401, - "purchases": 17402, - "157": 17403, - "vaccine": 17404, - "215": 17405, - "##tock": 17406, - "fixtures": 17407, - "playhouse": 17408, - "integrate": 17409, - "jai": 17410, - "oswald": 17411, - "intellectuals": 17412, - "##cky": 17413, - "booked": 17414, - "nests": 17415, - "mortimer": 17416, - "##isi": 17417, - "obsession": 17418, - "sept": 17419, - "##gler": 17420, - "##sum": 17421, - "440": 17422, - "scrutiny": 17423, - "simultaneous": 17424, - "squinted": 17425, - "##shin": 17426, - "collects": 17427, - "oven": 17428, - "shankar": 17429, - "penned": 17430, - "remarkably": 17431, - "##я": 17432, - "slips": 17433, - "luggage": 17434, - "spectral": 17435, - "1786": 17436, - "collaborations": 17437, - "louie": 17438, - "consolidation": 17439, - "##ailed": 17440, - "##ivating": 17441, - "420": 17442, - "hoover": 17443, - "blackpool": 17444, - "harness": 17445, - "ignition": 17446, - "vest": 17447, - "tails": 17448, - "belmont": 17449, - "mongol": 17450, - "skinner": 17451, - "##nae": 17452, - "visually": 17453, - "mage": 17454, - "derry": 17455, - "##tism": 17456, - "##unce": 17457, - "stevie": 17458, - "transitional": 17459, - "##rdy": 17460, - "redskins": 17461, - "drying": 17462, - "prep": 17463, - "prospective": 17464, - "##21": 17465, - "annoyance": 17466, - "oversee": 17467, - "##loaded": 17468, - "fills": 17469, - "##books": 17470, - "##iki": 17471, - "announces": 17472, - "fda": 17473, - "scowled": 17474, - "respects": 17475, - "prasad": 17476, - "mystic": 17477, - "tucson": 17478, - "##vale": 17479, - "revue": 17480, - "springer": 17481, - "bankrupt": 17482, - "1772": 17483, - "aristotle": 17484, - "salvatore": 17485, - "habsburg": 17486, - "##geny": 17487, - "dal": 17488, - "natal": 17489, - "nut": 17490, - "pod": 17491, - "chewing": 17492, - "darts": 17493, - "moroccan": 17494, - "walkover": 17495, - "rosario": 17496, - "lenin": 17497, - "punjabi": 17498, - "##ße": 17499, - "grossed": 17500, - "scattering": 17501, - "wired": 17502, - "invasive": 17503, - "hui": 17504, - "polynomial": 17505, - "corridors": 17506, - "wakes": 17507, - "gina": 17508, - "portrays": 17509, - "##cratic": 17510, - "arid": 17511, - "retreating": 17512, - "erich": 17513, - "irwin": 17514, - "sniper": 17515, - "##dha": 17516, - "linen": 17517, - "lindsey": 17518, - "maneuver": 17519, - "butch": 17520, - "shutting": 17521, - "socio": 17522, - "bounce": 17523, - "commemorative": 17524, - "postseason": 17525, - "jeremiah": 17526, - "pines": 17527, - "275": 17528, - "mystical": 17529, - "beads": 17530, - "bp": 17531, - "abbas": 17532, - "furnace": 17533, - "bidding": 17534, - "consulted": 17535, - "assaulted": 17536, - "empirical": 17537, - "rubble": 17538, - "enclosure": 17539, - "sob": 17540, - "weakly": 17541, - "cancel": 17542, - "polly": 17543, - "yielded": 17544, - "##emann": 17545, - "curly": 17546, - "prediction": 17547, - "battered": 17548, - "70s": 17549, - "vhs": 17550, - "jacqueline": 17551, - "render": 17552, - "sails": 17553, - "barked": 17554, - "detailing": 17555, - "grayson": 17556, - "riga": 17557, - "sloane": 17558, - "raging": 17559, - "##yah": 17560, - "herbs": 17561, - "bravo": 17562, - "##athlon": 17563, - "alloy": 17564, - "giggle": 17565, - "imminent": 17566, - "suffers": 17567, - "assumptions": 17568, - "waltz": 17569, - "##itate": 17570, - "accomplishments": 17571, - "##ited": 17572, - "bathing": 17573, - "remixed": 17574, - "deception": 17575, - "prefix": 17576, - "##emia": 17577, - "deepest": 17578, - "##tier": 17579, - "##eis": 17580, - "balkan": 17581, - "frogs": 17582, - "##rong": 17583, - "slab": 17584, - "##pate": 17585, - "philosophers": 17586, - "peterborough": 17587, - "grains": 17588, - "imports": 17589, - "dickinson": 17590, - "rwanda": 17591, - "##atics": 17592, - "1774": 17593, - "dirk": 17594, - "lan": 17595, - "tablets": 17596, - "##rove": 17597, - "clone": 17598, - "##rice": 17599, - "caretaker": 17600, - "hostilities": 17601, - "mclean": 17602, - "##gre": 17603, - "regimental": 17604, - "treasures": 17605, - "norms": 17606, - "impose": 17607, - "tsar": 17608, - "tango": 17609, - "diplomacy": 17610, - "variously": 17611, - "complain": 17612, - "192": 17613, - "recognise": 17614, - "arrests": 17615, - "1779": 17616, - "celestial": 17617, - "pulitzer": 17618, - "##dus": 17619, - "bing": 17620, - "libretto": 17621, - "##moor": 17622, - "adele": 17623, - "splash": 17624, - "##rite": 17625, - "expectation": 17626, - "lds": 17627, - "confronts": 17628, - "##izer": 17629, - "spontaneous": 17630, - "harmful": 17631, - "wedge": 17632, - "entrepreneurs": 17633, - "buyer": 17634, - "##ope": 17635, - "bilingual": 17636, - "translate": 17637, - "rugged": 17638, - "conner": 17639, - "circulated": 17640, - "uae": 17641, - "eaton": 17642, - "##gra": 17643, - "##zzle": 17644, - "lingered": 17645, - "lockheed": 17646, - "vishnu": 17647, - "reelection": 17648, - "alonso": 17649, - "##oom": 17650, - "joints": 17651, - "yankee": 17652, - "headline": 17653, - "cooperate": 17654, - "heinz": 17655, - "laureate": 17656, - "invading": 17657, - "##sford": 17658, - "echoes": 17659, - "scandinavian": 17660, - "##dham": 17661, - "hugging": 17662, - "vitamin": 17663, - "salute": 17664, - "micah": 17665, - "hind": 17666, - "trader": 17667, - "##sper": 17668, - "radioactive": 17669, - "##ndra": 17670, - "militants": 17671, - "poisoned": 17672, - "ratified": 17673, - "remark": 17674, - "campeonato": 17675, - "deprived": 17676, - "wander": 17677, - "prop": 17678, - "##dong": 17679, - "outlook": 17680, - "##tani": 17681, - "##rix": 17682, - "##eye": 17683, - "chiang": 17684, - "darcy": 17685, - "##oping": 17686, - "mandolin": 17687, - "spice": 17688, - "statesman": 17689, - "babylon": 17690, - "182": 17691, - "walled": 17692, - "forgetting": 17693, - "afro": 17694, - "##cap": 17695, - "158": 17696, - "giorgio": 17697, - "buffer": 17698, - "##polis": 17699, - "planetary": 17700, - "##gis": 17701, - "overlap": 17702, - "terminals": 17703, - "kinda": 17704, - "centenary": 17705, - "##bir": 17706, - "arising": 17707, - "manipulate": 17708, - "elm": 17709, - "ke": 17710, - "1770": 17711, - "ak": 17712, - "##tad": 17713, - "chrysler": 17714, - "mapped": 17715, - "moose": 17716, - "pomeranian": 17717, - "quad": 17718, - "macarthur": 17719, - "assemblies": 17720, - "shoreline": 17721, - "recalls": 17722, - "stratford": 17723, - "##rted": 17724, - "noticeable": 17725, - "##evic": 17726, - "imp": 17727, - "##rita": 17728, - "##sque": 17729, - "accustomed": 17730, - "supplying": 17731, - "tents": 17732, - "disgusted": 17733, - "vogue": 17734, - "sipped": 17735, - "filters": 17736, - "khz": 17737, - "reno": 17738, - "selecting": 17739, - "luftwaffe": 17740, - "mcmahon": 17741, - "tyne": 17742, - "masterpiece": 17743, - "carriages": 17744, - "collided": 17745, - "dunes": 17746, - "exercised": 17747, - "flare": 17748, - "remembers": 17749, - "muzzle": 17750, - "##mobile": 17751, - "heck": 17752, - "##rson": 17753, - "burgess": 17754, - "lunged": 17755, - "middleton": 17756, - "boycott": 17757, - "bilateral": 17758, - "##sity": 17759, - "hazardous": 17760, - "lumpur": 17761, - "multiplayer": 17762, - "spotlight": 17763, - "jackets": 17764, - "goldman": 17765, - "liege": 17766, - "porcelain": 17767, - "rag": 17768, - "waterford": 17769, - "benz": 17770, - "attracts": 17771, - "hopeful": 17772, - "battling": 17773, - "ottomans": 17774, - "kensington": 17775, - "baked": 17776, - "hymns": 17777, - "cheyenne": 17778, - "lattice": 17779, - "levine": 17780, - "borrow": 17781, - "polymer": 17782, - "clashes": 17783, - "michaels": 17784, - "monitored": 17785, - "commitments": 17786, - "denounced": 17787, - "##25": 17788, - "##von": 17789, - "cavity": 17790, - "##oney": 17791, - "hobby": 17792, - "akin": 17793, - "##holders": 17794, - "futures": 17795, - "intricate": 17796, - "cornish": 17797, - "patty": 17798, - "##oned": 17799, - "illegally": 17800, - "dolphin": 17801, - "##lag": 17802, - "barlow": 17803, - "yellowish": 17804, - "maddie": 17805, - "apologized": 17806, - "luton": 17807, - "plagued": 17808, - "##puram": 17809, - "nana": 17810, - "##rds": 17811, - "sway": 17812, - "fanny": 17813, - "łodz": 17814, - "##rino": 17815, - "psi": 17816, - "suspicions": 17817, - "hanged": 17818, - "##eding": 17819, - "initiate": 17820, - "charlton": 17821, - "##por": 17822, - "nak": 17823, - "competent": 17824, - "235": 17825, - "analytical": 17826, - "annex": 17827, - "wardrobe": 17828, - "reservations": 17829, - "##rma": 17830, - "sect": 17831, - "162": 17832, - "fairfax": 17833, - "hedge": 17834, - "piled": 17835, - "buckingham": 17836, - "uneven": 17837, - "bauer": 17838, - "simplicity": 17839, - "snyder": 17840, - "interpret": 17841, - "accountability": 17842, - "donors": 17843, - "moderately": 17844, - "byrd": 17845, - "continents": 17846, - "##cite": 17847, - "##max": 17848, - "disciple": 17849, - "hr": 17850, - "jamaican": 17851, - "ping": 17852, - "nominees": 17853, - "##uss": 17854, - "mongolian": 17855, - "diver": 17856, - "attackers": 17857, - "eagerly": 17858, - "ideological": 17859, - "pillows": 17860, - "miracles": 17861, - "apartheid": 17862, - "revolver": 17863, - "sulfur": 17864, - "clinics": 17865, - "moran": 17866, - "163": 17867, - "##enko": 17868, - "ile": 17869, - "katy": 17870, - "rhetoric": 17871, - "##icated": 17872, - "chronology": 17873, - "recycling": 17874, - "##hrer": 17875, - "elongated": 17876, - "mughal": 17877, - "pascal": 17878, - "profiles": 17879, - "vibration": 17880, - "databases": 17881, - "domination": 17882, - "##fare": 17883, - "##rant": 17884, - "matthias": 17885, - "digest": 17886, - "rehearsal": 17887, - "polling": 17888, - "weiss": 17889, - "initiation": 17890, - "reeves": 17891, - "clinging": 17892, - "flourished": 17893, - "impress": 17894, - "ngo": 17895, - "##hoff": 17896, - "##ume": 17897, - "buckley": 17898, - "symposium": 17899, - "rhythms": 17900, - "weed": 17901, - "emphasize": 17902, - "transforming": 17903, - "##taking": 17904, - "##gence": 17905, - "##yman": 17906, - "accountant": 17907, - "analyze": 17908, - "flicker": 17909, - "foil": 17910, - "priesthood": 17911, - "voluntarily": 17912, - "decreases": 17913, - "##80": 17914, - "##hya": 17915, - "slater": 17916, - "sv": 17917, - "charting": 17918, - "mcgill": 17919, - "##lde": 17920, - "moreno": 17921, - "##iu": 17922, - "besieged": 17923, - "zur": 17924, - "robes": 17925, - "##phic": 17926, - "admitting": 17927, - "api": 17928, - "deported": 17929, - "turmoil": 17930, - "peyton": 17931, - "earthquakes": 17932, - "##ares": 17933, - "nationalists": 17934, - "beau": 17935, - "clair": 17936, - "brethren": 17937, - "interrupt": 17938, - "welch": 17939, - "curated": 17940, - "galerie": 17941, - "requesting": 17942, - "164": 17943, - "##ested": 17944, - "impending": 17945, - "steward": 17946, - "viper": 17947, - "##vina": 17948, - "complaining": 17949, - "beautifully": 17950, - "brandy": 17951, - "foam": 17952, - "nl": 17953, - "1660": 17954, - "##cake": 17955, - "alessandro": 17956, - "punches": 17957, - "laced": 17958, - "explanations": 17959, - "##lim": 17960, - "attribute": 17961, - "clit": 17962, - "reggie": 17963, - "discomfort": 17964, - "##cards": 17965, - "smoothed": 17966, - "whales": 17967, - "##cene": 17968, - "adler": 17969, - "countered": 17970, - "duffy": 17971, - "disciplinary": 17972, - "widening": 17973, - "recipe": 17974, - "reliance": 17975, - "conducts": 17976, - "goats": 17977, - "gradient": 17978, - "preaching": 17979, - "##shaw": 17980, - "matilda": 17981, - "quasi": 17982, - "striped": 17983, - "meridian": 17984, - "cannabis": 17985, - "cordoba": 17986, - "certificates": 17987, - "##agh": 17988, - "##tering": 17989, - "graffiti": 17990, - "hangs": 17991, - "pilgrims": 17992, - "repeats": 17993, - "##ych": 17994, - "revive": 17995, - "urine": 17996, - "etat": 17997, - "##hawk": 17998, - "fueled": 17999, - "belts": 18000, - "fuzzy": 18001, - "susceptible": 18002, - "##hang": 18003, - "mauritius": 18004, - "salle": 18005, - "sincere": 18006, - "beers": 18007, - "hooks": 18008, - "##cki": 18009, - "arbitration": 18010, - "entrusted": 18011, - "advise": 18012, - "sniffed": 18013, - "seminar": 18014, - "junk": 18015, - "donnell": 18016, - "processors": 18017, - "principality": 18018, - "strapped": 18019, - "celia": 18020, - "mendoza": 18021, - "everton": 18022, - "fortunes": 18023, - "prejudice": 18024, - "starving": 18025, - "reassigned": 18026, - "steamer": 18027, - "##lund": 18028, - "tuck": 18029, - "evenly": 18030, - "foreman": 18031, - "##ffen": 18032, - "dans": 18033, - "375": 18034, - "envisioned": 18035, - "slit": 18036, - "##xy": 18037, - "baseman": 18038, - "liberia": 18039, - "rosemary": 18040, - "##weed": 18041, - "electrified": 18042, - "periodically": 18043, - "potassium": 18044, - "stride": 18045, - "contexts": 18046, - "sperm": 18047, - "slade": 18048, - "mariners": 18049, - "influx": 18050, - "bianca": 18051, - "subcommittee": 18052, - "##rane": 18053, - "spilling": 18054, - "icao": 18055, - "estuary": 18056, - "##nock": 18057, - "delivers": 18058, - "iphone": 18059, - "##ulata": 18060, - "isa": 18061, - "mira": 18062, - "bohemian": 18063, - "dessert": 18064, - "##sbury": 18065, - "welcoming": 18066, - "proudly": 18067, - "slowing": 18068, - "##chs": 18069, - "musee": 18070, - "ascension": 18071, - "russ": 18072, - "##vian": 18073, - "waits": 18074, - "##psy": 18075, - "africans": 18076, - "exploit": 18077, - "##morphic": 18078, - "gov": 18079, - "eccentric": 18080, - "crab": 18081, - "peck": 18082, - "##ull": 18083, - "entrances": 18084, - "formidable": 18085, - "marketplace": 18086, - "groom": 18087, - "bolted": 18088, - "metabolism": 18089, - "patton": 18090, - "robbins": 18091, - "courier": 18092, - "payload": 18093, - "endure": 18094, - "##ifier": 18095, - "andes": 18096, - "refrigerator": 18097, - "##pr": 18098, - "ornate": 18099, - "##uca": 18100, - "ruthless": 18101, - "illegitimate": 18102, - "masonry": 18103, - "strasbourg": 18104, - "bikes": 18105, - "adobe": 18106, - "##³": 18107, - "apples": 18108, - "quintet": 18109, - "willingly": 18110, - "niche": 18111, - "bakery": 18112, - "corpses": 18113, - "energetic": 18114, - "##cliffe": 18115, - "##sser": 18116, - "##ards": 18117, - "177": 18118, - "centimeters": 18119, - "centro": 18120, - "fuscous": 18121, - "cretaceous": 18122, - "rancho": 18123, - "##yde": 18124, - "andrei": 18125, - "telecom": 18126, - "tottenham": 18127, - "oasis": 18128, - "ordination": 18129, - "vulnerability": 18130, - "presiding": 18131, - "corey": 18132, - "cp": 18133, - "penguins": 18134, - "sims": 18135, - "##pis": 18136, - "malawi": 18137, - "piss": 18138, - "##48": 18139, - "correction": 18140, - "##cked": 18141, - "##ffle": 18142, - "##ryn": 18143, - "countdown": 18144, - "detectives": 18145, - "psychiatrist": 18146, - "psychedelic": 18147, - "dinosaurs": 18148, - "blouse": 18149, - "##get": 18150, - "choi": 18151, - "vowed": 18152, - "##oz": 18153, - "randomly": 18154, - "##pol": 18155, - "49ers": 18156, - "scrub": 18157, - "blanche": 18158, - "bruins": 18159, - "dusseldorf": 18160, - "##using": 18161, - "unwanted": 18162, - "##ums": 18163, - "212": 18164, - "dominique": 18165, - "elevations": 18166, - "headlights": 18167, - "om": 18168, - "laguna": 18169, - "##oga": 18170, - "1750": 18171, - "famously": 18172, - "ignorance": 18173, - "shrewsbury": 18174, - "##aine": 18175, - "ajax": 18176, - "breuning": 18177, - "che": 18178, - "confederacy": 18179, - "greco": 18180, - "overhaul": 18181, - "##screen": 18182, - "paz": 18183, - "skirts": 18184, - "disagreement": 18185, - "cruelty": 18186, - "jagged": 18187, - "phoebe": 18188, - "shifter": 18189, - "hovered": 18190, - "viruses": 18191, - "##wes": 18192, - "mandy": 18193, - "##lined": 18194, - "##gc": 18195, - "landlord": 18196, - "squirrel": 18197, - "dashed": 18198, - "##ι": 18199, - "ornamental": 18200, - "gag": 18201, - "wally": 18202, - "grange": 18203, - "literal": 18204, - "spurs": 18205, - "undisclosed": 18206, - "proceeding": 18207, - "yin": 18208, - "##text": 18209, - "billie": 18210, - "orphan": 18211, - "spanned": 18212, - "humidity": 18213, - "indy": 18214, - "weighted": 18215, - "presentations": 18216, - "explosions": 18217, - "lucian": 18218, - "##tary": 18219, - "vaughn": 18220, - "hindus": 18221, - "##anga": 18222, - "##hell": 18223, - "psycho": 18224, - "171": 18225, - "daytona": 18226, - "protects": 18227, - "efficiently": 18228, - "rematch": 18229, - "sly": 18230, - "tandem": 18231, - "##oya": 18232, - "rebranded": 18233, - "impaired": 18234, - "hee": 18235, - "metropolis": 18236, - "peach": 18237, - "godfrey": 18238, - "diaspora": 18239, - "ethnicity": 18240, - "prosperous": 18241, - "gleaming": 18242, - "dar": 18243, - "grossing": 18244, - "playback": 18245, - "##rden": 18246, - "stripe": 18247, - "pistols": 18248, - "##tain": 18249, - "births": 18250, - "labelled": 18251, - "##cating": 18252, - "172": 18253, - "rudy": 18254, - "alba": 18255, - "##onne": 18256, - "aquarium": 18257, - "hostility": 18258, - "##gb": 18259, - "##tase": 18260, - "shudder": 18261, - "sumatra": 18262, - "hardest": 18263, - "lakers": 18264, - "consonant": 18265, - "creeping": 18266, - "demos": 18267, - "homicide": 18268, - "capsule": 18269, - "zeke": 18270, - "liberties": 18271, - "expulsion": 18272, - "pueblo": 18273, - "##comb": 18274, - "trait": 18275, - "transporting": 18276, - "##ddin": 18277, - "##neck": 18278, - "##yna": 18279, - "depart": 18280, - "gregg": 18281, - "mold": 18282, - "ledge": 18283, - "hangar": 18284, - "oldham": 18285, - "playboy": 18286, - "termination": 18287, - "analysts": 18288, - "gmbh": 18289, - "romero": 18290, - "##itic": 18291, - "insist": 18292, - "cradle": 18293, - "filthy": 18294, - "brightness": 18295, - "slash": 18296, - "shootout": 18297, - "deposed": 18298, - "bordering": 18299, - "##truct": 18300, - "isis": 18301, - "microwave": 18302, - "tumbled": 18303, - "sheltered": 18304, - "cathy": 18305, - "werewolves": 18306, - "messy": 18307, - "andersen": 18308, - "convex": 18309, - "clapped": 18310, - "clinched": 18311, - "satire": 18312, - "wasting": 18313, - "edo": 18314, - "vc": 18315, - "rufus": 18316, - "##jak": 18317, - "mont": 18318, - "##etti": 18319, - "poznan": 18320, - "##keeping": 18321, - "restructuring": 18322, - "transverse": 18323, - "##rland": 18324, - "azerbaijani": 18325, - "slovene": 18326, - "gestures": 18327, - "roommate": 18328, - "choking": 18329, - "shear": 18330, - "##quist": 18331, - "vanguard": 18332, - "oblivious": 18333, - "##hiro": 18334, - "disagreed": 18335, - "baptism": 18336, - "##lich": 18337, - "coliseum": 18338, - "##aceae": 18339, - "salvage": 18340, - "societe": 18341, - "cory": 18342, - "locke": 18343, - "relocation": 18344, - "relying": 18345, - "versailles": 18346, - "ahl": 18347, - "swelling": 18348, - "##elo": 18349, - "cheerful": 18350, - "##word": 18351, - "##edes": 18352, - "gin": 18353, - "sarajevo": 18354, - "obstacle": 18355, - "diverted": 18356, - "##nac": 18357, - "messed": 18358, - "thoroughbred": 18359, - "fluttered": 18360, - "utrecht": 18361, - "chewed": 18362, - "acquaintance": 18363, - "assassins": 18364, - "dispatch": 18365, - "mirza": 18366, - "##wart": 18367, - "nike": 18368, - "salzburg": 18369, - "swell": 18370, - "yen": 18371, - "##gee": 18372, - "idle": 18373, - "ligue": 18374, - "samson": 18375, - "##nds": 18376, - "##igh": 18377, - "playful": 18378, - "spawned": 18379, - "##cise": 18380, - "tease": 18381, - "##case": 18382, - "burgundy": 18383, - "##bot": 18384, - "stirring": 18385, - "skeptical": 18386, - "interceptions": 18387, - "marathi": 18388, - "##dies": 18389, - "bedrooms": 18390, - "aroused": 18391, - "pinch": 18392, - "##lik": 18393, - "preferences": 18394, - "tattoos": 18395, - "buster": 18396, - "digitally": 18397, - "projecting": 18398, - "rust": 18399, - "##ital": 18400, - "kitten": 18401, - "priorities": 18402, - "addison": 18403, - "pseudo": 18404, - "##guard": 18405, - "dusk": 18406, - "icons": 18407, - "sermon": 18408, - "##psis": 18409, - "##iba": 18410, - "bt": 18411, - "##lift": 18412, - "##xt": 18413, - "ju": 18414, - "truce": 18415, - "rink": 18416, - "##dah": 18417, - "##wy": 18418, - "defects": 18419, - "psychiatry": 18420, - "offences": 18421, - "calculate": 18422, - "glucose": 18423, - "##iful": 18424, - "##rized": 18425, - "##unda": 18426, - "francaise": 18427, - "##hari": 18428, - "richest": 18429, - "warwickshire": 18430, - "carly": 18431, - "1763": 18432, - "purity": 18433, - "redemption": 18434, - "lending": 18435, - "##cious": 18436, - "muse": 18437, - "bruises": 18438, - "cerebral": 18439, - "aero": 18440, - "carving": 18441, - "##name": 18442, - "preface": 18443, - "terminology": 18444, - "invade": 18445, - "monty": 18446, - "##int": 18447, - "anarchist": 18448, - "blurred": 18449, - "##iled": 18450, - "rossi": 18451, - "treats": 18452, - "guts": 18453, - "shu": 18454, - "foothills": 18455, - "ballads": 18456, - "undertaking": 18457, - "premise": 18458, - "cecilia": 18459, - "affiliates": 18460, - "blasted": 18461, - "conditional": 18462, - "wilder": 18463, - "minors": 18464, - "drone": 18465, - "rudolph": 18466, - "buffy": 18467, - "swallowing": 18468, - "horton": 18469, - "attested": 18470, - "##hop": 18471, - "rutherford": 18472, - "howell": 18473, - "primetime": 18474, - "livery": 18475, - "penal": 18476, - "##bis": 18477, - "minimize": 18478, - "hydro": 18479, - "wrecked": 18480, - "wrought": 18481, - "palazzo": 18482, - "##gling": 18483, - "cans": 18484, - "vernacular": 18485, - "friedman": 18486, - "nobleman": 18487, - "shale": 18488, - "walnut": 18489, - "danielle": 18490, - "##ection": 18491, - "##tley": 18492, - "sears": 18493, - "##kumar": 18494, - "chords": 18495, - "lend": 18496, - "flipping": 18497, - "streamed": 18498, - "por": 18499, - "dracula": 18500, - "gallons": 18501, - "sacrifices": 18502, - "gamble": 18503, - "orphanage": 18504, - "##iman": 18505, - "mckenzie": 18506, - "##gible": 18507, - "boxers": 18508, - "daly": 18509, - "##balls": 18510, - "##ان": 18511, - "208": 18512, - "##ific": 18513, - "##rative": 18514, - "##iq": 18515, - "exploited": 18516, - "slated": 18517, - "##uity": 18518, - "circling": 18519, - "hillary": 18520, - "pinched": 18521, - "goldberg": 18522, - "provost": 18523, - "campaigning": 18524, - "lim": 18525, - "piles": 18526, - "ironically": 18527, - "jong": 18528, - "mohan": 18529, - "successors": 18530, - "usaf": 18531, - "##tem": 18532, - "##ught": 18533, - "autobiographical": 18534, - "haute": 18535, - "preserves": 18536, - "##ending": 18537, - "acquitted": 18538, - "comparisons": 18539, - "203": 18540, - "hydroelectric": 18541, - "gangs": 18542, - "cypriot": 18543, - "torpedoes": 18544, - "rushes": 18545, - "chrome": 18546, - "derive": 18547, - "bumps": 18548, - "instability": 18549, - "fiat": 18550, - "pets": 18551, - "##mbe": 18552, - "silas": 18553, - "dye": 18554, - "reckless": 18555, - "settler": 18556, - "##itation": 18557, - "info": 18558, - "heats": 18559, - "##writing": 18560, - "176": 18561, - "canonical": 18562, - "maltese": 18563, - "fins": 18564, - "mushroom": 18565, - "stacy": 18566, - "aspen": 18567, - "avid": 18568, - "##kur": 18569, - "##loading": 18570, - "vickers": 18571, - "gaston": 18572, - "hillside": 18573, - "statutes": 18574, - "wilde": 18575, - "gail": 18576, - "kung": 18577, - "sabine": 18578, - "comfortably": 18579, - "motorcycles": 18580, - "##rgo": 18581, - "169": 18582, - "pneumonia": 18583, - "fetch": 18584, - "##sonic": 18585, - "axel": 18586, - "faintly": 18587, - "parallels": 18588, - "##oop": 18589, - "mclaren": 18590, - "spouse": 18591, - "compton": 18592, - "interdisciplinary": 18593, - "miner": 18594, - "##eni": 18595, - "181": 18596, - "clamped": 18597, - "##chal": 18598, - "##llah": 18599, - "separates": 18600, - "versa": 18601, - "##mler": 18602, - "scarborough": 18603, - "labrador": 18604, - "##lity": 18605, - "##osing": 18606, - "rutgers": 18607, - "hurdles": 18608, - "como": 18609, - "166": 18610, - "burt": 18611, - "divers": 18612, - "##100": 18613, - "wichita": 18614, - "cade": 18615, - "coincided": 18616, - "##erson": 18617, - "bruised": 18618, - "mla": 18619, - "##pper": 18620, - "vineyard": 18621, - "##ili": 18622, - "##brush": 18623, - "notch": 18624, - "mentioning": 18625, - "jase": 18626, - "hearted": 18627, - "kits": 18628, - "doe": 18629, - "##acle": 18630, - "pomerania": 18631, - "##ady": 18632, - "ronan": 18633, - "seizure": 18634, - "pavel": 18635, - "problematic": 18636, - "##zaki": 18637, - "domenico": 18638, - "##ulin": 18639, - "catering": 18640, - "penelope": 18641, - "dependence": 18642, - "parental": 18643, - "emilio": 18644, - "ministerial": 18645, - "atkinson": 18646, - "##bolic": 18647, - "clarkson": 18648, - "chargers": 18649, - "colby": 18650, - "grill": 18651, - "peeked": 18652, - "arises": 18653, - "summon": 18654, - "##aged": 18655, - "fools": 18656, - "##grapher": 18657, - "faculties": 18658, - "qaeda": 18659, - "##vial": 18660, - "garner": 18661, - "refurbished": 18662, - "##hwa": 18663, - "geelong": 18664, - "disasters": 18665, - "nudged": 18666, - "bs": 18667, - "shareholder": 18668, - "lori": 18669, - "algae": 18670, - "reinstated": 18671, - "rot": 18672, - "##ades": 18673, - "##nous": 18674, - "invites": 18675, - "stainless": 18676, - "183": 18677, - "inclusive": 18678, - "##itude": 18679, - "diocesan": 18680, - "til": 18681, - "##icz": 18682, - "denomination": 18683, - "##xa": 18684, - "benton": 18685, - "floral": 18686, - "registers": 18687, - "##ider": 18688, - "##erman": 18689, - "##kell": 18690, - "absurd": 18691, - "brunei": 18692, - "guangzhou": 18693, - "hitter": 18694, - "retaliation": 18695, - "##uled": 18696, - "##eve": 18697, - "blanc": 18698, - "nh": 18699, - "consistency": 18700, - "contamination": 18701, - "##eres": 18702, - "##rner": 18703, - "dire": 18704, - "palermo": 18705, - "broadcasters": 18706, - "diaries": 18707, - "inspire": 18708, - "vols": 18709, - "brewer": 18710, - "tightening": 18711, - "ky": 18712, - "mixtape": 18713, - "hormone": 18714, - "##tok": 18715, - "stokes": 18716, - "##color": 18717, - "##dly": 18718, - "##ssi": 18719, - "pg": 18720, - "##ometer": 18721, - "##lington": 18722, - "sanitation": 18723, - "##tility": 18724, - "intercontinental": 18725, - "apps": 18726, - "##adt": 18727, - "¹⁄₂": 18728, - "cylinders": 18729, - "economies": 18730, - "favourable": 18731, - "unison": 18732, - "croix": 18733, - "gertrude": 18734, - "odyssey": 18735, - "vanity": 18736, - "dangling": 18737, - "##logists": 18738, - "upgrades": 18739, - "dice": 18740, - "middleweight": 18741, - "practitioner": 18742, - "##ight": 18743, - "206": 18744, - "henrik": 18745, - "parlor": 18746, - "orion": 18747, - "angered": 18748, - "lac": 18749, - "python": 18750, - "blurted": 18751, - "##rri": 18752, - "sensual": 18753, - "intends": 18754, - "swings": 18755, - "angled": 18756, - "##phs": 18757, - "husky": 18758, - "attain": 18759, - "peerage": 18760, - "precinct": 18761, - "textiles": 18762, - "cheltenham": 18763, - "shuffled": 18764, - "dai": 18765, - "confess": 18766, - "tasting": 18767, - "bhutan": 18768, - "##riation": 18769, - "tyrone": 18770, - "segregation": 18771, - "abrupt": 18772, - "ruiz": 18773, - "##rish": 18774, - "smirked": 18775, - "blackwell": 18776, - "confidential": 18777, - "browning": 18778, - "amounted": 18779, - "##put": 18780, - "vase": 18781, - "scarce": 18782, - "fabulous": 18783, - "raided": 18784, - "staple": 18785, - "guyana": 18786, - "unemployed": 18787, - "glider": 18788, - "shay": 18789, - "##tow": 18790, - "carmine": 18791, - "troll": 18792, - "intervene": 18793, - "squash": 18794, - "superstar": 18795, - "##uce": 18796, - "cylindrical": 18797, - "len": 18798, - "roadway": 18799, - "researched": 18800, - "handy": 18801, - "##rium": 18802, - "##jana": 18803, - "meta": 18804, - "lao": 18805, - "declares": 18806, - "##rring": 18807, - "##tadt": 18808, - "##elin": 18809, - "##kova": 18810, - "willem": 18811, - "shrubs": 18812, - "napoleonic": 18813, - "realms": 18814, - "skater": 18815, - "qi": 18816, - "volkswagen": 18817, - "##ł": 18818, - "tad": 18819, - "hara": 18820, - "archaeologist": 18821, - "awkwardly": 18822, - "eerie": 18823, - "##kind": 18824, - "wiley": 18825, - "##heimer": 18826, - "##24": 18827, - "titus": 18828, - "organizers": 18829, - "cfl": 18830, - "crusaders": 18831, - "lama": 18832, - "usb": 18833, - "vent": 18834, - "enraged": 18835, - "thankful": 18836, - "occupants": 18837, - "maximilian": 18838, - "##gaard": 18839, - "possessing": 18840, - "textbooks": 18841, - "##oran": 18842, - "collaborator": 18843, - "quaker": 18844, - "##ulo": 18845, - "avalanche": 18846, - "mono": 18847, - "silky": 18848, - "straits": 18849, - "isaiah": 18850, - "mustang": 18851, - "surged": 18852, - "resolutions": 18853, - "potomac": 18854, - "descend": 18855, - "cl": 18856, - "kilograms": 18857, - "plato": 18858, - "strains": 18859, - "saturdays": 18860, - "##olin": 18861, - "bernstein": 18862, - "##ype": 18863, - "holstein": 18864, - "ponytail": 18865, - "##watch": 18866, - "belize": 18867, - "conversely": 18868, - "heroine": 18869, - "perpetual": 18870, - "##ylus": 18871, - "charcoal": 18872, - "piedmont": 18873, - "glee": 18874, - "negotiating": 18875, - "backdrop": 18876, - "prologue": 18877, - "##jah": 18878, - "##mmy": 18879, - "pasadena": 18880, - "climbs": 18881, - "ramos": 18882, - "sunni": 18883, - "##holm": 18884, - "##tner": 18885, - "##tri": 18886, - "anand": 18887, - "deficiency": 18888, - "hertfordshire": 18889, - "stout": 18890, - "##avi": 18891, - "aperture": 18892, - "orioles": 18893, - "##irs": 18894, - "doncaster": 18895, - "intrigued": 18896, - "bombed": 18897, - "coating": 18898, - "otis": 18899, - "##mat": 18900, - "cocktail": 18901, - "##jit": 18902, - "##eto": 18903, - "amir": 18904, - "arousal": 18905, - "sar": 18906, - "##proof": 18907, - "##act": 18908, - "##ories": 18909, - "dixie": 18910, - "pots": 18911, - "##bow": 18912, - "whereabouts": 18913, - "159": 18914, - "##fted": 18915, - "drains": 18916, - "bullying": 18917, - "cottages": 18918, - "scripture": 18919, - "coherent": 18920, - "fore": 18921, - "poe": 18922, - "appetite": 18923, - "##uration": 18924, - "sampled": 18925, - "##ators": 18926, - "##dp": 18927, - "derrick": 18928, - "rotor": 18929, - "jays": 18930, - "peacock": 18931, - "installment": 18932, - "##rro": 18933, - "advisors": 18934, - "##coming": 18935, - "rodeo": 18936, - "scotch": 18937, - "##mot": 18938, - "##db": 18939, - "##fen": 18940, - "##vant": 18941, - "ensued": 18942, - "rodrigo": 18943, - "dictatorship": 18944, - "martyrs": 18945, - "twenties": 18946, - "##н": 18947, - "towed": 18948, - "incidence": 18949, - "marta": 18950, - "rainforest": 18951, - "sai": 18952, - "scaled": 18953, - "##cles": 18954, - "oceanic": 18955, - "qualifiers": 18956, - "symphonic": 18957, - "mcbride": 18958, - "dislike": 18959, - "generalized": 18960, - "aubrey": 18961, - "colonization": 18962, - "##iation": 18963, - "##lion": 18964, - "##ssing": 18965, - "disliked": 18966, - "lublin": 18967, - "salesman": 18968, - "##ulates": 18969, - "spherical": 18970, - "whatsoever": 18971, - "sweating": 18972, - "avalon": 18973, - "contention": 18974, - "punt": 18975, - "severity": 18976, - "alderman": 18977, - "atari": 18978, - "##dina": 18979, - "##grant": 18980, - "##rop": 18981, - "scarf": 18982, - "seville": 18983, - "vertices": 18984, - "annexation": 18985, - "fairfield": 18986, - "fascination": 18987, - "inspiring": 18988, - "launches": 18989, - "palatinate": 18990, - "regretted": 18991, - "##rca": 18992, - "feral": 18993, - "##iom": 18994, - "elk": 18995, - "nap": 18996, - "olsen": 18997, - "reddy": 18998, - "yong": 18999, - "##leader": 19000, - "##iae": 19001, - "garment": 19002, - "transports": 19003, - "feng": 19004, - "gracie": 19005, - "outrage": 19006, - "viceroy": 19007, - "insides": 19008, - "##esis": 19009, - "breakup": 19010, - "grady": 19011, - "organizer": 19012, - "softer": 19013, - "grimaced": 19014, - "222": 19015, - "murals": 19016, - "galicia": 19017, - "arranging": 19018, - "vectors": 19019, - "##rsten": 19020, - "bas": 19021, - "##sb": 19022, - "##cens": 19023, - "sloan": 19024, - "##eka": 19025, - "bitten": 19026, - "ara": 19027, - "fender": 19028, - "nausea": 19029, - "bumped": 19030, - "kris": 19031, - "banquet": 19032, - "comrades": 19033, - "detector": 19034, - "persisted": 19035, - "##llan": 19036, - "adjustment": 19037, - "endowed": 19038, - "cinemas": 19039, - "##shot": 19040, - "sellers": 19041, - "##uman": 19042, - "peek": 19043, - "epa": 19044, - "kindly": 19045, - "neglect": 19046, - "simpsons": 19047, - "talon": 19048, - "mausoleum": 19049, - "runaway": 19050, - "hangul": 19051, - "lookout": 19052, - "##cic": 19053, - "rewards": 19054, - "coughed": 19055, - "acquainted": 19056, - "chloride": 19057, - "##ald": 19058, - "quicker": 19059, - "accordion": 19060, - "neolithic": 19061, - "##qa": 19062, - "artemis": 19063, - "coefficient": 19064, - "lenny": 19065, - "pandora": 19066, - "tx": 19067, - "##xed": 19068, - "ecstasy": 19069, - "litter": 19070, - "segunda": 19071, - "chairperson": 19072, - "gemma": 19073, - "hiss": 19074, - "rumor": 19075, - "vow": 19076, - "nasal": 19077, - "antioch": 19078, - "compensate": 19079, - "patiently": 19080, - "transformers": 19081, - "##eded": 19082, - "judo": 19083, - "morrow": 19084, - "penis": 19085, - "posthumous": 19086, - "philips": 19087, - "bandits": 19088, - "husbands": 19089, - "denote": 19090, - "flaming": 19091, - "##any": 19092, - "##phones": 19093, - "langley": 19094, - "yorker": 19095, - "1760": 19096, - "walters": 19097, - "##uo": 19098, - "##kle": 19099, - "gubernatorial": 19100, - "fatty": 19101, - "samsung": 19102, - "leroy": 19103, - "outlaw": 19104, - "##nine": 19105, - "unpublished": 19106, - "poole": 19107, - "jakob": 19108, - "##ᵢ": 19109, - "##ₙ": 19110, - "crete": 19111, - "distorted": 19112, - "superiority": 19113, - "##dhi": 19114, - "intercept": 19115, - "crust": 19116, - "mig": 19117, - "claus": 19118, - "crashes": 19119, - "positioning": 19120, - "188": 19121, - "stallion": 19122, - "301": 19123, - "frontal": 19124, - "armistice": 19125, - "##estinal": 19126, - "elton": 19127, - "aj": 19128, - "encompassing": 19129, - "camel": 19130, - "commemorated": 19131, - "malaria": 19132, - "woodward": 19133, - "calf": 19134, - "cigar": 19135, - "penetrate": 19136, - "##oso": 19137, - "willard": 19138, - "##rno": 19139, - "##uche": 19140, - "illustrate": 19141, - "amusing": 19142, - "convergence": 19143, - "noteworthy": 19144, - "##lma": 19145, - "##rva": 19146, - "journeys": 19147, - "realise": 19148, - "manfred": 19149, - "##sable": 19150, - "410": 19151, - "##vocation": 19152, - "hearings": 19153, - "fiance": 19154, - "##posed": 19155, - "educators": 19156, - "provoked": 19157, - "adjusting": 19158, - "##cturing": 19159, - "modular": 19160, - "stockton": 19161, - "paterson": 19162, - "vlad": 19163, - "rejects": 19164, - "electors": 19165, - "selena": 19166, - "maureen": 19167, - "##tres": 19168, - "uber": 19169, - "##rce": 19170, - "swirled": 19171, - "##num": 19172, - "proportions": 19173, - "nanny": 19174, - "pawn": 19175, - "naturalist": 19176, - "parma": 19177, - "apostles": 19178, - "awoke": 19179, - "ethel": 19180, - "wen": 19181, - "##bey": 19182, - "monsoon": 19183, - "overview": 19184, - "##inating": 19185, - "mccain": 19186, - "rendition": 19187, - "risky": 19188, - "adorned": 19189, - "##ih": 19190, - "equestrian": 19191, - "germain": 19192, - "nj": 19193, - "conspicuous": 19194, - "confirming": 19195, - "##yoshi": 19196, - "shivering": 19197, - "##imeter": 19198, - "milestone": 19199, - "rumours": 19200, - "flinched": 19201, - "bounds": 19202, - "smacked": 19203, - "token": 19204, - "##bei": 19205, - "lectured": 19206, - "automobiles": 19207, - "##shore": 19208, - "impacted": 19209, - "##iable": 19210, - "nouns": 19211, - "nero": 19212, - "##leaf": 19213, - "ismail": 19214, - "prostitute": 19215, - "trams": 19216, - "##lace": 19217, - "bridget": 19218, - "sud": 19219, - "stimulus": 19220, - "impressions": 19221, - "reins": 19222, - "revolves": 19223, - "##oud": 19224, - "##gned": 19225, - "giro": 19226, - "honeymoon": 19227, - "##swell": 19228, - "criterion": 19229, - "##sms": 19230, - "##uil": 19231, - "libyan": 19232, - "prefers": 19233, - "##osition": 19234, - "211": 19235, - "preview": 19236, - "sucks": 19237, - "accusation": 19238, - "bursts": 19239, - "metaphor": 19240, - "diffusion": 19241, - "tolerate": 19242, - "faye": 19243, - "betting": 19244, - "cinematographer": 19245, - "liturgical": 19246, - "specials": 19247, - "bitterly": 19248, - "humboldt": 19249, - "##ckle": 19250, - "flux": 19251, - "rattled": 19252, - "##itzer": 19253, - "archaeologists": 19254, - "odor": 19255, - "authorised": 19256, - "marshes": 19257, - "discretion": 19258, - "##ов": 19259, - "alarmed": 19260, - "archaic": 19261, - "inverse": 19262, - "##leton": 19263, - "explorers": 19264, - "##pine": 19265, - "drummond": 19266, - "tsunami": 19267, - "woodlands": 19268, - "##minate": 19269, - "##tland": 19270, - "booklet": 19271, - "insanity": 19272, - "owning": 19273, - "insert": 19274, - "crafted": 19275, - "calculus": 19276, - "##tore": 19277, - "receivers": 19278, - "##bt": 19279, - "stung": 19280, - "##eca": 19281, - "##nched": 19282, - "prevailing": 19283, - "travellers": 19284, - "eyeing": 19285, - "lila": 19286, - "graphs": 19287, - "##borne": 19288, - "178": 19289, - "julien": 19290, - "##won": 19291, - "morale": 19292, - "adaptive": 19293, - "therapist": 19294, - "erica": 19295, - "cw": 19296, - "libertarian": 19297, - "bowman": 19298, - "pitches": 19299, - "vita": 19300, - "##ional": 19301, - "crook": 19302, - "##ads": 19303, - "##entation": 19304, - "caledonia": 19305, - "mutiny": 19306, - "##sible": 19307, - "1840s": 19308, - "automation": 19309, - "##ß": 19310, - "flock": 19311, - "##pia": 19312, - "ironic": 19313, - "pathology": 19314, - "##imus": 19315, - "remarried": 19316, - "##22": 19317, - "joker": 19318, - "withstand": 19319, - "energies": 19320, - "##att": 19321, - "shropshire": 19322, - "hostages": 19323, - "madeleine": 19324, - "tentatively": 19325, - "conflicting": 19326, - "mateo": 19327, - "recipes": 19328, - "euros": 19329, - "ol": 19330, - "mercenaries": 19331, - "nico": 19332, - "##ndon": 19333, - "albuquerque": 19334, - "augmented": 19335, - "mythical": 19336, - "bel": 19337, - "freud": 19338, - "##child": 19339, - "cough": 19340, - "##lica": 19341, - "365": 19342, - "freddy": 19343, - "lillian": 19344, - "genetically": 19345, - "nuremberg": 19346, - "calder": 19347, - "209": 19348, - "bonn": 19349, - "outdoors": 19350, - "paste": 19351, - "suns": 19352, - "urgency": 19353, - "vin": 19354, - "restraint": 19355, - "tyson": 19356, - "##cera": 19357, - "##selle": 19358, - "barrage": 19359, - "bethlehem": 19360, - "kahn": 19361, - "##par": 19362, - "mounts": 19363, - "nippon": 19364, - "barony": 19365, - "happier": 19366, - "ryu": 19367, - "makeshift": 19368, - "sheldon": 19369, - "blushed": 19370, - "castillo": 19371, - "barking": 19372, - "listener": 19373, - "taped": 19374, - "bethel": 19375, - "fluent": 19376, - "headlines": 19377, - "pornography": 19378, - "rum": 19379, - "disclosure": 19380, - "sighing": 19381, - "mace": 19382, - "doubling": 19383, - "gunther": 19384, - "manly": 19385, - "##plex": 19386, - "rt": 19387, - "interventions": 19388, - "physiological": 19389, - "forwards": 19390, - "emerges": 19391, - "##tooth": 19392, - "##gny": 19393, - "compliment": 19394, - "rib": 19395, - "recession": 19396, - "visibly": 19397, - "barge": 19398, - "faults": 19399, - "connector": 19400, - "exquisite": 19401, - "prefect": 19402, - "##rlin": 19403, - "patio": 19404, - "##cured": 19405, - "elevators": 19406, - "brandt": 19407, - "italics": 19408, - "pena": 19409, - "173": 19410, - "wasp": 19411, - "satin": 19412, - "ea": 19413, - "botswana": 19414, - "graceful": 19415, - "respectable": 19416, - "##jima": 19417, - "##rter": 19418, - "##oic": 19419, - "franciscan": 19420, - "generates": 19421, - "##dl": 19422, - "alfredo": 19423, - "disgusting": 19424, - "##olate": 19425, - "##iously": 19426, - "sherwood": 19427, - "warns": 19428, - "cod": 19429, - "promo": 19430, - "cheryl": 19431, - "sino": 19432, - "##ة": 19433, - "##escu": 19434, - "twitch": 19435, - "##zhi": 19436, - "brownish": 19437, - "thom": 19438, - "ortiz": 19439, - "##dron": 19440, - "densely": 19441, - "##beat": 19442, - "carmel": 19443, - "reinforce": 19444, - "##bana": 19445, - "187": 19446, - "anastasia": 19447, - "downhill": 19448, - "vertex": 19449, - "contaminated": 19450, - "remembrance": 19451, - "harmonic": 19452, - "homework": 19453, - "##sol": 19454, - "fiancee": 19455, - "gears": 19456, - "olds": 19457, - "angelica": 19458, - "loft": 19459, - "ramsay": 19460, - "quiz": 19461, - "colliery": 19462, - "sevens": 19463, - "##cape": 19464, - "autism": 19465, - "##hil": 19466, - "walkway": 19467, - "##boats": 19468, - "ruben": 19469, - "abnormal": 19470, - "ounce": 19471, - "khmer": 19472, - "##bbe": 19473, - "zachary": 19474, - "bedside": 19475, - "morphology": 19476, - "punching": 19477, - "##olar": 19478, - "sparrow": 19479, - "convinces": 19480, - "##35": 19481, - "hewitt": 19482, - "queer": 19483, - "remastered": 19484, - "rods": 19485, - "mabel": 19486, - "solemn": 19487, - "notified": 19488, - "lyricist": 19489, - "symmetric": 19490, - "##xide": 19491, - "174": 19492, - "encore": 19493, - "passports": 19494, - "wildcats": 19495, - "##uni": 19496, - "baja": 19497, - "##pac": 19498, - "mildly": 19499, - "##ease": 19500, - "bleed": 19501, - "commodity": 19502, - "mounds": 19503, - "glossy": 19504, - "orchestras": 19505, - "##omo": 19506, - "damian": 19507, - "prelude": 19508, - "ambitions": 19509, - "##vet": 19510, - "awhile": 19511, - "remotely": 19512, - "##aud": 19513, - "asserts": 19514, - "imply": 19515, - "##iques": 19516, - "distinctly": 19517, - "modelling": 19518, - "remedy": 19519, - "##dded": 19520, - "windshield": 19521, - "dani": 19522, - "xiao": 19523, - "##endra": 19524, - "audible": 19525, - "powerplant": 19526, - "1300": 19527, - "invalid": 19528, - "elemental": 19529, - "acquisitions": 19530, - "##hala": 19531, - "immaculate": 19532, - "libby": 19533, - "plata": 19534, - "smuggling": 19535, - "ventilation": 19536, - "denoted": 19537, - "minh": 19538, - "##morphism": 19539, - "430": 19540, - "differed": 19541, - "dion": 19542, - "kelley": 19543, - "lore": 19544, - "mocking": 19545, - "sabbath": 19546, - "spikes": 19547, - "hygiene": 19548, - "drown": 19549, - "runoff": 19550, - "stylized": 19551, - "tally": 19552, - "liberated": 19553, - "aux": 19554, - "interpreter": 19555, - "righteous": 19556, - "aba": 19557, - "siren": 19558, - "reaper": 19559, - "pearce": 19560, - "millie": 19561, - "##cier": 19562, - "##yra": 19563, - "gaius": 19564, - "##iso": 19565, - "captures": 19566, - "##ttering": 19567, - "dorm": 19568, - "claudio": 19569, - "##sic": 19570, - "benches": 19571, - "knighted": 19572, - "blackness": 19573, - "##ored": 19574, - "discount": 19575, - "fumble": 19576, - "oxidation": 19577, - "routed": 19578, - "##ς": 19579, - "novak": 19580, - "perpendicular": 19581, - "spoiled": 19582, - "fracture": 19583, - "splits": 19584, - "##urt": 19585, - "pads": 19586, - "topology": 19587, - "##cats": 19588, - "axes": 19589, - "fortunate": 19590, - "offenders": 19591, - "protestants": 19592, - "esteem": 19593, - "221": 19594, - "broadband": 19595, - "convened": 19596, - "frankly": 19597, - "hound": 19598, - "prototypes": 19599, - "isil": 19600, - "facilitated": 19601, - "keel": 19602, - "##sher": 19603, - "sahara": 19604, - "awaited": 19605, - "bubba": 19606, - "orb": 19607, - "prosecutors": 19608, - "186": 19609, - "hem": 19610, - "520": 19611, - "##xing": 19612, - "relaxing": 19613, - "remnant": 19614, - "romney": 19615, - "sorted": 19616, - "slalom": 19617, - "stefano": 19618, - "ulrich": 19619, - "##active": 19620, - "exemption": 19621, - "folder": 19622, - "pauses": 19623, - "foliage": 19624, - "hitchcock": 19625, - "epithet": 19626, - "204": 19627, - "criticisms": 19628, - "##aca": 19629, - "ballistic": 19630, - "brody": 19631, - "hinduism": 19632, - "chaotic": 19633, - "youths": 19634, - "equals": 19635, - "##pala": 19636, - "pts": 19637, - "thicker": 19638, - "analogous": 19639, - "capitalist": 19640, - "improvised": 19641, - "overseeing": 19642, - "sinatra": 19643, - "ascended": 19644, - "beverage": 19645, - "##tl": 19646, - "straightforward": 19647, - "##kon": 19648, - "curran": 19649, - "##west": 19650, - "bois": 19651, - "325": 19652, - "induce": 19653, - "surveying": 19654, - "emperors": 19655, - "sax": 19656, - "unpopular": 19657, - "##kk": 19658, - "cartoonist": 19659, - "fused": 19660, - "##mble": 19661, - "unto": 19662, - "##yuki": 19663, - "localities": 19664, - "##cko": 19665, - "##ln": 19666, - "darlington": 19667, - "slain": 19668, - "academie": 19669, - "lobbying": 19670, - "sediment": 19671, - "puzzles": 19672, - "##grass": 19673, - "defiance": 19674, - "dickens": 19675, - "manifest": 19676, - "tongues": 19677, - "alumnus": 19678, - "arbor": 19679, - "coincide": 19680, - "184": 19681, - "appalachian": 19682, - "mustafa": 19683, - "examiner": 19684, - "cabaret": 19685, - "traumatic": 19686, - "yves": 19687, - "bracelet": 19688, - "draining": 19689, - "heroin": 19690, - "magnum": 19691, - "baths": 19692, - "odessa": 19693, - "consonants": 19694, - "mitsubishi": 19695, - "##gua": 19696, - "kellan": 19697, - "vaudeville": 19698, - "##fr": 19699, - "joked": 19700, - "null": 19701, - "straps": 19702, - "probation": 19703, - "##ław": 19704, - "ceded": 19705, - "interfaces": 19706, - "##pas": 19707, - "##zawa": 19708, - "blinding": 19709, - "viet": 19710, - "224": 19711, - "rothschild": 19712, - "museo": 19713, - "640": 19714, - "huddersfield": 19715, - "##vr": 19716, - "tactic": 19717, - "##storm": 19718, - "brackets": 19719, - "dazed": 19720, - "incorrectly": 19721, - "##vu": 19722, - "reg": 19723, - "glazed": 19724, - "fearful": 19725, - "manifold": 19726, - "benefited": 19727, - "irony": 19728, - "##sun": 19729, - "stumbling": 19730, - "##rte": 19731, - "willingness": 19732, - "balkans": 19733, - "mei": 19734, - "wraps": 19735, - "##aba": 19736, - "injected": 19737, - "##lea": 19738, - "gu": 19739, - "syed": 19740, - "harmless": 19741, - "##hammer": 19742, - "bray": 19743, - "takeoff": 19744, - "poppy": 19745, - "timor": 19746, - "cardboard": 19747, - "astronaut": 19748, - "purdue": 19749, - "weeping": 19750, - "southbound": 19751, - "cursing": 19752, - "stalls": 19753, - "diagonal": 19754, - "##neer": 19755, - "lamar": 19756, - "bryce": 19757, - "comte": 19758, - "weekdays": 19759, - "harrington": 19760, - "##uba": 19761, - "negatively": 19762, - "##see": 19763, - "lays": 19764, - "grouping": 19765, - "##cken": 19766, - "##henko": 19767, - "affirmed": 19768, - "halle": 19769, - "modernist": 19770, - "##lai": 19771, - "hodges": 19772, - "smelling": 19773, - "aristocratic": 19774, - "baptized": 19775, - "dismiss": 19776, - "justification": 19777, - "oilers": 19778, - "##now": 19779, - "coupling": 19780, - "qin": 19781, - "snack": 19782, - "healer": 19783, - "##qing": 19784, - "gardener": 19785, - "layla": 19786, - "battled": 19787, - "formulated": 19788, - "stephenson": 19789, - "gravitational": 19790, - "##gill": 19791, - "##jun": 19792, - "1768": 19793, - "granny": 19794, - "coordinating": 19795, - "suites": 19796, - "##cd": 19797, - "##ioned": 19798, - "monarchs": 19799, - "##cote": 19800, - "##hips": 19801, - "sep": 19802, - "blended": 19803, - "apr": 19804, - "barrister": 19805, - "deposition": 19806, - "fia": 19807, - "mina": 19808, - "policemen": 19809, - "paranoid": 19810, - "##pressed": 19811, - "churchyard": 19812, - "covert": 19813, - "crumpled": 19814, - "creep": 19815, - "abandoning": 19816, - "tr": 19817, - "transmit": 19818, - "conceal": 19819, - "barr": 19820, - "understands": 19821, - "readiness": 19822, - "spire": 19823, - "##cology": 19824, - "##enia": 19825, - "##erry": 19826, - "610": 19827, - "startling": 19828, - "unlock": 19829, - "vida": 19830, - "bowled": 19831, - "slots": 19832, - "##nat": 19833, - "##islav": 19834, - "spaced": 19835, - "trusting": 19836, - "admire": 19837, - "rig": 19838, - "##ink": 19839, - "slack": 19840, - "##70": 19841, - "mv": 19842, - "207": 19843, - "casualty": 19844, - "##wei": 19845, - "classmates": 19846, - "##odes": 19847, - "##rar": 19848, - "##rked": 19849, - "amherst": 19850, - "furnished": 19851, - "evolve": 19852, - "foundry": 19853, - "menace": 19854, - "mead": 19855, - "##lein": 19856, - "flu": 19857, - "wesleyan": 19858, - "##kled": 19859, - "monterey": 19860, - "webber": 19861, - "##vos": 19862, - "wil": 19863, - "##mith": 19864, - "##на": 19865, - "bartholomew": 19866, - "justices": 19867, - "restrained": 19868, - "##cke": 19869, - "amenities": 19870, - "191": 19871, - "mediated": 19872, - "sewage": 19873, - "trenches": 19874, - "ml": 19875, - "mainz": 19876, - "##thus": 19877, - "1800s": 19878, - "##cula": 19879, - "##inski": 19880, - "caine": 19881, - "bonding": 19882, - "213": 19883, - "converts": 19884, - "spheres": 19885, - "superseded": 19886, - "marianne": 19887, - "crypt": 19888, - "sweaty": 19889, - "ensign": 19890, - "historia": 19891, - "##br": 19892, - "spruce": 19893, - "##post": 19894, - "##ask": 19895, - "forks": 19896, - "thoughtfully": 19897, - "yukon": 19898, - "pamphlet": 19899, - "ames": 19900, - "##uter": 19901, - "karma": 19902, - "##yya": 19903, - "bryn": 19904, - "negotiation": 19905, - "sighs": 19906, - "incapable": 19907, - "##mbre": 19908, - "##ntial": 19909, - "actresses": 19910, - "taft": 19911, - "##mill": 19912, - "luce": 19913, - "prevailed": 19914, - "##amine": 19915, - "1773": 19916, - "motionless": 19917, - "envoy": 19918, - "testify": 19919, - "investing": 19920, - "sculpted": 19921, - "instructors": 19922, - "provence": 19923, - "kali": 19924, - "cullen": 19925, - "horseback": 19926, - "##while": 19927, - "goodwin": 19928, - "##jos": 19929, - "gaa": 19930, - "norte": 19931, - "##ldon": 19932, - "modify": 19933, - "wavelength": 19934, - "abd": 19935, - "214": 19936, - "skinned": 19937, - "sprinter": 19938, - "forecast": 19939, - "scheduling": 19940, - "marries": 19941, - "squared": 19942, - "tentative": 19943, - "##chman": 19944, - "boer": 19945, - "##isch": 19946, - "bolts": 19947, - "swap": 19948, - "fisherman": 19949, - "assyrian": 19950, - "impatiently": 19951, - "guthrie": 19952, - "martins": 19953, - "murdoch": 19954, - "194": 19955, - "tanya": 19956, - "nicely": 19957, - "dolly": 19958, - "lacy": 19959, - "med": 19960, - "##45": 19961, - "syn": 19962, - "decks": 19963, - "fashionable": 19964, - "millionaire": 19965, - "##ust": 19966, - "surfing": 19967, - "##ml": 19968, - "##ision": 19969, - "heaved": 19970, - "tammy": 19971, - "consulate": 19972, - "attendees": 19973, - "routinely": 19974, - "197": 19975, - "fuse": 19976, - "saxophonist": 19977, - "backseat": 19978, - "malaya": 19979, - "##lord": 19980, - "scowl": 19981, - "tau": 19982, - "##ishly": 19983, - "193": 19984, - "sighted": 19985, - "steaming": 19986, - "##rks": 19987, - "303": 19988, - "911": 19989, - "##holes": 19990, - "##hong": 19991, - "ching": 19992, - "##wife": 19993, - "bless": 19994, - "conserved": 19995, - "jurassic": 19996, - "stacey": 19997, - "unix": 19998, - "zion": 19999, - "chunk": 20000, - "rigorous": 20001, - "blaine": 20002, - "198": 20003, - "peabody": 20004, - "slayer": 20005, - "dismay": 20006, - "brewers": 20007, - "nz": 20008, - "##jer": 20009, - "det": 20010, - "##glia": 20011, - "glover": 20012, - "postwar": 20013, - "int": 20014, - "penetration": 20015, - "sylvester": 20016, - "imitation": 20017, - "vertically": 20018, - "airlift": 20019, - "heiress": 20020, - "knoxville": 20021, - "viva": 20022, - "##uin": 20023, - "390": 20024, - "macon": 20025, - "##rim": 20026, - "##fighter": 20027, - "##gonal": 20028, - "janice": 20029, - "##orescence": 20030, - "##wari": 20031, - "marius": 20032, - "belongings": 20033, - "leicestershire": 20034, - "196": 20035, - "blanco": 20036, - "inverted": 20037, - "preseason": 20038, - "sanity": 20039, - "sobbing": 20040, - "##due": 20041, - "##elt": 20042, - "##dled": 20043, - "collingwood": 20044, - "regeneration": 20045, - "flickering": 20046, - "shortest": 20047, - "##mount": 20048, - "##osi": 20049, - "feminism": 20050, - "##lat": 20051, - "sherlock": 20052, - "cabinets": 20053, - "fumbled": 20054, - "northbound": 20055, - "precedent": 20056, - "snaps": 20057, - "##mme": 20058, - "researching": 20059, - "##akes": 20060, - "guillaume": 20061, - "insights": 20062, - "manipulated": 20063, - "vapor": 20064, - "neighbour": 20065, - "sap": 20066, - "gangster": 20067, - "frey": 20068, - "f1": 20069, - "stalking": 20070, - "scarcely": 20071, - "callie": 20072, - "barnett": 20073, - "tendencies": 20074, - "audi": 20075, - "doomed": 20076, - "assessing": 20077, - "slung": 20078, - "panchayat": 20079, - "ambiguous": 20080, - "bartlett": 20081, - "##etto": 20082, - "distributing": 20083, - "violating": 20084, - "wolverhampton": 20085, - "##hetic": 20086, - "swami": 20087, - "histoire": 20088, - "##urus": 20089, - "liable": 20090, - "pounder": 20091, - "groin": 20092, - "hussain": 20093, - "larsen": 20094, - "popping": 20095, - "surprises": 20096, - "##atter": 20097, - "vie": 20098, - "curt": 20099, - "##station": 20100, - "mute": 20101, - "relocate": 20102, - "musicals": 20103, - "authorization": 20104, - "richter": 20105, - "##sef": 20106, - "immortality": 20107, - "tna": 20108, - "bombings": 20109, - "##press": 20110, - "deteriorated": 20111, - "yiddish": 20112, - "##acious": 20113, - "robbed": 20114, - "colchester": 20115, - "cs": 20116, - "pmid": 20117, - "ao": 20118, - "verified": 20119, - "balancing": 20120, - "apostle": 20121, - "swayed": 20122, - "recognizable": 20123, - "oxfordshire": 20124, - "retention": 20125, - "nottinghamshire": 20126, - "contender": 20127, - "judd": 20128, - "invitational": 20129, - "shrimp": 20130, - "uhf": 20131, - "##icient": 20132, - "cleaner": 20133, - "longitudinal": 20134, - "tanker": 20135, - "##mur": 20136, - "acronym": 20137, - "broker": 20138, - "koppen": 20139, - "sundance": 20140, - "suppliers": 20141, - "##gil": 20142, - "4000": 20143, - "clipped": 20144, - "fuels": 20145, - "petite": 20146, - "##anne": 20147, - "landslide": 20148, - "helene": 20149, - "diversion": 20150, - "populous": 20151, - "landowners": 20152, - "auspices": 20153, - "melville": 20154, - "quantitative": 20155, - "##xes": 20156, - "ferries": 20157, - "nicky": 20158, - "##llus": 20159, - "doo": 20160, - "haunting": 20161, - "roche": 20162, - "carver": 20163, - "downed": 20164, - "unavailable": 20165, - "##pathy": 20166, - "approximation": 20167, - "hiroshima": 20168, - "##hue": 20169, - "garfield": 20170, - "valle": 20171, - "comparatively": 20172, - "keyboardist": 20173, - "traveler": 20174, - "##eit": 20175, - "congestion": 20176, - "calculating": 20177, - "subsidiaries": 20178, - "##bate": 20179, - "serb": 20180, - "modernization": 20181, - "fairies": 20182, - "deepened": 20183, - "ville": 20184, - "averages": 20185, - "##lore": 20186, - "inflammatory": 20187, - "tonga": 20188, - "##itch": 20189, - "co₂": 20190, - "squads": 20191, - "##hea": 20192, - "gigantic": 20193, - "serum": 20194, - "enjoyment": 20195, - "retailer": 20196, - "verona": 20197, - "35th": 20198, - "cis": 20199, - "##phobic": 20200, - "magna": 20201, - "technicians": 20202, - "##vati": 20203, - "arithmetic": 20204, - "##sport": 20205, - "levin": 20206, - "##dation": 20207, - "amtrak": 20208, - "chow": 20209, - "sienna": 20210, - "##eyer": 20211, - "backstage": 20212, - "entrepreneurship": 20213, - "##otic": 20214, - "learnt": 20215, - "tao": 20216, - "##udy": 20217, - "worcestershire": 20218, - "formulation": 20219, - "baggage": 20220, - "hesitant": 20221, - "bali": 20222, - "sabotage": 20223, - "##kari": 20224, - "barren": 20225, - "enhancing": 20226, - "murmur": 20227, - "pl": 20228, - "freshly": 20229, - "putnam": 20230, - "syntax": 20231, - "aces": 20232, - "medicines": 20233, - "resentment": 20234, - "bandwidth": 20235, - "##sier": 20236, - "grins": 20237, - "chili": 20238, - "guido": 20239, - "##sei": 20240, - "framing": 20241, - "implying": 20242, - "gareth": 20243, - "lissa": 20244, - "genevieve": 20245, - "pertaining": 20246, - "admissions": 20247, - "geo": 20248, - "thorpe": 20249, - "proliferation": 20250, - "sato": 20251, - "bela": 20252, - "analyzing": 20253, - "parting": 20254, - "##gor": 20255, - "awakened": 20256, - "##isman": 20257, - "huddled": 20258, - "secrecy": 20259, - "##kling": 20260, - "hush": 20261, - "gentry": 20262, - "540": 20263, - "dungeons": 20264, - "##ego": 20265, - "coasts": 20266, - "##utz": 20267, - "sacrificed": 20268, - "##chule": 20269, - "landowner": 20270, - "mutually": 20271, - "prevalence": 20272, - "programmer": 20273, - "adolescent": 20274, - "disrupted": 20275, - "seaside": 20276, - "gee": 20277, - "trusts": 20278, - "vamp": 20279, - "georgie": 20280, - "##nesian": 20281, - "##iol": 20282, - "schedules": 20283, - "sindh": 20284, - "##market": 20285, - "etched": 20286, - "hm": 20287, - "sparse": 20288, - "bey": 20289, - "beaux": 20290, - "scratching": 20291, - "gliding": 20292, - "unidentified": 20293, - "216": 20294, - "collaborating": 20295, - "gems": 20296, - "jesuits": 20297, - "oro": 20298, - "accumulation": 20299, - "shaping": 20300, - "mbe": 20301, - "anal": 20302, - "##xin": 20303, - "231": 20304, - "enthusiasts": 20305, - "newscast": 20306, - "##egan": 20307, - "janata": 20308, - "dewey": 20309, - "parkinson": 20310, - "179": 20311, - "ankara": 20312, - "biennial": 20313, - "towering": 20314, - "dd": 20315, - "inconsistent": 20316, - "950": 20317, - "##chet": 20318, - "thriving": 20319, - "terminate": 20320, - "cabins": 20321, - "furiously": 20322, - "eats": 20323, - "advocating": 20324, - "donkey": 20325, - "marley": 20326, - "muster": 20327, - "phyllis": 20328, - "leiden": 20329, - "##user": 20330, - "grassland": 20331, - "glittering": 20332, - "iucn": 20333, - "loneliness": 20334, - "217": 20335, - "memorandum": 20336, - "armenians": 20337, - "##ddle": 20338, - "popularized": 20339, - "rhodesia": 20340, - "60s": 20341, - "lame": 20342, - "##illon": 20343, - "sans": 20344, - "bikini": 20345, - "header": 20346, - "orbits": 20347, - "##xx": 20348, - "##finger": 20349, - "##ulator": 20350, - "sharif": 20351, - "spines": 20352, - "biotechnology": 20353, - "strolled": 20354, - "naughty": 20355, - "yates": 20356, - "##wire": 20357, - "fremantle": 20358, - "milo": 20359, - "##mour": 20360, - "abducted": 20361, - "removes": 20362, - "##atin": 20363, - "humming": 20364, - "wonderland": 20365, - "##chrome": 20366, - "##ester": 20367, - "hume": 20368, - "pivotal": 20369, - "##rates": 20370, - "armand": 20371, - "grams": 20372, - "believers": 20373, - "elector": 20374, - "rte": 20375, - "apron": 20376, - "bis": 20377, - "scraped": 20378, - "##yria": 20379, - "endorsement": 20380, - "initials": 20381, - "##llation": 20382, - "eps": 20383, - "dotted": 20384, - "hints": 20385, - "buzzing": 20386, - "emigration": 20387, - "nearer": 20388, - "##tom": 20389, - "indicators": 20390, - "##ulu": 20391, - "coarse": 20392, - "neutron": 20393, - "protectorate": 20394, - "##uze": 20395, - "directional": 20396, - "exploits": 20397, - "pains": 20398, - "loire": 20399, - "1830s": 20400, - "proponents": 20401, - "guggenheim": 20402, - "rabbits": 20403, - "ritchie": 20404, - "305": 20405, - "hectare": 20406, - "inputs": 20407, - "hutton": 20408, - "##raz": 20409, - "verify": 20410, - "##ako": 20411, - "boilers": 20412, - "longitude": 20413, - "##lev": 20414, - "skeletal": 20415, - "yer": 20416, - "emilia": 20417, - "citrus": 20418, - "compromised": 20419, - "##gau": 20420, - "pokemon": 20421, - "prescription": 20422, - "paragraph": 20423, - "eduard": 20424, - "cadillac": 20425, - "attire": 20426, - "categorized": 20427, - "kenyan": 20428, - "weddings": 20429, - "charley": 20430, - "##bourg": 20431, - "entertain": 20432, - "monmouth": 20433, - "##lles": 20434, - "nutrients": 20435, - "davey": 20436, - "mesh": 20437, - "incentive": 20438, - "practised": 20439, - "ecosystems": 20440, - "kemp": 20441, - "subdued": 20442, - "overheard": 20443, - "##rya": 20444, - "bodily": 20445, - "maxim": 20446, - "##nius": 20447, - "apprenticeship": 20448, - "ursula": 20449, - "##fight": 20450, - "lodged": 20451, - "rug": 20452, - "silesian": 20453, - "unconstitutional": 20454, - "patel": 20455, - "inspected": 20456, - "coyote": 20457, - "unbeaten": 20458, - "##hak": 20459, - "34th": 20460, - "disruption": 20461, - "convict": 20462, - "parcel": 20463, - "##cl": 20464, - "##nham": 20465, - "collier": 20466, - "implicated": 20467, - "mallory": 20468, - "##iac": 20469, - "##lab": 20470, - "susannah": 20471, - "winkler": 20472, - "##rber": 20473, - "shia": 20474, - "phelps": 20475, - "sediments": 20476, - "graphical": 20477, - "robotic": 20478, - "##sner": 20479, - "adulthood": 20480, - "mart": 20481, - "smoked": 20482, - "##isto": 20483, - "kathryn": 20484, - "clarified": 20485, - "##aran": 20486, - "divides": 20487, - "convictions": 20488, - "oppression": 20489, - "pausing": 20490, - "burying": 20491, - "##mt": 20492, - "federico": 20493, - "mathias": 20494, - "eileen": 20495, - "##tana": 20496, - "kite": 20497, - "hunched": 20498, - "##acies": 20499, - "189": 20500, - "##atz": 20501, - "disadvantage": 20502, - "liza": 20503, - "kinetic": 20504, - "greedy": 20505, - "paradox": 20506, - "yokohama": 20507, - "dowager": 20508, - "trunks": 20509, - "ventured": 20510, - "##gement": 20511, - "gupta": 20512, - "vilnius": 20513, - "olaf": 20514, - "##thest": 20515, - "crimean": 20516, - "hopper": 20517, - "##ej": 20518, - "progressively": 20519, - "arturo": 20520, - "mouthed": 20521, - "arrondissement": 20522, - "##fusion": 20523, - "rubin": 20524, - "simulcast": 20525, - "oceania": 20526, - "##orum": 20527, - "##stra": 20528, - "##rred": 20529, - "busiest": 20530, - "intensely": 20531, - "navigator": 20532, - "cary": 20533, - "##vine": 20534, - "##hini": 20535, - "##bies": 20536, - "fife": 20537, - "rowe": 20538, - "rowland": 20539, - "posing": 20540, - "insurgents": 20541, - "shafts": 20542, - "lawsuits": 20543, - "activate": 20544, - "conor": 20545, - "inward": 20546, - "culturally": 20547, - "garlic": 20548, - "265": 20549, - "##eering": 20550, - "eclectic": 20551, - "##hui": 20552, - "##kee": 20553, - "##nl": 20554, - "furrowed": 20555, - "vargas": 20556, - "meteorological": 20557, - "rendezvous": 20558, - "##aus": 20559, - "culinary": 20560, - "commencement": 20561, - "##dition": 20562, - "quota": 20563, - "##notes": 20564, - "mommy": 20565, - "salaries": 20566, - "overlapping": 20567, - "mule": 20568, - "##iology": 20569, - "##mology": 20570, - "sums": 20571, - "wentworth": 20572, - "##isk": 20573, - "##zione": 20574, - "mainline": 20575, - "subgroup": 20576, - "##illy": 20577, - "hack": 20578, - "plaintiff": 20579, - "verdi": 20580, - "bulb": 20581, - "differentiation": 20582, - "engagements": 20583, - "multinational": 20584, - "supplemented": 20585, - "bertrand": 20586, - "caller": 20587, - "regis": 20588, - "##naire": 20589, - "##sler": 20590, - "##arts": 20591, - "##imated": 20592, - "blossom": 20593, - "propagation": 20594, - "kilometer": 20595, - "viaduct": 20596, - "vineyards": 20597, - "##uate": 20598, - "beckett": 20599, - "optimization": 20600, - "golfer": 20601, - "songwriters": 20602, - "seminal": 20603, - "semitic": 20604, - "thud": 20605, - "volatile": 20606, - "evolving": 20607, - "ridley": 20608, - "##wley": 20609, - "trivial": 20610, - "distributions": 20611, - "scandinavia": 20612, - "jiang": 20613, - "##ject": 20614, - "wrestled": 20615, - "insistence": 20616, - "##dio": 20617, - "emphasizes": 20618, - "napkin": 20619, - "##ods": 20620, - "adjunct": 20621, - "rhyme": 20622, - "##ricted": 20623, - "##eti": 20624, - "hopeless": 20625, - "surrounds": 20626, - "tremble": 20627, - "32nd": 20628, - "smoky": 20629, - "##ntly": 20630, - "oils": 20631, - "medicinal": 20632, - "padded": 20633, - "steer": 20634, - "wilkes": 20635, - "219": 20636, - "255": 20637, - "concessions": 20638, - "hue": 20639, - "uniquely": 20640, - "blinded": 20641, - "landon": 20642, - "yahoo": 20643, - "##lane": 20644, - "hendrix": 20645, - "commemorating": 20646, - "dex": 20647, - "specify": 20648, - "chicks": 20649, - "##ggio": 20650, - "intercity": 20651, - "1400": 20652, - "morley": 20653, - "##torm": 20654, - "highlighting": 20655, - "##oting": 20656, - "pang": 20657, - "oblique": 20658, - "stalled": 20659, - "##liner": 20660, - "flirting": 20661, - "newborn": 20662, - "1769": 20663, - "bishopric": 20664, - "shaved": 20665, - "232": 20666, - "currie": 20667, - "##ush": 20668, - "dharma": 20669, - "spartan": 20670, - "##ooped": 20671, - "favorites": 20672, - "smug": 20673, - "novella": 20674, - "sirens": 20675, - "abusive": 20676, - "creations": 20677, - "espana": 20678, - "##lage": 20679, - "paradigm": 20680, - "semiconductor": 20681, - "sheen": 20682, - "##rdo": 20683, - "##yen": 20684, - "##zak": 20685, - "nrl": 20686, - "renew": 20687, - "##pose": 20688, - "##tur": 20689, - "adjutant": 20690, - "marches": 20691, - "norma": 20692, - "##enity": 20693, - "ineffective": 20694, - "weimar": 20695, - "grunt": 20696, - "##gat": 20697, - "lordship": 20698, - "plotting": 20699, - "expenditure": 20700, - "infringement": 20701, - "lbs": 20702, - "refrain": 20703, - "av": 20704, - "mimi": 20705, - "mistakenly": 20706, - "postmaster": 20707, - "1771": 20708, - "##bara": 20709, - "ras": 20710, - "motorsports": 20711, - "tito": 20712, - "199": 20713, - "subjective": 20714, - "##zza": 20715, - "bully": 20716, - "stew": 20717, - "##kaya": 20718, - "prescott": 20719, - "1a": 20720, - "##raphic": 20721, - "##zam": 20722, - "bids": 20723, - "styling": 20724, - "paranormal": 20725, - "reeve": 20726, - "sneaking": 20727, - "exploding": 20728, - "katz": 20729, - "akbar": 20730, - "migrant": 20731, - "syllables": 20732, - "indefinitely": 20733, - "##ogical": 20734, - "destroys": 20735, - "replaces": 20736, - "applause": 20737, - "##phine": 20738, - "pest": 20739, - "##fide": 20740, - "218": 20741, - "articulated": 20742, - "bertie": 20743, - "##thing": 20744, - "##cars": 20745, - "##ptic": 20746, - "courtroom": 20747, - "crowley": 20748, - "aesthetics": 20749, - "cummings": 20750, - "tehsil": 20751, - "hormones": 20752, - "titanic": 20753, - "dangerously": 20754, - "##ibe": 20755, - "stadion": 20756, - "jaenelle": 20757, - "auguste": 20758, - "ciudad": 20759, - "##chu": 20760, - "mysore": 20761, - "partisans": 20762, - "##sio": 20763, - "lucan": 20764, - "philipp": 20765, - "##aly": 20766, - "debating": 20767, - "henley": 20768, - "interiors": 20769, - "##rano": 20770, - "##tious": 20771, - "homecoming": 20772, - "beyonce": 20773, - "usher": 20774, - "henrietta": 20775, - "prepares": 20776, - "weeds": 20777, - "##oman": 20778, - "ely": 20779, - "plucked": 20780, - "##pire": 20781, - "##dable": 20782, - "luxurious": 20783, - "##aq": 20784, - "artifact": 20785, - "password": 20786, - "pasture": 20787, - "juno": 20788, - "maddy": 20789, - "minsk": 20790, - "##dder": 20791, - "##ologies": 20792, - "##rone": 20793, - "assessments": 20794, - "martian": 20795, - "royalist": 20796, - "1765": 20797, - "examines": 20798, - "##mani": 20799, - "##rge": 20800, - "nino": 20801, - "223": 20802, - "parry": 20803, - "scooped": 20804, - "relativity": 20805, - "##eli": 20806, - "##uting": 20807, - "##cao": 20808, - "congregational": 20809, - "noisy": 20810, - "traverse": 20811, - "##agawa": 20812, - "strikeouts": 20813, - "nickelodeon": 20814, - "obituary": 20815, - "transylvania": 20816, - "binds": 20817, - "depictions": 20818, - "polk": 20819, - "trolley": 20820, - "##yed": 20821, - "##lard": 20822, - "breeders": 20823, - "##under": 20824, - "dryly": 20825, - "hokkaido": 20826, - "1762": 20827, - "strengths": 20828, - "stacks": 20829, - "bonaparte": 20830, - "connectivity": 20831, - "neared": 20832, - "prostitutes": 20833, - "stamped": 20834, - "anaheim": 20835, - "gutierrez": 20836, - "sinai": 20837, - "##zzling": 20838, - "bram": 20839, - "fresno": 20840, - "madhya": 20841, - "##86": 20842, - "proton": 20843, - "##lena": 20844, - "##llum": 20845, - "##phon": 20846, - "reelected": 20847, - "wanda": 20848, - "##anus": 20849, - "##lb": 20850, - "ample": 20851, - "distinguishing": 20852, - "##yler": 20853, - "grasping": 20854, - "sermons": 20855, - "tomato": 20856, - "bland": 20857, - "stimulation": 20858, - "avenues": 20859, - "##eux": 20860, - "spreads": 20861, - "scarlett": 20862, - "fern": 20863, - "pentagon": 20864, - "assert": 20865, - "baird": 20866, - "chesapeake": 20867, - "ir": 20868, - "calmed": 20869, - "distortion": 20870, - "fatalities": 20871, - "##olis": 20872, - "correctional": 20873, - "pricing": 20874, - "##astic": 20875, - "##gina": 20876, - "prom": 20877, - "dammit": 20878, - "ying": 20879, - "collaborate": 20880, - "##chia": 20881, - "welterweight": 20882, - "33rd": 20883, - "pointer": 20884, - "substitution": 20885, - "bonded": 20886, - "umpire": 20887, - "communicating": 20888, - "multitude": 20889, - "paddle": 20890, - "##obe": 20891, - "federally": 20892, - "intimacy": 20893, - "##insky": 20894, - "betray": 20895, - "ssr": 20896, - "##lett": 20897, - "##lean": 20898, - "##lves": 20899, - "##therapy": 20900, - "airbus": 20901, - "##tery": 20902, - "functioned": 20903, - "ud": 20904, - "bearer": 20905, - "biomedical": 20906, - "netflix": 20907, - "##hire": 20908, - "##nca": 20909, - "condom": 20910, - "brink": 20911, - "ik": 20912, - "##nical": 20913, - "macy": 20914, - "##bet": 20915, - "flap": 20916, - "gma": 20917, - "experimented": 20918, - "jelly": 20919, - "lavender": 20920, - "##icles": 20921, - "##ulia": 20922, - "munro": 20923, - "##mian": 20924, - "##tial": 20925, - "rye": 20926, - "##rle": 20927, - "60th": 20928, - "gigs": 20929, - "hottest": 20930, - "rotated": 20931, - "predictions": 20932, - "fuji": 20933, - "bu": 20934, - "##erence": 20935, - "##omi": 20936, - "barangay": 20937, - "##fulness": 20938, - "##sas": 20939, - "clocks": 20940, - "##rwood": 20941, - "##liness": 20942, - "cereal": 20943, - "roe": 20944, - "wight": 20945, - "decker": 20946, - "uttered": 20947, - "babu": 20948, - "onion": 20949, - "xml": 20950, - "forcibly": 20951, - "##df": 20952, - "petra": 20953, - "sarcasm": 20954, - "hartley": 20955, - "peeled": 20956, - "storytelling": 20957, - "##42": 20958, - "##xley": 20959, - "##ysis": 20960, - "##ffa": 20961, - "fibre": 20962, - "kiel": 20963, - "auditor": 20964, - "fig": 20965, - "harald": 20966, - "greenville": 20967, - "##berries": 20968, - "geographically": 20969, - "nell": 20970, - "quartz": 20971, - "##athic": 20972, - "cemeteries": 20973, - "##lr": 20974, - "crossings": 20975, - "nah": 20976, - "holloway": 20977, - "reptiles": 20978, - "chun": 20979, - "sichuan": 20980, - "snowy": 20981, - "660": 20982, - "corrections": 20983, - "##ivo": 20984, - "zheng": 20985, - "ambassadors": 20986, - "blacksmith": 20987, - "fielded": 20988, - "fluids": 20989, - "hardcover": 20990, - "turnover": 20991, - "medications": 20992, - "melvin": 20993, - "academies": 20994, - "##erton": 20995, - "ro": 20996, - "roach": 20997, - "absorbing": 20998, - "spaniards": 20999, - "colton": 21000, - "##founded": 21001, - "outsider": 21002, - "espionage": 21003, - "kelsey": 21004, - "245": 21005, - "edible": 21006, - "##ulf": 21007, - "dora": 21008, - "establishes": 21009, - "##sham": 21010, - "##tries": 21011, - "contracting": 21012, - "##tania": 21013, - "cinematic": 21014, - "costello": 21015, - "nesting": 21016, - "##uron": 21017, - "connolly": 21018, - "duff": 21019, - "##nology": 21020, - "mma": 21021, - "##mata": 21022, - "fergus": 21023, - "sexes": 21024, - "gi": 21025, - "optics": 21026, - "spectator": 21027, - "woodstock": 21028, - "banning": 21029, - "##hee": 21030, - "##fle": 21031, - "differentiate": 21032, - "outfielder": 21033, - "refinery": 21034, - "226": 21035, - "312": 21036, - "gerhard": 21037, - "horde": 21038, - "lair": 21039, - "drastically": 21040, - "##udi": 21041, - "landfall": 21042, - "##cheng": 21043, - "motorsport": 21044, - "odi": 21045, - "##achi": 21046, - "predominant": 21047, - "quay": 21048, - "skins": 21049, - "##ental": 21050, - "edna": 21051, - "harshly": 21052, - "complementary": 21053, - "murdering": 21054, - "##aves": 21055, - "wreckage": 21056, - "##90": 21057, - "ono": 21058, - "outstretched": 21059, - "lennox": 21060, - "munitions": 21061, - "galen": 21062, - "reconcile": 21063, - "470": 21064, - "scalp": 21065, - "bicycles": 21066, - "gillespie": 21067, - "questionable": 21068, - "rosenberg": 21069, - "guillermo": 21070, - "hostel": 21071, - "jarvis": 21072, - "kabul": 21073, - "volvo": 21074, - "opium": 21075, - "yd": 21076, - "##twined": 21077, - "abuses": 21078, - "decca": 21079, - "outpost": 21080, - "##cino": 21081, - "sensible": 21082, - "neutrality": 21083, - "##64": 21084, - "ponce": 21085, - "anchorage": 21086, - "atkins": 21087, - "turrets": 21088, - "inadvertently": 21089, - "disagree": 21090, - "libre": 21091, - "vodka": 21092, - "reassuring": 21093, - "weighs": 21094, - "##yal": 21095, - "glide": 21096, - "jumper": 21097, - "ceilings": 21098, - "repertory": 21099, - "outs": 21100, - "stain": 21101, - "##bial": 21102, - "envy": 21103, - "##ucible": 21104, - "smashing": 21105, - "heightened": 21106, - "policing": 21107, - "hyun": 21108, - "mixes": 21109, - "lai": 21110, - "prima": 21111, - "##ples": 21112, - "celeste": 21113, - "##bina": 21114, - "lucrative": 21115, - "intervened": 21116, - "kc": 21117, - "manually": 21118, - "##rned": 21119, - "stature": 21120, - "staffed": 21121, - "bun": 21122, - "bastards": 21123, - "nairobi": 21124, - "priced": 21125, - "##auer": 21126, - "thatcher": 21127, - "##kia": 21128, - "tripped": 21129, - "comune": 21130, - "##ogan": 21131, - "##pled": 21132, - "brasil": 21133, - "incentives": 21134, - "emanuel": 21135, - "hereford": 21136, - "musica": 21137, - "##kim": 21138, - "benedictine": 21139, - "biennale": 21140, - "##lani": 21141, - "eureka": 21142, - "gardiner": 21143, - "rb": 21144, - "knocks": 21145, - "sha": 21146, - "##ael": 21147, - "##elled": 21148, - "##onate": 21149, - "efficacy": 21150, - "ventura": 21151, - "masonic": 21152, - "sanford": 21153, - "maize": 21154, - "leverage": 21155, - "##feit": 21156, - "capacities": 21157, - "santana": 21158, - "##aur": 21159, - "novelty": 21160, - "vanilla": 21161, - "##cter": 21162, - "##tour": 21163, - "benin": 21164, - "##oir": 21165, - "##rain": 21166, - "neptune": 21167, - "drafting": 21168, - "tallinn": 21169, - "##cable": 21170, - "humiliation": 21171, - "##boarding": 21172, - "schleswig": 21173, - "fabian": 21174, - "bernardo": 21175, - "liturgy": 21176, - "spectacle": 21177, - "sweeney": 21178, - "pont": 21179, - "routledge": 21180, - "##tment": 21181, - "cosmos": 21182, - "ut": 21183, - "hilt": 21184, - "sleek": 21185, - "universally": 21186, - "##eville": 21187, - "##gawa": 21188, - "typed": 21189, - "##dry": 21190, - "favors": 21191, - "allegheny": 21192, - "glaciers": 21193, - "##rly": 21194, - "recalling": 21195, - "aziz": 21196, - "##log": 21197, - "parasite": 21198, - "requiem": 21199, - "auf": 21200, - "##berto": 21201, - "##llin": 21202, - "illumination": 21203, - "##breaker": 21204, - "##issa": 21205, - "festivities": 21206, - "bows": 21207, - "govern": 21208, - "vibe": 21209, - "vp": 21210, - "333": 21211, - "sprawled": 21212, - "larson": 21213, - "pilgrim": 21214, - "bwf": 21215, - "leaping": 21216, - "##rts": 21217, - "##ssel": 21218, - "alexei": 21219, - "greyhound": 21220, - "hoarse": 21221, - "##dler": 21222, - "##oration": 21223, - "seneca": 21224, - "##cule": 21225, - "gaping": 21226, - "##ulously": 21227, - "##pura": 21228, - "cinnamon": 21229, - "##gens": 21230, - "##rricular": 21231, - "craven": 21232, - "fantasies": 21233, - "houghton": 21234, - "engined": 21235, - "reigned": 21236, - "dictator": 21237, - "supervising": 21238, - "##oris": 21239, - "bogota": 21240, - "commentaries": 21241, - "unnatural": 21242, - "fingernails": 21243, - "spirituality": 21244, - "tighten": 21245, - "##tm": 21246, - "canadiens": 21247, - "protesting": 21248, - "intentional": 21249, - "cheers": 21250, - "sparta": 21251, - "##ytic": 21252, - "##iere": 21253, - "##zine": 21254, - "widen": 21255, - "belgarath": 21256, - "controllers": 21257, - "dodd": 21258, - "iaaf": 21259, - "navarre": 21260, - "##ication": 21261, - "defect": 21262, - "squire": 21263, - "steiner": 21264, - "whisky": 21265, - "##mins": 21266, - "560": 21267, - "inevitably": 21268, - "tome": 21269, - "##gold": 21270, - "chew": 21271, - "##uid": 21272, - "##lid": 21273, - "elastic": 21274, - "##aby": 21275, - "streaked": 21276, - "alliances": 21277, - "jailed": 21278, - "regal": 21279, - "##ined": 21280, - "##phy": 21281, - "czechoslovak": 21282, - "narration": 21283, - "absently": 21284, - "##uld": 21285, - "bluegrass": 21286, - "guangdong": 21287, - "quran": 21288, - "criticizing": 21289, - "hose": 21290, - "hari": 21291, - "##liest": 21292, - "##owa": 21293, - "skier": 21294, - "streaks": 21295, - "deploy": 21296, - "##lom": 21297, - "raft": 21298, - "bose": 21299, - "dialed": 21300, - "huff": 21301, - "##eira": 21302, - "haifa": 21303, - "simplest": 21304, - "bursting": 21305, - "endings": 21306, - "ib": 21307, - "sultanate": 21308, - "##titled": 21309, - "franks": 21310, - "whitman": 21311, - "ensures": 21312, - "sven": 21313, - "##ggs": 21314, - "collaborators": 21315, - "forster": 21316, - "organising": 21317, - "ui": 21318, - "banished": 21319, - "napier": 21320, - "injustice": 21321, - "teller": 21322, - "layered": 21323, - "thump": 21324, - "##otti": 21325, - "roc": 21326, - "battleships": 21327, - "evidenced": 21328, - "fugitive": 21329, - "sadie": 21330, - "robotics": 21331, - "##roud": 21332, - "equatorial": 21333, - "geologist": 21334, - "##iza": 21335, - "yielding": 21336, - "##bron": 21337, - "##sr": 21338, - "internationale": 21339, - "mecca": 21340, - "##diment": 21341, - "sbs": 21342, - "skyline": 21343, - "toad": 21344, - "uploaded": 21345, - "reflective": 21346, - "undrafted": 21347, - "lal": 21348, - "leafs": 21349, - "bayern": 21350, - "##dai": 21351, - "lakshmi": 21352, - "shortlisted": 21353, - "##stick": 21354, - "##wicz": 21355, - "camouflage": 21356, - "donate": 21357, - "af": 21358, - "christi": 21359, - "lau": 21360, - "##acio": 21361, - "disclosed": 21362, - "nemesis": 21363, - "1761": 21364, - "assemble": 21365, - "straining": 21366, - "northamptonshire": 21367, - "tal": 21368, - "##asi": 21369, - "bernardino": 21370, - "premature": 21371, - "heidi": 21372, - "42nd": 21373, - "coefficients": 21374, - "galactic": 21375, - "reproduce": 21376, - "buzzed": 21377, - "sensations": 21378, - "zionist": 21379, - "monsieur": 21380, - "myrtle": 21381, - "##eme": 21382, - "archery": 21383, - "strangled": 21384, - "musically": 21385, - "viewpoint": 21386, - "antiquities": 21387, - "bei": 21388, - "trailers": 21389, - "seahawks": 21390, - "cured": 21391, - "pee": 21392, - "preferring": 21393, - "tasmanian": 21394, - "lange": 21395, - "sul": 21396, - "##mail": 21397, - "##working": 21398, - "colder": 21399, - "overland": 21400, - "lucivar": 21401, - "massey": 21402, - "gatherings": 21403, - "haitian": 21404, - "##smith": 21405, - "disapproval": 21406, - "flaws": 21407, - "##cco": 21408, - "##enbach": 21409, - "1766": 21410, - "npr": 21411, - "##icular": 21412, - "boroughs": 21413, - "creole": 21414, - "forums": 21415, - "techno": 21416, - "1755": 21417, - "dent": 21418, - "abdominal": 21419, - "streetcar": 21420, - "##eson": 21421, - "##stream": 21422, - "procurement": 21423, - "gemini": 21424, - "predictable": 21425, - "##tya": 21426, - "acheron": 21427, - "christoph": 21428, - "feeder": 21429, - "fronts": 21430, - "vendor": 21431, - "bernhard": 21432, - "jammu": 21433, - "tumors": 21434, - "slang": 21435, - "##uber": 21436, - "goaltender": 21437, - "twists": 21438, - "curving": 21439, - "manson": 21440, - "vuelta": 21441, - "mer": 21442, - "peanut": 21443, - "confessions": 21444, - "pouch": 21445, - "unpredictable": 21446, - "allowance": 21447, - "theodor": 21448, - "vascular": 21449, - "##factory": 21450, - "bala": 21451, - "authenticity": 21452, - "metabolic": 21453, - "coughing": 21454, - "nanjing": 21455, - "##cea": 21456, - "pembroke": 21457, - "##bard": 21458, - "splendid": 21459, - "36th": 21460, - "ff": 21461, - "hourly": 21462, - "##ahu": 21463, - "elmer": 21464, - "handel": 21465, - "##ivate": 21466, - "awarding": 21467, - "thrusting": 21468, - "dl": 21469, - "experimentation": 21470, - "##hesion": 21471, - "##46": 21472, - "caressed": 21473, - "entertained": 21474, - "steak": 21475, - "##rangle": 21476, - "biologist": 21477, - "orphans": 21478, - "baroness": 21479, - "oyster": 21480, - "stepfather": 21481, - "##dridge": 21482, - "mirage": 21483, - "reefs": 21484, - "speeding": 21485, - "##31": 21486, - "barons": 21487, - "1764": 21488, - "227": 21489, - "inhabit": 21490, - "preached": 21491, - "repealed": 21492, - "##tral": 21493, - "honoring": 21494, - "boogie": 21495, - "captives": 21496, - "administer": 21497, - "johanna": 21498, - "##imate": 21499, - "gel": 21500, - "suspiciously": 21501, - "1767": 21502, - "sobs": 21503, - "##dington": 21504, - "backbone": 21505, - "hayward": 21506, - "garry": 21507, - "##folding": 21508, - "##nesia": 21509, - "maxi": 21510, - "##oof": 21511, - "##ppe": 21512, - "ellison": 21513, - "galileo": 21514, - "##stand": 21515, - "crimea": 21516, - "frenzy": 21517, - "amour": 21518, - "bumper": 21519, - "matrices": 21520, - "natalia": 21521, - "baking": 21522, - "garth": 21523, - "palestinians": 21524, - "##grove": 21525, - "smack": 21526, - "conveyed": 21527, - "ensembles": 21528, - "gardening": 21529, - "##manship": 21530, - "##rup": 21531, - "##stituting": 21532, - "1640": 21533, - "harvesting": 21534, - "topography": 21535, - "jing": 21536, - "shifters": 21537, - "dormitory": 21538, - "##carriage": 21539, - "##lston": 21540, - "ist": 21541, - "skulls": 21542, - "##stadt": 21543, - "dolores": 21544, - "jewellery": 21545, - "sarawak": 21546, - "##wai": 21547, - "##zier": 21548, - "fences": 21549, - "christy": 21550, - "confinement": 21551, - "tumbling": 21552, - "credibility": 21553, - "fir": 21554, - "stench": 21555, - "##bria": 21556, - "##plication": 21557, - "##nged": 21558, - "##sam": 21559, - "virtues": 21560, - "##belt": 21561, - "marjorie": 21562, - "pba": 21563, - "##eem": 21564, - "##made": 21565, - "celebrates": 21566, - "schooner": 21567, - "agitated": 21568, - "barley": 21569, - "fulfilling": 21570, - "anthropologist": 21571, - "##pro": 21572, - "restrict": 21573, - "novi": 21574, - "regulating": 21575, - "##nent": 21576, - "padres": 21577, - "##rani": 21578, - "##hesive": 21579, - "loyola": 21580, - "tabitha": 21581, - "milky": 21582, - "olson": 21583, - "proprietor": 21584, - "crambidae": 21585, - "guarantees": 21586, - "intercollegiate": 21587, - "ljubljana": 21588, - "hilda": 21589, - "##sko": 21590, - "ignorant": 21591, - "hooded": 21592, - "##lts": 21593, - "sardinia": 21594, - "##lidae": 21595, - "##vation": 21596, - "frontman": 21597, - "privileged": 21598, - "witchcraft": 21599, - "##gp": 21600, - "jammed": 21601, - "laude": 21602, - "poking": 21603, - "##than": 21604, - "bracket": 21605, - "amazement": 21606, - "yunnan": 21607, - "##erus": 21608, - "maharaja": 21609, - "linnaeus": 21610, - "264": 21611, - "commissioning": 21612, - "milano": 21613, - "peacefully": 21614, - "##logies": 21615, - "akira": 21616, - "rani": 21617, - "regulator": 21618, - "##36": 21619, - "grasses": 21620, - "##rance": 21621, - "luzon": 21622, - "crows": 21623, - "compiler": 21624, - "gretchen": 21625, - "seaman": 21626, - "edouard": 21627, - "tab": 21628, - "buccaneers": 21629, - "ellington": 21630, - "hamlets": 21631, - "whig": 21632, - "socialists": 21633, - "##anto": 21634, - "directorial": 21635, - "easton": 21636, - "mythological": 21637, - "##kr": 21638, - "##vary": 21639, - "rhineland": 21640, - "semantic": 21641, - "taut": 21642, - "dune": 21643, - "inventions": 21644, - "succeeds": 21645, - "##iter": 21646, - "replication": 21647, - "branched": 21648, - "##pired": 21649, - "jul": 21650, - "prosecuted": 21651, - "kangaroo": 21652, - "penetrated": 21653, - "##avian": 21654, - "middlesbrough": 21655, - "doses": 21656, - "bleak": 21657, - "madam": 21658, - "predatory": 21659, - "relentless": 21660, - "##vili": 21661, - "reluctance": 21662, - "##vir": 21663, - "hailey": 21664, - "crore": 21665, - "silvery": 21666, - "1759": 21667, - "monstrous": 21668, - "swimmers": 21669, - "transmissions": 21670, - "hawthorn": 21671, - "informing": 21672, - "##eral": 21673, - "toilets": 21674, - "caracas": 21675, - "crouch": 21676, - "kb": 21677, - "##sett": 21678, - "295": 21679, - "cartel": 21680, - "hadley": 21681, - "##aling": 21682, - "alexia": 21683, - "yvonne": 21684, - "##biology": 21685, - "cinderella": 21686, - "eton": 21687, - "superb": 21688, - "blizzard": 21689, - "stabbing": 21690, - "industrialist": 21691, - "maximus": 21692, - "##gm": 21693, - "##orus": 21694, - "groves": 21695, - "maud": 21696, - "clade": 21697, - "oversized": 21698, - "comedic": 21699, - "##bella": 21700, - "rosen": 21701, - "nomadic": 21702, - "fulham": 21703, - "montane": 21704, - "beverages": 21705, - "galaxies": 21706, - "redundant": 21707, - "swarm": 21708, - "##rot": 21709, - "##folia": 21710, - "##llis": 21711, - "buckinghamshire": 21712, - "fen": 21713, - "bearings": 21714, - "bahadur": 21715, - "##rom": 21716, - "gilles": 21717, - "phased": 21718, - "dynamite": 21719, - "faber": 21720, - "benoit": 21721, - "vip": 21722, - "##ount": 21723, - "##wd": 21724, - "booking": 21725, - "fractured": 21726, - "tailored": 21727, - "anya": 21728, - "spices": 21729, - "westwood": 21730, - "cairns": 21731, - "auditions": 21732, - "inflammation": 21733, - "steamed": 21734, - "##rocity": 21735, - "##acion": 21736, - "##urne": 21737, - "skyla": 21738, - "thereof": 21739, - "watford": 21740, - "torment": 21741, - "archdeacon": 21742, - "transforms": 21743, - "lulu": 21744, - "demeanor": 21745, - "fucked": 21746, - "serge": 21747, - "##sor": 21748, - "mckenna": 21749, - "minas": 21750, - "entertainer": 21751, - "##icide": 21752, - "caress": 21753, - "originate": 21754, - "residue": 21755, - "##sty": 21756, - "1740": 21757, - "##ilised": 21758, - "##org": 21759, - "beech": 21760, - "##wana": 21761, - "subsidies": 21762, - "##ghton": 21763, - "emptied": 21764, - "gladstone": 21765, - "ru": 21766, - "firefighters": 21767, - "voodoo": 21768, - "##rcle": 21769, - "het": 21770, - "nightingale": 21771, - "tamara": 21772, - "edmond": 21773, - "ingredient": 21774, - "weaknesses": 21775, - "silhouette": 21776, - "285": 21777, - "compatibility": 21778, - "withdrawing": 21779, - "hampson": 21780, - "##mona": 21781, - "anguish": 21782, - "giggling": 21783, - "##mber": 21784, - "bookstore": 21785, - "##jiang": 21786, - "southernmost": 21787, - "tilting": 21788, - "##vance": 21789, - "bai": 21790, - "economical": 21791, - "rf": 21792, - "briefcase": 21793, - "dreadful": 21794, - "hinted": 21795, - "projections": 21796, - "shattering": 21797, - "totaling": 21798, - "##rogate": 21799, - "analogue": 21800, - "indicted": 21801, - "periodical": 21802, - "fullback": 21803, - "##dman": 21804, - "haynes": 21805, - "##tenberg": 21806, - "##ffs": 21807, - "##ishment": 21808, - "1745": 21809, - "thirst": 21810, - "stumble": 21811, - "penang": 21812, - "vigorous": 21813, - "##ddling": 21814, - "##kor": 21815, - "##lium": 21816, - "octave": 21817, - "##ove": 21818, - "##enstein": 21819, - "##inen": 21820, - "##ones": 21821, - "siberian": 21822, - "##uti": 21823, - "cbn": 21824, - "repeal": 21825, - "swaying": 21826, - "##vington": 21827, - "khalid": 21828, - "tanaka": 21829, - "unicorn": 21830, - "otago": 21831, - "plastered": 21832, - "lobe": 21833, - "riddle": 21834, - "##rella": 21835, - "perch": 21836, - "##ishing": 21837, - "croydon": 21838, - "filtered": 21839, - "graeme": 21840, - "tripoli": 21841, - "##ossa": 21842, - "crocodile": 21843, - "##chers": 21844, - "sufi": 21845, - "mined": 21846, - "##tung": 21847, - "inferno": 21848, - "lsu": 21849, - "##phi": 21850, - "swelled": 21851, - "utilizes": 21852, - "£2": 21853, - "cale": 21854, - "periodicals": 21855, - "styx": 21856, - "hike": 21857, - "informally": 21858, - "coop": 21859, - "lund": 21860, - "##tidae": 21861, - "ala": 21862, - "hen": 21863, - "qui": 21864, - "transformations": 21865, - "disposed": 21866, - "sheath": 21867, - "chickens": 21868, - "##cade": 21869, - "fitzroy": 21870, - "sas": 21871, - "silesia": 21872, - "unacceptable": 21873, - "odisha": 21874, - "1650": 21875, - "sabrina": 21876, - "pe": 21877, - "spokane": 21878, - "ratios": 21879, - "athena": 21880, - "massage": 21881, - "shen": 21882, - "dilemma": 21883, - "##drum": 21884, - "##riz": 21885, - "##hul": 21886, - "corona": 21887, - "doubtful": 21888, - "niall": 21889, - "##pha": 21890, - "##bino": 21891, - "fines": 21892, - "cite": 21893, - "acknowledging": 21894, - "bangor": 21895, - "ballard": 21896, - "bathurst": 21897, - "##resh": 21898, - "huron": 21899, - "mustered": 21900, - "alzheimer": 21901, - "garments": 21902, - "kinase": 21903, - "tyre": 21904, - "warship": 21905, - "##cp": 21906, - "flashback": 21907, - "pulmonary": 21908, - "braun": 21909, - "cheat": 21910, - "kamal": 21911, - "cyclists": 21912, - "constructions": 21913, - "grenades": 21914, - "ndp": 21915, - "traveller": 21916, - "excuses": 21917, - "stomped": 21918, - "signalling": 21919, - "trimmed": 21920, - "futsal": 21921, - "mosques": 21922, - "relevance": 21923, - "##wine": 21924, - "wta": 21925, - "##23": 21926, - "##vah": 21927, - "##lter": 21928, - "hoc": 21929, - "##riding": 21930, - "optimistic": 21931, - "##´s": 21932, - "deco": 21933, - "sim": 21934, - "interacting": 21935, - "rejecting": 21936, - "moniker": 21937, - "waterways": 21938, - "##ieri": 21939, - "##oku": 21940, - "mayors": 21941, - "gdansk": 21942, - "outnumbered": 21943, - "pearls": 21944, - "##ended": 21945, - "##hampton": 21946, - "fairs": 21947, - "totals": 21948, - "dominating": 21949, - "262": 21950, - "notions": 21951, - "stairway": 21952, - "compiling": 21953, - "pursed": 21954, - "commodities": 21955, - "grease": 21956, - "yeast": 21957, - "##jong": 21958, - "carthage": 21959, - "griffiths": 21960, - "residual": 21961, - "amc": 21962, - "contraction": 21963, - "laird": 21964, - "sapphire": 21965, - "##marine": 21966, - "##ivated": 21967, - "amalgamation": 21968, - "dissolve": 21969, - "inclination": 21970, - "lyle": 21971, - "packaged": 21972, - "altitudes": 21973, - "suez": 21974, - "canons": 21975, - "graded": 21976, - "lurched": 21977, - "narrowing": 21978, - "boasts": 21979, - "guise": 21980, - "wed": 21981, - "enrico": 21982, - "##ovsky": 21983, - "rower": 21984, - "scarred": 21985, - "bree": 21986, - "cub": 21987, - "iberian": 21988, - "protagonists": 21989, - "bargaining": 21990, - "proposing": 21991, - "trainers": 21992, - "voyages": 21993, - "vans": 21994, - "fishes": 21995, - "##aea": 21996, - "##ivist": 21997, - "##verance": 21998, - "encryption": 21999, - "artworks": 22000, - "kazan": 22001, - "sabre": 22002, - "cleopatra": 22003, - "hepburn": 22004, - "rotting": 22005, - "supremacy": 22006, - "mecklenburg": 22007, - "##brate": 22008, - "burrows": 22009, - "hazards": 22010, - "outgoing": 22011, - "flair": 22012, - "organizes": 22013, - "##ctions": 22014, - "scorpion": 22015, - "##usions": 22016, - "boo": 22017, - "234": 22018, - "chevalier": 22019, - "dunedin": 22020, - "slapping": 22021, - "##34": 22022, - "ineligible": 22023, - "pensions": 22024, - "##38": 22025, - "##omic": 22026, - "manufactures": 22027, - "emails": 22028, - "bismarck": 22029, - "238": 22030, - "weakening": 22031, - "blackish": 22032, - "ding": 22033, - "mcgee": 22034, - "quo": 22035, - "##rling": 22036, - "northernmost": 22037, - "xx": 22038, - "manpower": 22039, - "greed": 22040, - "sampson": 22041, - "clicking": 22042, - "##ange": 22043, - "##horpe": 22044, - "##inations": 22045, - "##roving": 22046, - "torre": 22047, - "##eptive": 22048, - "##moral": 22049, - "symbolism": 22050, - "38th": 22051, - "asshole": 22052, - "meritorious": 22053, - "outfits": 22054, - "splashed": 22055, - "biographies": 22056, - "sprung": 22057, - "astros": 22058, - "##tale": 22059, - "302": 22060, - "737": 22061, - "filly": 22062, - "raoul": 22063, - "nw": 22064, - "tokugawa": 22065, - "linden": 22066, - "clubhouse": 22067, - "##apa": 22068, - "tracts": 22069, - "romano": 22070, - "##pio": 22071, - "putin": 22072, - "tags": 22073, - "##note": 22074, - "chained": 22075, - "dickson": 22076, - "gunshot": 22077, - "moe": 22078, - "gunn": 22079, - "rashid": 22080, - "##tails": 22081, - "zipper": 22082, - "##bas": 22083, - "##nea": 22084, - "contrasted": 22085, - "##ply": 22086, - "##udes": 22087, - "plum": 22088, - "pharaoh": 22089, - "##pile": 22090, - "aw": 22091, - "comedies": 22092, - "ingrid": 22093, - "sandwiches": 22094, - "subdivisions": 22095, - "1100": 22096, - "mariana": 22097, - "nokia": 22098, - "kamen": 22099, - "hz": 22100, - "delaney": 22101, - "veto": 22102, - "herring": 22103, - "##words": 22104, - "possessive": 22105, - "outlines": 22106, - "##roup": 22107, - "siemens": 22108, - "stairwell": 22109, - "rc": 22110, - "gallantry": 22111, - "messiah": 22112, - "palais": 22113, - "yells": 22114, - "233": 22115, - "zeppelin": 22116, - "##dm": 22117, - "bolivar": 22118, - "##cede": 22119, - "smackdown": 22120, - "mckinley": 22121, - "##mora": 22122, - "##yt": 22123, - "muted": 22124, - "geologic": 22125, - "finely": 22126, - "unitary": 22127, - "avatar": 22128, - "hamas": 22129, - "maynard": 22130, - "rees": 22131, - "bog": 22132, - "contrasting": 22133, - "##rut": 22134, - "liv": 22135, - "chico": 22136, - "disposition": 22137, - "pixel": 22138, - "##erate": 22139, - "becca": 22140, - "dmitry": 22141, - "yeshiva": 22142, - "narratives": 22143, - "##lva": 22144, - "##ulton": 22145, - "mercenary": 22146, - "sharpe": 22147, - "tempered": 22148, - "navigate": 22149, - "stealth": 22150, - "amassed": 22151, - "keynes": 22152, - "##lini": 22153, - "untouched": 22154, - "##rrie": 22155, - "havoc": 22156, - "lithium": 22157, - "##fighting": 22158, - "abyss": 22159, - "graf": 22160, - "southward": 22161, - "wolverine": 22162, - "balloons": 22163, - "implements": 22164, - "ngos": 22165, - "transitions": 22166, - "##icum": 22167, - "ambushed": 22168, - "concacaf": 22169, - "dormant": 22170, - "economists": 22171, - "##dim": 22172, - "costing": 22173, - "csi": 22174, - "rana": 22175, - "universite": 22176, - "boulders": 22177, - "verity": 22178, - "##llon": 22179, - "collin": 22180, - "mellon": 22181, - "misses": 22182, - "cypress": 22183, - "fluorescent": 22184, - "lifeless": 22185, - "spence": 22186, - "##ulla": 22187, - "crewe": 22188, - "shepard": 22189, - "pak": 22190, - "revelations": 22191, - "##م": 22192, - "jolly": 22193, - "gibbons": 22194, - "paw": 22195, - "##dro": 22196, - "##quel": 22197, - "freeing": 22198, - "##test": 22199, - "shack": 22200, - "fries": 22201, - "palatine": 22202, - "##51": 22203, - "##hiko": 22204, - "accompaniment": 22205, - "cruising": 22206, - "recycled": 22207, - "##aver": 22208, - "erwin": 22209, - "sorting": 22210, - "synthesizers": 22211, - "dyke": 22212, - "realities": 22213, - "sg": 22214, - "strides": 22215, - "enslaved": 22216, - "wetland": 22217, - "##ghan": 22218, - "competence": 22219, - "gunpowder": 22220, - "grassy": 22221, - "maroon": 22222, - "reactors": 22223, - "objection": 22224, - "##oms": 22225, - "carlson": 22226, - "gearbox": 22227, - "macintosh": 22228, - "radios": 22229, - "shelton": 22230, - "##sho": 22231, - "clergyman": 22232, - "prakash": 22233, - "254": 22234, - "mongols": 22235, - "trophies": 22236, - "oricon": 22237, - "228": 22238, - "stimuli": 22239, - "twenty20": 22240, - "cantonese": 22241, - "cortes": 22242, - "mirrored": 22243, - "##saurus": 22244, - "bhp": 22245, - "cristina": 22246, - "melancholy": 22247, - "##lating": 22248, - "enjoyable": 22249, - "nuevo": 22250, - "##wny": 22251, - "downfall": 22252, - "schumacher": 22253, - "##ind": 22254, - "banging": 22255, - "lausanne": 22256, - "rumbled": 22257, - "paramilitary": 22258, - "reflex": 22259, - "ax": 22260, - "amplitude": 22261, - "migratory": 22262, - "##gall": 22263, - "##ups": 22264, - "midi": 22265, - "barnard": 22266, - "lastly": 22267, - "sherry": 22268, - "##hp": 22269, - "##nall": 22270, - "keystone": 22271, - "##kra": 22272, - "carleton": 22273, - "slippery": 22274, - "##53": 22275, - "coloring": 22276, - "foe": 22277, - "socket": 22278, - "otter": 22279, - "##rgos": 22280, - "mats": 22281, - "##tose": 22282, - "consultants": 22283, - "bafta": 22284, - "bison": 22285, - "topping": 22286, - "##km": 22287, - "490": 22288, - "primal": 22289, - "abandonment": 22290, - "transplant": 22291, - "atoll": 22292, - "hideous": 22293, - "mort": 22294, - "pained": 22295, - "reproduced": 22296, - "tae": 22297, - "howling": 22298, - "##turn": 22299, - "unlawful": 22300, - "billionaire": 22301, - "hotter": 22302, - "poised": 22303, - "lansing": 22304, - "##chang": 22305, - "dinamo": 22306, - "retro": 22307, - "messing": 22308, - "nfc": 22309, - "domesday": 22310, - "##mina": 22311, - "blitz": 22312, - "timed": 22313, - "##athing": 22314, - "##kley": 22315, - "ascending": 22316, - "gesturing": 22317, - "##izations": 22318, - "signaled": 22319, - "tis": 22320, - "chinatown": 22321, - "mermaid": 22322, - "savanna": 22323, - "jameson": 22324, - "##aint": 22325, - "catalina": 22326, - "##pet": 22327, - "##hers": 22328, - "cochrane": 22329, - "cy": 22330, - "chatting": 22331, - "##kus": 22332, - "alerted": 22333, - "computation": 22334, - "mused": 22335, - "noelle": 22336, - "majestic": 22337, - "mohawk": 22338, - "campo": 22339, - "octagonal": 22340, - "##sant": 22341, - "##hend": 22342, - "241": 22343, - "aspiring": 22344, - "##mart": 22345, - "comprehend": 22346, - "iona": 22347, - "paralyzed": 22348, - "shimmering": 22349, - "swindon": 22350, - "rhone": 22351, - "##eley": 22352, - "reputed": 22353, - "configurations": 22354, - "pitchfork": 22355, - "agitation": 22356, - "francais": 22357, - "gillian": 22358, - "lipstick": 22359, - "##ilo": 22360, - "outsiders": 22361, - "pontifical": 22362, - "resisting": 22363, - "bitterness": 22364, - "sewer": 22365, - "rockies": 22366, - "##edd": 22367, - "##ucher": 22368, - "misleading": 22369, - "1756": 22370, - "exiting": 22371, - "galloway": 22372, - "##nging": 22373, - "risked": 22374, - "##heart": 22375, - "246": 22376, - "commemoration": 22377, - "schultz": 22378, - "##rka": 22379, - "integrating": 22380, - "##rsa": 22381, - "poses": 22382, - "shrieked": 22383, - "##weiler": 22384, - "guineas": 22385, - "gladys": 22386, - "jerking": 22387, - "owls": 22388, - "goldsmith": 22389, - "nightly": 22390, - "penetrating": 22391, - "##unced": 22392, - "lia": 22393, - "##33": 22394, - "ignited": 22395, - "betsy": 22396, - "##aring": 22397, - "##thorpe": 22398, - "follower": 22399, - "vigorously": 22400, - "##rave": 22401, - "coded": 22402, - "kiran": 22403, - "knit": 22404, - "zoology": 22405, - "tbilisi": 22406, - "##28": 22407, - "##bered": 22408, - "repository": 22409, - "govt": 22410, - "deciduous": 22411, - "dino": 22412, - "growling": 22413, - "##bba": 22414, - "enhancement": 22415, - "unleashed": 22416, - "chanting": 22417, - "pussy": 22418, - "biochemistry": 22419, - "##eric": 22420, - "kettle": 22421, - "repression": 22422, - "toxicity": 22423, - "nrhp": 22424, - "##arth": 22425, - "##kko": 22426, - "##bush": 22427, - "ernesto": 22428, - "commended": 22429, - "outspoken": 22430, - "242": 22431, - "mca": 22432, - "parchment": 22433, - "sms": 22434, - "kristen": 22435, - "##aton": 22436, - "bisexual": 22437, - "raked": 22438, - "glamour": 22439, - "navajo": 22440, - "a2": 22441, - "conditioned": 22442, - "showcased": 22443, - "##hma": 22444, - "spacious": 22445, - "youthful": 22446, - "##esa": 22447, - "usl": 22448, - "appliances": 22449, - "junta": 22450, - "brest": 22451, - "layne": 22452, - "conglomerate": 22453, - "enchanted": 22454, - "chao": 22455, - "loosened": 22456, - "picasso": 22457, - "circulating": 22458, - "inspect": 22459, - "montevideo": 22460, - "##centric": 22461, - "##kti": 22462, - "piazza": 22463, - "spurred": 22464, - "##aith": 22465, - "bari": 22466, - "freedoms": 22467, - "poultry": 22468, - "stamford": 22469, - "lieu": 22470, - "##ect": 22471, - "indigo": 22472, - "sarcastic": 22473, - "bahia": 22474, - "stump": 22475, - "attach": 22476, - "dvds": 22477, - "frankenstein": 22478, - "lille": 22479, - "approx": 22480, - "scriptures": 22481, - "pollen": 22482, - "##script": 22483, - "nmi": 22484, - "overseen": 22485, - "##ivism": 22486, - "tides": 22487, - "proponent": 22488, - "newmarket": 22489, - "inherit": 22490, - "milling": 22491, - "##erland": 22492, - "centralized": 22493, - "##rou": 22494, - "distributors": 22495, - "credentials": 22496, - "drawers": 22497, - "abbreviation": 22498, - "##lco": 22499, - "##xon": 22500, - "downing": 22501, - "uncomfortably": 22502, - "ripe": 22503, - "##oes": 22504, - "erase": 22505, - "franchises": 22506, - "##ever": 22507, - "populace": 22508, - "##bery": 22509, - "##khar": 22510, - "decomposition": 22511, - "pleas": 22512, - "##tet": 22513, - "daryl": 22514, - "sabah": 22515, - "##stle": 22516, - "##wide": 22517, - "fearless": 22518, - "genie": 22519, - "lesions": 22520, - "annette": 22521, - "##ogist": 22522, - "oboe": 22523, - "appendix": 22524, - "nair": 22525, - "dripped": 22526, - "petitioned": 22527, - "maclean": 22528, - "mosquito": 22529, - "parrot": 22530, - "rpg": 22531, - "hampered": 22532, - "1648": 22533, - "operatic": 22534, - "reservoirs": 22535, - "##tham": 22536, - "irrelevant": 22537, - "jolt": 22538, - "summarized": 22539, - "##fp": 22540, - "medallion": 22541, - "##taff": 22542, - "##−": 22543, - "clawed": 22544, - "harlow": 22545, - "narrower": 22546, - "goddard": 22547, - "marcia": 22548, - "bodied": 22549, - "fremont": 22550, - "suarez": 22551, - "altering": 22552, - "tempest": 22553, - "mussolini": 22554, - "porn": 22555, - "##isms": 22556, - "sweetly": 22557, - "oversees": 22558, - "walkers": 22559, - "solitude": 22560, - "grimly": 22561, - "shrines": 22562, - "hk": 22563, - "ich": 22564, - "supervisors": 22565, - "hostess": 22566, - "dietrich": 22567, - "legitimacy": 22568, - "brushes": 22569, - "expressive": 22570, - "##yp": 22571, - "dissipated": 22572, - "##rse": 22573, - "localized": 22574, - "systemic": 22575, - "##nikov": 22576, - "gettysburg": 22577, - "##js": 22578, - "##uaries": 22579, - "dialogues": 22580, - "muttering": 22581, - "251": 22582, - "housekeeper": 22583, - "sicilian": 22584, - "discouraged": 22585, - "##frey": 22586, - "beamed": 22587, - "kaladin": 22588, - "halftime": 22589, - "kidnap": 22590, - "##amo": 22591, - "##llet": 22592, - "1754": 22593, - "synonymous": 22594, - "depleted": 22595, - "instituto": 22596, - "insulin": 22597, - "reprised": 22598, - "##opsis": 22599, - "clashed": 22600, - "##ctric": 22601, - "interrupting": 22602, - "radcliffe": 22603, - "insisting": 22604, - "medici": 22605, - "1715": 22606, - "ejected": 22607, - "playfully": 22608, - "turbulent": 22609, - "##47": 22610, - "starvation": 22611, - "##rini": 22612, - "shipment": 22613, - "rebellious": 22614, - "petersen": 22615, - "verification": 22616, - "merits": 22617, - "##rified": 22618, - "cakes": 22619, - "##charged": 22620, - "1757": 22621, - "milford": 22622, - "shortages": 22623, - "spying": 22624, - "fidelity": 22625, - "##aker": 22626, - "emitted": 22627, - "storylines": 22628, - "harvested": 22629, - "seismic": 22630, - "##iform": 22631, - "cheung": 22632, - "kilda": 22633, - "theoretically": 22634, - "barbie": 22635, - "lynx": 22636, - "##rgy": 22637, - "##tius": 22638, - "goblin": 22639, - "mata": 22640, - "poisonous": 22641, - "##nburg": 22642, - "reactive": 22643, - "residues": 22644, - "obedience": 22645, - "##евич": 22646, - "conjecture": 22647, - "##rac": 22648, - "401": 22649, - "hating": 22650, - "sixties": 22651, - "kicker": 22652, - "moaning": 22653, - "motown": 22654, - "##bha": 22655, - "emancipation": 22656, - "neoclassical": 22657, - "##hering": 22658, - "consoles": 22659, - "ebert": 22660, - "professorship": 22661, - "##tures": 22662, - "sustaining": 22663, - "assaults": 22664, - "obeyed": 22665, - "affluent": 22666, - "incurred": 22667, - "tornadoes": 22668, - "##eber": 22669, - "##zow": 22670, - "emphasizing": 22671, - "highlanders": 22672, - "cheated": 22673, - "helmets": 22674, - "##ctus": 22675, - "internship": 22676, - "terence": 22677, - "bony": 22678, - "executions": 22679, - "legislators": 22680, - "berries": 22681, - "peninsular": 22682, - "tinged": 22683, - "##aco": 22684, - "1689": 22685, - "amplifier": 22686, - "corvette": 22687, - "ribbons": 22688, - "lavish": 22689, - "pennant": 22690, - "##lander": 22691, - "worthless": 22692, - "##chfield": 22693, - "##forms": 22694, - "mariano": 22695, - "pyrenees": 22696, - "expenditures": 22697, - "##icides": 22698, - "chesterfield": 22699, - "mandir": 22700, - "tailor": 22701, - "39th": 22702, - "sergey": 22703, - "nestled": 22704, - "willed": 22705, - "aristocracy": 22706, - "devotees": 22707, - "goodnight": 22708, - "raaf": 22709, - "rumored": 22710, - "weaponry": 22711, - "remy": 22712, - "appropriations": 22713, - "harcourt": 22714, - "burr": 22715, - "riaa": 22716, - "##lence": 22717, - "limitation": 22718, - "unnoticed": 22719, - "guo": 22720, - "soaking": 22721, - "swamps": 22722, - "##tica": 22723, - "collapsing": 22724, - "tatiana": 22725, - "descriptive": 22726, - "brigham": 22727, - "psalm": 22728, - "##chment": 22729, - "maddox": 22730, - "##lization": 22731, - "patti": 22732, - "caliph": 22733, - "##aja": 22734, - "akron": 22735, - "injuring": 22736, - "serra": 22737, - "##ganj": 22738, - "basins": 22739, - "##sari": 22740, - "astonished": 22741, - "launcher": 22742, - "##church": 22743, - "hilary": 22744, - "wilkins": 22745, - "sewing": 22746, - "##sf": 22747, - "stinging": 22748, - "##fia": 22749, - "##ncia": 22750, - "underwood": 22751, - "startup": 22752, - "##ition": 22753, - "compilations": 22754, - "vibrations": 22755, - "embankment": 22756, - "jurist": 22757, - "##nity": 22758, - "bard": 22759, - "juventus": 22760, - "groundwater": 22761, - "kern": 22762, - "palaces": 22763, - "helium": 22764, - "boca": 22765, - "cramped": 22766, - "marissa": 22767, - "soto": 22768, - "##worm": 22769, - "jae": 22770, - "princely": 22771, - "##ggy": 22772, - "faso": 22773, - "bazaar": 22774, - "warmly": 22775, - "##voking": 22776, - "229": 22777, - "pairing": 22778, - "##lite": 22779, - "##grate": 22780, - "##nets": 22781, - "wien": 22782, - "freaked": 22783, - "ulysses": 22784, - "rebirth": 22785, - "##alia": 22786, - "##rent": 22787, - "mummy": 22788, - "guzman": 22789, - "jimenez": 22790, - "stilled": 22791, - "##nitz": 22792, - "trajectory": 22793, - "tha": 22794, - "woken": 22795, - "archival": 22796, - "professions": 22797, - "##pts": 22798, - "##pta": 22799, - "hilly": 22800, - "shadowy": 22801, - "shrink": 22802, - "##bolt": 22803, - "norwood": 22804, - "glued": 22805, - "migrate": 22806, - "stereotypes": 22807, - "devoid": 22808, - "##pheus": 22809, - "625": 22810, - "evacuate": 22811, - "horrors": 22812, - "infancy": 22813, - "gotham": 22814, - "knowles": 22815, - "optic": 22816, - "downloaded": 22817, - "sachs": 22818, - "kingsley": 22819, - "parramatta": 22820, - "darryl": 22821, - "mor": 22822, - "##onale": 22823, - "shady": 22824, - "commence": 22825, - "confesses": 22826, - "kan": 22827, - "##meter": 22828, - "##placed": 22829, - "marlborough": 22830, - "roundabout": 22831, - "regents": 22832, - "frigates": 22833, - "io": 22834, - "##imating": 22835, - "gothenburg": 22836, - "revoked": 22837, - "carvings": 22838, - "clockwise": 22839, - "convertible": 22840, - "intruder": 22841, - "##sche": 22842, - "banged": 22843, - "##ogo": 22844, - "vicky": 22845, - "bourgeois": 22846, - "##mony": 22847, - "dupont": 22848, - "footing": 22849, - "##gum": 22850, - "pd": 22851, - "##real": 22852, - "buckle": 22853, - "yun": 22854, - "penthouse": 22855, - "sane": 22856, - "720": 22857, - "serviced": 22858, - "stakeholders": 22859, - "neumann": 22860, - "bb": 22861, - "##eers": 22862, - "comb": 22863, - "##gam": 22864, - "catchment": 22865, - "pinning": 22866, - "rallies": 22867, - "typing": 22868, - "##elles": 22869, - "forefront": 22870, - "freiburg": 22871, - "sweetie": 22872, - "giacomo": 22873, - "widowed": 22874, - "goodwill": 22875, - "worshipped": 22876, - "aspirations": 22877, - "midday": 22878, - "##vat": 22879, - "fishery": 22880, - "##trick": 22881, - "bournemouth": 22882, - "turk": 22883, - "243": 22884, - "hearth": 22885, - "ethanol": 22886, - "guadalajara": 22887, - "murmurs": 22888, - "sl": 22889, - "##uge": 22890, - "afforded": 22891, - "scripted": 22892, - "##hta": 22893, - "wah": 22894, - "##jn": 22895, - "coroner": 22896, - "translucent": 22897, - "252": 22898, - "memorials": 22899, - "puck": 22900, - "progresses": 22901, - "clumsy": 22902, - "##race": 22903, - "315": 22904, - "candace": 22905, - "recounted": 22906, - "##27": 22907, - "##slin": 22908, - "##uve": 22909, - "filtering": 22910, - "##mac": 22911, - "howl": 22912, - "strata": 22913, - "heron": 22914, - "leveled": 22915, - "##ays": 22916, - "dubious": 22917, - "##oja": 22918, - "##т": 22919, - "##wheel": 22920, - "citations": 22921, - "exhibiting": 22922, - "##laya": 22923, - "##mics": 22924, - "##pods": 22925, - "turkic": 22926, - "##lberg": 22927, - "injunction": 22928, - "##ennial": 22929, - "##mit": 22930, - "antibodies": 22931, - "##44": 22932, - "organise": 22933, - "##rigues": 22934, - "cardiovascular": 22935, - "cushion": 22936, - "inverness": 22937, - "##zquez": 22938, - "dia": 22939, - "cocoa": 22940, - "sibling": 22941, - "##tman": 22942, - "##roid": 22943, - "expanse": 22944, - "feasible": 22945, - "tunisian": 22946, - "algiers": 22947, - "##relli": 22948, - "rus": 22949, - "bloomberg": 22950, - "dso": 22951, - "westphalia": 22952, - "bro": 22953, - "tacoma": 22954, - "281": 22955, - "downloads": 22956, - "##ours": 22957, - "konrad": 22958, - "duran": 22959, - "##hdi": 22960, - "continuum": 22961, - "jett": 22962, - "compares": 22963, - "legislator": 22964, - "secession": 22965, - "##nable": 22966, - "##gues": 22967, - "##zuka": 22968, - "translating": 22969, - "reacher": 22970, - "##gley": 22971, - "##ła": 22972, - "aleppo": 22973, - "##agi": 22974, - "tc": 22975, - "orchards": 22976, - "trapping": 22977, - "linguist": 22978, - "versatile": 22979, - "drumming": 22980, - "postage": 22981, - "calhoun": 22982, - "superiors": 22983, - "##mx": 22984, - "barefoot": 22985, - "leary": 22986, - "##cis": 22987, - "ignacio": 22988, - "alfa": 22989, - "kaplan": 22990, - "##rogen": 22991, - "bratislava": 22992, - "mori": 22993, - "##vot": 22994, - "disturb": 22995, - "haas": 22996, - "313": 22997, - "cartridges": 22998, - "gilmore": 22999, - "radiated": 23000, - "salford": 23001, - "tunic": 23002, - "hades": 23003, - "##ulsive": 23004, - "archeological": 23005, - "delilah": 23006, - "magistrates": 23007, - "auditioned": 23008, - "brewster": 23009, - "charters": 23010, - "empowerment": 23011, - "blogs": 23012, - "cappella": 23013, - "dynasties": 23014, - "iroquois": 23015, - "whipping": 23016, - "##krishna": 23017, - "raceway": 23018, - "truths": 23019, - "myra": 23020, - "weaken": 23021, - "judah": 23022, - "mcgregor": 23023, - "##horse": 23024, - "mic": 23025, - "refueling": 23026, - "37th": 23027, - "burnley": 23028, - "bosses": 23029, - "markus": 23030, - "premio": 23031, - "query": 23032, - "##gga": 23033, - "dunbar": 23034, - "##economic": 23035, - "darkest": 23036, - "lyndon": 23037, - "sealing": 23038, - "commendation": 23039, - "reappeared": 23040, - "##mun": 23041, - "addicted": 23042, - "ezio": 23043, - "slaughtered": 23044, - "satisfactory": 23045, - "shuffle": 23046, - "##eves": 23047, - "##thic": 23048, - "##uj": 23049, - "fortification": 23050, - "warrington": 23051, - "##otto": 23052, - "resurrected": 23053, - "fargo": 23054, - "mane": 23055, - "##utable": 23056, - "##lei": 23057, - "##space": 23058, - "foreword": 23059, - "ox": 23060, - "##aris": 23061, - "##vern": 23062, - "abrams": 23063, - "hua": 23064, - "##mento": 23065, - "sakura": 23066, - "##alo": 23067, - "uv": 23068, - "sentimental": 23069, - "##skaya": 23070, - "midfield": 23071, - "##eses": 23072, - "sturdy": 23073, - "scrolls": 23074, - "macleod": 23075, - "##kyu": 23076, - "entropy": 23077, - "##lance": 23078, - "mitochondrial": 23079, - "cicero": 23080, - "excelled": 23081, - "thinner": 23082, - "convoys": 23083, - "perceive": 23084, - "##oslav": 23085, - "##urable": 23086, - "systematically": 23087, - "grind": 23088, - "burkina": 23089, - "287": 23090, - "##tagram": 23091, - "ops": 23092, - "##aman": 23093, - "guantanamo": 23094, - "##cloth": 23095, - "##tite": 23096, - "forcefully": 23097, - "wavy": 23098, - "##jou": 23099, - "pointless": 23100, - "##linger": 23101, - "##tze": 23102, - "layton": 23103, - "portico": 23104, - "superficial": 23105, - "clerical": 23106, - "outlaws": 23107, - "##hism": 23108, - "burials": 23109, - "muir": 23110, - "##inn": 23111, - "creditors": 23112, - "hauling": 23113, - "rattle": 23114, - "##leg": 23115, - "calais": 23116, - "monde": 23117, - "archers": 23118, - "reclaimed": 23119, - "dwell": 23120, - "wexford": 23121, - "hellenic": 23122, - "falsely": 23123, - "remorse": 23124, - "##tek": 23125, - "dough": 23126, - "furnishings": 23127, - "##uttered": 23128, - "gabon": 23129, - "neurological": 23130, - "novice": 23131, - "##igraphy": 23132, - "contemplated": 23133, - "pulpit": 23134, - "nightstand": 23135, - "saratoga": 23136, - "##istan": 23137, - "documenting": 23138, - "pulsing": 23139, - "taluk": 23140, - "##firmed": 23141, - "busted": 23142, - "marital": 23143, - "##rien": 23144, - "disagreements": 23145, - "wasps": 23146, - "##yes": 23147, - "hodge": 23148, - "mcdonnell": 23149, - "mimic": 23150, - "fran": 23151, - "pendant": 23152, - "dhabi": 23153, - "musa": 23154, - "##nington": 23155, - "congratulations": 23156, - "argent": 23157, - "darrell": 23158, - "concussion": 23159, - "losers": 23160, - "regrets": 23161, - "thessaloniki": 23162, - "reversal": 23163, - "donaldson": 23164, - "hardwood": 23165, - "thence": 23166, - "achilles": 23167, - "ritter": 23168, - "##eran": 23169, - "demonic": 23170, - "jurgen": 23171, - "prophets": 23172, - "goethe": 23173, - "eki": 23174, - "classmate": 23175, - "buff": 23176, - "##cking": 23177, - "yank": 23178, - "irrational": 23179, - "##inging": 23180, - "perished": 23181, - "seductive": 23182, - "qur": 23183, - "sourced": 23184, - "##crat": 23185, - "##typic": 23186, - "mustard": 23187, - "ravine": 23188, - "barre": 23189, - "horizontally": 23190, - "characterization": 23191, - "phylogenetic": 23192, - "boise": 23193, - "##dit": 23194, - "##runner": 23195, - "##tower": 23196, - "brutally": 23197, - "intercourse": 23198, - "seduce": 23199, - "##bbing": 23200, - "fay": 23201, - "ferris": 23202, - "ogden": 23203, - "amar": 23204, - "nik": 23205, - "unarmed": 23206, - "##inator": 23207, - "evaluating": 23208, - "kyrgyzstan": 23209, - "sweetness": 23210, - "##lford": 23211, - "##oki": 23212, - "mccormick": 23213, - "meiji": 23214, - "notoriety": 23215, - "stimulate": 23216, - "disrupt": 23217, - "figuring": 23218, - "instructional": 23219, - "mcgrath": 23220, - "##zoo": 23221, - "groundbreaking": 23222, - "##lto": 23223, - "flinch": 23224, - "khorasan": 23225, - "agrarian": 23226, - "bengals": 23227, - "mixer": 23228, - "radiating": 23229, - "##sov": 23230, - "ingram": 23231, - "pitchers": 23232, - "nad": 23233, - "tariff": 23234, - "##cript": 23235, - "tata": 23236, - "##codes": 23237, - "##emi": 23238, - "##ungen": 23239, - "appellate": 23240, - "lehigh": 23241, - "##bled": 23242, - "##giri": 23243, - "brawl": 23244, - "duct": 23245, - "texans": 23246, - "##ciation": 23247, - "##ropolis": 23248, - "skipper": 23249, - "speculative": 23250, - "vomit": 23251, - "doctrines": 23252, - "stresses": 23253, - "253": 23254, - "davy": 23255, - "graders": 23256, - "whitehead": 23257, - "jozef": 23258, - "timely": 23259, - "cumulative": 23260, - "haryana": 23261, - "paints": 23262, - "appropriately": 23263, - "boon": 23264, - "cactus": 23265, - "##ales": 23266, - "##pid": 23267, - "dow": 23268, - "legions": 23269, - "##pit": 23270, - "perceptions": 23271, - "1730": 23272, - "picturesque": 23273, - "##yse": 23274, - "periphery": 23275, - "rune": 23276, - "wr": 23277, - "##aha": 23278, - "celtics": 23279, - "sentencing": 23280, - "whoa": 23281, - "##erin": 23282, - "confirms": 23283, - "variance": 23284, - "425": 23285, - "moines": 23286, - "mathews": 23287, - "spade": 23288, - "rave": 23289, - "m1": 23290, - "fronted": 23291, - "fx": 23292, - "blending": 23293, - "alleging": 23294, - "reared": 23295, - "##gl": 23296, - "237": 23297, - "##paper": 23298, - "grassroots": 23299, - "eroded": 23300, - "##free": 23301, - "##physical": 23302, - "directs": 23303, - "ordeal": 23304, - "##sław": 23305, - "accelerate": 23306, - "hacker": 23307, - "rooftop": 23308, - "##inia": 23309, - "lev": 23310, - "buys": 23311, - "cebu": 23312, - "devote": 23313, - "##lce": 23314, - "specialising": 23315, - "##ulsion": 23316, - "choreographed": 23317, - "repetition": 23318, - "warehouses": 23319, - "##ryl": 23320, - "paisley": 23321, - "tuscany": 23322, - "analogy": 23323, - "sorcerer": 23324, - "hash": 23325, - "huts": 23326, - "shards": 23327, - "descends": 23328, - "exclude": 23329, - "nix": 23330, - "chaplin": 23331, - "gaga": 23332, - "ito": 23333, - "vane": 23334, - "##drich": 23335, - "causeway": 23336, - "misconduct": 23337, - "limo": 23338, - "orchestrated": 23339, - "glands": 23340, - "jana": 23341, - "##kot": 23342, - "u2": 23343, - "##mple": 23344, - "##sons": 23345, - "branching": 23346, - "contrasts": 23347, - "scoop": 23348, - "longed": 23349, - "##virus": 23350, - "chattanooga": 23351, - "##75": 23352, - "syrup": 23353, - "cornerstone": 23354, - "##tized": 23355, - "##mind": 23356, - "##iaceae": 23357, - "careless": 23358, - "precedence": 23359, - "frescoes": 23360, - "##uet": 23361, - "chilled": 23362, - "consult": 23363, - "modelled": 23364, - "snatch": 23365, - "peat": 23366, - "##thermal": 23367, - "caucasian": 23368, - "humane": 23369, - "relaxation": 23370, - "spins": 23371, - "temperance": 23372, - "##lbert": 23373, - "occupations": 23374, - "lambda": 23375, - "hybrids": 23376, - "moons": 23377, - "mp3": 23378, - "##oese": 23379, - "247": 23380, - "rolf": 23381, - "societal": 23382, - "yerevan": 23383, - "ness": 23384, - "##ssler": 23385, - "befriended": 23386, - "mechanized": 23387, - "nominate": 23388, - "trough": 23389, - "boasted": 23390, - "cues": 23391, - "seater": 23392, - "##hom": 23393, - "bends": 23394, - "##tangle": 23395, - "conductors": 23396, - "emptiness": 23397, - "##lmer": 23398, - "eurasian": 23399, - "adriatic": 23400, - "tian": 23401, - "##cie": 23402, - "anxiously": 23403, - "lark": 23404, - "propellers": 23405, - "chichester": 23406, - "jock": 23407, - "ev": 23408, - "2a": 23409, - "##holding": 23410, - "credible": 23411, - "recounts": 23412, - "tori": 23413, - "loyalist": 23414, - "abduction": 23415, - "##hoot": 23416, - "##redo": 23417, - "nepali": 23418, - "##mite": 23419, - "ventral": 23420, - "tempting": 23421, - "##ango": 23422, - "##crats": 23423, - "steered": 23424, - "##wice": 23425, - "javelin": 23426, - "dipping": 23427, - "laborers": 23428, - "prentice": 23429, - "looming": 23430, - "titanium": 23431, - "##ː": 23432, - "badges": 23433, - "emir": 23434, - "tensor": 23435, - "##ntation": 23436, - "egyptians": 23437, - "rash": 23438, - "denies": 23439, - "hawthorne": 23440, - "lombard": 23441, - "showers": 23442, - "wehrmacht": 23443, - "dietary": 23444, - "trojan": 23445, - "##reus": 23446, - "welles": 23447, - "executing": 23448, - "horseshoe": 23449, - "lifeboat": 23450, - "##lak": 23451, - "elsa": 23452, - "infirmary": 23453, - "nearing": 23454, - "roberta": 23455, - "boyer": 23456, - "mutter": 23457, - "trillion": 23458, - "joanne": 23459, - "##fine": 23460, - "##oked": 23461, - "sinks": 23462, - "vortex": 23463, - "uruguayan": 23464, - "clasp": 23465, - "sirius": 23466, - "##block": 23467, - "accelerator": 23468, - "prohibit": 23469, - "sunken": 23470, - "byu": 23471, - "chronological": 23472, - "diplomats": 23473, - "ochreous": 23474, - "510": 23475, - "symmetrical": 23476, - "1644": 23477, - "maia": 23478, - "##tology": 23479, - "salts": 23480, - "reigns": 23481, - "atrocities": 23482, - "##ия": 23483, - "hess": 23484, - "bared": 23485, - "issn": 23486, - "##vyn": 23487, - "cater": 23488, - "saturated": 23489, - "##cycle": 23490, - "##isse": 23491, - "sable": 23492, - "voyager": 23493, - "dyer": 23494, - "yusuf": 23495, - "##inge": 23496, - "fountains": 23497, - "wolff": 23498, - "##39": 23499, - "##nni": 23500, - "engraving": 23501, - "rollins": 23502, - "atheist": 23503, - "ominous": 23504, - "##ault": 23505, - "herr": 23506, - "chariot": 23507, - "martina": 23508, - "strung": 23509, - "##fell": 23510, - "##farlane": 23511, - "horrific": 23512, - "sahib": 23513, - "gazes": 23514, - "saetan": 23515, - "erased": 23516, - "ptolemy": 23517, - "##olic": 23518, - "flushing": 23519, - "lauderdale": 23520, - "analytic": 23521, - "##ices": 23522, - "530": 23523, - "navarro": 23524, - "beak": 23525, - "gorilla": 23526, - "herrera": 23527, - "broom": 23528, - "guadalupe": 23529, - "raiding": 23530, - "sykes": 23531, - "311": 23532, - "bsc": 23533, - "deliveries": 23534, - "1720": 23535, - "invasions": 23536, - "carmichael": 23537, - "tajikistan": 23538, - "thematic": 23539, - "ecumenical": 23540, - "sentiments": 23541, - "onstage": 23542, - "##rians": 23543, - "##brand": 23544, - "##sume": 23545, - "catastrophic": 23546, - "flanks": 23547, - "molten": 23548, - "##arns": 23549, - "waller": 23550, - "aimee": 23551, - "terminating": 23552, - "##icing": 23553, - "alternately": 23554, - "##oche": 23555, - "nehru": 23556, - "printers": 23557, - "outraged": 23558, - "##eving": 23559, - "empires": 23560, - "template": 23561, - "banners": 23562, - "repetitive": 23563, - "za": 23564, - "##oise": 23565, - "vegetarian": 23566, - "##tell": 23567, - "guiana": 23568, - "opt": 23569, - "cavendish": 23570, - "lucknow": 23571, - "synthesized": 23572, - "##hani": 23573, - "##mada": 23574, - "finalized": 23575, - "##ctable": 23576, - "fictitious": 23577, - "mayoral": 23578, - "unreliable": 23579, - "##enham": 23580, - "embracing": 23581, - "peppers": 23582, - "rbis": 23583, - "##chio": 23584, - "##neo": 23585, - "inhibition": 23586, - "slashed": 23587, - "togo": 23588, - "orderly": 23589, - "embroidered": 23590, - "safari": 23591, - "salty": 23592, - "236": 23593, - "barron": 23594, - "benito": 23595, - "totaled": 23596, - "##dak": 23597, - "pubs": 23598, - "simulated": 23599, - "caden": 23600, - "devin": 23601, - "tolkien": 23602, - "momma": 23603, - "welding": 23604, - "sesame": 23605, - "##ept": 23606, - "gottingen": 23607, - "hardness": 23608, - "630": 23609, - "shaman": 23610, - "temeraire": 23611, - "620": 23612, - "adequately": 23613, - "pediatric": 23614, - "##kit": 23615, - "ck": 23616, - "assertion": 23617, - "radicals": 23618, - "composure": 23619, - "cadence": 23620, - "seafood": 23621, - "beaufort": 23622, - "lazarus": 23623, - "mani": 23624, - "warily": 23625, - "cunning": 23626, - "kurdistan": 23627, - "249": 23628, - "cantata": 23629, - "##kir": 23630, - "ares": 23631, - "##41": 23632, - "##clusive": 23633, - "nape": 23634, - "townland": 23635, - "geared": 23636, - "insulted": 23637, - "flutter": 23638, - "boating": 23639, - "violate": 23640, - "draper": 23641, - "dumping": 23642, - "malmo": 23643, - "##hh": 23644, - "##romatic": 23645, - "firearm": 23646, - "alta": 23647, - "bono": 23648, - "obscured": 23649, - "##clave": 23650, - "exceeds": 23651, - "panorama": 23652, - "unbelievable": 23653, - "##train": 23654, - "preschool": 23655, - "##essed": 23656, - "disconnected": 23657, - "installing": 23658, - "rescuing": 23659, - "secretaries": 23660, - "accessibility": 23661, - "##castle": 23662, - "##drive": 23663, - "##ifice": 23664, - "##film": 23665, - "bouts": 23666, - "slug": 23667, - "waterway": 23668, - "mindanao": 23669, - "##buro": 23670, - "##ratic": 23671, - "halves": 23672, - "##ل": 23673, - "calming": 23674, - "liter": 23675, - "maternity": 23676, - "adorable": 23677, - "bragg": 23678, - "electrification": 23679, - "mcc": 23680, - "##dote": 23681, - "roxy": 23682, - "schizophrenia": 23683, - "##body": 23684, - "munoz": 23685, - "kaye": 23686, - "whaling": 23687, - "239": 23688, - "mil": 23689, - "tingling": 23690, - "tolerant": 23691, - "##ago": 23692, - "unconventional": 23693, - "volcanoes": 23694, - "##finder": 23695, - "deportivo": 23696, - "##llie": 23697, - "robson": 23698, - "kaufman": 23699, - "neuroscience": 23700, - "wai": 23701, - "deportation": 23702, - "masovian": 23703, - "scraping": 23704, - "converse": 23705, - "##bh": 23706, - "hacking": 23707, - "bulge": 23708, - "##oun": 23709, - "administratively": 23710, - "yao": 23711, - "580": 23712, - "amp": 23713, - "mammoth": 23714, - "booster": 23715, - "claremont": 23716, - "hooper": 23717, - "nomenclature": 23718, - "pursuits": 23719, - "mclaughlin": 23720, - "melinda": 23721, - "##sul": 23722, - "catfish": 23723, - "barclay": 23724, - "substrates": 23725, - "taxa": 23726, - "zee": 23727, - "originals": 23728, - "kimberly": 23729, - "packets": 23730, - "padma": 23731, - "##ality": 23732, - "borrowing": 23733, - "ostensibly": 23734, - "solvent": 23735, - "##bri": 23736, - "##genesis": 23737, - "##mist": 23738, - "lukas": 23739, - "shreveport": 23740, - "veracruz": 23741, - "##ь": 23742, - "##lou": 23743, - "##wives": 23744, - "cheney": 23745, - "tt": 23746, - "anatolia": 23747, - "hobbs": 23748, - "##zyn": 23749, - "cyclic": 23750, - "radiant": 23751, - "alistair": 23752, - "greenish": 23753, - "siena": 23754, - "dat": 23755, - "independents": 23756, - "##bation": 23757, - "conform": 23758, - "pieter": 23759, - "hyper": 23760, - "applicant": 23761, - "bradshaw": 23762, - "spores": 23763, - "telangana": 23764, - "vinci": 23765, - "inexpensive": 23766, - "nuclei": 23767, - "322": 23768, - "jang": 23769, - "nme": 23770, - "soho": 23771, - "spd": 23772, - "##ign": 23773, - "cradled": 23774, - "receptionist": 23775, - "pow": 23776, - "##43": 23777, - "##rika": 23778, - "fascism": 23779, - "##ifer": 23780, - "experimenting": 23781, - "##ading": 23782, - "##iec": 23783, - "##region": 23784, - "345": 23785, - "jocelyn": 23786, - "maris": 23787, - "stair": 23788, - "nocturnal": 23789, - "toro": 23790, - "constabulary": 23791, - "elgin": 23792, - "##kker": 23793, - "msc": 23794, - "##giving": 23795, - "##schen": 23796, - "##rase": 23797, - "doherty": 23798, - "doping": 23799, - "sarcastically": 23800, - "batter": 23801, - "maneuvers": 23802, - "##cano": 23803, - "##apple": 23804, - "##gai": 23805, - "##git": 23806, - "intrinsic": 23807, - "##nst": 23808, - "##stor": 23809, - "1753": 23810, - "showtime": 23811, - "cafes": 23812, - "gasps": 23813, - "lviv": 23814, - "ushered": 23815, - "##thed": 23816, - "fours": 23817, - "restart": 23818, - "astonishment": 23819, - "transmitting": 23820, - "flyer": 23821, - "shrugs": 23822, - "##sau": 23823, - "intriguing": 23824, - "cones": 23825, - "dictated": 23826, - "mushrooms": 23827, - "medial": 23828, - "##kovsky": 23829, - "##elman": 23830, - "escorting": 23831, - "gaped": 23832, - "##26": 23833, - "godfather": 23834, - "##door": 23835, - "##sell": 23836, - "djs": 23837, - "recaptured": 23838, - "timetable": 23839, - "vila": 23840, - "1710": 23841, - "3a": 23842, - "aerodrome": 23843, - "mortals": 23844, - "scientology": 23845, - "##orne": 23846, - "angelina": 23847, - "mag": 23848, - "convection": 23849, - "unpaid": 23850, - "insertion": 23851, - "intermittent": 23852, - "lego": 23853, - "##nated": 23854, - "endeavor": 23855, - "kota": 23856, - "pereira": 23857, - "##lz": 23858, - "304": 23859, - "bwv": 23860, - "glamorgan": 23861, - "insults": 23862, - "agatha": 23863, - "fey": 23864, - "##cend": 23865, - "fleetwood": 23866, - "mahogany": 23867, - "protruding": 23868, - "steamship": 23869, - "zeta": 23870, - "##arty": 23871, - "mcguire": 23872, - "suspense": 23873, - "##sphere": 23874, - "advising": 23875, - "urges": 23876, - "##wala": 23877, - "hurriedly": 23878, - "meteor": 23879, - "gilded": 23880, - "inline": 23881, - "arroyo": 23882, - "stalker": 23883, - "##oge": 23884, - "excitedly": 23885, - "revered": 23886, - "##cure": 23887, - "earle": 23888, - "introductory": 23889, - "##break": 23890, - "##ilde": 23891, - "mutants": 23892, - "puff": 23893, - "pulses": 23894, - "reinforcement": 23895, - "##haling": 23896, - "curses": 23897, - "lizards": 23898, - "stalk": 23899, - "correlated": 23900, - "##fixed": 23901, - "fallout": 23902, - "macquarie": 23903, - "##unas": 23904, - "bearded": 23905, - "denton": 23906, - "heaving": 23907, - "802": 23908, - "##ocation": 23909, - "winery": 23910, - "assign": 23911, - "dortmund": 23912, - "##lkirk": 23913, - "everest": 23914, - "invariant": 23915, - "charismatic": 23916, - "susie": 23917, - "##elling": 23918, - "bled": 23919, - "lesley": 23920, - "telegram": 23921, - "sumner": 23922, - "bk": 23923, - "##ogen": 23924, - "##к": 23925, - "wilcox": 23926, - "needy": 23927, - "colbert": 23928, - "duval": 23929, - "##iferous": 23930, - "##mbled": 23931, - "allotted": 23932, - "attends": 23933, - "imperative": 23934, - "##hita": 23935, - "replacements": 23936, - "hawker": 23937, - "##inda": 23938, - "insurgency": 23939, - "##zee": 23940, - "##eke": 23941, - "casts": 23942, - "##yla": 23943, - "680": 23944, - "ives": 23945, - "transitioned": 23946, - "##pack": 23947, - "##powering": 23948, - "authoritative": 23949, - "baylor": 23950, - "flex": 23951, - "cringed": 23952, - "plaintiffs": 23953, - "woodrow": 23954, - "##skie": 23955, - "drastic": 23956, - "ape": 23957, - "aroma": 23958, - "unfolded": 23959, - "commotion": 23960, - "nt": 23961, - "preoccupied": 23962, - "theta": 23963, - "routines": 23964, - "lasers": 23965, - "privatization": 23966, - "wand": 23967, - "domino": 23968, - "ek": 23969, - "clenching": 23970, - "nsa": 23971, - "strategically": 23972, - "showered": 23973, - "bile": 23974, - "handkerchief": 23975, - "pere": 23976, - "storing": 23977, - "christophe": 23978, - "insulting": 23979, - "316": 23980, - "nakamura": 23981, - "romani": 23982, - "asiatic": 23983, - "magdalena": 23984, - "palma": 23985, - "cruises": 23986, - "stripping": 23987, - "405": 23988, - "konstantin": 23989, - "soaring": 23990, - "##berman": 23991, - "colloquially": 23992, - "forerunner": 23993, - "havilland": 23994, - "incarcerated": 23995, - "parasites": 23996, - "sincerity": 23997, - "##utus": 23998, - "disks": 23999, - "plank": 24000, - "saigon": 24001, - "##ining": 24002, - "corbin": 24003, - "homo": 24004, - "ornaments": 24005, - "powerhouse": 24006, - "##tlement": 24007, - "chong": 24008, - "fastened": 24009, - "feasibility": 24010, - "idf": 24011, - "morphological": 24012, - "usable": 24013, - "##nish": 24014, - "##zuki": 24015, - "aqueduct": 24016, - "jaguars": 24017, - "keepers": 24018, - "##flies": 24019, - "aleksandr": 24020, - "faust": 24021, - "assigns": 24022, - "ewing": 24023, - "bacterium": 24024, - "hurled": 24025, - "tricky": 24026, - "hungarians": 24027, - "integers": 24028, - "wallis": 24029, - "321": 24030, - "yamaha": 24031, - "##isha": 24032, - "hushed": 24033, - "oblivion": 24034, - "aviator": 24035, - "evangelist": 24036, - "friars": 24037, - "##eller": 24038, - "monograph": 24039, - "ode": 24040, - "##nary": 24041, - "airplanes": 24042, - "labourers": 24043, - "charms": 24044, - "##nee": 24045, - "1661": 24046, - "hagen": 24047, - "tnt": 24048, - "rudder": 24049, - "fiesta": 24050, - "transcript": 24051, - "dorothea": 24052, - "ska": 24053, - "inhibitor": 24054, - "maccabi": 24055, - "retorted": 24056, - "raining": 24057, - "encompassed": 24058, - "clauses": 24059, - "menacing": 24060, - "1642": 24061, - "lineman": 24062, - "##gist": 24063, - "vamps": 24064, - "##ape": 24065, - "##dick": 24066, - "gloom": 24067, - "##rera": 24068, - "dealings": 24069, - "easing": 24070, - "seekers": 24071, - "##nut": 24072, - "##pment": 24073, - "helens": 24074, - "unmanned": 24075, - "##anu": 24076, - "##isson": 24077, - "basics": 24078, - "##amy": 24079, - "##ckman": 24080, - "adjustments": 24081, - "1688": 24082, - "brutality": 24083, - "horne": 24084, - "##zell": 24085, - "sui": 24086, - "##55": 24087, - "##mable": 24088, - "aggregator": 24089, - "##thal": 24090, - "rhino": 24091, - "##drick": 24092, - "##vira": 24093, - "counters": 24094, - "zoom": 24095, - "##01": 24096, - "##rting": 24097, - "mn": 24098, - "montenegrin": 24099, - "packard": 24100, - "##unciation": 24101, - "##♭": 24102, - "##kki": 24103, - "reclaim": 24104, - "scholastic": 24105, - "thugs": 24106, - "pulsed": 24107, - "##icia": 24108, - "syriac": 24109, - "quan": 24110, - "saddam": 24111, - "banda": 24112, - "kobe": 24113, - "blaming": 24114, - "buddies": 24115, - "dissent": 24116, - "##lusion": 24117, - "##usia": 24118, - "corbett": 24119, - "jaya": 24120, - "delle": 24121, - "erratic": 24122, - "lexie": 24123, - "##hesis": 24124, - "435": 24125, - "amiga": 24126, - "hermes": 24127, - "##pressing": 24128, - "##leen": 24129, - "chapels": 24130, - "gospels": 24131, - "jamal": 24132, - "##uating": 24133, - "compute": 24134, - "revolving": 24135, - "warp": 24136, - "##sso": 24137, - "##thes": 24138, - "armory": 24139, - "##eras": 24140, - "##gol": 24141, - "antrim": 24142, - "loki": 24143, - "##kow": 24144, - "##asian": 24145, - "##good": 24146, - "##zano": 24147, - "braid": 24148, - "handwriting": 24149, - "subdistrict": 24150, - "funky": 24151, - "pantheon": 24152, - "##iculate": 24153, - "concurrency": 24154, - "estimation": 24155, - "improper": 24156, - "juliana": 24157, - "##his": 24158, - "newcomers": 24159, - "johnstone": 24160, - "staten": 24161, - "communicated": 24162, - "##oco": 24163, - "##alle": 24164, - "sausage": 24165, - "stormy": 24166, - "##stered": 24167, - "##tters": 24168, - "superfamily": 24169, - "##grade": 24170, - "acidic": 24171, - "collateral": 24172, - "tabloid": 24173, - "##oped": 24174, - "##rza": 24175, - "bladder": 24176, - "austen": 24177, - "##ellant": 24178, - "mcgraw": 24179, - "##hay": 24180, - "hannibal": 24181, - "mein": 24182, - "aquino": 24183, - "lucifer": 24184, - "wo": 24185, - "badger": 24186, - "boar": 24187, - "cher": 24188, - "christensen": 24189, - "greenberg": 24190, - "interruption": 24191, - "##kken": 24192, - "jem": 24193, - "244": 24194, - "mocked": 24195, - "bottoms": 24196, - "cambridgeshire": 24197, - "##lide": 24198, - "sprawling": 24199, - "##bbly": 24200, - "eastwood": 24201, - "ghent": 24202, - "synth": 24203, - "##buck": 24204, - "advisers": 24205, - "##bah": 24206, - "nominally": 24207, - "hapoel": 24208, - "qu": 24209, - "daggers": 24210, - "estranged": 24211, - "fabricated": 24212, - "towels": 24213, - "vinnie": 24214, - "wcw": 24215, - "misunderstanding": 24216, - "anglia": 24217, - "nothin": 24218, - "unmistakable": 24219, - "##dust": 24220, - "##lova": 24221, - "chilly": 24222, - "marquette": 24223, - "truss": 24224, - "##edge": 24225, - "##erine": 24226, - "reece": 24227, - "##lty": 24228, - "##chemist": 24229, - "##connected": 24230, - "272": 24231, - "308": 24232, - "41st": 24233, - "bash": 24234, - "raion": 24235, - "waterfalls": 24236, - "##ump": 24237, - "##main": 24238, - "labyrinth": 24239, - "queue": 24240, - "theorist": 24241, - "##istle": 24242, - "bharatiya": 24243, - "flexed": 24244, - "soundtracks": 24245, - "rooney": 24246, - "leftist": 24247, - "patrolling": 24248, - "wharton": 24249, - "plainly": 24250, - "alleviate": 24251, - "eastman": 24252, - "schuster": 24253, - "topographic": 24254, - "engages": 24255, - "immensely": 24256, - "unbearable": 24257, - "fairchild": 24258, - "1620": 24259, - "dona": 24260, - "lurking": 24261, - "parisian": 24262, - "oliveira": 24263, - "ia": 24264, - "indictment": 24265, - "hahn": 24266, - "bangladeshi": 24267, - "##aster": 24268, - "vivo": 24269, - "##uming": 24270, - "##ential": 24271, - "antonia": 24272, - "expects": 24273, - "indoors": 24274, - "kildare": 24275, - "harlan": 24276, - "##logue": 24277, - "##ogenic": 24278, - "##sities": 24279, - "forgiven": 24280, - "##wat": 24281, - "childish": 24282, - "tavi": 24283, - "##mide": 24284, - "##orra": 24285, - "plausible": 24286, - "grimm": 24287, - "successively": 24288, - "scooted": 24289, - "##bola": 24290, - "##dget": 24291, - "##rith": 24292, - "spartans": 24293, - "emery": 24294, - "flatly": 24295, - "azure": 24296, - "epilogue": 24297, - "##wark": 24298, - "flourish": 24299, - "##iny": 24300, - "##tracted": 24301, - "##overs": 24302, - "##oshi": 24303, - "bestseller": 24304, - "distressed": 24305, - "receipt": 24306, - "spitting": 24307, - "hermit": 24308, - "topological": 24309, - "##cot": 24310, - "drilled": 24311, - "subunit": 24312, - "francs": 24313, - "##layer": 24314, - "eel": 24315, - "##fk": 24316, - "##itas": 24317, - "octopus": 24318, - "footprint": 24319, - "petitions": 24320, - "ufo": 24321, - "##say": 24322, - "##foil": 24323, - "interfering": 24324, - "leaking": 24325, - "palo": 24326, - "##metry": 24327, - "thistle": 24328, - "valiant": 24329, - "##pic": 24330, - "narayan": 24331, - "mcpherson": 24332, - "##fast": 24333, - "gonzales": 24334, - "##ym": 24335, - "##enne": 24336, - "dustin": 24337, - "novgorod": 24338, - "solos": 24339, - "##zman": 24340, - "doin": 24341, - "##raph": 24342, - "##patient": 24343, - "##meyer": 24344, - "soluble": 24345, - "ashland": 24346, - "cuffs": 24347, - "carole": 24348, - "pendleton": 24349, - "whistling": 24350, - "vassal": 24351, - "##river": 24352, - "deviation": 24353, - "revisited": 24354, - "constituents": 24355, - "rallied": 24356, - "rotate": 24357, - "loomed": 24358, - "##eil": 24359, - "##nting": 24360, - "amateurs": 24361, - "augsburg": 24362, - "auschwitz": 24363, - "crowns": 24364, - "skeletons": 24365, - "##cona": 24366, - "bonnet": 24367, - "257": 24368, - "dummy": 24369, - "globalization": 24370, - "simeon": 24371, - "sleeper": 24372, - "mandal": 24373, - "differentiated": 24374, - "##crow": 24375, - "##mare": 24376, - "milne": 24377, - "bundled": 24378, - "exasperated": 24379, - "talmud": 24380, - "owes": 24381, - "segregated": 24382, - "##feng": 24383, - "##uary": 24384, - "dentist": 24385, - "piracy": 24386, - "props": 24387, - "##rang": 24388, - "devlin": 24389, - "##torium": 24390, - "malicious": 24391, - "paws": 24392, - "##laid": 24393, - "dependency": 24394, - "##ergy": 24395, - "##fers": 24396, - "##enna": 24397, - "258": 24398, - "pistons": 24399, - "rourke": 24400, - "jed": 24401, - "grammatical": 24402, - "tres": 24403, - "maha": 24404, - "wig": 24405, - "512": 24406, - "ghostly": 24407, - "jayne": 24408, - "##achal": 24409, - "##creen": 24410, - "##ilis": 24411, - "##lins": 24412, - "##rence": 24413, - "designate": 24414, - "##with": 24415, - "arrogance": 24416, - "cambodian": 24417, - "clones": 24418, - "showdown": 24419, - "throttle": 24420, - "twain": 24421, - "##ception": 24422, - "lobes": 24423, - "metz": 24424, - "nagoya": 24425, - "335": 24426, - "braking": 24427, - "##furt": 24428, - "385": 24429, - "roaming": 24430, - "##minster": 24431, - "amin": 24432, - "crippled": 24433, - "##37": 24434, - "##llary": 24435, - "indifferent": 24436, - "hoffmann": 24437, - "idols": 24438, - "intimidating": 24439, - "1751": 24440, - "261": 24441, - "influenza": 24442, - "memo": 24443, - "onions": 24444, - "1748": 24445, - "bandage": 24446, - "consciously": 24447, - "##landa": 24448, - "##rage": 24449, - "clandestine": 24450, - "observes": 24451, - "swiped": 24452, - "tangle": 24453, - "##ener": 24454, - "##jected": 24455, - "##trum": 24456, - "##bill": 24457, - "##lta": 24458, - "hugs": 24459, - "congresses": 24460, - "josiah": 24461, - "spirited": 24462, - "##dek": 24463, - "humanist": 24464, - "managerial": 24465, - "filmmaking": 24466, - "inmate": 24467, - "rhymes": 24468, - "debuting": 24469, - "grimsby": 24470, - "ur": 24471, - "##laze": 24472, - "duplicate": 24473, - "vigor": 24474, - "##tf": 24475, - "republished": 24476, - "bolshevik": 24477, - "refurbishment": 24478, - "antibiotics": 24479, - "martini": 24480, - "methane": 24481, - "newscasts": 24482, - "royale": 24483, - "horizons": 24484, - "levant": 24485, - "iain": 24486, - "visas": 24487, - "##ischen": 24488, - "paler": 24489, - "##around": 24490, - "manifestation": 24491, - "snuck": 24492, - "alf": 24493, - "chop": 24494, - "futile": 24495, - "pedestal": 24496, - "rehab": 24497, - "##kat": 24498, - "bmg": 24499, - "kerman": 24500, - "res": 24501, - "fairbanks": 24502, - "jarrett": 24503, - "abstraction": 24504, - "saharan": 24505, - "##zek": 24506, - "1746": 24507, - "procedural": 24508, - "clearer": 24509, - "kincaid": 24510, - "sash": 24511, - "luciano": 24512, - "##ffey": 24513, - "crunch": 24514, - "helmut": 24515, - "##vara": 24516, - "revolutionaries": 24517, - "##tute": 24518, - "creamy": 24519, - "leach": 24520, - "##mmon": 24521, - "1747": 24522, - "permitting": 24523, - "nes": 24524, - "plight": 24525, - "wendell": 24526, - "##lese": 24527, - "contra": 24528, - "ts": 24529, - "clancy": 24530, - "ipa": 24531, - "mach": 24532, - "staples": 24533, - "autopsy": 24534, - "disturbances": 24535, - "nueva": 24536, - "karin": 24537, - "pontiac": 24538, - "##uding": 24539, - "proxy": 24540, - "venerable": 24541, - "haunt": 24542, - "leto": 24543, - "bergman": 24544, - "expands": 24545, - "##helm": 24546, - "wal": 24547, - "##pipe": 24548, - "canning": 24549, - "celine": 24550, - "cords": 24551, - "obesity": 24552, - "##enary": 24553, - "intrusion": 24554, - "planner": 24555, - "##phate": 24556, - "reasoned": 24557, - "sequencing": 24558, - "307": 24559, - "harrow": 24560, - "##chon": 24561, - "##dora": 24562, - "marred": 24563, - "mcintyre": 24564, - "repay": 24565, - "tarzan": 24566, - "darting": 24567, - "248": 24568, - "harrisburg": 24569, - "margarita": 24570, - "repulsed": 24571, - "##hur": 24572, - "##lding": 24573, - "belinda": 24574, - "hamburger": 24575, - "novo": 24576, - "compliant": 24577, - "runways": 24578, - "bingham": 24579, - "registrar": 24580, - "skyscraper": 24581, - "ic": 24582, - "cuthbert": 24583, - "improvisation": 24584, - "livelihood": 24585, - "##corp": 24586, - "##elial": 24587, - "admiring": 24588, - "##dened": 24589, - "sporadic": 24590, - "believer": 24591, - "casablanca": 24592, - "popcorn": 24593, - "##29": 24594, - "asha": 24595, - "shovel": 24596, - "##bek": 24597, - "##dice": 24598, - "coiled": 24599, - "tangible": 24600, - "##dez": 24601, - "casper": 24602, - "elsie": 24603, - "resin": 24604, - "tenderness": 24605, - "rectory": 24606, - "##ivision": 24607, - "avail": 24608, - "sonar": 24609, - "##mori": 24610, - "boutique": 24611, - "##dier": 24612, - "guerre": 24613, - "bathed": 24614, - "upbringing": 24615, - "vaulted": 24616, - "sandals": 24617, - "blessings": 24618, - "##naut": 24619, - "##utnant": 24620, - "1680": 24621, - "306": 24622, - "foxes": 24623, - "pia": 24624, - "corrosion": 24625, - "hesitantly": 24626, - "confederates": 24627, - "crystalline": 24628, - "footprints": 24629, - "shapiro": 24630, - "tirana": 24631, - "valentin": 24632, - "drones": 24633, - "45th": 24634, - "microscope": 24635, - "shipments": 24636, - "texted": 24637, - "inquisition": 24638, - "wry": 24639, - "guernsey": 24640, - "unauthorized": 24641, - "resigning": 24642, - "760": 24643, - "ripple": 24644, - "schubert": 24645, - "stu": 24646, - "reassure": 24647, - "felony": 24648, - "##ardo": 24649, - "brittle": 24650, - "koreans": 24651, - "##havan": 24652, - "##ives": 24653, - "dun": 24654, - "implicit": 24655, - "tyres": 24656, - "##aldi": 24657, - "##lth": 24658, - "magnolia": 24659, - "##ehan": 24660, - "##puri": 24661, - "##poulos": 24662, - "aggressively": 24663, - "fei": 24664, - "gr": 24665, - "familiarity": 24666, - "##poo": 24667, - "indicative": 24668, - "##trust": 24669, - "fundamentally": 24670, - "jimmie": 24671, - "overrun": 24672, - "395": 24673, - "anchors": 24674, - "moans": 24675, - "##opus": 24676, - "britannia": 24677, - "armagh": 24678, - "##ggle": 24679, - "purposely": 24680, - "seizing": 24681, - "##vao": 24682, - "bewildered": 24683, - "mundane": 24684, - "avoidance": 24685, - "cosmopolitan": 24686, - "geometridae": 24687, - "quartermaster": 24688, - "caf": 24689, - "415": 24690, - "chatter": 24691, - "engulfed": 24692, - "gleam": 24693, - "purge": 24694, - "##icate": 24695, - "juliette": 24696, - "jurisprudence": 24697, - "guerra": 24698, - "revisions": 24699, - "##bn": 24700, - "casimir": 24701, - "brew": 24702, - "##jm": 24703, - "1749": 24704, - "clapton": 24705, - "cloudy": 24706, - "conde": 24707, - "hermitage": 24708, - "278": 24709, - "simulations": 24710, - "torches": 24711, - "vincenzo": 24712, - "matteo": 24713, - "##rill": 24714, - "hidalgo": 24715, - "booming": 24716, - "westbound": 24717, - "accomplishment": 24718, - "tentacles": 24719, - "unaffected": 24720, - "##sius": 24721, - "annabelle": 24722, - "flopped": 24723, - "sloping": 24724, - "##litz": 24725, - "dreamer": 24726, - "interceptor": 24727, - "vu": 24728, - "##loh": 24729, - "consecration": 24730, - "copying": 24731, - "messaging": 24732, - "breaker": 24733, - "climates": 24734, - "hospitalized": 24735, - "1752": 24736, - "torino": 24737, - "afternoons": 24738, - "winfield": 24739, - "witnessing": 24740, - "##teacher": 24741, - "breakers": 24742, - "choirs": 24743, - "sawmill": 24744, - "coldly": 24745, - "##ege": 24746, - "sipping": 24747, - "haste": 24748, - "uninhabited": 24749, - "conical": 24750, - "bibliography": 24751, - "pamphlets": 24752, - "severn": 24753, - "edict": 24754, - "##oca": 24755, - "deux": 24756, - "illnesses": 24757, - "grips": 24758, - "##pl": 24759, - "rehearsals": 24760, - "sis": 24761, - "thinkers": 24762, - "tame": 24763, - "##keepers": 24764, - "1690": 24765, - "acacia": 24766, - "reformer": 24767, - "##osed": 24768, - "##rys": 24769, - "shuffling": 24770, - "##iring": 24771, - "##shima": 24772, - "eastbound": 24773, - "ionic": 24774, - "rhea": 24775, - "flees": 24776, - "littered": 24777, - "##oum": 24778, - "rocker": 24779, - "vomiting": 24780, - "groaning": 24781, - "champ": 24782, - "overwhelmingly": 24783, - "civilizations": 24784, - "paces": 24785, - "sloop": 24786, - "adoptive": 24787, - "##tish": 24788, - "skaters": 24789, - "##vres": 24790, - "aiding": 24791, - "mango": 24792, - "##joy": 24793, - "nikola": 24794, - "shriek": 24795, - "##ignon": 24796, - "pharmaceuticals": 24797, - "##mg": 24798, - "tuna": 24799, - "calvert": 24800, - "gustavo": 24801, - "stocked": 24802, - "yearbook": 24803, - "##urai": 24804, - "##mana": 24805, - "computed": 24806, - "subsp": 24807, - "riff": 24808, - "hanoi": 24809, - "kelvin": 24810, - "hamid": 24811, - "moors": 24812, - "pastures": 24813, - "summons": 24814, - "jihad": 24815, - "nectar": 24816, - "##ctors": 24817, - "bayou": 24818, - "untitled": 24819, - "pleasing": 24820, - "vastly": 24821, - "republics": 24822, - "intellect": 24823, - "##η": 24824, - "##ulio": 24825, - "##tou": 24826, - "crumbling": 24827, - "stylistic": 24828, - "sb": 24829, - "##ی": 24830, - "consolation": 24831, - "frequented": 24832, - "h₂o": 24833, - "walden": 24834, - "widows": 24835, - "##iens": 24836, - "404": 24837, - "##ignment": 24838, - "chunks": 24839, - "improves": 24840, - "288": 24841, - "grit": 24842, - "recited": 24843, - "##dev": 24844, - "snarl": 24845, - "sociological": 24846, - "##arte": 24847, - "##gul": 24848, - "inquired": 24849, - "##held": 24850, - "bruise": 24851, - "clube": 24852, - "consultancy": 24853, - "homogeneous": 24854, - "hornets": 24855, - "multiplication": 24856, - "pasta": 24857, - "prick": 24858, - "savior": 24859, - "##grin": 24860, - "##kou": 24861, - "##phile": 24862, - "yoon": 24863, - "##gara": 24864, - "grimes": 24865, - "vanishing": 24866, - "cheering": 24867, - "reacting": 24868, - "bn": 24869, - "distillery": 24870, - "##quisite": 24871, - "##vity": 24872, - "coe": 24873, - "dockyard": 24874, - "massif": 24875, - "##jord": 24876, - "escorts": 24877, - "voss": 24878, - "##valent": 24879, - "byte": 24880, - "chopped": 24881, - "hawke": 24882, - "illusions": 24883, - "workings": 24884, - "floats": 24885, - "##koto": 24886, - "##vac": 24887, - "kv": 24888, - "annapolis": 24889, - "madden": 24890, - "##onus": 24891, - "alvaro": 24892, - "noctuidae": 24893, - "##cum": 24894, - "##scopic": 24895, - "avenge": 24896, - "steamboat": 24897, - "forte": 24898, - "illustrates": 24899, - "erika": 24900, - "##trip": 24901, - "570": 24902, - "dew": 24903, - "nationalities": 24904, - "bran": 24905, - "manifested": 24906, - "thirsty": 24907, - "diversified": 24908, - "muscled": 24909, - "reborn": 24910, - "##standing": 24911, - "arson": 24912, - "##lessness": 24913, - "##dran": 24914, - "##logram": 24915, - "##boys": 24916, - "##kushima": 24917, - "##vious": 24918, - "willoughby": 24919, - "##phobia": 24920, - "286": 24921, - "alsace": 24922, - "dashboard": 24923, - "yuki": 24924, - "##chai": 24925, - "granville": 24926, - "myspace": 24927, - "publicized": 24928, - "tricked": 24929, - "##gang": 24930, - "adjective": 24931, - "##ater": 24932, - "relic": 24933, - "reorganisation": 24934, - "enthusiastically": 24935, - "indications": 24936, - "saxe": 24937, - "##lassified": 24938, - "consolidate": 24939, - "iec": 24940, - "padua": 24941, - "helplessly": 24942, - "ramps": 24943, - "renaming": 24944, - "regulars": 24945, - "pedestrians": 24946, - "accents": 24947, - "convicts": 24948, - "inaccurate": 24949, - "lowers": 24950, - "mana": 24951, - "##pati": 24952, - "barrie": 24953, - "bjp": 24954, - "outta": 24955, - "someplace": 24956, - "berwick": 24957, - "flanking": 24958, - "invoked": 24959, - "marrow": 24960, - "sparsely": 24961, - "excerpts": 24962, - "clothed": 24963, - "rei": 24964, - "##ginal": 24965, - "wept": 24966, - "##straße": 24967, - "##vish": 24968, - "alexa": 24969, - "excel": 24970, - "##ptive": 24971, - "membranes": 24972, - "aquitaine": 24973, - "creeks": 24974, - "cutler": 24975, - "sheppard": 24976, - "implementations": 24977, - "ns": 24978, - "##dur": 24979, - "fragrance": 24980, - "budge": 24981, - "concordia": 24982, - "magnesium": 24983, - "marcelo": 24984, - "##antes": 24985, - "gladly": 24986, - "vibrating": 24987, - "##rral": 24988, - "##ggles": 24989, - "montrose": 24990, - "##omba": 24991, - "lew": 24992, - "seamus": 24993, - "1630": 24994, - "cocky": 24995, - "##ament": 24996, - "##uen": 24997, - "bjorn": 24998, - "##rrick": 24999, - "fielder": 25000, - "fluttering": 25001, - "##lase": 25002, - "methyl": 25003, - "kimberley": 25004, - "mcdowell": 25005, - "reductions": 25006, - "barbed": 25007, - "##jic": 25008, - "##tonic": 25009, - "aeronautical": 25010, - "condensed": 25011, - "distracting": 25012, - "##promising": 25013, - "huffed": 25014, - "##cala": 25015, - "##sle": 25016, - "claudius": 25017, - "invincible": 25018, - "missy": 25019, - "pious": 25020, - "balthazar": 25021, - "ci": 25022, - "##lang": 25023, - "butte": 25024, - "combo": 25025, - "orson": 25026, - "##dication": 25027, - "myriad": 25028, - "1707": 25029, - "silenced": 25030, - "##fed": 25031, - "##rh": 25032, - "coco": 25033, - "netball": 25034, - "yourselves": 25035, - "##oza": 25036, - "clarify": 25037, - "heller": 25038, - "peg": 25039, - "durban": 25040, - "etudes": 25041, - "offender": 25042, - "roast": 25043, - "blackmail": 25044, - "curvature": 25045, - "##woods": 25046, - "vile": 25047, - "309": 25048, - "illicit": 25049, - "suriname": 25050, - "##linson": 25051, - "overture": 25052, - "1685": 25053, - "bubbling": 25054, - "gymnast": 25055, - "tucking": 25056, - "##mming": 25057, - "##ouin": 25058, - "maldives": 25059, - "##bala": 25060, - "gurney": 25061, - "##dda": 25062, - "##eased": 25063, - "##oides": 25064, - "backside": 25065, - "pinto": 25066, - "jars": 25067, - "racehorse": 25068, - "tending": 25069, - "##rdial": 25070, - "baronetcy": 25071, - "wiener": 25072, - "duly": 25073, - "##rke": 25074, - "barbarian": 25075, - "cupping": 25076, - "flawed": 25077, - "##thesis": 25078, - "bertha": 25079, - "pleistocene": 25080, - "puddle": 25081, - "swearing": 25082, - "##nob": 25083, - "##tically": 25084, - "fleeting": 25085, - "prostate": 25086, - "amulet": 25087, - "educating": 25088, - "##mined": 25089, - "##iti": 25090, - "##tler": 25091, - "75th": 25092, - "jens": 25093, - "respondents": 25094, - "analytics": 25095, - "cavaliers": 25096, - "papacy": 25097, - "raju": 25098, - "##iente": 25099, - "##ulum": 25100, - "##tip": 25101, - "funnel": 25102, - "271": 25103, - "disneyland": 25104, - "##lley": 25105, - "sociologist": 25106, - "##iam": 25107, - "2500": 25108, - "faulkner": 25109, - "louvre": 25110, - "menon": 25111, - "##dson": 25112, - "276": 25113, - "##ower": 25114, - "afterlife": 25115, - "mannheim": 25116, - "peptide": 25117, - "referees": 25118, - "comedians": 25119, - "meaningless": 25120, - "##anger": 25121, - "##laise": 25122, - "fabrics": 25123, - "hurley": 25124, - "renal": 25125, - "sleeps": 25126, - "##bour": 25127, - "##icle": 25128, - "breakout": 25129, - "kristin": 25130, - "roadside": 25131, - "animator": 25132, - "clover": 25133, - "disdain": 25134, - "unsafe": 25135, - "redesign": 25136, - "##urity": 25137, - "firth": 25138, - "barnsley": 25139, - "portage": 25140, - "reset": 25141, - "narrows": 25142, - "268": 25143, - "commandos": 25144, - "expansive": 25145, - "speechless": 25146, - "tubular": 25147, - "##lux": 25148, - "essendon": 25149, - "eyelashes": 25150, - "smashwords": 25151, - "##yad": 25152, - "##bang": 25153, - "##claim": 25154, - "craved": 25155, - "sprinted": 25156, - "chet": 25157, - "somme": 25158, - "astor": 25159, - "wrocław": 25160, - "orton": 25161, - "266": 25162, - "bane": 25163, - "##erving": 25164, - "##uing": 25165, - "mischief": 25166, - "##amps": 25167, - "##sund": 25168, - "scaling": 25169, - "terre": 25170, - "##xious": 25171, - "impairment": 25172, - "offenses": 25173, - "undermine": 25174, - "moi": 25175, - "soy": 25176, - "contiguous": 25177, - "arcadia": 25178, - "inuit": 25179, - "seam": 25180, - "##tops": 25181, - "macbeth": 25182, - "rebelled": 25183, - "##icative": 25184, - "##iot": 25185, - "590": 25186, - "elaborated": 25187, - "frs": 25188, - "uniformed": 25189, - "##dberg": 25190, - "259": 25191, - "powerless": 25192, - "priscilla": 25193, - "stimulated": 25194, - "980": 25195, - "qc": 25196, - "arboretum": 25197, - "frustrating": 25198, - "trieste": 25199, - "bullock": 25200, - "##nified": 25201, - "enriched": 25202, - "glistening": 25203, - "intern": 25204, - "##adia": 25205, - "locus": 25206, - "nouvelle": 25207, - "ollie": 25208, - "ike": 25209, - "lash": 25210, - "starboard": 25211, - "ee": 25212, - "tapestry": 25213, - "headlined": 25214, - "hove": 25215, - "rigged": 25216, - "##vite": 25217, - "pollock": 25218, - "##yme": 25219, - "thrive": 25220, - "clustered": 25221, - "cas": 25222, - "roi": 25223, - "gleamed": 25224, - "olympiad": 25225, - "##lino": 25226, - "pressured": 25227, - "regimes": 25228, - "##hosis": 25229, - "##lick": 25230, - "ripley": 25231, - "##ophone": 25232, - "kickoff": 25233, - "gallon": 25234, - "rockwell": 25235, - "##arable": 25236, - "crusader": 25237, - "glue": 25238, - "revolutions": 25239, - "scrambling": 25240, - "1714": 25241, - "grover": 25242, - "##jure": 25243, - "englishman": 25244, - "aztec": 25245, - "263": 25246, - "contemplating": 25247, - "coven": 25248, - "ipad": 25249, - "preach": 25250, - "triumphant": 25251, - "tufts": 25252, - "##esian": 25253, - "rotational": 25254, - "##phus": 25255, - "328": 25256, - "falkland": 25257, - "##brates": 25258, - "strewn": 25259, - "clarissa": 25260, - "rejoin": 25261, - "environmentally": 25262, - "glint": 25263, - "banded": 25264, - "drenched": 25265, - "moat": 25266, - "albanians": 25267, - "johor": 25268, - "rr": 25269, - "maestro": 25270, - "malley": 25271, - "nouveau": 25272, - "shaded": 25273, - "taxonomy": 25274, - "v6": 25275, - "adhere": 25276, - "bunk": 25277, - "airfields": 25278, - "##ritan": 25279, - "1741": 25280, - "encompass": 25281, - "remington": 25282, - "tran": 25283, - "##erative": 25284, - "amelie": 25285, - "mazda": 25286, - "friar": 25287, - "morals": 25288, - "passions": 25289, - "##zai": 25290, - "breadth": 25291, - "vis": 25292, - "##hae": 25293, - "argus": 25294, - "burnham": 25295, - "caressing": 25296, - "insider": 25297, - "rudd": 25298, - "##imov": 25299, - "##mini": 25300, - "##rso": 25301, - "italianate": 25302, - "murderous": 25303, - "textual": 25304, - "wainwright": 25305, - "armada": 25306, - "bam": 25307, - "weave": 25308, - "timer": 25309, - "##taken": 25310, - "##nh": 25311, - "fra": 25312, - "##crest": 25313, - "ardent": 25314, - "salazar": 25315, - "taps": 25316, - "tunis": 25317, - "##ntino": 25318, - "allegro": 25319, - "gland": 25320, - "philanthropic": 25321, - "##chester": 25322, - "implication": 25323, - "##optera": 25324, - "esq": 25325, - "judas": 25326, - "noticeably": 25327, - "wynn": 25328, - "##dara": 25329, - "inched": 25330, - "indexed": 25331, - "crises": 25332, - "villiers": 25333, - "bandit": 25334, - "royalties": 25335, - "patterned": 25336, - "cupboard": 25337, - "interspersed": 25338, - "accessory": 25339, - "isla": 25340, - "kendrick": 25341, - "entourage": 25342, - "stitches": 25343, - "##esthesia": 25344, - "headwaters": 25345, - "##ior": 25346, - "interlude": 25347, - "distraught": 25348, - "draught": 25349, - "1727": 25350, - "##basket": 25351, - "biased": 25352, - "sy": 25353, - "transient": 25354, - "triad": 25355, - "subgenus": 25356, - "adapting": 25357, - "kidd": 25358, - "shortstop": 25359, - "##umatic": 25360, - "dimly": 25361, - "spiked": 25362, - "mcleod": 25363, - "reprint": 25364, - "nellie": 25365, - "pretoria": 25366, - "windmill": 25367, - "##cek": 25368, - "singled": 25369, - "##mps": 25370, - "273": 25371, - "reunite": 25372, - "##orous": 25373, - "747": 25374, - "bankers": 25375, - "outlying": 25376, - "##omp": 25377, - "##ports": 25378, - "##tream": 25379, - "apologies": 25380, - "cosmetics": 25381, - "patsy": 25382, - "##deh": 25383, - "##ocks": 25384, - "##yson": 25385, - "bender": 25386, - "nantes": 25387, - "serene": 25388, - "##nad": 25389, - "lucha": 25390, - "mmm": 25391, - "323": 25392, - "##cius": 25393, - "##gli": 25394, - "cmll": 25395, - "coinage": 25396, - "nestor": 25397, - "juarez": 25398, - "##rook": 25399, - "smeared": 25400, - "sprayed": 25401, - "twitching": 25402, - "sterile": 25403, - "irina": 25404, - "embodied": 25405, - "juveniles": 25406, - "enveloped": 25407, - "miscellaneous": 25408, - "cancers": 25409, - "dq": 25410, - "gulped": 25411, - "luisa": 25412, - "crested": 25413, - "swat": 25414, - "donegal": 25415, - "ref": 25416, - "##anov": 25417, - "##acker": 25418, - "hearst": 25419, - "mercantile": 25420, - "##lika": 25421, - "doorbell": 25422, - "ua": 25423, - "vicki": 25424, - "##alla": 25425, - "##som": 25426, - "bilbao": 25427, - "psychologists": 25428, - "stryker": 25429, - "sw": 25430, - "horsemen": 25431, - "turkmenistan": 25432, - "wits": 25433, - "##national": 25434, - "anson": 25435, - "mathew": 25436, - "screenings": 25437, - "##umb": 25438, - "rihanna": 25439, - "##agne": 25440, - "##nessy": 25441, - "aisles": 25442, - "##iani": 25443, - "##osphere": 25444, - "hines": 25445, - "kenton": 25446, - "saskatoon": 25447, - "tasha": 25448, - "truncated": 25449, - "##champ": 25450, - "##itan": 25451, - "mildred": 25452, - "advises": 25453, - "fredrik": 25454, - "interpreting": 25455, - "inhibitors": 25456, - "##athi": 25457, - "spectroscopy": 25458, - "##hab": 25459, - "##kong": 25460, - "karim": 25461, - "panda": 25462, - "##oia": 25463, - "##nail": 25464, - "##vc": 25465, - "conqueror": 25466, - "kgb": 25467, - "leukemia": 25468, - "##dity": 25469, - "arrivals": 25470, - "cheered": 25471, - "pisa": 25472, - "phosphorus": 25473, - "shielded": 25474, - "##riated": 25475, - "mammal": 25476, - "unitarian": 25477, - "urgently": 25478, - "chopin": 25479, - "sanitary": 25480, - "##mission": 25481, - "spicy": 25482, - "drugged": 25483, - "hinges": 25484, - "##tort": 25485, - "tipping": 25486, - "trier": 25487, - "impoverished": 25488, - "westchester": 25489, - "##caster": 25490, - "267": 25491, - "epoch": 25492, - "nonstop": 25493, - "##gman": 25494, - "##khov": 25495, - "aromatic": 25496, - "centrally": 25497, - "cerro": 25498, - "##tively": 25499, - "##vio": 25500, - "billions": 25501, - "modulation": 25502, - "sedimentary": 25503, - "283": 25504, - "facilitating": 25505, - "outrageous": 25506, - "goldstein": 25507, - "##eak": 25508, - "##kt": 25509, - "ld": 25510, - "maitland": 25511, - "penultimate": 25512, - "pollard": 25513, - "##dance": 25514, - "fleets": 25515, - "spaceship": 25516, - "vertebrae": 25517, - "##nig": 25518, - "alcoholism": 25519, - "als": 25520, - "recital": 25521, - "##bham": 25522, - "##ference": 25523, - "##omics": 25524, - "m2": 25525, - "##bm": 25526, - "trois": 25527, - "##tropical": 25528, - "##в": 25529, - "commemorates": 25530, - "##meric": 25531, - "marge": 25532, - "##raction": 25533, - "1643": 25534, - "670": 25535, - "cosmetic": 25536, - "ravaged": 25537, - "##ige": 25538, - "catastrophe": 25539, - "eng": 25540, - "##shida": 25541, - "albrecht": 25542, - "arterial": 25543, - "bellamy": 25544, - "decor": 25545, - "harmon": 25546, - "##rde": 25547, - "bulbs": 25548, - "synchronized": 25549, - "vito": 25550, - "easiest": 25551, - "shetland": 25552, - "shielding": 25553, - "wnba": 25554, - "##glers": 25555, - "##ssar": 25556, - "##riam": 25557, - "brianna": 25558, - "cumbria": 25559, - "##aceous": 25560, - "##rard": 25561, - "cores": 25562, - "thayer": 25563, - "##nsk": 25564, - "brood": 25565, - "hilltop": 25566, - "luminous": 25567, - "carts": 25568, - "keynote": 25569, - "larkin": 25570, - "logos": 25571, - "##cta": 25572, - "##ا": 25573, - "##mund": 25574, - "##quay": 25575, - "lilith": 25576, - "tinted": 25577, - "277": 25578, - "wrestle": 25579, - "mobilization": 25580, - "##uses": 25581, - "sequential": 25582, - "siam": 25583, - "bloomfield": 25584, - "takahashi": 25585, - "274": 25586, - "##ieving": 25587, - "presenters": 25588, - "ringo": 25589, - "blazed": 25590, - "witty": 25591, - "##oven": 25592, - "##ignant": 25593, - "devastation": 25594, - "haydn": 25595, - "harmed": 25596, - "newt": 25597, - "therese": 25598, - "##peed": 25599, - "gershwin": 25600, - "molina": 25601, - "rabbis": 25602, - "sudanese": 25603, - "001": 25604, - "innate": 25605, - "restarted": 25606, - "##sack": 25607, - "##fus": 25608, - "slices": 25609, - "wb": 25610, - "##shah": 25611, - "enroll": 25612, - "hypothetical": 25613, - "hysterical": 25614, - "1743": 25615, - "fabio": 25616, - "indefinite": 25617, - "warped": 25618, - "##hg": 25619, - "exchanging": 25620, - "525": 25621, - "unsuitable": 25622, - "##sboro": 25623, - "gallo": 25624, - "1603": 25625, - "bret": 25626, - "cobalt": 25627, - "homemade": 25628, - "##hunter": 25629, - "mx": 25630, - "operatives": 25631, - "##dhar": 25632, - "terraces": 25633, - "durable": 25634, - "latch": 25635, - "pens": 25636, - "whorls": 25637, - "##ctuated": 25638, - "##eaux": 25639, - "billing": 25640, - "ligament": 25641, - "succumbed": 25642, - "##gly": 25643, - "regulators": 25644, - "spawn": 25645, - "##brick": 25646, - "##stead": 25647, - "filmfare": 25648, - "rochelle": 25649, - "##nzo": 25650, - "1725": 25651, - "circumstance": 25652, - "saber": 25653, - "supplements": 25654, - "##nsky": 25655, - "##tson": 25656, - "crowe": 25657, - "wellesley": 25658, - "carrot": 25659, - "##9th": 25660, - "##movable": 25661, - "primate": 25662, - "drury": 25663, - "sincerely": 25664, - "topical": 25665, - "##mad": 25666, - "##rao": 25667, - "callahan": 25668, - "kyiv": 25669, - "smarter": 25670, - "tits": 25671, - "undo": 25672, - "##yeh": 25673, - "announcements": 25674, - "anthologies": 25675, - "barrio": 25676, - "nebula": 25677, - "##islaus": 25678, - "##shaft": 25679, - "##tyn": 25680, - "bodyguards": 25681, - "2021": 25682, - "assassinate": 25683, - "barns": 25684, - "emmett": 25685, - "scully": 25686, - "##mah": 25687, - "##yd": 25688, - "##eland": 25689, - "##tino": 25690, - "##itarian": 25691, - "demoted": 25692, - "gorman": 25693, - "lashed": 25694, - "prized": 25695, - "adventist": 25696, - "writ": 25697, - "##gui": 25698, - "alla": 25699, - "invertebrates": 25700, - "##ausen": 25701, - "1641": 25702, - "amman": 25703, - "1742": 25704, - "align": 25705, - "healy": 25706, - "redistribution": 25707, - "##gf": 25708, - "##rize": 25709, - "insulation": 25710, - "##drop": 25711, - "adherents": 25712, - "hezbollah": 25713, - "vitro": 25714, - "ferns": 25715, - "yanking": 25716, - "269": 25717, - "php": 25718, - "registering": 25719, - "uppsala": 25720, - "cheerleading": 25721, - "confines": 25722, - "mischievous": 25723, - "tully": 25724, - "##ross": 25725, - "49th": 25726, - "docked": 25727, - "roam": 25728, - "stipulated": 25729, - "pumpkin": 25730, - "##bry": 25731, - "prompt": 25732, - "##ezer": 25733, - "blindly": 25734, - "shuddering": 25735, - "craftsmen": 25736, - "frail": 25737, - "scented": 25738, - "katharine": 25739, - "scramble": 25740, - "shaggy": 25741, - "sponge": 25742, - "helix": 25743, - "zaragoza": 25744, - "279": 25745, - "##52": 25746, - "43rd": 25747, - "backlash": 25748, - "fontaine": 25749, - "seizures": 25750, - "posse": 25751, - "cowan": 25752, - "nonfiction": 25753, - "telenovela": 25754, - "wwii": 25755, - "hammered": 25756, - "undone": 25757, - "##gpur": 25758, - "encircled": 25759, - "irs": 25760, - "##ivation": 25761, - "artefacts": 25762, - "oneself": 25763, - "searing": 25764, - "smallpox": 25765, - "##belle": 25766, - "##osaurus": 25767, - "shandong": 25768, - "breached": 25769, - "upland": 25770, - "blushing": 25771, - "rankin": 25772, - "infinitely": 25773, - "psyche": 25774, - "tolerated": 25775, - "docking": 25776, - "evicted": 25777, - "##col": 25778, - "unmarked": 25779, - "##lving": 25780, - "gnome": 25781, - "lettering": 25782, - "litres": 25783, - "musique": 25784, - "##oint": 25785, - "benevolent": 25786, - "##jal": 25787, - "blackened": 25788, - "##anna": 25789, - "mccall": 25790, - "racers": 25791, - "tingle": 25792, - "##ocene": 25793, - "##orestation": 25794, - "introductions": 25795, - "radically": 25796, - "292": 25797, - "##hiff": 25798, - "##باد": 25799, - "1610": 25800, - "1739": 25801, - "munchen": 25802, - "plead": 25803, - "##nka": 25804, - "condo": 25805, - "scissors": 25806, - "##sight": 25807, - "##tens": 25808, - "apprehension": 25809, - "##cey": 25810, - "##yin": 25811, - "hallmark": 25812, - "watering": 25813, - "formulas": 25814, - "sequels": 25815, - "##llas": 25816, - "aggravated": 25817, - "bae": 25818, - "commencing": 25819, - "##building": 25820, - "enfield": 25821, - "prohibits": 25822, - "marne": 25823, - "vedic": 25824, - "civilized": 25825, - "euclidean": 25826, - "jagger": 25827, - "beforehand": 25828, - "blasts": 25829, - "dumont": 25830, - "##arney": 25831, - "##nem": 25832, - "740": 25833, - "conversions": 25834, - "hierarchical": 25835, - "rios": 25836, - "simulator": 25837, - "##dya": 25838, - "##lellan": 25839, - "hedges": 25840, - "oleg": 25841, - "thrusts": 25842, - "shadowed": 25843, - "darby": 25844, - "maximize": 25845, - "1744": 25846, - "gregorian": 25847, - "##nded": 25848, - "##routed": 25849, - "sham": 25850, - "unspecified": 25851, - "##hog": 25852, - "emory": 25853, - "factual": 25854, - "##smo": 25855, - "##tp": 25856, - "fooled": 25857, - "##rger": 25858, - "ortega": 25859, - "wellness": 25860, - "marlon": 25861, - "##oton": 25862, - "##urance": 25863, - "casket": 25864, - "keating": 25865, - "ley": 25866, - "enclave": 25867, - "##ayan": 25868, - "char": 25869, - "influencing": 25870, - "jia": 25871, - "##chenko": 25872, - "412": 25873, - "ammonia": 25874, - "erebidae": 25875, - "incompatible": 25876, - "violins": 25877, - "cornered": 25878, - "##arat": 25879, - "grooves": 25880, - "astronauts": 25881, - "columbian": 25882, - "rampant": 25883, - "fabrication": 25884, - "kyushu": 25885, - "mahmud": 25886, - "vanish": 25887, - "##dern": 25888, - "mesopotamia": 25889, - "##lete": 25890, - "ict": 25891, - "##rgen": 25892, - "caspian": 25893, - "kenji": 25894, - "pitted": 25895, - "##vered": 25896, - "999": 25897, - "grimace": 25898, - "roanoke": 25899, - "tchaikovsky": 25900, - "twinned": 25901, - "##analysis": 25902, - "##awan": 25903, - "xinjiang": 25904, - "arias": 25905, - "clemson": 25906, - "kazakh": 25907, - "sizable": 25908, - "1662": 25909, - "##khand": 25910, - "##vard": 25911, - "plunge": 25912, - "tatum": 25913, - "vittorio": 25914, - "##nden": 25915, - "cholera": 25916, - "##dana": 25917, - "##oper": 25918, - "bracing": 25919, - "indifference": 25920, - "projectile": 25921, - "superliga": 25922, - "##chee": 25923, - "realises": 25924, - "upgrading": 25925, - "299": 25926, - "porte": 25927, - "retribution": 25928, - "##vies": 25929, - "nk": 25930, - "stil": 25931, - "##resses": 25932, - "ama": 25933, - "bureaucracy": 25934, - "blackberry": 25935, - "bosch": 25936, - "testosterone": 25937, - "collapses": 25938, - "greer": 25939, - "##pathic": 25940, - "ioc": 25941, - "fifties": 25942, - "malls": 25943, - "##erved": 25944, - "bao": 25945, - "baskets": 25946, - "adolescents": 25947, - "siegfried": 25948, - "##osity": 25949, - "##tosis": 25950, - "mantra": 25951, - "detecting": 25952, - "existent": 25953, - "fledgling": 25954, - "##cchi": 25955, - "dissatisfied": 25956, - "gan": 25957, - "telecommunication": 25958, - "mingled": 25959, - "sobbed": 25960, - "6000": 25961, - "controversies": 25962, - "outdated": 25963, - "taxis": 25964, - "##raus": 25965, - "fright": 25966, - "slams": 25967, - "##lham": 25968, - "##fect": 25969, - "##tten": 25970, - "detectors": 25971, - "fetal": 25972, - "tanned": 25973, - "##uw": 25974, - "fray": 25975, - "goth": 25976, - "olympian": 25977, - "skipping": 25978, - "mandates": 25979, - "scratches": 25980, - "sheng": 25981, - "unspoken": 25982, - "hyundai": 25983, - "tracey": 25984, - "hotspur": 25985, - "restrictive": 25986, - "##buch": 25987, - "americana": 25988, - "mundo": 25989, - "##bari": 25990, - "burroughs": 25991, - "diva": 25992, - "vulcan": 25993, - "##6th": 25994, - "distinctions": 25995, - "thumping": 25996, - "##ngen": 25997, - "mikey": 25998, - "sheds": 25999, - "fide": 26000, - "rescues": 26001, - "springsteen": 26002, - "vested": 26003, - "valuation": 26004, - "##ece": 26005, - "##ely": 26006, - "pinnacle": 26007, - "rake": 26008, - "sylvie": 26009, - "##edo": 26010, - "almond": 26011, - "quivering": 26012, - "##irus": 26013, - "alteration": 26014, - "faltered": 26015, - "##wad": 26016, - "51st": 26017, - "hydra": 26018, - "ticked": 26019, - "##kato": 26020, - "recommends": 26021, - "##dicated": 26022, - "antigua": 26023, - "arjun": 26024, - "stagecoach": 26025, - "wilfred": 26026, - "trickle": 26027, - "pronouns": 26028, - "##pon": 26029, - "aryan": 26030, - "nighttime": 26031, - "##anian": 26032, - "gall": 26033, - "pea": 26034, - "stitch": 26035, - "##hei": 26036, - "leung": 26037, - "milos": 26038, - "##dini": 26039, - "eritrea": 26040, - "nexus": 26041, - "starved": 26042, - "snowfall": 26043, - "kant": 26044, - "parasitic": 26045, - "cot": 26046, - "discus": 26047, - "hana": 26048, - "strikers": 26049, - "appleton": 26050, - "kitchens": 26051, - "##erina": 26052, - "##partisan": 26053, - "##itha": 26054, - "##vius": 26055, - "disclose": 26056, - "metis": 26057, - "##channel": 26058, - "1701": 26059, - "tesla": 26060, - "##vera": 26061, - "fitch": 26062, - "1735": 26063, - "blooded": 26064, - "##tila": 26065, - "decimal": 26066, - "##tang": 26067, - "##bai": 26068, - "cyclones": 26069, - "eun": 26070, - "bottled": 26071, - "peas": 26072, - "pensacola": 26073, - "basha": 26074, - "bolivian": 26075, - "crabs": 26076, - "boil": 26077, - "lanterns": 26078, - "partridge": 26079, - "roofed": 26080, - "1645": 26081, - "necks": 26082, - "##phila": 26083, - "opined": 26084, - "patting": 26085, - "##kla": 26086, - "##lland": 26087, - "chuckles": 26088, - "volta": 26089, - "whereupon": 26090, - "##nche": 26091, - "devout": 26092, - "euroleague": 26093, - "suicidal": 26094, - "##dee": 26095, - "inherently": 26096, - "involuntary": 26097, - "knitting": 26098, - "nasser": 26099, - "##hide": 26100, - "puppets": 26101, - "colourful": 26102, - "courageous": 26103, - "southend": 26104, - "stills": 26105, - "miraculous": 26106, - "hodgson": 26107, - "richer": 26108, - "rochdale": 26109, - "ethernet": 26110, - "greta": 26111, - "uniting": 26112, - "prism": 26113, - "umm": 26114, - "##haya": 26115, - "##itical": 26116, - "##utation": 26117, - "deterioration": 26118, - "pointe": 26119, - "prowess": 26120, - "##ropriation": 26121, - "lids": 26122, - "scranton": 26123, - "billings": 26124, - "subcontinent": 26125, - "##koff": 26126, - "##scope": 26127, - "brute": 26128, - "kellogg": 26129, - "psalms": 26130, - "degraded": 26131, - "##vez": 26132, - "stanisław": 26133, - "##ructured": 26134, - "ferreira": 26135, - "pun": 26136, - "astonishing": 26137, - "gunnar": 26138, - "##yat": 26139, - "arya": 26140, - "prc": 26141, - "gottfried": 26142, - "##tight": 26143, - "excursion": 26144, - "##ographer": 26145, - "dina": 26146, - "##quil": 26147, - "##nare": 26148, - "huffington": 26149, - "illustrious": 26150, - "wilbur": 26151, - "gundam": 26152, - "verandah": 26153, - "##zard": 26154, - "naacp": 26155, - "##odle": 26156, - "constructive": 26157, - "fjord": 26158, - "kade": 26159, - "##naud": 26160, - "generosity": 26161, - "thrilling": 26162, - "baseline": 26163, - "cayman": 26164, - "frankish": 26165, - "plastics": 26166, - "accommodations": 26167, - "zoological": 26168, - "##fting": 26169, - "cedric": 26170, - "qb": 26171, - "motorized": 26172, - "##dome": 26173, - "##otted": 26174, - "squealed": 26175, - "tackled": 26176, - "canucks": 26177, - "budgets": 26178, - "situ": 26179, - "asthma": 26180, - "dail": 26181, - "gabled": 26182, - "grasslands": 26183, - "whimpered": 26184, - "writhing": 26185, - "judgments": 26186, - "##65": 26187, - "minnie": 26188, - "pv": 26189, - "##carbon": 26190, - "bananas": 26191, - "grille": 26192, - "domes": 26193, - "monique": 26194, - "odin": 26195, - "maguire": 26196, - "markham": 26197, - "tierney": 26198, - "##estra": 26199, - "##chua": 26200, - "libel": 26201, - "poke": 26202, - "speedy": 26203, - "atrium": 26204, - "laval": 26205, - "notwithstanding": 26206, - "##edly": 26207, - "fai": 26208, - "kala": 26209, - "##sur": 26210, - "robb": 26211, - "##sma": 26212, - "listings": 26213, - "luz": 26214, - "supplementary": 26215, - "tianjin": 26216, - "##acing": 26217, - "enzo": 26218, - "jd": 26219, - "ric": 26220, - "scanner": 26221, - "croats": 26222, - "transcribed": 26223, - "##49": 26224, - "arden": 26225, - "cv": 26226, - "##hair": 26227, - "##raphy": 26228, - "##lver": 26229, - "##uy": 26230, - "357": 26231, - "seventies": 26232, - "staggering": 26233, - "alam": 26234, - "horticultural": 26235, - "hs": 26236, - "regression": 26237, - "timbers": 26238, - "blasting": 26239, - "##ounded": 26240, - "montagu": 26241, - "manipulating": 26242, - "##cit": 26243, - "catalytic": 26244, - "1550": 26245, - "troopers": 26246, - "##meo": 26247, - "condemnation": 26248, - "fitzpatrick": 26249, - "##oire": 26250, - "##roved": 26251, - "inexperienced": 26252, - "1670": 26253, - "castes": 26254, - "##lative": 26255, - "outing": 26256, - "314": 26257, - "dubois": 26258, - "flicking": 26259, - "quarrel": 26260, - "ste": 26261, - "learners": 26262, - "1625": 26263, - "iq": 26264, - "whistled": 26265, - "##class": 26266, - "282": 26267, - "classify": 26268, - "tariffs": 26269, - "temperament": 26270, - "355": 26271, - "folly": 26272, - "liszt": 26273, - "##yles": 26274, - "immersed": 26275, - "jordanian": 26276, - "ceasefire": 26277, - "apparel": 26278, - "extras": 26279, - "maru": 26280, - "fished": 26281, - "##bio": 26282, - "harta": 26283, - "stockport": 26284, - "assortment": 26285, - "craftsman": 26286, - "paralysis": 26287, - "transmitters": 26288, - "##cola": 26289, - "blindness": 26290, - "##wk": 26291, - "fatally": 26292, - "proficiency": 26293, - "solemnly": 26294, - "##orno": 26295, - "repairing": 26296, - "amore": 26297, - "groceries": 26298, - "ultraviolet": 26299, - "##chase": 26300, - "schoolhouse": 26301, - "##tua": 26302, - "resurgence": 26303, - "nailed": 26304, - "##otype": 26305, - "##×": 26306, - "ruse": 26307, - "saliva": 26308, - "diagrams": 26309, - "##tructing": 26310, - "albans": 26311, - "rann": 26312, - "thirties": 26313, - "1b": 26314, - "antennas": 26315, - "hilarious": 26316, - "cougars": 26317, - "paddington": 26318, - "stats": 26319, - "##eger": 26320, - "breakaway": 26321, - "ipod": 26322, - "reza": 26323, - "authorship": 26324, - "prohibiting": 26325, - "scoffed": 26326, - "##etz": 26327, - "##ttle": 26328, - "conscription": 26329, - "defected": 26330, - "trondheim": 26331, - "##fires": 26332, - "ivanov": 26333, - "keenan": 26334, - "##adan": 26335, - "##ciful": 26336, - "##fb": 26337, - "##slow": 26338, - "locating": 26339, - "##ials": 26340, - "##tford": 26341, - "cadiz": 26342, - "basalt": 26343, - "blankly": 26344, - "interned": 26345, - "rags": 26346, - "rattling": 26347, - "##tick": 26348, - "carpathian": 26349, - "reassured": 26350, - "sync": 26351, - "bum": 26352, - "guildford": 26353, - "iss": 26354, - "staunch": 26355, - "##onga": 26356, - "astronomers": 26357, - "sera": 26358, - "sofie": 26359, - "emergencies": 26360, - "susquehanna": 26361, - "##heard": 26362, - "duc": 26363, - "mastery": 26364, - "vh1": 26365, - "williamsburg": 26366, - "bayer": 26367, - "buckled": 26368, - "craving": 26369, - "##khan": 26370, - "##rdes": 26371, - "bloomington": 26372, - "##write": 26373, - "alton": 26374, - "barbecue": 26375, - "##bians": 26376, - "justine": 26377, - "##hri": 26378, - "##ndt": 26379, - "delightful": 26380, - "smartphone": 26381, - "newtown": 26382, - "photon": 26383, - "retrieval": 26384, - "peugeot": 26385, - "hissing": 26386, - "##monium": 26387, - "##orough": 26388, - "flavors": 26389, - "lighted": 26390, - "relaunched": 26391, - "tainted": 26392, - "##games": 26393, - "##lysis": 26394, - "anarchy": 26395, - "microscopic": 26396, - "hopping": 26397, - "adept": 26398, - "evade": 26399, - "evie": 26400, - "##beau": 26401, - "inhibit": 26402, - "sinn": 26403, - "adjustable": 26404, - "hurst": 26405, - "intuition": 26406, - "wilton": 26407, - "cisco": 26408, - "44th": 26409, - "lawful": 26410, - "lowlands": 26411, - "stockings": 26412, - "thierry": 26413, - "##dalen": 26414, - "##hila": 26415, - "##nai": 26416, - "fates": 26417, - "prank": 26418, - "tb": 26419, - "maison": 26420, - "lobbied": 26421, - "provocative": 26422, - "1724": 26423, - "4a": 26424, - "utopia": 26425, - "##qual": 26426, - "carbonate": 26427, - "gujarati": 26428, - "purcell": 26429, - "##rford": 26430, - "curtiss": 26431, - "##mei": 26432, - "overgrown": 26433, - "arenas": 26434, - "mediation": 26435, - "swallows": 26436, - "##rnik": 26437, - "respectful": 26438, - "turnbull": 26439, - "##hedron": 26440, - "##hope": 26441, - "alyssa": 26442, - "ozone": 26443, - "##ʻi": 26444, - "ami": 26445, - "gestapo": 26446, - "johansson": 26447, - "snooker": 26448, - "canteen": 26449, - "cuff": 26450, - "declines": 26451, - "empathy": 26452, - "stigma": 26453, - "##ags": 26454, - "##iner": 26455, - "##raine": 26456, - "taxpayers": 26457, - "gui": 26458, - "volga": 26459, - "##wright": 26460, - "##copic": 26461, - "lifespan": 26462, - "overcame": 26463, - "tattooed": 26464, - "enactment": 26465, - "giggles": 26466, - "##ador": 26467, - "##camp": 26468, - "barrington": 26469, - "bribe": 26470, - "obligatory": 26471, - "orbiting": 26472, - "peng": 26473, - "##enas": 26474, - "elusive": 26475, - "sucker": 26476, - "##vating": 26477, - "cong": 26478, - "hardship": 26479, - "empowered": 26480, - "anticipating": 26481, - "estrada": 26482, - "cryptic": 26483, - "greasy": 26484, - "detainees": 26485, - "planck": 26486, - "sudbury": 26487, - "plaid": 26488, - "dod": 26489, - "marriott": 26490, - "kayla": 26491, - "##ears": 26492, - "##vb": 26493, - "##zd": 26494, - "mortally": 26495, - "##hein": 26496, - "cognition": 26497, - "radha": 26498, - "319": 26499, - "liechtenstein": 26500, - "meade": 26501, - "richly": 26502, - "argyle": 26503, - "harpsichord": 26504, - "liberalism": 26505, - "trumpets": 26506, - "lauded": 26507, - "tyrant": 26508, - "salsa": 26509, - "tiled": 26510, - "lear": 26511, - "promoters": 26512, - "reused": 26513, - "slicing": 26514, - "trident": 26515, - "##chuk": 26516, - "##gami": 26517, - "##lka": 26518, - "cantor": 26519, - "checkpoint": 26520, - "##points": 26521, - "gaul": 26522, - "leger": 26523, - "mammalian": 26524, - "##tov": 26525, - "##aar": 26526, - "##schaft": 26527, - "doha": 26528, - "frenchman": 26529, - "nirvana": 26530, - "##vino": 26531, - "delgado": 26532, - "headlining": 26533, - "##eron": 26534, - "##iography": 26535, - "jug": 26536, - "tko": 26537, - "1649": 26538, - "naga": 26539, - "intersections": 26540, - "##jia": 26541, - "benfica": 26542, - "nawab": 26543, - "##suka": 26544, - "ashford": 26545, - "gulp": 26546, - "##deck": 26547, - "##vill": 26548, - "##rug": 26549, - "brentford": 26550, - "frazier": 26551, - "pleasures": 26552, - "dunne": 26553, - "potsdam": 26554, - "shenzhen": 26555, - "dentistry": 26556, - "##tec": 26557, - "flanagan": 26558, - "##dorff": 26559, - "##hear": 26560, - "chorale": 26561, - "dinah": 26562, - "prem": 26563, - "quezon": 26564, - "##rogated": 26565, - "relinquished": 26566, - "sutra": 26567, - "terri": 26568, - "##pani": 26569, - "flaps": 26570, - "##rissa": 26571, - "poly": 26572, - "##rnet": 26573, - "homme": 26574, - "aback": 26575, - "##eki": 26576, - "linger": 26577, - "womb": 26578, - "##kson": 26579, - "##lewood": 26580, - "doorstep": 26581, - "orthodoxy": 26582, - "threaded": 26583, - "westfield": 26584, - "##rval": 26585, - "dioceses": 26586, - "fridays": 26587, - "subsided": 26588, - "##gata": 26589, - "loyalists": 26590, - "##biotic": 26591, - "##ettes": 26592, - "letterman": 26593, - "lunatic": 26594, - "prelate": 26595, - "tenderly": 26596, - "invariably": 26597, - "souza": 26598, - "thug": 26599, - "winslow": 26600, - "##otide": 26601, - "furlongs": 26602, - "gogh": 26603, - "jeopardy": 26604, - "##runa": 26605, - "pegasus": 26606, - "##umble": 26607, - "humiliated": 26608, - "standalone": 26609, - "tagged": 26610, - "##roller": 26611, - "freshmen": 26612, - "klan": 26613, - "##bright": 26614, - "attaining": 26615, - "initiating": 26616, - "transatlantic": 26617, - "logged": 26618, - "viz": 26619, - "##uance": 26620, - "1723": 26621, - "combatants": 26622, - "intervening": 26623, - "stephane": 26624, - "chieftain": 26625, - "despised": 26626, - "grazed": 26627, - "317": 26628, - "cdc": 26629, - "galveston": 26630, - "godzilla": 26631, - "macro": 26632, - "simulate": 26633, - "##planes": 26634, - "parades": 26635, - "##esses": 26636, - "960": 26637, - "##ductive": 26638, - "##unes": 26639, - "equator": 26640, - "overdose": 26641, - "##cans": 26642, - "##hosh": 26643, - "##lifting": 26644, - "joshi": 26645, - "epstein": 26646, - "sonora": 26647, - "treacherous": 26648, - "aquatics": 26649, - "manchu": 26650, - "responsive": 26651, - "##sation": 26652, - "supervisory": 26653, - "##christ": 26654, - "##llins": 26655, - "##ibar": 26656, - "##balance": 26657, - "##uso": 26658, - "kimball": 26659, - "karlsruhe": 26660, - "mab": 26661, - "##emy": 26662, - "ignores": 26663, - "phonetic": 26664, - "reuters": 26665, - "spaghetti": 26666, - "820": 26667, - "almighty": 26668, - "danzig": 26669, - "rumbling": 26670, - "tombstone": 26671, - "designations": 26672, - "lured": 26673, - "outset": 26674, - "##felt": 26675, - "supermarkets": 26676, - "##wt": 26677, - "grupo": 26678, - "kei": 26679, - "kraft": 26680, - "susanna": 26681, - "##blood": 26682, - "comprehension": 26683, - "genealogy": 26684, - "##aghan": 26685, - "##verted": 26686, - "redding": 26687, - "##ythe": 26688, - "1722": 26689, - "bowing": 26690, - "##pore": 26691, - "##roi": 26692, - "lest": 26693, - "sharpened": 26694, - "fulbright": 26695, - "valkyrie": 26696, - "sikhs": 26697, - "##unds": 26698, - "swans": 26699, - "bouquet": 26700, - "merritt": 26701, - "##tage": 26702, - "##venting": 26703, - "commuted": 26704, - "redhead": 26705, - "clerks": 26706, - "leasing": 26707, - "cesare": 26708, - "dea": 26709, - "hazy": 26710, - "##vances": 26711, - "fledged": 26712, - "greenfield": 26713, - "servicemen": 26714, - "##gical": 26715, - "armando": 26716, - "blackout": 26717, - "dt": 26718, - "sagged": 26719, - "downloadable": 26720, - "intra": 26721, - "potion": 26722, - "pods": 26723, - "##4th": 26724, - "##mism": 26725, - "xp": 26726, - "attendants": 26727, - "gambia": 26728, - "stale": 26729, - "##ntine": 26730, - "plump": 26731, - "asteroids": 26732, - "rediscovered": 26733, - "buds": 26734, - "flea": 26735, - "hive": 26736, - "##neas": 26737, - "1737": 26738, - "classifications": 26739, - "debuts": 26740, - "##eles": 26741, - "olympus": 26742, - "scala": 26743, - "##eurs": 26744, - "##gno": 26745, - "##mute": 26746, - "hummed": 26747, - "sigismund": 26748, - "visuals": 26749, - "wiggled": 26750, - "await": 26751, - "pilasters": 26752, - "clench": 26753, - "sulfate": 26754, - "##ances": 26755, - "bellevue": 26756, - "enigma": 26757, - "trainee": 26758, - "snort": 26759, - "##sw": 26760, - "clouded": 26761, - "denim": 26762, - "##rank": 26763, - "##rder": 26764, - "churning": 26765, - "hartman": 26766, - "lodges": 26767, - "riches": 26768, - "sima": 26769, - "##missible": 26770, - "accountable": 26771, - "socrates": 26772, - "regulates": 26773, - "mueller": 26774, - "##cr": 26775, - "1702": 26776, - "avoids": 26777, - "solids": 26778, - "himalayas": 26779, - "nutrient": 26780, - "pup": 26781, - "##jevic": 26782, - "squat": 26783, - "fades": 26784, - "nec": 26785, - "##lates": 26786, - "##pina": 26787, - "##rona": 26788, - "##ου": 26789, - "privateer": 26790, - "tequila": 26791, - "##gative": 26792, - "##mpton": 26793, - "apt": 26794, - "hornet": 26795, - "immortals": 26796, - "##dou": 26797, - "asturias": 26798, - "cleansing": 26799, - "dario": 26800, - "##rries": 26801, - "##anta": 26802, - "etymology": 26803, - "servicing": 26804, - "zhejiang": 26805, - "##venor": 26806, - "##nx": 26807, - "horned": 26808, - "erasmus": 26809, - "rayon": 26810, - "relocating": 26811, - "£10": 26812, - "##bags": 26813, - "escalated": 26814, - "promenade": 26815, - "stubble": 26816, - "2010s": 26817, - "artisans": 26818, - "axial": 26819, - "liquids": 26820, - "mora": 26821, - "sho": 26822, - "yoo": 26823, - "##tsky": 26824, - "bundles": 26825, - "oldies": 26826, - "##nally": 26827, - "notification": 26828, - "bastion": 26829, - "##ths": 26830, - "sparkle": 26831, - "##lved": 26832, - "1728": 26833, - "leash": 26834, - "pathogen": 26835, - "highs": 26836, - "##hmi": 26837, - "immature": 26838, - "880": 26839, - "gonzaga": 26840, - "ignatius": 26841, - "mansions": 26842, - "monterrey": 26843, - "sweets": 26844, - "bryson": 26845, - "##loe": 26846, - "polled": 26847, - "regatta": 26848, - "brightest": 26849, - "pei": 26850, - "rosy": 26851, - "squid": 26852, - "hatfield": 26853, - "payroll": 26854, - "addict": 26855, - "meath": 26856, - "cornerback": 26857, - "heaviest": 26858, - "lodging": 26859, - "##mage": 26860, - "capcom": 26861, - "rippled": 26862, - "##sily": 26863, - "barnet": 26864, - "mayhem": 26865, - "ymca": 26866, - "snuggled": 26867, - "rousseau": 26868, - "##cute": 26869, - "blanchard": 26870, - "284": 26871, - "fragmented": 26872, - "leighton": 26873, - "chromosomes": 26874, - "risking": 26875, - "##md": 26876, - "##strel": 26877, - "##utter": 26878, - "corinne": 26879, - "coyotes": 26880, - "cynical": 26881, - "hiroshi": 26882, - "yeomanry": 26883, - "##ractive": 26884, - "ebook": 26885, - "grading": 26886, - "mandela": 26887, - "plume": 26888, - "agustin": 26889, - "magdalene": 26890, - "##rkin": 26891, - "bea": 26892, - "femme": 26893, - "trafford": 26894, - "##coll": 26895, - "##lun": 26896, - "##tance": 26897, - "52nd": 26898, - "fourier": 26899, - "upton": 26900, - "##mental": 26901, - "camilla": 26902, - "gust": 26903, - "iihf": 26904, - "islamabad": 26905, - "longevity": 26906, - "##kala": 26907, - "feldman": 26908, - "netting": 26909, - "##rization": 26910, - "endeavour": 26911, - "foraging": 26912, - "mfa": 26913, - "orr": 26914, - "##open": 26915, - "greyish": 26916, - "contradiction": 26917, - "graz": 26918, - "##ruff": 26919, - "handicapped": 26920, - "marlene": 26921, - "tweed": 26922, - "oaxaca": 26923, - "spp": 26924, - "campos": 26925, - "miocene": 26926, - "pri": 26927, - "configured": 26928, - "cooks": 26929, - "pluto": 26930, - "cozy": 26931, - "pornographic": 26932, - "##entes": 26933, - "70th": 26934, - "fairness": 26935, - "glided": 26936, - "jonny": 26937, - "lynne": 26938, - "rounding": 26939, - "sired": 26940, - "##emon": 26941, - "##nist": 26942, - "remade": 26943, - "uncover": 26944, - "##mack": 26945, - "complied": 26946, - "lei": 26947, - "newsweek": 26948, - "##jured": 26949, - "##parts": 26950, - "##enting": 26951, - "##pg": 26952, - "293": 26953, - "finer": 26954, - "guerrillas": 26955, - "athenian": 26956, - "deng": 26957, - "disused": 26958, - "stepmother": 26959, - "accuse": 26960, - "gingerly": 26961, - "seduction": 26962, - "521": 26963, - "confronting": 26964, - "##walker": 26965, - "##going": 26966, - "gora": 26967, - "nostalgia": 26968, - "sabres": 26969, - "virginity": 26970, - "wrenched": 26971, - "##minated": 26972, - "syndication": 26973, - "wielding": 26974, - "eyre": 26975, - "##56": 26976, - "##gnon": 26977, - "##igny": 26978, - "behaved": 26979, - "taxpayer": 26980, - "sweeps": 26981, - "##growth": 26982, - "childless": 26983, - "gallant": 26984, - "##ywood": 26985, - "amplified": 26986, - "geraldine": 26987, - "scrape": 26988, - "##ffi": 26989, - "babylonian": 26990, - "fresco": 26991, - "##rdan": 26992, - "##kney": 26993, - "##position": 26994, - "1718": 26995, - "restricting": 26996, - "tack": 26997, - "fukuoka": 26998, - "osborn": 26999, - "selector": 27000, - "partnering": 27001, - "##dlow": 27002, - "318": 27003, - "gnu": 27004, - "kia": 27005, - "tak": 27006, - "whitley": 27007, - "gables": 27008, - "##54": 27009, - "##mania": 27010, - "mri": 27011, - "softness": 27012, - "immersion": 27013, - "##bots": 27014, - "##evsky": 27015, - "1713": 27016, - "chilling": 27017, - "insignificant": 27018, - "pcs": 27019, - "##uis": 27020, - "elites": 27021, - "lina": 27022, - "purported": 27023, - "supplemental": 27024, - "teaming": 27025, - "##americana": 27026, - "##dding": 27027, - "##inton": 27028, - "proficient": 27029, - "rouen": 27030, - "##nage": 27031, - "##rret": 27032, - "niccolo": 27033, - "selects": 27034, - "##bread": 27035, - "fluffy": 27036, - "1621": 27037, - "gruff": 27038, - "knotted": 27039, - "mukherjee": 27040, - "polgara": 27041, - "thrash": 27042, - "nicholls": 27043, - "secluded": 27044, - "smoothing": 27045, - "thru": 27046, - "corsica": 27047, - "loaf": 27048, - "whitaker": 27049, - "inquiries": 27050, - "##rrier": 27051, - "##kam": 27052, - "indochina": 27053, - "289": 27054, - "marlins": 27055, - "myles": 27056, - "peking": 27057, - "##tea": 27058, - "extracts": 27059, - "pastry": 27060, - "superhuman": 27061, - "connacht": 27062, - "vogel": 27063, - "##ditional": 27064, - "##het": 27065, - "##udged": 27066, - "##lash": 27067, - "gloss": 27068, - "quarries": 27069, - "refit": 27070, - "teaser": 27071, - "##alic": 27072, - "##gaon": 27073, - "20s": 27074, - "materialized": 27075, - "sling": 27076, - "camped": 27077, - "pickering": 27078, - "tung": 27079, - "tracker": 27080, - "pursuant": 27081, - "##cide": 27082, - "cranes": 27083, - "soc": 27084, - "##cini": 27085, - "##typical": 27086, - "##viere": 27087, - "anhalt": 27088, - "overboard": 27089, - "workout": 27090, - "chores": 27091, - "fares": 27092, - "orphaned": 27093, - "stains": 27094, - "##logie": 27095, - "fenton": 27096, - "surpassing": 27097, - "joyah": 27098, - "triggers": 27099, - "##itte": 27100, - "grandmaster": 27101, - "##lass": 27102, - "##lists": 27103, - "clapping": 27104, - "fraudulent": 27105, - "ledger": 27106, - "nagasaki": 27107, - "##cor": 27108, - "##nosis": 27109, - "##tsa": 27110, - "eucalyptus": 27111, - "tun": 27112, - "##icio": 27113, - "##rney": 27114, - "##tara": 27115, - "dax": 27116, - "heroism": 27117, - "ina": 27118, - "wrexham": 27119, - "onboard": 27120, - "unsigned": 27121, - "##dates": 27122, - "moshe": 27123, - "galley": 27124, - "winnie": 27125, - "droplets": 27126, - "exiles": 27127, - "praises": 27128, - "watered": 27129, - "noodles": 27130, - "##aia": 27131, - "fein": 27132, - "adi": 27133, - "leland": 27134, - "multicultural": 27135, - "stink": 27136, - "bingo": 27137, - "comets": 27138, - "erskine": 27139, - "modernized": 27140, - "canned": 27141, - "constraint": 27142, - "domestically": 27143, - "chemotherapy": 27144, - "featherweight": 27145, - "stifled": 27146, - "##mum": 27147, - "darkly": 27148, - "irresistible": 27149, - "refreshing": 27150, - "hasty": 27151, - "isolate": 27152, - "##oys": 27153, - "kitchener": 27154, - "planners": 27155, - "##wehr": 27156, - "cages": 27157, - "yarn": 27158, - "implant": 27159, - "toulon": 27160, - "elects": 27161, - "childbirth": 27162, - "yue": 27163, - "##lind": 27164, - "##lone": 27165, - "cn": 27166, - "rightful": 27167, - "sportsman": 27168, - "junctions": 27169, - "remodeled": 27170, - "specifies": 27171, - "##rgh": 27172, - "291": 27173, - "##oons": 27174, - "complimented": 27175, - "##urgent": 27176, - "lister": 27177, - "ot": 27178, - "##logic": 27179, - "bequeathed": 27180, - "cheekbones": 27181, - "fontana": 27182, - "gabby": 27183, - "##dial": 27184, - "amadeus": 27185, - "corrugated": 27186, - "maverick": 27187, - "resented": 27188, - "triangles": 27189, - "##hered": 27190, - "##usly": 27191, - "nazareth": 27192, - "tyrol": 27193, - "1675": 27194, - "assent": 27195, - "poorer": 27196, - "sectional": 27197, - "aegean": 27198, - "##cous": 27199, - "296": 27200, - "nylon": 27201, - "ghanaian": 27202, - "##egorical": 27203, - "##weig": 27204, - "cushions": 27205, - "forbid": 27206, - "fusiliers": 27207, - "obstruction": 27208, - "somerville": 27209, - "##scia": 27210, - "dime": 27211, - "earrings": 27212, - "elliptical": 27213, - "leyte": 27214, - "oder": 27215, - "polymers": 27216, - "timmy": 27217, - "atm": 27218, - "midtown": 27219, - "piloted": 27220, - "settles": 27221, - "continual": 27222, - "externally": 27223, - "mayfield": 27224, - "##uh": 27225, - "enrichment": 27226, - "henson": 27227, - "keane": 27228, - "persians": 27229, - "1733": 27230, - "benji": 27231, - "braden": 27232, - "pep": 27233, - "324": 27234, - "##efe": 27235, - "contenders": 27236, - "pepsi": 27237, - "valet": 27238, - "##isches": 27239, - "298": 27240, - "##asse": 27241, - "##earing": 27242, - "goofy": 27243, - "stroll": 27244, - "##amen": 27245, - "authoritarian": 27246, - "occurrences": 27247, - "adversary": 27248, - "ahmedabad": 27249, - "tangent": 27250, - "toppled": 27251, - "dorchester": 27252, - "1672": 27253, - "modernism": 27254, - "marxism": 27255, - "islamist": 27256, - "charlemagne": 27257, - "exponential": 27258, - "racks": 27259, - "unicode": 27260, - "brunette": 27261, - "mbc": 27262, - "pic": 27263, - "skirmish": 27264, - "##bund": 27265, - "##lad": 27266, - "##powered": 27267, - "##yst": 27268, - "hoisted": 27269, - "messina": 27270, - "shatter": 27271, - "##ctum": 27272, - "jedi": 27273, - "vantage": 27274, - "##music": 27275, - "##neil": 27276, - "clemens": 27277, - "mahmoud": 27278, - "corrupted": 27279, - "authentication": 27280, - "lowry": 27281, - "nils": 27282, - "##washed": 27283, - "omnibus": 27284, - "wounding": 27285, - "jillian": 27286, - "##itors": 27287, - "##opped": 27288, - "serialized": 27289, - "narcotics": 27290, - "handheld": 27291, - "##arm": 27292, - "##plicity": 27293, - "intersecting": 27294, - "stimulating": 27295, - "##onis": 27296, - "crate": 27297, - "fellowships": 27298, - "hemingway": 27299, - "casinos": 27300, - "climatic": 27301, - "fordham": 27302, - "copeland": 27303, - "drip": 27304, - "beatty": 27305, - "leaflets": 27306, - "robber": 27307, - "brothel": 27308, - "madeira": 27309, - "##hedral": 27310, - "sphinx": 27311, - "ultrasound": 27312, - "##vana": 27313, - "valor": 27314, - "forbade": 27315, - "leonid": 27316, - "villas": 27317, - "##aldo": 27318, - "duane": 27319, - "marquez": 27320, - "##cytes": 27321, - "disadvantaged": 27322, - "forearms": 27323, - "kawasaki": 27324, - "reacts": 27325, - "consular": 27326, - "lax": 27327, - "uncles": 27328, - "uphold": 27329, - "##hopper": 27330, - "concepcion": 27331, - "dorsey": 27332, - "lass": 27333, - "##izan": 27334, - "arching": 27335, - "passageway": 27336, - "1708": 27337, - "researches": 27338, - "tia": 27339, - "internationals": 27340, - "##graphs": 27341, - "##opers": 27342, - "distinguishes": 27343, - "javanese": 27344, - "divert": 27345, - "##uven": 27346, - "plotted": 27347, - "##listic": 27348, - "##rwin": 27349, - "##erik": 27350, - "##tify": 27351, - "affirmative": 27352, - "signifies": 27353, - "validation": 27354, - "##bson": 27355, - "kari": 27356, - "felicity": 27357, - "georgina": 27358, - "zulu": 27359, - "##eros": 27360, - "##rained": 27361, - "##rath": 27362, - "overcoming": 27363, - "##dot": 27364, - "argyll": 27365, - "##rbin": 27366, - "1734": 27367, - "chiba": 27368, - "ratification": 27369, - "windy": 27370, - "earls": 27371, - "parapet": 27372, - "##marks": 27373, - "hunan": 27374, - "pristine": 27375, - "astrid": 27376, - "punta": 27377, - "##gart": 27378, - "brodie": 27379, - "##kota": 27380, - "##oder": 27381, - "malaga": 27382, - "minerva": 27383, - "rouse": 27384, - "##phonic": 27385, - "bellowed": 27386, - "pagoda": 27387, - "portals": 27388, - "reclamation": 27389, - "##gur": 27390, - "##odies": 27391, - "##⁄₄": 27392, - "parentheses": 27393, - "quoting": 27394, - "allergic": 27395, - "palette": 27396, - "showcases": 27397, - "benefactor": 27398, - "heartland": 27399, - "nonlinear": 27400, - "##tness": 27401, - "bladed": 27402, - "cheerfully": 27403, - "scans": 27404, - "##ety": 27405, - "##hone": 27406, - "1666": 27407, - "girlfriends": 27408, - "pedersen": 27409, - "hiram": 27410, - "sous": 27411, - "##liche": 27412, - "##nator": 27413, - "1683": 27414, - "##nery": 27415, - "##orio": 27416, - "##umen": 27417, - "bobo": 27418, - "primaries": 27419, - "smiley": 27420, - "##cb": 27421, - "unearthed": 27422, - "uniformly": 27423, - "fis": 27424, - "metadata": 27425, - "1635": 27426, - "ind": 27427, - "##oted": 27428, - "recoil": 27429, - "##titles": 27430, - "##tura": 27431, - "##ια": 27432, - "406": 27433, - "hilbert": 27434, - "jamestown": 27435, - "mcmillan": 27436, - "tulane": 27437, - "seychelles": 27438, - "##frid": 27439, - "antics": 27440, - "coli": 27441, - "fated": 27442, - "stucco": 27443, - "##grants": 27444, - "1654": 27445, - "bulky": 27446, - "accolades": 27447, - "arrays": 27448, - "caledonian": 27449, - "carnage": 27450, - "optimism": 27451, - "puebla": 27452, - "##tative": 27453, - "##cave": 27454, - "enforcing": 27455, - "rotherham": 27456, - "seo": 27457, - "dunlop": 27458, - "aeronautics": 27459, - "chimed": 27460, - "incline": 27461, - "zoning": 27462, - "archduke": 27463, - "hellenistic": 27464, - "##oses": 27465, - "##sions": 27466, - "candi": 27467, - "thong": 27468, - "##ople": 27469, - "magnate": 27470, - "rustic": 27471, - "##rsk": 27472, - "projective": 27473, - "slant": 27474, - "##offs": 27475, - "danes": 27476, - "hollis": 27477, - "vocalists": 27478, - "##ammed": 27479, - "congenital": 27480, - "contend": 27481, - "gesellschaft": 27482, - "##ocating": 27483, - "##pressive": 27484, - "douglass": 27485, - "quieter": 27486, - "##cm": 27487, - "##kshi": 27488, - "howled": 27489, - "salim": 27490, - "spontaneously": 27491, - "townsville": 27492, - "buena": 27493, - "southport": 27494, - "##bold": 27495, - "kato": 27496, - "1638": 27497, - "faerie": 27498, - "stiffly": 27499, - "##vus": 27500, - "##rled": 27501, - "297": 27502, - "flawless": 27503, - "realising": 27504, - "taboo": 27505, - "##7th": 27506, - "bytes": 27507, - "straightening": 27508, - "356": 27509, - "jena": 27510, - "##hid": 27511, - "##rmin": 27512, - "cartwright": 27513, - "berber": 27514, - "bertram": 27515, - "soloists": 27516, - "411": 27517, - "noses": 27518, - "417": 27519, - "coping": 27520, - "fission": 27521, - "hardin": 27522, - "inca": 27523, - "##cen": 27524, - "1717": 27525, - "mobilized": 27526, - "vhf": 27527, - "##raf": 27528, - "biscuits": 27529, - "curate": 27530, - "##85": 27531, - "##anial": 27532, - "331": 27533, - "gaunt": 27534, - "neighbourhoods": 27535, - "1540": 27536, - "##abas": 27537, - "blanca": 27538, - "bypassed": 27539, - "sockets": 27540, - "behold": 27541, - "coincidentally": 27542, - "##bane": 27543, - "nara": 27544, - "shave": 27545, - "splinter": 27546, - "terrific": 27547, - "##arion": 27548, - "##erian": 27549, - "commonplace": 27550, - "juris": 27551, - "redwood": 27552, - "waistband": 27553, - "boxed": 27554, - "caitlin": 27555, - "fingerprints": 27556, - "jennie": 27557, - "naturalized": 27558, - "##ired": 27559, - "balfour": 27560, - "craters": 27561, - "jody": 27562, - "bungalow": 27563, - "hugely": 27564, - "quilt": 27565, - "glitter": 27566, - "pigeons": 27567, - "undertaker": 27568, - "bulging": 27569, - "constrained": 27570, - "goo": 27571, - "##sil": 27572, - "##akh": 27573, - "assimilation": 27574, - "reworked": 27575, - "##person": 27576, - "persuasion": 27577, - "##pants": 27578, - "felicia": 27579, - "##cliff": 27580, - "##ulent": 27581, - "1732": 27582, - "explodes": 27583, - "##dun": 27584, - "##inium": 27585, - "##zic": 27586, - "lyman": 27587, - "vulture": 27588, - "hog": 27589, - "overlook": 27590, - "begs": 27591, - "northwards": 27592, - "ow": 27593, - "spoil": 27594, - "##urer": 27595, - "fatima": 27596, - "favorably": 27597, - "accumulate": 27598, - "sargent": 27599, - "sorority": 27600, - "corresponded": 27601, - "dispersal": 27602, - "kochi": 27603, - "toned": 27604, - "##imi": 27605, - "##lita": 27606, - "internacional": 27607, - "newfound": 27608, - "##agger": 27609, - "##lynn": 27610, - "##rigue": 27611, - "booths": 27612, - "peanuts": 27613, - "##eborg": 27614, - "medicare": 27615, - "muriel": 27616, - "nur": 27617, - "##uram": 27618, - "crates": 27619, - "millennia": 27620, - "pajamas": 27621, - "worsened": 27622, - "##breakers": 27623, - "jimi": 27624, - "vanuatu": 27625, - "yawned": 27626, - "##udeau": 27627, - "carousel": 27628, - "##hony": 27629, - "hurdle": 27630, - "##ccus": 27631, - "##mounted": 27632, - "##pod": 27633, - "rv": 27634, - "##eche": 27635, - "airship": 27636, - "ambiguity": 27637, - "compulsion": 27638, - "recapture": 27639, - "##claiming": 27640, - "arthritis": 27641, - "##osomal": 27642, - "1667": 27643, - "asserting": 27644, - "ngc": 27645, - "sniffing": 27646, - "dade": 27647, - "discontent": 27648, - "glendale": 27649, - "ported": 27650, - "##amina": 27651, - "defamation": 27652, - "rammed": 27653, - "##scent": 27654, - "fling": 27655, - "livingstone": 27656, - "##fleet": 27657, - "875": 27658, - "##ppy": 27659, - "apocalyptic": 27660, - "comrade": 27661, - "lcd": 27662, - "##lowe": 27663, - "cessna": 27664, - "eine": 27665, - "persecuted": 27666, - "subsistence": 27667, - "demi": 27668, - "hoop": 27669, - "reliefs": 27670, - "710": 27671, - "coptic": 27672, - "progressing": 27673, - "stemmed": 27674, - "perpetrators": 27675, - "1665": 27676, - "priestess": 27677, - "##nio": 27678, - "dobson": 27679, - "ebony": 27680, - "rooster": 27681, - "itf": 27682, - "tortricidae": 27683, - "##bbon": 27684, - "##jian": 27685, - "cleanup": 27686, - "##jean": 27687, - "##øy": 27688, - "1721": 27689, - "eighties": 27690, - "taxonomic": 27691, - "holiness": 27692, - "##hearted": 27693, - "##spar": 27694, - "antilles": 27695, - "showcasing": 27696, - "stabilized": 27697, - "##nb": 27698, - "gia": 27699, - "mascara": 27700, - "michelangelo": 27701, - "dawned": 27702, - "##uria": 27703, - "##vinsky": 27704, - "extinguished": 27705, - "fitz": 27706, - "grotesque": 27707, - "£100": 27708, - "##fera": 27709, - "##loid": 27710, - "##mous": 27711, - "barges": 27712, - "neue": 27713, - "throbbed": 27714, - "cipher": 27715, - "johnnie": 27716, - "##a1": 27717, - "##mpt": 27718, - "outburst": 27719, - "##swick": 27720, - "spearheaded": 27721, - "administrations": 27722, - "c1": 27723, - "heartbreak": 27724, - "pixels": 27725, - "pleasantly": 27726, - "##enay": 27727, - "lombardy": 27728, - "plush": 27729, - "##nsed": 27730, - "bobbie": 27731, - "##hly": 27732, - "reapers": 27733, - "tremor": 27734, - "xiang": 27735, - "minogue": 27736, - "substantive": 27737, - "hitch": 27738, - "barak": 27739, - "##wyl": 27740, - "kwan": 27741, - "##encia": 27742, - "910": 27743, - "obscene": 27744, - "elegance": 27745, - "indus": 27746, - "surfer": 27747, - "bribery": 27748, - "conserve": 27749, - "##hyllum": 27750, - "##masters": 27751, - "horatio": 27752, - "##fat": 27753, - "apes": 27754, - "rebound": 27755, - "psychotic": 27756, - "##pour": 27757, - "iteration": 27758, - "##mium": 27759, - "##vani": 27760, - "botanic": 27761, - "horribly": 27762, - "antiques": 27763, - "dispose": 27764, - "paxton": 27765, - "##hli": 27766, - "##wg": 27767, - "timeless": 27768, - "1704": 27769, - "disregard": 27770, - "engraver": 27771, - "hounds": 27772, - "##bau": 27773, - "##version": 27774, - "looted": 27775, - "uno": 27776, - "facilitates": 27777, - "groans": 27778, - "masjid": 27779, - "rutland": 27780, - "antibody": 27781, - "disqualification": 27782, - "decatur": 27783, - "footballers": 27784, - "quake": 27785, - "slacks": 27786, - "48th": 27787, - "rein": 27788, - "scribe": 27789, - "stabilize": 27790, - "commits": 27791, - "exemplary": 27792, - "tho": 27793, - "##hort": 27794, - "##chison": 27795, - "pantry": 27796, - "traversed": 27797, - "##hiti": 27798, - "disrepair": 27799, - "identifiable": 27800, - "vibrated": 27801, - "baccalaureate": 27802, - "##nnis": 27803, - "csa": 27804, - "interviewing": 27805, - "##iensis": 27806, - "##raße": 27807, - "greaves": 27808, - "wealthiest": 27809, - "343": 27810, - "classed": 27811, - "jogged": 27812, - "£5": 27813, - "##58": 27814, - "##atal": 27815, - "illuminating": 27816, - "knicks": 27817, - "respecting": 27818, - "##uno": 27819, - "scrubbed": 27820, - "##iji": 27821, - "##dles": 27822, - "kruger": 27823, - "moods": 27824, - "growls": 27825, - "raider": 27826, - "silvia": 27827, - "chefs": 27828, - "kam": 27829, - "vr": 27830, - "cree": 27831, - "percival": 27832, - "##terol": 27833, - "gunter": 27834, - "counterattack": 27835, - "defiant": 27836, - "henan": 27837, - "ze": 27838, - "##rasia": 27839, - "##riety": 27840, - "equivalence": 27841, - "submissions": 27842, - "##fra": 27843, - "##thor": 27844, - "bautista": 27845, - "mechanically": 27846, - "##heater": 27847, - "cornice": 27848, - "herbal": 27849, - "templar": 27850, - "##mering": 27851, - "outputs": 27852, - "ruining": 27853, - "ligand": 27854, - "renumbered": 27855, - "extravagant": 27856, - "mika": 27857, - "blockbuster": 27858, - "eta": 27859, - "insurrection": 27860, - "##ilia": 27861, - "darkening": 27862, - "ferocious": 27863, - "pianos": 27864, - "strife": 27865, - "kinship": 27866, - "##aer": 27867, - "melee": 27868, - "##anor": 27869, - "##iste": 27870, - "##may": 27871, - "##oue": 27872, - "decidedly": 27873, - "weep": 27874, - "##jad": 27875, - "##missive": 27876, - "##ppel": 27877, - "354": 27878, - "puget": 27879, - "unease": 27880, - "##gnant": 27881, - "1629": 27882, - "hammering": 27883, - "kassel": 27884, - "ob": 27885, - "wessex": 27886, - "##lga": 27887, - "bromwich": 27888, - "egan": 27889, - "paranoia": 27890, - "utilization": 27891, - "##atable": 27892, - "##idad": 27893, - "contradictory": 27894, - "provoke": 27895, - "##ols": 27896, - "##ouring": 27897, - "##tangled": 27898, - "knesset": 27899, - "##very": 27900, - "##lette": 27901, - "plumbing": 27902, - "##sden": 27903, - "##¹": 27904, - "greensboro": 27905, - "occult": 27906, - "sniff": 27907, - "338": 27908, - "zev": 27909, - "beaming": 27910, - "gamer": 27911, - "haggard": 27912, - "mahal": 27913, - "##olt": 27914, - "##pins": 27915, - "mendes": 27916, - "utmost": 27917, - "briefing": 27918, - "gunnery": 27919, - "##gut": 27920, - "##pher": 27921, - "##zh": 27922, - "##rok": 27923, - "1679": 27924, - "khalifa": 27925, - "sonya": 27926, - "##boot": 27927, - "principals": 27928, - "urbana": 27929, - "wiring": 27930, - "##liffe": 27931, - "##minating": 27932, - "##rrado": 27933, - "dahl": 27934, - "nyu": 27935, - "skepticism": 27936, - "np": 27937, - "townspeople": 27938, - "ithaca": 27939, - "lobster": 27940, - "somethin": 27941, - "##fur": 27942, - "##arina": 27943, - "##−1": 27944, - "freighter": 27945, - "zimmerman": 27946, - "biceps": 27947, - "contractual": 27948, - "##herton": 27949, - "amend": 27950, - "hurrying": 27951, - "subconscious": 27952, - "##anal": 27953, - "336": 27954, - "meng": 27955, - "clermont": 27956, - "spawning": 27957, - "##eia": 27958, - "##lub": 27959, - "dignitaries": 27960, - "impetus": 27961, - "snacks": 27962, - "spotting": 27963, - "twigs": 27964, - "##bilis": 27965, - "##cz": 27966, - "##ouk": 27967, - "libertadores": 27968, - "nic": 27969, - "skylar": 27970, - "##aina": 27971, - "##firm": 27972, - "gustave": 27973, - "asean": 27974, - "##anum": 27975, - "dieter": 27976, - "legislatures": 27977, - "flirt": 27978, - "bromley": 27979, - "trolls": 27980, - "umar": 27981, - "##bbies": 27982, - "##tyle": 27983, - "blah": 27984, - "parc": 27985, - "bridgeport": 27986, - "crank": 27987, - "negligence": 27988, - "##nction": 27989, - "46th": 27990, - "constantin": 27991, - "molded": 27992, - "bandages": 27993, - "seriousness": 27994, - "00pm": 27995, - "siegel": 27996, - "carpets": 27997, - "compartments": 27998, - "upbeat": 27999, - "statehood": 28000, - "##dner": 28001, - "##edging": 28002, - "marko": 28003, - "730": 28004, - "platt": 28005, - "##hane": 28006, - "paving": 28007, - "##iy": 28008, - "1738": 28009, - "abbess": 28010, - "impatience": 28011, - "limousine": 28012, - "nbl": 28013, - "##talk": 28014, - "441": 28015, - "lucille": 28016, - "mojo": 28017, - "nightfall": 28018, - "robbers": 28019, - "##nais": 28020, - "karel": 28021, - "brisk": 28022, - "calves": 28023, - "replicate": 28024, - "ascribed": 28025, - "telescopes": 28026, - "##olf": 28027, - "intimidated": 28028, - "##reen": 28029, - "ballast": 28030, - "specialization": 28031, - "##sit": 28032, - "aerodynamic": 28033, - "caliphate": 28034, - "rainer": 28035, - "visionary": 28036, - "##arded": 28037, - "epsilon": 28038, - "##aday": 28039, - "##onte": 28040, - "aggregation": 28041, - "auditory": 28042, - "boosted": 28043, - "reunification": 28044, - "kathmandu": 28045, - "loco": 28046, - "robyn": 28047, - "402": 28048, - "acknowledges": 28049, - "appointing": 28050, - "humanoid": 28051, - "newell": 28052, - "redeveloped": 28053, - "restraints": 28054, - "##tained": 28055, - "barbarians": 28056, - "chopper": 28057, - "1609": 28058, - "italiana": 28059, - "##lez": 28060, - "##lho": 28061, - "investigates": 28062, - "wrestlemania": 28063, - "##anies": 28064, - "##bib": 28065, - "690": 28066, - "##falls": 28067, - "creaked": 28068, - "dragoons": 28069, - "gravely": 28070, - "minions": 28071, - "stupidity": 28072, - "volley": 28073, - "##harat": 28074, - "##week": 28075, - "musik": 28076, - "##eries": 28077, - "##uously": 28078, - "fungal": 28079, - "massimo": 28080, - "semantics": 28081, - "malvern": 28082, - "##ahl": 28083, - "##pee": 28084, - "discourage": 28085, - "embryo": 28086, - "imperialism": 28087, - "1910s": 28088, - "profoundly": 28089, - "##ddled": 28090, - "jiangsu": 28091, - "sparkled": 28092, - "stat": 28093, - "##holz": 28094, - "sweatshirt": 28095, - "tobin": 28096, - "##iction": 28097, - "sneered": 28098, - "##cheon": 28099, - "##oit": 28100, - "brit": 28101, - "causal": 28102, - "smyth": 28103, - "##neuve": 28104, - "diffuse": 28105, - "perrin": 28106, - "silvio": 28107, - "##ipes": 28108, - "##recht": 28109, - "detonated": 28110, - "iqbal": 28111, - "selma": 28112, - "##nism": 28113, - "##zumi": 28114, - "roasted": 28115, - "##riders": 28116, - "tay": 28117, - "##ados": 28118, - "##mament": 28119, - "##mut": 28120, - "##rud": 28121, - "840": 28122, - "completes": 28123, - "nipples": 28124, - "cfa": 28125, - "flavour": 28126, - "hirsch": 28127, - "##laus": 28128, - "calderon": 28129, - "sneakers": 28130, - "moravian": 28131, - "##ksha": 28132, - "1622": 28133, - "rq": 28134, - "294": 28135, - "##imeters": 28136, - "bodo": 28137, - "##isance": 28138, - "##pre": 28139, - "##ronia": 28140, - "anatomical": 28141, - "excerpt": 28142, - "##lke": 28143, - "dh": 28144, - "kunst": 28145, - "##tablished": 28146, - "##scoe": 28147, - "biomass": 28148, - "panted": 28149, - "unharmed": 28150, - "gael": 28151, - "housemates": 28152, - "montpellier": 28153, - "##59": 28154, - "coa": 28155, - "rodents": 28156, - "tonic": 28157, - "hickory": 28158, - "singleton": 28159, - "##taro": 28160, - "451": 28161, - "1719": 28162, - "aldo": 28163, - "breaststroke": 28164, - "dempsey": 28165, - "och": 28166, - "rocco": 28167, - "##cuit": 28168, - "merton": 28169, - "dissemination": 28170, - "midsummer": 28171, - "serials": 28172, - "##idi": 28173, - "haji": 28174, - "polynomials": 28175, - "##rdon": 28176, - "gs": 28177, - "enoch": 28178, - "prematurely": 28179, - "shutter": 28180, - "taunton": 28181, - "£3": 28182, - "##grating": 28183, - "##inates": 28184, - "archangel": 28185, - "harassed": 28186, - "##asco": 28187, - "326": 28188, - "archway": 28189, - "dazzling": 28190, - "##ecin": 28191, - "1736": 28192, - "sumo": 28193, - "wat": 28194, - "##kovich": 28195, - "1086": 28196, - "honneur": 28197, - "##ently": 28198, - "##nostic": 28199, - "##ttal": 28200, - "##idon": 28201, - "1605": 28202, - "403": 28203, - "1716": 28204, - "blogger": 28205, - "rents": 28206, - "##gnan": 28207, - "hires": 28208, - "##ikh": 28209, - "##dant": 28210, - "howie": 28211, - "##rons": 28212, - "handler": 28213, - "retracted": 28214, - "shocks": 28215, - "1632": 28216, - "arun": 28217, - "duluth": 28218, - "kepler": 28219, - "trumpeter": 28220, - "##lary": 28221, - "peeking": 28222, - "seasoned": 28223, - "trooper": 28224, - "##mara": 28225, - "laszlo": 28226, - "##iciencies": 28227, - "##rti": 28228, - "heterosexual": 28229, - "##inatory": 28230, - "##ssion": 28231, - "indira": 28232, - "jogging": 28233, - "##inga": 28234, - "##lism": 28235, - "beit": 28236, - "dissatisfaction": 28237, - "malice": 28238, - "##ately": 28239, - "nedra": 28240, - "peeling": 28241, - "##rgeon": 28242, - "47th": 28243, - "stadiums": 28244, - "475": 28245, - "vertigo": 28246, - "##ains": 28247, - "iced": 28248, - "restroom": 28249, - "##plify": 28250, - "##tub": 28251, - "illustrating": 28252, - "pear": 28253, - "##chner": 28254, - "##sibility": 28255, - "inorganic": 28256, - "rappers": 28257, - "receipts": 28258, - "watery": 28259, - "##kura": 28260, - "lucinda": 28261, - "##oulos": 28262, - "reintroduced": 28263, - "##8th": 28264, - "##tched": 28265, - "gracefully": 28266, - "saxons": 28267, - "nutritional": 28268, - "wastewater": 28269, - "rained": 28270, - "favourites": 28271, - "bedrock": 28272, - "fisted": 28273, - "hallways": 28274, - "likeness": 28275, - "upscale": 28276, - "##lateral": 28277, - "1580": 28278, - "blinds": 28279, - "prequel": 28280, - "##pps": 28281, - "##tama": 28282, - "deter": 28283, - "humiliating": 28284, - "restraining": 28285, - "tn": 28286, - "vents": 28287, - "1659": 28288, - "laundering": 28289, - "recess": 28290, - "rosary": 28291, - "tractors": 28292, - "coulter": 28293, - "federer": 28294, - "##ifiers": 28295, - "##plin": 28296, - "persistence": 28297, - "##quitable": 28298, - "geschichte": 28299, - "pendulum": 28300, - "quakers": 28301, - "##beam": 28302, - "bassett": 28303, - "pictorial": 28304, - "buffet": 28305, - "koln": 28306, - "##sitor": 28307, - "drills": 28308, - "reciprocal": 28309, - "shooters": 28310, - "##57": 28311, - "##cton": 28312, - "##tees": 28313, - "converge": 28314, - "pip": 28315, - "dmitri": 28316, - "donnelly": 28317, - "yamamoto": 28318, - "aqua": 28319, - "azores": 28320, - "demographics": 28321, - "hypnotic": 28322, - "spitfire": 28323, - "suspend": 28324, - "wryly": 28325, - "roderick": 28326, - "##rran": 28327, - "sebastien": 28328, - "##asurable": 28329, - "mavericks": 28330, - "##fles": 28331, - "##200": 28332, - "himalayan": 28333, - "prodigy": 28334, - "##iance": 28335, - "transvaal": 28336, - "demonstrators": 28337, - "handcuffs": 28338, - "dodged": 28339, - "mcnamara": 28340, - "sublime": 28341, - "1726": 28342, - "crazed": 28343, - "##efined": 28344, - "##till": 28345, - "ivo": 28346, - "pondered": 28347, - "reconciled": 28348, - "shrill": 28349, - "sava": 28350, - "##duk": 28351, - "bal": 28352, - "cad": 28353, - "heresy": 28354, - "jaipur": 28355, - "goran": 28356, - "##nished": 28357, - "341": 28358, - "lux": 28359, - "shelly": 28360, - "whitehall": 28361, - "##hre": 28362, - "israelis": 28363, - "peacekeeping": 28364, - "##wled": 28365, - "1703": 28366, - "demetrius": 28367, - "ousted": 28368, - "##arians": 28369, - "##zos": 28370, - "beale": 28371, - "anwar": 28372, - "backstroke": 28373, - "raged": 28374, - "shrinking": 28375, - "cremated": 28376, - "##yck": 28377, - "benign": 28378, - "towing": 28379, - "wadi": 28380, - "darmstadt": 28381, - "landfill": 28382, - "parana": 28383, - "soothe": 28384, - "colleen": 28385, - "sidewalks": 28386, - "mayfair": 28387, - "tumble": 28388, - "hepatitis": 28389, - "ferrer": 28390, - "superstructure": 28391, - "##gingly": 28392, - "##urse": 28393, - "##wee": 28394, - "anthropological": 28395, - "translators": 28396, - "##mies": 28397, - "closeness": 28398, - "hooves": 28399, - "##pw": 28400, - "mondays": 28401, - "##roll": 28402, - "##vita": 28403, - "landscaping": 28404, - "##urized": 28405, - "purification": 28406, - "sock": 28407, - "thorns": 28408, - "thwarted": 28409, - "jalan": 28410, - "tiberius": 28411, - "##taka": 28412, - "saline": 28413, - "##rito": 28414, - "confidently": 28415, - "khyber": 28416, - "sculptors": 28417, - "##ij": 28418, - "brahms": 28419, - "hammersmith": 28420, - "inspectors": 28421, - "battista": 28422, - "fivb": 28423, - "fragmentation": 28424, - "hackney": 28425, - "##uls": 28426, - "arresting": 28427, - "exercising": 28428, - "antoinette": 28429, - "bedfordshire": 28430, - "##zily": 28431, - "dyed": 28432, - "##hema": 28433, - "1656": 28434, - "racetrack": 28435, - "variability": 28436, - "##tique": 28437, - "1655": 28438, - "austrians": 28439, - "deteriorating": 28440, - "madman": 28441, - "theorists": 28442, - "aix": 28443, - "lehman": 28444, - "weathered": 28445, - "1731": 28446, - "decreed": 28447, - "eruptions": 28448, - "1729": 28449, - "flaw": 28450, - "quinlan": 28451, - "sorbonne": 28452, - "flutes": 28453, - "nunez": 28454, - "1711": 28455, - "adored": 28456, - "downwards": 28457, - "fable": 28458, - "rasped": 28459, - "1712": 28460, - "moritz": 28461, - "mouthful": 28462, - "renegade": 28463, - "shivers": 28464, - "stunts": 28465, - "dysfunction": 28466, - "restrain": 28467, - "translit": 28468, - "327": 28469, - "pancakes": 28470, - "##avio": 28471, - "##cision": 28472, - "##tray": 28473, - "351": 28474, - "vial": 28475, - "##lden": 28476, - "bain": 28477, - "##maid": 28478, - "##oxide": 28479, - "chihuahua": 28480, - "malacca": 28481, - "vimes": 28482, - "##rba": 28483, - "##rnier": 28484, - "1664": 28485, - "donnie": 28486, - "plaques": 28487, - "##ually": 28488, - "337": 28489, - "bangs": 28490, - "floppy": 28491, - "huntsville": 28492, - "loretta": 28493, - "nikolay": 28494, - "##otte": 28495, - "eater": 28496, - "handgun": 28497, - "ubiquitous": 28498, - "##hett": 28499, - "eras": 28500, - "zodiac": 28501, - "1634": 28502, - "##omorphic": 28503, - "1820s": 28504, - "##zog": 28505, - "cochran": 28506, - "##bula": 28507, - "##lithic": 28508, - "warring": 28509, - "##rada": 28510, - "dalai": 28511, - "excused": 28512, - "blazers": 28513, - "mcconnell": 28514, - "reeling": 28515, - "bot": 28516, - "este": 28517, - "##abi": 28518, - "geese": 28519, - "hoax": 28520, - "taxon": 28521, - "##bla": 28522, - "guitarists": 28523, - "##icon": 28524, - "condemning": 28525, - "hunts": 28526, - "inversion": 28527, - "moffat": 28528, - "taekwondo": 28529, - "##lvis": 28530, - "1624": 28531, - "stammered": 28532, - "##rest": 28533, - "##rzy": 28534, - "sousa": 28535, - "fundraiser": 28536, - "marylebone": 28537, - "navigable": 28538, - "uptown": 28539, - "cabbage": 28540, - "daniela": 28541, - "salman": 28542, - "shitty": 28543, - "whimper": 28544, - "##kian": 28545, - "##utive": 28546, - "programmers": 28547, - "protections": 28548, - "rm": 28549, - "##rmi": 28550, - "##rued": 28551, - "forceful": 28552, - "##enes": 28553, - "fuss": 28554, - "##tao": 28555, - "##wash": 28556, - "brat": 28557, - "oppressive": 28558, - "reykjavik": 28559, - "spartak": 28560, - "ticking": 28561, - "##inkles": 28562, - "##kiewicz": 28563, - "adolph": 28564, - "horst": 28565, - "maui": 28566, - "protege": 28567, - "straighten": 28568, - "cpc": 28569, - "landau": 28570, - "concourse": 28571, - "clements": 28572, - "resultant": 28573, - "##ando": 28574, - "imaginative": 28575, - "joo": 28576, - "reactivated": 28577, - "##rem": 28578, - "##ffled": 28579, - "##uising": 28580, - "consultative": 28581, - "##guide": 28582, - "flop": 28583, - "kaitlyn": 28584, - "mergers": 28585, - "parenting": 28586, - "somber": 28587, - "##vron": 28588, - "supervise": 28589, - "vidhan": 28590, - "##imum": 28591, - "courtship": 28592, - "exemplified": 28593, - "harmonies": 28594, - "medallist": 28595, - "refining": 28596, - "##rrow": 28597, - "##ка": 28598, - "amara": 28599, - "##hum": 28600, - "780": 28601, - "goalscorer": 28602, - "sited": 28603, - "overshadowed": 28604, - "rohan": 28605, - "displeasure": 28606, - "secretive": 28607, - "multiplied": 28608, - "osman": 28609, - "##orth": 28610, - "engravings": 28611, - "padre": 28612, - "##kali": 28613, - "##veda": 28614, - "miniatures": 28615, - "mis": 28616, - "##yala": 28617, - "clap": 28618, - "pali": 28619, - "rook": 28620, - "##cana": 28621, - "1692": 28622, - "57th": 28623, - "antennae": 28624, - "astro": 28625, - "oskar": 28626, - "1628": 28627, - "bulldog": 28628, - "crotch": 28629, - "hackett": 28630, - "yucatan": 28631, - "##sure": 28632, - "amplifiers": 28633, - "brno": 28634, - "ferrara": 28635, - "migrating": 28636, - "##gree": 28637, - "thanking": 28638, - "turing": 28639, - "##eza": 28640, - "mccann": 28641, - "ting": 28642, - "andersson": 28643, - "onslaught": 28644, - "gaines": 28645, - "ganga": 28646, - "incense": 28647, - "standardization": 28648, - "##mation": 28649, - "sentai": 28650, - "scuba": 28651, - "stuffing": 28652, - "turquoise": 28653, - "waivers": 28654, - "alloys": 28655, - "##vitt": 28656, - "regaining": 28657, - "vaults": 28658, - "##clops": 28659, - "##gizing": 28660, - "digger": 28661, - "furry": 28662, - "memorabilia": 28663, - "probing": 28664, - "##iad": 28665, - "payton": 28666, - "rec": 28667, - "deutschland": 28668, - "filippo": 28669, - "opaque": 28670, - "seamen": 28671, - "zenith": 28672, - "afrikaans": 28673, - "##filtration": 28674, - "disciplined": 28675, - "inspirational": 28676, - "##merie": 28677, - "banco": 28678, - "confuse": 28679, - "grafton": 28680, - "tod": 28681, - "##dgets": 28682, - "championed": 28683, - "simi": 28684, - "anomaly": 28685, - "biplane": 28686, - "##ceptive": 28687, - "electrode": 28688, - "##para": 28689, - "1697": 28690, - "cleavage": 28691, - "crossbow": 28692, - "swirl": 28693, - "informant": 28694, - "##lars": 28695, - "##osta": 28696, - "afi": 28697, - "bonfire": 28698, - "spec": 28699, - "##oux": 28700, - "lakeside": 28701, - "slump": 28702, - "##culus": 28703, - "##lais": 28704, - "##qvist": 28705, - "##rrigan": 28706, - "1016": 28707, - "facades": 28708, - "borg": 28709, - "inwardly": 28710, - "cervical": 28711, - "xl": 28712, - "pointedly": 28713, - "050": 28714, - "stabilization": 28715, - "##odon": 28716, - "chests": 28717, - "1699": 28718, - "hacked": 28719, - "ctv": 28720, - "orthogonal": 28721, - "suzy": 28722, - "##lastic": 28723, - "gaulle": 28724, - "jacobite": 28725, - "rearview": 28726, - "##cam": 28727, - "##erted": 28728, - "ashby": 28729, - "##drik": 28730, - "##igate": 28731, - "##mise": 28732, - "##zbek": 28733, - "affectionately": 28734, - "canine": 28735, - "disperse": 28736, - "latham": 28737, - "##istles": 28738, - "##ivar": 28739, - "spielberg": 28740, - "##orin": 28741, - "##idium": 28742, - "ezekiel": 28743, - "cid": 28744, - "##sg": 28745, - "durga": 28746, - "middletown": 28747, - "##cina": 28748, - "customized": 28749, - "frontiers": 28750, - "harden": 28751, - "##etano": 28752, - "##zzy": 28753, - "1604": 28754, - "bolsheviks": 28755, - "##66": 28756, - "coloration": 28757, - "yoko": 28758, - "##bedo": 28759, - "briefs": 28760, - "slabs": 28761, - "debra": 28762, - "liquidation": 28763, - "plumage": 28764, - "##oin": 28765, - "blossoms": 28766, - "dementia": 28767, - "subsidy": 28768, - "1611": 28769, - "proctor": 28770, - "relational": 28771, - "jerseys": 28772, - "parochial": 28773, - "ter": 28774, - "##ici": 28775, - "esa": 28776, - "peshawar": 28777, - "cavalier": 28778, - "loren": 28779, - "cpi": 28780, - "idiots": 28781, - "shamrock": 28782, - "1646": 28783, - "dutton": 28784, - "malabar": 28785, - "mustache": 28786, - "##endez": 28787, - "##ocytes": 28788, - "referencing": 28789, - "terminates": 28790, - "marche": 28791, - "yarmouth": 28792, - "##sop": 28793, - "acton": 28794, - "mated": 28795, - "seton": 28796, - "subtly": 28797, - "baptised": 28798, - "beige": 28799, - "extremes": 28800, - "jolted": 28801, - "kristina": 28802, - "telecast": 28803, - "##actic": 28804, - "safeguard": 28805, - "waldo": 28806, - "##baldi": 28807, - "##bular": 28808, - "endeavors": 28809, - "sloppy": 28810, - "subterranean": 28811, - "##ensburg": 28812, - "##itung": 28813, - "delicately": 28814, - "pigment": 28815, - "tq": 28816, - "##scu": 28817, - "1626": 28818, - "##ound": 28819, - "collisions": 28820, - "coveted": 28821, - "herds": 28822, - "##personal": 28823, - "##meister": 28824, - "##nberger": 28825, - "chopra": 28826, - "##ricting": 28827, - "abnormalities": 28828, - "defective": 28829, - "galician": 28830, - "lucie": 28831, - "##dilly": 28832, - "alligator": 28833, - "likened": 28834, - "##genase": 28835, - "burundi": 28836, - "clears": 28837, - "complexion": 28838, - "derelict": 28839, - "deafening": 28840, - "diablo": 28841, - "fingered": 28842, - "champaign": 28843, - "dogg": 28844, - "enlist": 28845, - "isotope": 28846, - "labeling": 28847, - "mrna": 28848, - "##erre": 28849, - "brilliance": 28850, - "marvelous": 28851, - "##ayo": 28852, - "1652": 28853, - "crawley": 28854, - "ether": 28855, - "footed": 28856, - "dwellers": 28857, - "deserts": 28858, - "hamish": 28859, - "rubs": 28860, - "warlock": 28861, - "skimmed": 28862, - "##lizer": 28863, - "870": 28864, - "buick": 28865, - "embark": 28866, - "heraldic": 28867, - "irregularities": 28868, - "##ajan": 28869, - "kiara": 28870, - "##kulam": 28871, - "##ieg": 28872, - "antigen": 28873, - "kowalski": 28874, - "##lge": 28875, - "oakley": 28876, - "visitation": 28877, - "##mbit": 28878, - "vt": 28879, - "##suit": 28880, - "1570": 28881, - "murderers": 28882, - "##miento": 28883, - "##rites": 28884, - "chimneys": 28885, - "##sling": 28886, - "condemn": 28887, - "custer": 28888, - "exchequer": 28889, - "havre": 28890, - "##ghi": 28891, - "fluctuations": 28892, - "##rations": 28893, - "dfb": 28894, - "hendricks": 28895, - "vaccines": 28896, - "##tarian": 28897, - "nietzsche": 28898, - "biking": 28899, - "juicy": 28900, - "##duced": 28901, - "brooding": 28902, - "scrolling": 28903, - "selangor": 28904, - "##ragan": 28905, - "352": 28906, - "annum": 28907, - "boomed": 28908, - "seminole": 28909, - "sugarcane": 28910, - "##dna": 28911, - "departmental": 28912, - "dismissing": 28913, - "innsbruck": 28914, - "arteries": 28915, - "ashok": 28916, - "batavia": 28917, - "daze": 28918, - "kun": 28919, - "overtook": 28920, - "##rga": 28921, - "##tlan": 28922, - "beheaded": 28923, - "gaddafi": 28924, - "holm": 28925, - "electronically": 28926, - "faulty": 28927, - "galilee": 28928, - "fractures": 28929, - "kobayashi": 28930, - "##lized": 28931, - "gunmen": 28932, - "magma": 28933, - "aramaic": 28934, - "mala": 28935, - "eastenders": 28936, - "inference": 28937, - "messengers": 28938, - "bf": 28939, - "##qu": 28940, - "407": 28941, - "bathrooms": 28942, - "##vere": 28943, - "1658": 28944, - "flashbacks": 28945, - "ideally": 28946, - "misunderstood": 28947, - "##jali": 28948, - "##weather": 28949, - "mendez": 28950, - "##grounds": 28951, - "505": 28952, - "uncanny": 28953, - "##iii": 28954, - "1709": 28955, - "friendships": 28956, - "##nbc": 28957, - "sacrament": 28958, - "accommodated": 28959, - "reiterated": 28960, - "logistical": 28961, - "pebbles": 28962, - "thumped": 28963, - "##escence": 28964, - "administering": 28965, - "decrees": 28966, - "drafts": 28967, - "##flight": 28968, - "##cased": 28969, - "##tula": 28970, - "futuristic": 28971, - "picket": 28972, - "intimidation": 28973, - "winthrop": 28974, - "##fahan": 28975, - "interfered": 28976, - "339": 28977, - "afar": 28978, - "francoise": 28979, - "morally": 28980, - "uta": 28981, - "cochin": 28982, - "croft": 28983, - "dwarfs": 28984, - "##bruck": 28985, - "##dents": 28986, - "##nami": 28987, - "biker": 28988, - "##hner": 28989, - "##meral": 28990, - "nano": 28991, - "##isen": 28992, - "##ometric": 28993, - "##pres": 28994, - "##ан": 28995, - "brightened": 28996, - "meek": 28997, - "parcels": 28998, - "securely": 28999, - "gunners": 29000, - "##jhl": 29001, - "##zko": 29002, - "agile": 29003, - "hysteria": 29004, - "##lten": 29005, - "##rcus": 29006, - "bukit": 29007, - "champs": 29008, - "chevy": 29009, - "cuckoo": 29010, - "leith": 29011, - "sadler": 29012, - "theologians": 29013, - "welded": 29014, - "##section": 29015, - "1663": 29016, - "jj": 29017, - "plurality": 29018, - "xander": 29019, - "##rooms": 29020, - "##formed": 29021, - "shredded": 29022, - "temps": 29023, - "intimately": 29024, - "pau": 29025, - "tormented": 29026, - "##lok": 29027, - "##stellar": 29028, - "1618": 29029, - "charred": 29030, - "ems": 29031, - "essen": 29032, - "##mmel": 29033, - "alarms": 29034, - "spraying": 29035, - "ascot": 29036, - "blooms": 29037, - "twinkle": 29038, - "##abia": 29039, - "##apes": 29040, - "internment": 29041, - "obsidian": 29042, - "##chaft": 29043, - "snoop": 29044, - "##dav": 29045, - "##ooping": 29046, - "malibu": 29047, - "##tension": 29048, - "quiver": 29049, - "##itia": 29050, - "hays": 29051, - "mcintosh": 29052, - "travers": 29053, - "walsall": 29054, - "##ffie": 29055, - "1623": 29056, - "beverley": 29057, - "schwarz": 29058, - "plunging": 29059, - "structurally": 29060, - "m3": 29061, - "rosenthal": 29062, - "vikram": 29063, - "##tsk": 29064, - "770": 29065, - "ghz": 29066, - "##onda": 29067, - "##tiv": 29068, - "chalmers": 29069, - "groningen": 29070, - "pew": 29071, - "reckon": 29072, - "unicef": 29073, - "##rvis": 29074, - "55th": 29075, - "##gni": 29076, - "1651": 29077, - "sulawesi": 29078, - "avila": 29079, - "cai": 29080, - "metaphysical": 29081, - "screwing": 29082, - "turbulence": 29083, - "##mberg": 29084, - "augusto": 29085, - "samba": 29086, - "56th": 29087, - "baffled": 29088, - "momentary": 29089, - "toxin": 29090, - "##urian": 29091, - "##wani": 29092, - "aachen": 29093, - "condoms": 29094, - "dali": 29095, - "steppe": 29096, - "##3d": 29097, - "##app": 29098, - "##oed": 29099, - "##year": 29100, - "adolescence": 29101, - "dauphin": 29102, - "electrically": 29103, - "inaccessible": 29104, - "microscopy": 29105, - "nikita": 29106, - "##ega": 29107, - "atv": 29108, - "##cel": 29109, - "##enter": 29110, - "##oles": 29111, - "##oteric": 29112, - "##ы": 29113, - "accountants": 29114, - "punishments": 29115, - "wrongly": 29116, - "bribes": 29117, - "adventurous": 29118, - "clinch": 29119, - "flinders": 29120, - "southland": 29121, - "##hem": 29122, - "##kata": 29123, - "gough": 29124, - "##ciency": 29125, - "lads": 29126, - "soared": 29127, - "##ה": 29128, - "undergoes": 29129, - "deformation": 29130, - "outlawed": 29131, - "rubbish": 29132, - "##arus": 29133, - "##mussen": 29134, - "##nidae": 29135, - "##rzburg": 29136, - "arcs": 29137, - "##ingdon": 29138, - "##tituted": 29139, - "1695": 29140, - "wheelbase": 29141, - "wheeling": 29142, - "bombardier": 29143, - "campground": 29144, - "zebra": 29145, - "##lices": 29146, - "##oj": 29147, - "##bain": 29148, - "lullaby": 29149, - "##ecure": 29150, - "donetsk": 29151, - "wylie": 29152, - "grenada": 29153, - "##arding": 29154, - "##ης": 29155, - "squinting": 29156, - "eireann": 29157, - "opposes": 29158, - "##andra": 29159, - "maximal": 29160, - "runes": 29161, - "##broken": 29162, - "##cuting": 29163, - "##iface": 29164, - "##ror": 29165, - "##rosis": 29166, - "additive": 29167, - "britney": 29168, - "adultery": 29169, - "triggering": 29170, - "##drome": 29171, - "detrimental": 29172, - "aarhus": 29173, - "containment": 29174, - "jc": 29175, - "swapped": 29176, - "vichy": 29177, - "##ioms": 29178, - "madly": 29179, - "##oric": 29180, - "##rag": 29181, - "brant": 29182, - "##ckey": 29183, - "##trix": 29184, - "1560": 29185, - "1612": 29186, - "broughton": 29187, - "rustling": 29188, - "##stems": 29189, - "##uder": 29190, - "asbestos": 29191, - "mentoring": 29192, - "##nivorous": 29193, - "finley": 29194, - "leaps": 29195, - "##isan": 29196, - "apical": 29197, - "pry": 29198, - "slits": 29199, - "substitutes": 29200, - "##dict": 29201, - "intuitive": 29202, - "fantasia": 29203, - "insistent": 29204, - "unreasonable": 29205, - "##igen": 29206, - "##vna": 29207, - "domed": 29208, - "hannover": 29209, - "margot": 29210, - "ponder": 29211, - "##zziness": 29212, - "impromptu": 29213, - "jian": 29214, - "lc": 29215, - "rampage": 29216, - "stemming": 29217, - "##eft": 29218, - "andrey": 29219, - "gerais": 29220, - "whichever": 29221, - "amnesia": 29222, - "appropriated": 29223, - "anzac": 29224, - "clicks": 29225, - "modifying": 29226, - "ultimatum": 29227, - "cambrian": 29228, - "maids": 29229, - "verve": 29230, - "yellowstone": 29231, - "##mbs": 29232, - "conservatoire": 29233, - "##scribe": 29234, - "adherence": 29235, - "dinners": 29236, - "spectra": 29237, - "imperfect": 29238, - "mysteriously": 29239, - "sidekick": 29240, - "tatar": 29241, - "tuba": 29242, - "##aks": 29243, - "##ifolia": 29244, - "distrust": 29245, - "##athan": 29246, - "##zle": 29247, - "c2": 29248, - "ronin": 29249, - "zac": 29250, - "##pse": 29251, - "celaena": 29252, - "instrumentalist": 29253, - "scents": 29254, - "skopje": 29255, - "##mbling": 29256, - "comical": 29257, - "compensated": 29258, - "vidal": 29259, - "condor": 29260, - "intersect": 29261, - "jingle": 29262, - "wavelengths": 29263, - "##urrent": 29264, - "mcqueen": 29265, - "##izzly": 29266, - "carp": 29267, - "weasel": 29268, - "422": 29269, - "kanye": 29270, - "militias": 29271, - "postdoctoral": 29272, - "eugen": 29273, - "gunslinger": 29274, - "##ɛ": 29275, - "faux": 29276, - "hospice": 29277, - "##for": 29278, - "appalled": 29279, - "derivation": 29280, - "dwarves": 29281, - "##elis": 29282, - "dilapidated": 29283, - "##folk": 29284, - "astoria": 29285, - "philology": 29286, - "##lwyn": 29287, - "##otho": 29288, - "##saka": 29289, - "inducing": 29290, - "philanthropy": 29291, - "##bf": 29292, - "##itative": 29293, - "geek": 29294, - "markedly": 29295, - "sql": 29296, - "##yce": 29297, - "bessie": 29298, - "indices": 29299, - "rn": 29300, - "##flict": 29301, - "495": 29302, - "frowns": 29303, - "resolving": 29304, - "weightlifting": 29305, - "tugs": 29306, - "cleric": 29307, - "contentious": 29308, - "1653": 29309, - "mania": 29310, - "rms": 29311, - "##miya": 29312, - "##reate": 29313, - "##ruck": 29314, - "##tucket": 29315, - "bien": 29316, - "eels": 29317, - "marek": 29318, - "##ayton": 29319, - "##cence": 29320, - "discreet": 29321, - "unofficially": 29322, - "##ife": 29323, - "leaks": 29324, - "##bber": 29325, - "1705": 29326, - "332": 29327, - "dung": 29328, - "compressor": 29329, - "hillsborough": 29330, - "pandit": 29331, - "shillings": 29332, - "distal": 29333, - "##skin": 29334, - "381": 29335, - "##tat": 29336, - "##you": 29337, - "nosed": 29338, - "##nir": 29339, - "mangrove": 29340, - "undeveloped": 29341, - "##idia": 29342, - "textures": 29343, - "##inho": 29344, - "##500": 29345, - "##rise": 29346, - "ae": 29347, - "irritating": 29348, - "nay": 29349, - "amazingly": 29350, - "bancroft": 29351, - "apologetic": 29352, - "compassionate": 29353, - "kata": 29354, - "symphonies": 29355, - "##lovic": 29356, - "airspace": 29357, - "##lch": 29358, - "930": 29359, - "gifford": 29360, - "precautions": 29361, - "fulfillment": 29362, - "sevilla": 29363, - "vulgar": 29364, - "martinique": 29365, - "##urities": 29366, - "looting": 29367, - "piccolo": 29368, - "tidy": 29369, - "##dermott": 29370, - "quadrant": 29371, - "armchair": 29372, - "incomes": 29373, - "mathematicians": 29374, - "stampede": 29375, - "nilsson": 29376, - "##inking": 29377, - "##scan": 29378, - "foo": 29379, - "quarterfinal": 29380, - "##ostal": 29381, - "shang": 29382, - "shouldered": 29383, - "squirrels": 29384, - "##owe": 29385, - "344": 29386, - "vinegar": 29387, - "##bner": 29388, - "##rchy": 29389, - "##systems": 29390, - "delaying": 29391, - "##trics": 29392, - "ars": 29393, - "dwyer": 29394, - "rhapsody": 29395, - "sponsoring": 29396, - "##gration": 29397, - "bipolar": 29398, - "cinder": 29399, - "starters": 29400, - "##olio": 29401, - "##urst": 29402, - "421": 29403, - "signage": 29404, - "##nty": 29405, - "aground": 29406, - "figurative": 29407, - "mons": 29408, - "acquaintances": 29409, - "duets": 29410, - "erroneously": 29411, - "soyuz": 29412, - "elliptic": 29413, - "recreated": 29414, - "##cultural": 29415, - "##quette": 29416, - "##ssed": 29417, - "##tma": 29418, - "##zcz": 29419, - "moderator": 29420, - "scares": 29421, - "##itaire": 29422, - "##stones": 29423, - "##udence": 29424, - "juniper": 29425, - "sighting": 29426, - "##just": 29427, - "##nsen": 29428, - "britten": 29429, - "calabria": 29430, - "ry": 29431, - "bop": 29432, - "cramer": 29433, - "forsyth": 29434, - "stillness": 29435, - "##л": 29436, - "airmen": 29437, - "gathers": 29438, - "unfit": 29439, - "##umber": 29440, - "##upt": 29441, - "taunting": 29442, - "##rip": 29443, - "seeker": 29444, - "streamlined": 29445, - "##bution": 29446, - "holster": 29447, - "schumann": 29448, - "tread": 29449, - "vox": 29450, - "##gano": 29451, - "##onzo": 29452, - "strive": 29453, - "dil": 29454, - "reforming": 29455, - "covent": 29456, - "newbury": 29457, - "predicting": 29458, - "##orro": 29459, - "decorate": 29460, - "tre": 29461, - "##puted": 29462, - "andover": 29463, - "ie": 29464, - "asahi": 29465, - "dept": 29466, - "dunkirk": 29467, - "gills": 29468, - "##tori": 29469, - "buren": 29470, - "huskies": 29471, - "##stis": 29472, - "##stov": 29473, - "abstracts": 29474, - "bets": 29475, - "loosen": 29476, - "##opa": 29477, - "1682": 29478, - "yearning": 29479, - "##glio": 29480, - "##sir": 29481, - "berman": 29482, - "effortlessly": 29483, - "enamel": 29484, - "napoli": 29485, - "persist": 29486, - "##peration": 29487, - "##uez": 29488, - "attache": 29489, - "elisa": 29490, - "b1": 29491, - "invitations": 29492, - "##kic": 29493, - "accelerating": 29494, - "reindeer": 29495, - "boardwalk": 29496, - "clutches": 29497, - "nelly": 29498, - "polka": 29499, - "starbucks": 29500, - "##kei": 29501, - "adamant": 29502, - "huey": 29503, - "lough": 29504, - "unbroken": 29505, - "adventurer": 29506, - "embroidery": 29507, - "inspecting": 29508, - "stanza": 29509, - "##ducted": 29510, - "naia": 29511, - "taluka": 29512, - "##pone": 29513, - "##roids": 29514, - "chases": 29515, - "deprivation": 29516, - "florian": 29517, - "##jing": 29518, - "##ppet": 29519, - "earthly": 29520, - "##lib": 29521, - "##ssee": 29522, - "colossal": 29523, - "foreigner": 29524, - "vet": 29525, - "freaks": 29526, - "patrice": 29527, - "rosewood": 29528, - "triassic": 29529, - "upstate": 29530, - "##pkins": 29531, - "dominates": 29532, - "ata": 29533, - "chants": 29534, - "ks": 29535, - "vo": 29536, - "##400": 29537, - "##bley": 29538, - "##raya": 29539, - "##rmed": 29540, - "555": 29541, - "agra": 29542, - "infiltrate": 29543, - "##ailing": 29544, - "##ilation": 29545, - "##tzer": 29546, - "##uppe": 29547, - "##werk": 29548, - "binoculars": 29549, - "enthusiast": 29550, - "fujian": 29551, - "squeak": 29552, - "##avs": 29553, - "abolitionist": 29554, - "almeida": 29555, - "boredom": 29556, - "hampstead": 29557, - "marsden": 29558, - "rations": 29559, - "##ands": 29560, - "inflated": 29561, - "334": 29562, - "bonuses": 29563, - "rosalie": 29564, - "patna": 29565, - "##rco": 29566, - "329": 29567, - "detachments": 29568, - "penitentiary": 29569, - "54th": 29570, - "flourishing": 29571, - "woolf": 29572, - "##dion": 29573, - "##etched": 29574, - "papyrus": 29575, - "##lster": 29576, - "##nsor": 29577, - "##toy": 29578, - "bobbed": 29579, - "dismounted": 29580, - "endelle": 29581, - "inhuman": 29582, - "motorola": 29583, - "tbs": 29584, - "wince": 29585, - "wreath": 29586, - "##ticus": 29587, - "hideout": 29588, - "inspections": 29589, - "sanjay": 29590, - "disgrace": 29591, - "infused": 29592, - "pudding": 29593, - "stalks": 29594, - "##urbed": 29595, - "arsenic": 29596, - "leases": 29597, - "##hyl": 29598, - "##rrard": 29599, - "collarbone": 29600, - "##waite": 29601, - "##wil": 29602, - "dowry": 29603, - "##bant": 29604, - "##edance": 29605, - "genealogical": 29606, - "nitrate": 29607, - "salamanca": 29608, - "scandals": 29609, - "thyroid": 29610, - "necessitated": 29611, - "##!": 29612, - "##\"": 29613, - "###": 29614, - "##$": 29615, - "##%": 29616, - "##&": 29617, - "##'": 29618, - "##(": 29619, - "##)": 29620, - "##*": 29621, - "##+": 29622, - "##,": 29623, - "##-": 29624, - "##.": 29625, - "##/": 29626, - "##:": 29627, - "##;": 29628, - "##<": 29629, - "##=": 29630, - "##>": 29631, - "##?": 29632, - "##@": 29633, - "##[": 29634, - "##\\": 29635, - "##]": 29636, - "##^": 29637, - "##_": 29638, - "##`": 29639, - "##{": 29640, - "##|": 29641, - "##}": 29642, - "##~": 29643, - "##¡": 29644, - "##¢": 29645, - "##£": 29646, - "##¤": 29647, - "##¥": 29648, - "##¦": 29649, - "##§": 29650, - "##¨": 29651, - "##©": 29652, - "##ª": 29653, - "##«": 29654, - "##¬": 29655, - "##®": 29656, - "##±": 29657, - "##´": 29658, - "##µ": 29659, - "##¶": 29660, - "##·": 29661, - "##º": 29662, - "##»": 29663, - "##¼": 29664, - "##¾": 29665, - "##¿": 29666, - "##æ": 29667, - "##ð": 29668, - "##÷": 29669, - "##þ": 29670, - "##đ": 29671, - "##ħ": 29672, - "##ŋ": 29673, - "##œ": 29674, - "##ƒ": 29675, - "##ɐ": 29676, - "##ɑ": 29677, - "##ɒ": 29678, - "##ɔ": 29679, - "##ɕ": 29680, - "##ə": 29681, - "##ɡ": 29682, - "##ɣ": 29683, - "##ɨ": 29684, - "##ɪ": 29685, - "##ɫ": 29686, - "##ɬ": 29687, - "##ɯ": 29688, - "##ɲ": 29689, - "##ɴ": 29690, - "##ɹ": 29691, - "##ɾ": 29692, - "##ʀ": 29693, - "##ʁ": 29694, - "##ʂ": 29695, - "##ʃ": 29696, - "##ʉ": 29697, - "##ʊ": 29698, - "##ʋ": 29699, - "##ʌ": 29700, - "##ʎ": 29701, - "##ʐ": 29702, - "##ʑ": 29703, - "##ʒ": 29704, - "##ʔ": 29705, - "##ʰ": 29706, - "##ʲ": 29707, - "##ʳ": 29708, - "##ʷ": 29709, - "##ʸ": 29710, - "##ʻ": 29711, - "##ʼ": 29712, - "##ʾ": 29713, - "##ʿ": 29714, - "##ˈ": 29715, - "##ˡ": 29716, - "##ˢ": 29717, - "##ˣ": 29718, - "##ˤ": 29719, - "##β": 29720, - "##γ": 29721, - "##δ": 29722, - "##ε": 29723, - "##ζ": 29724, - "##θ": 29725, - "##κ": 29726, - "##λ": 29727, - "##μ": 29728, - "##ξ": 29729, - "##ο": 29730, - "##π": 29731, - "##ρ": 29732, - "##σ": 29733, - "##τ": 29734, - "##υ": 29735, - "##φ": 29736, - "##χ": 29737, - "##ψ": 29738, - "##ω": 29739, - "##б": 29740, - "##г": 29741, - "##д": 29742, - "##ж": 29743, - "##з": 29744, - "##м": 29745, - "##п": 29746, - "##с": 29747, - "##у": 29748, - "##ф": 29749, - "##х": 29750, - "##ц": 29751, - "##ч": 29752, - "##ш": 29753, - "##щ": 29754, - "##ъ": 29755, - "##э": 29756, - "##ю": 29757, - "##ђ": 29758, - "##є": 29759, - "##і": 29760, - "##ј": 29761, - "##љ": 29762, - "##њ": 29763, - "##ћ": 29764, - "##ӏ": 29765, - "##ա": 29766, - "##բ": 29767, - "##գ": 29768, - "##դ": 29769, - "##ե": 29770, - "##թ": 29771, - "##ի": 29772, - "##լ": 29773, - "##կ": 29774, - "##հ": 29775, - "##մ": 29776, - "##յ": 29777, - "##ն": 29778, - "##ո": 29779, - "##պ": 29780, - "##ս": 29781, - "##վ": 29782, - "##տ": 29783, - "##ր": 29784, - "##ւ": 29785, - "##ք": 29786, - "##־": 29787, - "##א": 29788, - "##ב": 29789, - "##ג": 29790, - "##ד": 29791, - "##ו": 29792, - "##ז": 29793, - "##ח": 29794, - "##ט": 29795, - "##י": 29796, - "##ך": 29797, - "##כ": 29798, - "##ל": 29799, - "##ם": 29800, - "##מ": 29801, - "##ן": 29802, - "##נ": 29803, - "##ס": 29804, - "##ע": 29805, - "##ף": 29806, - "##פ": 29807, - "##ץ": 29808, - "##צ": 29809, - "##ק": 29810, - "##ר": 29811, - "##ש": 29812, - "##ת": 29813, - "##،": 29814, - "##ء": 29815, - "##ب": 29816, - "##ت": 29817, - "##ث": 29818, - "##ج": 29819, - "##ح": 29820, - "##خ": 29821, - "##ذ": 29822, - "##ز": 29823, - "##س": 29824, - "##ش": 29825, - "##ص": 29826, - "##ض": 29827, - "##ط": 29828, - "##ظ": 29829, - "##ع": 29830, - "##غ": 29831, - "##ـ": 29832, - "##ف": 29833, - "##ق": 29834, - "##ك": 29835, - "##و": 29836, - "##ى": 29837, - "##ٹ": 29838, - "##پ": 29839, - "##چ": 29840, - "##ک": 29841, - "##گ": 29842, - "##ں": 29843, - "##ھ": 29844, - "##ہ": 29845, - "##ے": 29846, - "##अ": 29847, - "##आ": 29848, - "##उ": 29849, - "##ए": 29850, - "##क": 29851, - "##ख": 29852, - "##ग": 29853, - "##च": 29854, - "##ज": 29855, - "##ट": 29856, - "##ड": 29857, - "##ण": 29858, - "##त": 29859, - "##थ": 29860, - "##द": 29861, - "##ध": 29862, - "##न": 29863, - "##प": 29864, - "##ब": 29865, - "##भ": 29866, - "##म": 29867, - "##य": 29868, - "##र": 29869, - "##ल": 29870, - "##व": 29871, - "##श": 29872, - "##ष": 29873, - "##स": 29874, - "##ह": 29875, - "##ा": 29876, - "##ि": 29877, - "##ी": 29878, - "##ो": 29879, - "##।": 29880, - "##॥": 29881, - "##ং": 29882, - "##অ": 29883, - "##আ": 29884, - "##ই": 29885, - "##উ": 29886, - "##এ": 29887, - "##ও": 29888, - "##ক": 29889, - "##খ": 29890, - "##গ": 29891, - "##চ": 29892, - "##ছ": 29893, - "##জ": 29894, - "##ট": 29895, - "##ড": 29896, - "##ণ": 29897, - "##ত": 29898, - "##থ": 29899, - "##দ": 29900, - "##ধ": 29901, - "##ন": 29902, - "##প": 29903, - "##ব": 29904, - "##ভ": 29905, - "##ম": 29906, - "##য": 29907, - "##র": 29908, - "##ল": 29909, - "##শ": 29910, - "##ষ": 29911, - "##স": 29912, - "##হ": 29913, - "##া": 29914, - "##ি": 29915, - "##ী": 29916, - "##ে": 29917, - "##க": 29918, - "##ச": 29919, - "##ட": 29920, - "##த": 29921, - "##ந": 29922, - "##ன": 29923, - "##ப": 29924, - "##ம": 29925, - "##ய": 29926, - "##ர": 29927, - "##ல": 29928, - "##ள": 29929, - "##வ": 29930, - "##ா": 29931, - "##ி": 29932, - "##ு": 29933, - "##ே": 29934, - "##ை": 29935, - "##ನ": 29936, - "##ರ": 29937, - "##ಾ": 29938, - "##ක": 29939, - "##ය": 29940, - "##ර": 29941, - "##ල": 29942, - "##ව": 29943, - "##ා": 29944, - "##ก": 29945, - "##ง": 29946, - "##ต": 29947, - "##ท": 29948, - "##น": 29949, - "##พ": 29950, - "##ม": 29951, - "##ย": 29952, - "##ร": 29953, - "##ล": 29954, - "##ว": 29955, - "##ส": 29956, - "##อ": 29957, - "##า": 29958, - "##เ": 29959, - "##་": 29960, - "##།": 29961, - "##ག": 29962, - "##ང": 29963, - "##ད": 29964, - "##ན": 29965, - "##པ": 29966, - "##བ": 29967, - "##མ": 29968, - "##འ": 29969, - "##ར": 29970, - "##ལ": 29971, - "##ས": 29972, - "##မ": 29973, - "##ა": 29974, - "##ბ": 29975, - "##გ": 29976, - "##დ": 29977, - "##ე": 29978, - "##ვ": 29979, - "##თ": 29980, - "##ი": 29981, - "##კ": 29982, - "##ლ": 29983, - "##მ": 29984, - "##ნ": 29985, - "##ო": 29986, - "##რ": 29987, - "##ს": 29988, - "##ტ": 29989, - "##უ": 29990, - "##ᄀ": 29991, - "##ᄂ": 29992, - "##ᄃ": 29993, - "##ᄅ": 29994, - "##ᄆ": 29995, - "##ᄇ": 29996, - "##ᄉ": 29997, - "##ᄊ": 29998, - "##ᄋ": 29999, - "##ᄌ": 30000, - "##ᄎ": 30001, - "##ᄏ": 30002, - "##ᄐ": 30003, - "##ᄑ": 30004, - "##ᄒ": 30005, - "##ᅡ": 30006, - "##ᅢ": 30007, - "##ᅥ": 30008, - "##ᅦ": 30009, - "##ᅧ": 30010, - "##ᅩ": 30011, - "##ᅪ": 30012, - "##ᅭ": 30013, - "##ᅮ": 30014, - "##ᅯ": 30015, - "##ᅲ": 30016, - "##ᅳ": 30017, - "##ᅴ": 30018, - "##ᅵ": 30019, - "##ᆨ": 30020, - "##ᆫ": 30021, - "##ᆯ": 30022, - "##ᆷ": 30023, - "##ᆸ": 30024, - "##ᆼ": 30025, - "##ᴬ": 30026, - "##ᴮ": 30027, - "##ᴰ": 30028, - "##ᴵ": 30029, - "##ᴺ": 30030, - "##ᵀ": 30031, - "##ᵃ": 30032, - "##ᵇ": 30033, - "##ᵈ": 30034, - "##ᵉ": 30035, - "##ᵍ": 30036, - "##ᵏ": 30037, - "##ᵐ": 30038, - "##ᵒ": 30039, - "##ᵖ": 30040, - "##ᵗ": 30041, - "##ᵘ": 30042, - "##ᵣ": 30043, - "##ᵤ": 30044, - "##ᵥ": 30045, - "##ᶜ": 30046, - "##ᶠ": 30047, - "##‐": 30048, - "##‑": 30049, - "##‒": 30050, - "##–": 30051, - "##—": 30052, - "##―": 30053, - "##‖": 30054, - "##‘": 30055, - "##’": 30056, - "##‚": 30057, - "##“": 30058, - "##”": 30059, - "##„": 30060, - "##†": 30061, - "##‡": 30062, - "##•": 30063, - "##…": 30064, - "##‰": 30065, - "##′": 30066, - "##″": 30067, - "##›": 30068, - "##‿": 30069, - "##⁄": 30070, - "##⁰": 30071, - "##ⁱ": 30072, - "##⁴": 30073, - "##⁵": 30074, - "##⁶": 30075, - "##⁷": 30076, - "##⁸": 30077, - "##⁹": 30078, - "##⁻": 30079, - "##ⁿ": 30080, - "##₅": 30081, - "##₆": 30082, - "##₇": 30083, - "##₈": 30084, - "##₉": 30085, - "##₊": 30086, - "##₍": 30087, - "##₎": 30088, - "##ₐ": 30089, - "##ₑ": 30090, - "##ₒ": 30091, - "##ₓ": 30092, - "##ₕ": 30093, - "##ₖ": 30094, - "##ₗ": 30095, - "##ₘ": 30096, - "##ₚ": 30097, - "##ₛ": 30098, - "##ₜ": 30099, - "##₤": 30100, - "##₩": 30101, - "##€": 30102, - "##₱": 30103, - "##₹": 30104, - "##ℓ": 30105, - "##№": 30106, - "##ℝ": 30107, - "##™": 30108, - "##⅓": 30109, - "##⅔": 30110, - "##←": 30111, - "##↑": 30112, - "##→": 30113, - "##↓": 30114, - "##↔": 30115, - "##↦": 30116, - "##⇄": 30117, - "##⇌": 30118, - "##⇒": 30119, - "##∂": 30120, - "##∅": 30121, - "##∆": 30122, - "##∇": 30123, - "##∈": 30124, - "##∗": 30125, - "##∘": 30126, - "##√": 30127, - "##∞": 30128, - "##∧": 30129, - "##∨": 30130, - "##∩": 30131, - "##∪": 30132, - "##≈": 30133, - "##≡": 30134, - "##≤": 30135, - "##≥": 30136, - "##⊂": 30137, - "##⊆": 30138, - "##⊕": 30139, - "##⊗": 30140, - "##⋅": 30141, - "##─": 30142, - "##│": 30143, - "##■": 30144, - "##▪": 30145, - "##●": 30146, - "##★": 30147, - "##☆": 30148, - "##☉": 30149, - "##♠": 30150, - "##♣": 30151, - "##♥": 30152, - "##♦": 30153, - "##♯": 30154, - "##⟨": 30155, - "##⟩": 30156, - "##ⱼ": 30157, - "##⺩": 30158, - "##⺼": 30159, - "##⽥": 30160, - "##、": 30161, - "##。": 30162, - "##〈": 30163, - "##〉": 30164, - "##《": 30165, - "##》": 30166, - "##「": 30167, - "##」": 30168, - "##『": 30169, - "##』": 30170, - "##〜": 30171, - "##あ": 30172, - "##い": 30173, - "##う": 30174, - "##え": 30175, - "##お": 30176, - "##か": 30177, - "##き": 30178, - "##く": 30179, - "##け": 30180, - "##こ": 30181, - "##さ": 30182, - "##し": 30183, - "##す": 30184, - "##せ": 30185, - "##そ": 30186, - "##た": 30187, - "##ち": 30188, - "##っ": 30189, - "##つ": 30190, - "##て": 30191, - "##と": 30192, - "##な": 30193, - "##に": 30194, - "##ぬ": 30195, - "##ね": 30196, - "##の": 30197, - "##は": 30198, - "##ひ": 30199, - "##ふ": 30200, - "##へ": 30201, - "##ほ": 30202, - "##ま": 30203, - "##み": 30204, - "##む": 30205, - "##め": 30206, - "##も": 30207, - "##や": 30208, - "##ゆ": 30209, - "##よ": 30210, - "##ら": 30211, - "##り": 30212, - "##る": 30213, - "##れ": 30214, - "##ろ": 30215, - "##を": 30216, - "##ん": 30217, - "##ァ": 30218, - "##ア": 30219, - "##ィ": 30220, - "##イ": 30221, - "##ウ": 30222, - "##ェ": 30223, - "##エ": 30224, - "##オ": 30225, - "##カ": 30226, - "##キ": 30227, - "##ク": 30228, - "##ケ": 30229, - "##コ": 30230, - "##サ": 30231, - "##シ": 30232, - "##ス": 30233, - "##セ": 30234, - "##タ": 30235, - "##チ": 30236, - "##ッ": 30237, - "##ツ": 30238, - "##テ": 30239, - "##ト": 30240, - "##ナ": 30241, - "##ニ": 30242, - "##ノ": 30243, - "##ハ": 30244, - "##ヒ": 30245, - "##フ": 30246, - "##ヘ": 30247, - "##ホ": 30248, - "##マ": 30249, - "##ミ": 30250, - "##ム": 30251, - "##メ": 30252, - "##モ": 30253, - "##ャ": 30254, - "##ュ": 30255, - "##ョ": 30256, - "##ラ": 30257, - "##リ": 30258, - "##ル": 30259, - "##レ": 30260, - "##ロ": 30261, - "##ワ": 30262, - "##ン": 30263, - "##・": 30264, - "##ー": 30265, - "##一": 30266, - "##三": 30267, - "##上": 30268, - "##下": 30269, - "##不": 30270, - "##世": 30271, - "##中": 30272, - "##主": 30273, - "##久": 30274, - "##之": 30275, - "##也": 30276, - "##事": 30277, - "##二": 30278, - "##五": 30279, - "##井": 30280, - "##京": 30281, - "##人": 30282, - "##亻": 30283, - "##仁": 30284, - "##介": 30285, - "##代": 30286, - "##仮": 30287, - "##伊": 30288, - "##会": 30289, - "##佐": 30290, - "##侍": 30291, - "##保": 30292, - "##信": 30293, - "##健": 30294, - "##元": 30295, - "##光": 30296, - "##八": 30297, - "##公": 30298, - "##内": 30299, - "##出": 30300, - "##分": 30301, - "##前": 30302, - "##劉": 30303, - "##力": 30304, - "##加": 30305, - "##勝": 30306, - "##北": 30307, - "##区": 30308, - "##十": 30309, - "##千": 30310, - "##南": 30311, - "##博": 30312, - "##原": 30313, - "##口": 30314, - "##古": 30315, - "##史": 30316, - "##司": 30317, - "##合": 30318, - "##吉": 30319, - "##同": 30320, - "##名": 30321, - "##和": 30322, - "##囗": 30323, - "##四": 30324, - "##国": 30325, - "##國": 30326, - "##土": 30327, - "##地": 30328, - "##坂": 30329, - "##城": 30330, - "##堂": 30331, - "##場": 30332, - "##士": 30333, - "##夏": 30334, - "##外": 30335, - "##大": 30336, - "##天": 30337, - "##太": 30338, - "##夫": 30339, - "##奈": 30340, - "##女": 30341, - "##子": 30342, - "##学": 30343, - "##宀": 30344, - "##宇": 30345, - "##安": 30346, - "##宗": 30347, - "##定": 30348, - "##宣": 30349, - "##宮": 30350, - "##家": 30351, - "##宿": 30352, - "##寺": 30353, - "##將": 30354, - "##小": 30355, - "##尚": 30356, - "##山": 30357, - "##岡": 30358, - "##島": 30359, - "##崎": 30360, - "##川": 30361, - "##州": 30362, - "##巿": 30363, - "##帝": 30364, - "##平": 30365, - "##年": 30366, - "##幸": 30367, - "##广": 30368, - "##弘": 30369, - "##張": 30370, - "##彳": 30371, - "##後": 30372, - "##御": 30373, - "##德": 30374, - "##心": 30375, - "##忄": 30376, - "##志": 30377, - "##忠": 30378, - "##愛": 30379, - "##成": 30380, - "##我": 30381, - "##戦": 30382, - "##戸": 30383, - "##手": 30384, - "##扌": 30385, - "##政": 30386, - "##文": 30387, - "##新": 30388, - "##方": 30389, - "##日": 30390, - "##明": 30391, - "##星": 30392, - "##春": 30393, - "##昭": 30394, - "##智": 30395, - "##曲": 30396, - "##書": 30397, - "##月": 30398, - "##有": 30399, - "##朝": 30400, - "##木": 30401, - "##本": 30402, - "##李": 30403, - "##村": 30404, - "##東": 30405, - "##松": 30406, - "##林": 30407, - "##森": 30408, - "##楊": 30409, - "##樹": 30410, - "##橋": 30411, - "##歌": 30412, - "##止": 30413, - "##正": 30414, - "##武": 30415, - "##比": 30416, - "##氏": 30417, - "##民": 30418, - "##水": 30419, - "##氵": 30420, - "##氷": 30421, - "##永": 30422, - "##江": 30423, - "##沢": 30424, - "##河": 30425, - "##治": 30426, - "##法": 30427, - "##海": 30428, - "##清": 30429, - "##漢": 30430, - "##瀬": 30431, - "##火": 30432, - "##版": 30433, - "##犬": 30434, - "##王": 30435, - "##生": 30436, - "##田": 30437, - "##男": 30438, - "##疒": 30439, - "##発": 30440, - "##白": 30441, - "##的": 30442, - "##皇": 30443, - "##目": 30444, - "##相": 30445, - "##省": 30446, - "##真": 30447, - "##石": 30448, - "##示": 30449, - "##社": 30450, - "##神": 30451, - "##福": 30452, - "##禾": 30453, - "##秀": 30454, - "##秋": 30455, - "##空": 30456, - "##立": 30457, - "##章": 30458, - "##竹": 30459, - "##糹": 30460, - "##美": 30461, - "##義": 30462, - "##耳": 30463, - "##良": 30464, - "##艹": 30465, - "##花": 30466, - "##英": 30467, - "##華": 30468, - "##葉": 30469, - "##藤": 30470, - "##行": 30471, - "##街": 30472, - "##西": 30473, - "##見": 30474, - "##訁": 30475, - "##語": 30476, - "##谷": 30477, - "##貝": 30478, - "##貴": 30479, - "##車": 30480, - "##軍": 30481, - "##辶": 30482, - "##道": 30483, - "##郎": 30484, - "##郡": 30485, - "##部": 30486, - "##都": 30487, - "##里": 30488, - "##野": 30489, - "##金": 30490, - "##鈴": 30491, - "##镇": 30492, - "##長": 30493, - "##門": 30494, - "##間": 30495, - "##阝": 30496, - "##阿": 30497, - "##陳": 30498, - "##陽": 30499, - "##雄": 30500, - "##青": 30501, - "##面": 30502, - "##風": 30503, - "##食": 30504, - "##香": 30505, - "##馬": 30506, - "##高": 30507, - "##龍": 30508, - "##龸": 30509, - "##fi": 30510, - "##fl": 30511, - "##!": 30512, - "##(": 30513, - "##)": 30514, - "##,": 30515, - "##-": 30516, - "##.": 30517, - "##/": 30518, - "##:": 30519, - "##?": 30520, - "##~": 30521, - "bowang": 30522, - "georgiosmastrapas": 30523, - "jackminong": 30524, - "alaeddineabdessalem": 30525, - "isabellemohr": 30526, - "michaelguenther": 30527 - } - } -} \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer_config.json b/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer_config.json deleted file mode 100644 index 91f9e357cc..0000000000 --- a/api/core/model_runtime/model_providers/jina/text_embedding/tokenizer/tokenizer_config.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "clean_up_tokenization_spaces": true, - "cls_token": "[CLS]", - "do_basic_tokenize": true, - "do_lower_case": true, - "mask_token": "[MASK]", - "model_max_length": 2147483648, - "never_split": null, - "pad_token": "[PAD]", - "sep_token": "[SEP]", - "strip_accents": null, - "tokenize_chinese_chars": true, - "tokenizer_class": "BertTokenizer", - "unk_token": "[UNK]" -} diff --git a/api/core/model_runtime/model_providers/leptonai/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/leptonai/_assets/icon_l_en.png deleted file mode 100644 index 719122b284eb9e66a38a10fa8464a2973e1aa0a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 528134 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%0DSRf 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�NG+BSgFt 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%0AwyWT zYkqC~g%mmL_bcch)odc za`%`vjZXlzf%a8X%1FZQbzCGxCU9HUGLc~7 zRIf!M1-xtFm!>SQoS-`wV?W|^(h0Q8$jJAB_U;K(sX-wxzHGVw7X}p)faH?w5y*Ac zu@K7ZINvBS&DIP`d^qglU4-Zy zEjRas-_IiysN|Ucy=Dr(6X2atG9nWHf30MG-i3hC9~t?%2n?XBD9uEZPe(G;u)o=0 zvks}gca$~b0TLxQ(SW->?=Lw<(tPig15{es>k~DC#DD!-v>J(d?q%Jf^x80X&&4e_ z6yn!P^#@3k=wsA`jtPXXkOIW}B|L zC}FKMu06aJ#_H)XX+(Ba3~n}c^89#tvmNMp`JnoETWa=uHql)~A{Wa)>uWU1H;km5S7tQBF|w&4DG$-q zT+rrB(pH06^SrR^o2NjYyh0+J>I*WBnhy8n7Z@c837eAt58)L_+@XpJG zVk;1((uF~hg$Pn9Mpq(9@coe$lMK}*k)QH7QN4ni6=pd2DdI4t8!str;>}@H%Nljm z%vP$YElqgF=Bvb);i~@SP+GwAVEU`VB%L|4lBQ&@SbIuEg)SXwo2*&7Z&|ifBUqHT$`*(M` z=YBX7Kb&K1Wevzc7!&Qd7 z{3o=%m=0=rGz+&Vr#U{u45`E&bsxVn!1)of|BmCA1Szc)cy(PH%mWmc$;+MXlzZ{e zQK942SRi^lVhtbSeau}hEgkv@#5?=rmzEo8^6ZbQ80Xv*?$8<2e3{HDB<3cgy`OEM=4a=NE8P#-|8%3Wy-gsqpTylz@W?hAIo-iV5{G=l zB%T=G+|LU(BEbxaR;9)s3MlMnK6WN@hey@hO(7G}U1NHCIOmNJmu5S?{jrw}6evJQ zGNi)m>?(|c!zQa{20U8Bv%s@Q z8Lv;*LAkl>t^|XLaY&Tn8mOufj91jiym&(Uj`VY&=_s%*p%NG44BTR)(RyT+=AV-B zXO?T&k2Y#^Ugn8H?_^)+2nBfejC0T;2W{ z9#xi7iuZ?oo*QQQ&v}1iMoA1NL3u_=NQu6Waxz~Im7Iea00@PAIW0h;7aA8pj!ywp zoYVg1t&7ggeS1)W02oeM;FuHXPrjZ~<)ZAd0ciZrzwC)o9IJK&u6Cn#z1MKgBxC(o zw8^-Rak8B@PtFO1+?v|NBSV10gy#cSiv7Ow1d3J5Q`kNIu#y?6TfnDPTDTAnq&0eNO4!(6}X zqr!T_l`=?3agw45+s*A9yS24dBlHt=EwH3>KMfPw&9;W`wJ|j_5N0EFXpiKs5$KdGvCC&+C}>4$$VPH|6Zp;BHVJa3yz-=gQ1Pkajn zoL%-Aj~RMF16Jp+9Zy87yrQFoOx2cvPR7QuYpau1mjv>2zKPVzCC6L+&(@v>$tbAP zbOKdVi`&|d$nVaV*~hF*!>_ONha6A^vL7Zh6M-{_7O?=8N1Qk zR6>|JQ&>Eqyi(B8C+asU1Zq`^Hx{wge)XRPFbpUd)N$O^q9)kg%T&Fq;6C_^)A$&01y%-)`_-uyMSC);y{qw-CgJAE?JP@(R0=uDu+G^W zUx~M7ZF43lTo(>M_Yp_(3j<}|z^7E7>J850XNEPR+F4g_8q@|TNvXcEuHUvxEB zro1{ftATkAYMDE$!cd^n+HJKQ|Gq!1d8WEw_96#08a%%DV+93DX%EuAZ#cz3Q66ur zYt)yV`=&Jv@ZFfzHB(B$ELnXEsf3j&0jn=Y9#;NJB#P4S)-X|vJJiX{^u>^++6Ia2 zLePzmn#JqnedD8HxuZMi6d$Hg_o$jkK?Z22=ZlCqG6#Z7zSm$NF5{)AiJoQmZpSeb zDAY@VFmKo`g7I8Pk5iz|lUQDvE}B?wdHS?%zA&?5_89EVHs0TUJbAYJBIA1c_`TY` zSD#({j)|VLG%l;%A)b;ty=`pirq?)tQjBI9{ln`44w8u>ymd;z0)x&q=rQ)0;hk7| z(O2U4MbDs&tgNx?yVdtfZFP-)m)4S8*9Dip5k0t*Nq8^;k{W+7PRRj-WJ{k(nj4aE zP-P^+Y~)3}{Io%#KR&-Q{Xb6z{gu9iHVXWIGtW<1gbuIesD){Ty)H$tD!HZ+2}2IK zeaY+yO46lLW>C3{)(kLsvh44G(scY}U1(3aS&3HFHS2l2%&uTp_@44T{JM!&X2~l9 zq{~LWS`;C*6jMkRc@7{T2hml2&b4*t{El>Hqam$H8mNrxV_>cBeO^}(c$Jy@W#Cpu zdzV<-lfrN;!cvguD;#E2Qc=lig>ModA~DK%$fb-V)*G7d8kit8g0B?9|^ zH|Yv!3ANhjZDxWvlc0g;$_1+->d~g2#ROYUCIZneZym*>YbI3ZS)Eki@#}*Q+syO) zt;p4nY&pGFDu4wGr2!*iA4@_&xfRO2{vcL{6B0lEQ=Hj%oMhDBGcb?yA`mxv<;H<7@Vly}IyazK!M!r!Reu@R8zz+B*G9_-Z>yvKJL?i1 zW5~`dJ;IC^Cawv_0y{`;IU(2C&*p!j6oEUl$r@z=w^=r?D@x|=RcJ)C+K+5vhA3g@ zASZE|xX7Htn%J33?fU21?Ctz_j(?${px~wp)IRKkiep<-pGMsl1rFJ)cc*oRYs>aE zc5RKmZhu^Z2Vx(HCt7+@f!HhY0g!RdHKUX(V$JVYw46CrQImoD0zkjPW`RyQ^tb68S#8QoJxszCt}IVVZwJqj!qELZJyO1Ka8U z4nP0z!QJI3H$;5qrwP#?g9JC%&$IS_*U$GUt(7(CGdo?_@1MIvo12?UUqHaK4et;H z`+$l49X3BF>RykpfYR$9OrOzRf#k4rs(YeD`q$p%Zt0dx6x&3ftSXqFn6PX4H~zm; z$mJ?Dp_osR(O<1-#DkG%ie|M*cw{>_4DS#q=M~2}?Sl}1K;W&NL5Q{}jd1{!NkBjn z(c*O>6J2m9r%w`-3b9|N#JBeOGhcKQ^Of`DU`C*Gp$l;1u6EYzJgy+ratEM?Ut0Ugg>X3$L>TBos%iN>GId)L>*2X7k_4ilf6|;g_ZN85 z@5Lr6$*$oj()x*<)K9e6Ok|WWps5>vaqo5x1bLX8X&t$D%O+y948e8#eOeMt8yU5% zNpOc4=a_+bRvK7}HexTt=K|f*Q0+xS`E&pTkj2*Ei z^PU29UXHvo3XO})sgCQNAz=Bx;WL)GGSl6)(dnHxj_vTodbOf^vmX59$)JEyoWN;a0D_E(8K3i|$&JkVv6% zj63LoM?RNT=_Cx|IxCAcx`_8$aD*KKXIdh(}H7hQxZP`7zKxyay%c$i#mZOv?+pV(B)=iiJQY!DwOC z>gC{!3H?QB*6qV=?BO%=Laq&z4NO~TxB9q-U%qixtal%WJ6oKJky;puhd1rUEb5}X zqPJZ1;g7k>U#AvviM$Kj`+G8;RY#0lXR(s%aaXfoUsz1xc$5!b#o&g(v_A=19xgJYBO~P>w)7z!hIDpYc1Q~ z@j$1M3{hpq3IkVn({#-xWDk4d8mzxBWmJ^L9+8U2?X);M_36>x`u9)h*McJ}ND(J6 z14TUiPI$EP{9K8$e98>`R*D0f2v(SiJ75A9HzFP)X;eW<;|$F4mntay2#FMqF|3zB zzX3dv(z&0@LQvIEYy)*DjUReI(G4So%?2UQoLb&9KAvCEgA;;Cb~B-e?Iq*GfTunU z^*CiQUj$@1@*%&T(*H-+J4QzucHN(`)ltW`ZQJRvla8HqY^!1?72E7|jE-&Fwrx#4 zGxNOff6aWWRp08qYn|&l`~3FVPN&8*CUh@;F}w`Far%jnVq^XKp{hLfe?FZ4NoCG8 z)5e1+0`i;*=S@oj;ZnTBBS^~sLqSD2Z4OKQZ&Ch#x`<7UV8Im2k#(w&VbE5TeY=jF zC0E}b-Y)d)=Q6uf&>9dUZNdt(wFb3KSI;SP^jVgNUCo-#uSl=qG9%>$01OOv`OOtE zbyysD3xSBb!49(mN})ax9MuvU~Ht*ZpFm%0#tG#9=MES|VFOgjW$vI9Has@w~WSX16`OrD6kNA{n|*h-g^@ zRvuwB7fhMRAWtoZjUl%Jm59uHRZ@^hf1HcuawUf_qIlqE4n)Ktt)*#KFeK-67&R{d zooNof7EE-enGU#MQs{qi3#sE+qw&2;+DzlY4%M}&#GQ~AuqYE`>8#h9SZqj;9&sUx z3<(%__UKvDE9W@06G=T=$i!6@r9+FRQ8hvypa;vjmEu;Eati)!(~Ln#Dgg3~D9Oa7 zn#zfOGi9(b%P*E~k_Ksb=}^B-LlnN&%Ef8)2e43xH*r#7%VpXu(=H}62%1_oP2Jtc zTkvXX3kPKL1TYCSv|8hHDjPsb{}<2PK6b+TjD ze3lV@SA}Ht@bI6_hhOEQiZWmlr}T((@n*eje#lpG;NnF{w)rnbNFrmtw;|37blF?~ zuI>pSfjM7nIkd+;n6I@WROJ#eM(GWV#uqrNgM!CH6E1w|x|Jy{EkBcKl290ze^&d4rflA!GAJe?G@F@}t$p8bC9HMim8^t&c`Bcd8`FdT><1czncYSgC= zB$V4`ld_Cwlc|SVa=ue@1X`08p^i1DVc`OkJ#XUO5cB4W%ngbnL%yXebo*D&F>F}C zDNJDE&vyodl7Nx&K{c1b+)TvYxuJHI?cxALz%S}opG%N46gkcTRDh);bn1uGt<&$8qwOS(Zr1oK(lbnZP zCp$jLxv?ssyG|E~8~umU=N8?>$P&vh#WKDGdIi2POz8eO6bh+jmXIHHh_8dvB?&1& zMS6_Z1&V|+D8A>(RNKek;_9+h8h#qR(gV@8Liw*6TJ=_vaLIv86iR9$wds4g% z%d!?(D6dym%E~&H{d(*Kl`2y|sw3rTsmhzO2~on_>b2#oL{;6gD=GdXq>RFny1TnK zc|Zb+%t1pc&9)7@5NpCbda8=ySP4Qx^%hT^yhD36J-T?-I%+mu#C`3G0Pzw-)dB?a zz>CU1r)Yp8A~VpP+6JrL7iN|@2@qSo{R=4(Qxr`rbGT@3d{cte2Se|jQG|F#Tlsp-}qor24-I(|=)Bf?Yt73q&XdXdhGJh8wpq0m`2e+g#Ty|9Pj+f{F~Q z4UV}c+tPxw+d$b<0_~vOZvrW({1ni!!qoU8YNRZYtpk-oTZt&!k|^*T910B1V5;+f zT@(ZhkOaglTXR?z&9#4-+-|$DtxS~6>FHzQAIGhs;2phVzaBN&o${0RgJ5^Qu{u7* z#7H9zH#vHr$w3@zG@5t|Z8dDqC}dI96xmUAa}JG7WtZc=OLkn|NiDJ_dTh*)U5Jtz zBVguV%}N|iv>zhJMdP?Vo%Jnfqcqb&7!A8;V!& z!<^2yVF#IDCnob?PckS013hW7ZS-YdA+50yG(?C*vp}Z~ql1e9fGJC1 zbi#&jB|vDofpNyN?=6@8)1go(9*}77__D(%6XB|$-5;5yql5E$8AtkqMQkxaCSyRsI zI938|J3``=x?QwDBvkpJ*$*#5=P6o|YwnB+Q<(tDkIo!<9wi-9Bcr5?goJTnLN9Ec zdpwKCoMoUluQACm%&&z9q^4o))*xZyI(x*AO{??Zj3^+?$jXt3^3G@5TpS8bt_*BF zdh9zZ4A;dWZr3*-K4c&OQ7GW6k@3!_BW=pQ@q-ffnYN+Ni{9)bv?Qvt=ie)p`EAm_ z?&QH0V);`)xhgNaQ?P@+eKvH%gN3g;AflS5a6v z^&1RPzS*l8`t>1Taj(q-f#zT`y_PvPpAdGw-rRvScIyv!G|Nx=UmZ+}0;ZdV90@Xy zi6lQrdJwKF$7UNu@pHbvlrW4oHto%E9y2q%gEh5p1Wc@Hx;F9>?1x%8fN82#?*yc< zjsl?8DP&S#W}uO?tZzB*#G&36rAw-7UmzXi>cz1QPZ6(~^C+LaAe#J8K9ShudU%ZQ zf+^u5e|6*h(uDhGjfdi;1~BOBz9j!pkn?`$txz1biQMfzmwi2jf9yo2(vI!tyl-rs zH$%M({Q(zwdw+oTI@(R}7;k-I=$r+mk4vNG>$fOWR`4BXO6A~ACcWExSc!egv?&-> zk1P*rPe1x@o4)x_NGo6-7KSL#0#o=wMYd<6kbIjD_1bNheQSBH)~}%Bt6wkCap=!j z9lK=}snBC8360{auv7=F#1JjRcgYzzhz@oI$w=}s+DYt8vRI?R4AHM|= z*v&wDFjmB^#`cXy#ri*)UhX^uZV?a~#mL8D=7s|rk1mw~;$?TO-}Jr@vQnF*eDlQy zh->r}pyqY$sB4SOVlH8z|4`O~DhPf)M6Y^403X^7_@=j>!70x+N+(mUVp0S)BksL?}x8$U2B&f z8Vi{ECRktELoW@S%8wj*U>HZ+QgAv6S}=tc48-eQ4WJkI$*C{ze7x+8th zK4}6|2cEP|9nbD0Yv^6<#Ao-{G8V7)@{*ch0tzUO6;$^_7{&nOR2b>z<>02Y(`Bh!_h0v zYG;#&_6S&#q82j}R0>D$Dv#%$k2z>5N5H zXAM>v(HZmV=LV)DL3ZF1*z5BuQt&H~B}I@Q`A#y8i{_&KOVzEyXi*`0tSN@Jfw@dF zqwc?a*iC}bCd zh@2iiVCA=IB#6XxQmxF@)7$L5Iv0t#l%$Rqv{>S}AUgDX!1k%SQq|=`boY;rxhfAI zqX-!Zk4(lGtTP5eFt7eWp3sb3K^E@WAfSz;8}>pKYmNdZao*7RDRIP9 zFjLV!CF)?};9$Tu#k`r({;GR05V`si)JAVCRqJoS&*b}w$FsB5l1#5ai-*|wo8$#L zUu1n^^mq`OEEHP!fVsSiO4egiPC1*hDt4`=0(?u>;7B+NX=7vadJ-wR<1?5KHxR)INZLpypM*GaBD#?kyMcX z`CoO#LKe6Iu6vp%G~p!HutT2iM@qUY*7X52MA0lJE(?0>fK4RCgF#EqWXV$0!29d& z?k?UgYP1m=Nh|nw!;ZBJuI6TL{Og;u_TjcV8oqGkc;3Fc^;j%B5(^i@vMnXn(Acv& z=QoFzyj>hsnS8&#UV6Aw@GTHx_YRl-E0JTvQ4EGeA!MIN+!=#dOBiN3w z9||R5KJQN!fa@w4_CqH-zkykT?O%413>~4dMMDW(`y~Pnb%E7yqyZ4ZAQmtmEkOm6 zNrZ5~iDw2Wp&dn3T{kLgh6#=lOo6=?H01}EN&1{2)DqN^jER^R=mTWQO@^qoa0Gzx z_+s4jhO?Br!`H)A?4y5iQ(xKe{lbg-&eW5~ot>T7VW$pYf(C(MI)BdkaNs_r_;t1w z(JWLZb+&IJsd%6<-k0qKB9j7t?y*V6_{~P3Hm(SjoSpmnT5nlx2b955AV6_K3wIuR z>}Z4p@c3W{hukijM@z@ghoM&qh?cYs{ok$4f6j=>^pzX#|DOQAMR6#U+~JOt1cVZ0 zFwy8IJfzEffD zeykwP=Vfnm(g41qMSK3)R;RjvK~6_M4TX#dFn3iYJ^k33(dlBNDW!x-Bs zSV3ou>B1Yq8r4kGK9WD&=Uvp-GjWmsecWe)FP|JwlBq z)l@t2Clc1A1dm`R<&@)&8fCP|T4LEUo+*@Y1PgpOr@M!V%AHS~$IU+-^p|9Tx)$26 z6%5o%%sIkspZ9OM=f9E_7G2&xM14L^f4%2+JwCOzwIRQRY<=DB!wNqObwF(V(`7hz z;nFx-y4B=zC3k-dYHw+w?7Bhy%x~{#@urWa&-J+?ulx1)*oKQd5yWs6keF%r@(Sqm zv{Y~Nef#$haQ6>Pu=7>=>$o|yy7RT7t)qi7QxV9LHIxQL&A5$WVm zn+Pb4EFJ-AdIux{$ivZJwQ()Wt~(FL3+FJf(9P>Wl1IRd(CW#QD=$c#u-a4jiZr*4 z`{KQajf5bDsxk2i?fjK%N-wc;$~<~mx}JF{vZ98|BkQ|6i0`o)--1`Z(-T-FR=j+9 zE{r>lI-1e|RD-`&IPWl0yUqbCDh}M5sSQy_lsi3Ww>a-+)81FN-e%gZR_UP*dwDOz zA!JDtG0$*}VPFI1XE?+`@p8GLPn-mMpF zZWrKcvpcnVXN=pSD6?vB$yTiykJwb1uqXFP6XurjSnr1Fk?7#`7VwVrjO&@B(y;wZ z{OP|;>~zhp=|D*TZxZVrVcD<6%hk8->&7Ct(V)YpI9@%E`kMet2=OTBWHr_u$CQ`1 zFLlN5CTKzv&!sp?AVX)Cas*c$@?w+eC^E(2CLbUxi?Spk)y(Ak_WdC}HLg_y<>-*g zh^{6dpcxQGvmJt$M>bZE`z*mjtph332wR7w+JycQhP5@k)X98BxEkGCT<&bK6??Xa zLLc|{ghEReMqCA$u;a-w*;jk{Tn4GR7407F9owUA=l;w>Tl^xzO%?hVyM&Dk8GSzB;0 z?dIv`#H~PNg5)dzTfy5Q|C2b@@RsA>jiz&;2aW&w_5K=H;!u^@m){$Fg^k-W^F3@y zizWi#r(Bb#I9C(lR;x?F`tV*(bS@<`C3eP~K1_XNruBoq*c2=X?=PiyN%JNjy_7B~ zUqo~-atM#)DDV!-b)B9qphy*cF6TeoYHsSbj74dL+bNPOFr82aU#?yKV)p3Hz_-p%mFul`&NR=lcE#W*gCOuPe%a zVb#r<;*dS4$3CWcb{Hc`pj9&y)CXI8@s}_P$hb>HZ z641e;NUhW<$$sI6<6A@v9$cpvQM|&}RVDBw5{1LaD2{YfVC}H06tqjtJ~`oDZUhfa zrVMU^`bue6f)>!%jVBblK9RS2aeWw5?VmAUpUz~3&HrPWL#G@yK_r(3SN*#CdR@vgQ9Kma=`MOp$P4k^3>JF}FzUDyC};eXKMrOWjHUP6UHlJWU+=yN@N+@@Dkc z+toMEBrWUaC;5Mb)m6v!Z%AF#(m<0pcS5k#drf0kxq8{#__XJ^N@WKn!8f&x5)Ex^ zZqFP83vIVVsuAROlsXWxU?++l7l{XZuxRA#TD-(D0CQ>EaH475$6KBZAPZyvG_FBa z)(Y{#NW7Rl&wn^MkB_JIou1(y`KbO}Fz(Pc@cm4j!Vgjzo=>1TF)UeNDRG;X7q0)Db zP{3pxWBJw<>_=PM%5z+;-Yf7^hnBURPg06kP7t$9@|38@Ta4-3Rjb)G@5}4W{x8+J z1Zr2%1q5=s*4w6<+{!wP%*}?yL5`GFR7mg{-OfgIdf`dMPa8i#Glt;rGsj4qkPb_UPpgQORG-4 z`=8`GQ+2eu%jH|9aJ@n`P&fwAild`HoHC;7EQGdAA3Z)c>Ef&+0LDEchMoiNX|yv& z{NJkI51eiEpE|2&^f7KhY3a3VE)}St{H>92y}Yjn<}W8dXK#MdLDlKG?+(nlqTY_a zzIVHiZoV3Xr<6xpjf5C2@k}*gqrBKa7_V6i^arrVS+fGdrMMI#yZ%9fn4nthM*!mQ zN0v8^ZP^pWf0I_tKXgO;vREhHhcTn%5cQwIZLsP}FEAQ4s+Zs_2M5ukt(IL2L?_)H zDmmXrmDsB&C!~o5uRhOnKP_5oy&t&-pURJv66+|s-e842HnM=IJe^@&ul)W;SGUYv zJ}ccG74bIyRjzh4xkDLpe}5Ky{>dX8_vs9p((9w}hhF&)Vr`Ms0aZ3uk6&$qQXkNLp*K1*r_r8U;?W+3$< z&i|bsHZ5~F+%?D%-o{Z8_0X~|#ClU~yovGF2l2|@;V(=l5-XBucAA`Nb}DEpp+F(W z8I@hAl;hwO0ITV?tk=GaF7_Cp`r{cBQQC3QXeJs{6mq%rn!5(_I>X3h#8^nV`^ji% z2A`B!zdc-zN-pWjV^dhd@4k>YrY`Ag!T(E`uG8nmb%sL`+$$f9@K<+jREj(Aa|&)h zMFf~{piR@Du+J!+8X2N%!TE31zzv>#$xh~58*z>`H0qfC&Vu_8!^WZuvnnNR) zud;t0z@R|3&Ct>9vyh_Nk$M#J>E(qL?gSU^6eA&c{Q(zCw}S(%s~j5V`Z?P5kb2^O zllxVAML`sOn`X`*@#D&#q`W;Bl32|;E_KN`SHcthl@-)XTySDf0D*}hY6%2I6y7Ro4fL`qW+~?co|+d+KdxpnHtB~8DVHL?6>(Y<_mcF zs}YTeoQD!dWNxBR&z@SQExO z#G~N9$!K>U)ujigwhb-=|7^2`1r}-i=Vfpq z*MOpw-=kNFhg7TK$3S_=YA9;bdIvjV!T)pIq5GdSTP1RFr+ebszemJ;b@gp zCd@}@J5KjPJTQpQW-u69ZL(Sj_H4j~R<~BWP1_}?Me~8@O1`84%))IL_xk5ptVh#6 z5yci(e8?wm0U-D-+z$k?0zuBS!A$@8O(I2Q=U!s|37FR(nq}PPGk9`@-g_MT+xbD_ zau?;9(Z3}h!^gc-dM0+^k6FF$UH#Amos_n2KD^_QHgVoE`Qyy4-N$_XJoea+cKN+L zfV?c&jP?nA)zNa~hAzBk*$Ye}p|Fm!_Q z)`NqlQvo%`Wc>?DR)bxz5_;p}Rw?jy*5xNAbnIWw{yDWF?(;0{u`40Ab}st5?>Mf?3TK-7&^da#(ZF5Cz@X-S5TtX@Bt9 zQ=9Q9iUU;h-bB6v4U}C73biR4yRHUV_8F@ed@{jZEvlfaC)iqCK|UzO6tm1ans{2V z0ax3xqii&~Q1As!H$rS>bcsMw*HWEPn`VlcIsL$RNRN4{sxUBGs|o>6k$TwOi;X(xqwZnekR?|l`=tjBr{gC4xcV*!~($Uvhhr?CPCF?su)PV~`Gp~Ic z68Lk)weA$!mrG+HZPjexg-uPoZtRrosFU@1SYu|)-jv`9^ht^{d{e@A&p^Z73N`4Y zekB(td8?R8qvIEOQ)yUc$`eH5Z(htlKlZ)gke61KUcT-v!AJivnb~9z=uzTpU3j~B zLw{bVZqmi8t5Cvm{FH>Ec9@bkO$WT?V0q>Uc)LB=)-+4MFj-62T#r|(oBXRJpz8Q~ z+_EEB@trJce%XDH?sbcLXCWsdRYYoP6V7yGl;x((Dsy1Rp*xuA-zN-)H4AC|;O__R zj7`_9(tUw7v7{$6+X2P>Ciu0X)hUW}BF!jSHuJ~Q<+#** zX?2{@9pX8qZvs;&+8O6N7EP+*f8!zOqwS3*0h{D3QuTTn6gs9b-K>bh;UnL;L?JQ} zfv3YCK*X*w2{x)9(7yUiIMFwPH|^WxYVx{#C9C>eImIG z4Ap#9@~KC)+F@r!(XKBNxWKqq%Ckl^65smv7e;&6@WM~X0r zB1f*2%si8bSaWkja7y)?a|q8jw1V}p-n;JiQv5e zFBX6#i^RusB@s4G27e*3DjRMVkNfjG9`4M*4$8@jFlZUxeWT`pi9o_FQHLZbDJ7Q4 z700VRk(l3?9Z3TKLewY$8We+Wj8omINq8j5_S;ucBQ9+6v!-G&P2k_}8450a7=FLr zd*Ch_6Fgji-Ifu`OYi-F*&=+=3o9Ep0V1FSF7gs#D9n_q<Qv{wN+)_J6B1K68g zfmeI0F?P|$Gns7^vhF-xJB_~whon1xrkjfv00acVa=ojvTXG0%!Du~@U#($%8)>7Q zkSlDG&0bm=UhsBPD3|9Y=NzEOnRQTzhdANqtpGprV`tVTo~V*FM_J4l{R$k?i!jM~ zaDFz_V5S$TvxL0|2?k*z{aT<(lof-CS09cg0F{zb3mirsy2iB^A5BxMisiz1MS^>a znH#2~?Cum}asMBIMR4%DG1Nf>p5fHS-x0|Z)su!!8HXBYwW+zx#h{h zn%Ab77@Q;-tA(5`(C_#Y+*cx_qN2YjTj7_~ijaIgXlHw~Ywx3NCWqco$Z;#wAdcp0 z|L7jCEvFaqC3etF>W|g*wfgO#XrYrp`itqNt$ikh)+*&e@ry5zZN zn-dullPHA#VCv&jNn$qjJfwIgOo=v6uuof$wLKjEZeDXVt18O5(u&zw zK=z$%6F1n^+)54QoiPL6(p}55LBx|yY9W-!cLhiXQAC8f)zuy(@oY2hVih&YzImAl znIsy+Cb6R+{9aZ@hvMuWgU=tPkYwhk&1|)+!B_30FvI~R3L*~;wk_tO8aWl&d&Htz zXx(sH2}hP$nRG7m*399vizUpF0GHzUXW6+_e~Ws73(2BH7nm9*vSnF>OW=a3F1F9T z#bwrdyG0(bRsnOb#4~Ly-xRZs#hZ=!U!!^Z*%Bw*_^T^U>ug_~r>lv@b)6TSCbq*~ zu`g|v1X1I1~&(q^@c^qzQizzR9FDfVXA39$hIzDkVL@M z!V7+OcrmcMRo66d9Ty3R44rtBni zw|zE|x%69e9*u2VY;Stwtb-&ML3lNV^wm7{40Lpa-@C&0@wkWo7a56>B3>h~M<~y; zgR2?b4dund_WFpz3r{Izrry2&9KUk6`cIq4sPZ+;9XvggJ>%8UqGbfeVR2jeIa)~Q zP!l>Jd-xZis2Ad~7SC7Duoj6ysMoV4H0`Bn&j-bq+jS5Z*+nEtI792^rE4T`#+8_^O5UI3i3Wgiz z*oRRI)~UZqh=N`fXVL=|NJ2}Cl_`K7)teUM+~WaBifzKXpu~PMRyAE8Me<~vfQO&J z6!k~=#}KVaG1B4>FN1M-E?b5_X+;tUjtzzl8YP6A{bZ6D*MH<#ta%r^#_AaY87XVy z*WRkZl?7{~otR~(cn)fnm7$pu%;-noxGM@l#TB{a2b8R&XMfeL?`GWpUOOpAoAIS$ zUyl^$en6RAS7pkQGE*c>6(?;S5q`C2icYi)V)k-eZhn*5=4u%Y@D!A_#Ryd#y=1xfAmqxuy*m+cED)~=AQ z4uBgn*X}Qu4bH38pDgAHv?vc|Iw_Lu*$S%^iZ)_K+_RU6M3nn}vf)>wcE1Mc$4C~a zj}r5L*Q|z}O;j;4f3JVBQAs&Kx9Ns>g-S1E^1DrB{+0b4ixq5C6?6=RFurn?m`v-) zVn+cb%p0wW9leijBh1BMlKfTfzXrlmP{R3eDZX+~J-mZLxzr!s^C0@2a#o^8gr-)( zWwig*)$Z}-XQA~`NPe%e?Up|IHOVN6cTderf{$p@@H?hss&b9J7t?g^qe zL{{_(+(QZ%R7wRDqJ8s3iCa$xPxYXI6=&v7%f#4keLE1w8Ao|0!Z2Mos&MH&9TN`n zXcUr(iCLa0=?OAp(=_UA9LVU83lX#65@Ok=Uqmzq96JH$Lq4;t0rO}&yaC!L!P*?r zxiZ_O8C&MVkMfnulRYwx+9?fIK!R?4BeHkbgd+o`(zz+nEcNvqgyUpVSY?y$$>kIh zRwt;WBAv(Cy*14iZK^YOM%XnC?M`A>6WgK6-&GD%CE`ZYrkMZkngH%!%7L@ujiV1x zEStiZ903{ymY#@vLzQ0?i-TvOcMEu2ZCz1YtwlJ4*-{fd5K0RwXHwy zp?@IFkT?daa$M^W5^E^KM4zc5$DgWRd&#Bo>t#?C(eSW|8RsWJk1yatMGjNAk_d@p zCp_ESIMSSvsJM?Sj0Iqc-W7&!)A)~$bWc&l+RG58ILg7`Ux~i9KM$NKV&Os&?@tc}>LnoDk~O0ALD@fLsWjNfr3cg~_|DH{_y)BHTGRj|G6B*7>KoK_sA|TGF zMY+jEXe&fi^Y+GXEtkskqr#UNjMxTxQ6jn- z%~&4AgsCAwo<`4i$5CF#^ddA0jQ|6^fDKv^70iM&gh|gCE?bmNrPv45ZYMR`LU?Q6UGjl@M<&IswQn?OH~pq+=8hn4+rEtRE8(*=d+&sl~ixUp;oCkZA#g zfFch>Baw+YK3gZLn-^594>4g}|0}&-V(ugT~YXW;gI+Bp$OJ#yiHrU2xZDx z+J%^TvieGWZclxYQQ_?b%?hfZDh=qO*Rt4 z`A)W@2UOV{Aqv1$KXV^Sm}oG*_k~qAyG;=MDGHNuMb`7xrftB(Zd8UOber=Ks#ClLp-?0} zyQdBnk{&n`z;lx3a%Y)6zxJ&rR~RA8UY5Py<>8?rC+WbiR=3ii-NWy0_oe#Ab@p;=W8>nQUzZY|8zrNHq21t|*=ztIyTrY!=mXmud?b<&3m zFtm4dN6U*eUN0uM!aml+r^Jg6{)dukL0C6yoN;M`cew|uF`LIm%6yR z4QuipGKVS-spkvho>~k^lL+lu`Co=}3}ALZ)vBg-563GOOM2&G1H^2cKuDQ+U8mean%wI?KVQ2gL8idaY_5$gb?=@dMR zKr*N${72ERW)(@lC$-Ce9*}~>3$YWwPMG6lc43D$dfN<#+It9iz)oXZa#d`0f0<@ayTyB+T;gEQL9* zk;R=q2A0iyvAWKX>HefR=ph)-MQiGO2{pt8r#M)pkY^t1YjA z{Ts!C35J`t9!IQ%6q_Wd7qPt+OYw?;DMA+%FU11-b5_dCVh-_Rd#OpT8`2N&8BRvu zRuLAZ9ehdszt-{FullE|>L!LYU#*x5ze(ODliVTS7k%xz>KF|Fa0-C9m>3*9D-IMm zbfT7ljeKo36f}W2OK7kN`a305G~5f&pCr&{+5n9V16n}@c=x0GSgy78N#JqQy1MbO z&480l%JrwahWk|GqL9Uv@8W9PAa95FWQ*72O1<~IhkDG`Gc-Ipg-kq&C;;leAKJ-Z zKDNHTUF};25&L|>zAP-b`hl+Zbrsl(a|{iOh)g^JM6uf1nzS9*&eXhWA1p)tg|*~B zv1;-z9T><?vcp0QS-{+-_d{5jn0CmR7)!v)pENG%p@{4+-)wdg zIv`7yv)eG|9)V5jY_ka4BJQ`*m#FS+LWR~-hRDu)|8$tQINYx+BMiC^PX83zALnGK z?OU8$P~LBRpK(!{7pxcgViy^QKye6lEoN@OH4r_@YU{O?0&kG8b%{9EfJQLxq$=`% zajMnwdmR}%>Uz-Wnt+zK`}Sv}S8V@{WBAAYxJOWho8-K%A^iq~#+F~s=eVa7B8>PA zK!{y2tpmup*2^%ZUt)F37FkIDiXS)i$IhG5V;o_5mY1N-u`>Ij{JU=teA^S+j-$3) zr;+^!;|BBPKfFW(6Ugs+Jx*7ed2QpQsZ9lpzLiZ;Vm=#ly|DM>l|Rt9*qztxqvi0dWQyZ{~l=<@@ep+xx~ zl&zq6h>0-&V$TR~%MtseJ6I8nnis7X2f~W1`Hz)^>~(3n-%l5hY^tZu#blGVcE!Lp z6rWWAC*e;VEp9J8+dbFVTtnrj)J(kgJnt08c&WO4>$ak^&e>Z1*}uB8g1^&}Tjs!` ztWt-0o22jd0~Wt+aJXGh5xs-G#@-N-JD0le`H@1D=q57aY&j)Hs%)}D94Lx?BQP<9 zS%2!6eF&rvf@Sk+vdK7ne|>$umG-|TpEP4Isk*#9`w{tsjM{Ip9b^m%%fmBH{9>vOCMb@#!pHWSK$fktE?e{shM+i-L2+>(e-R0xSOgUD>AdqO zzbaIE5y|D}cYJFpn9~)|Y+`+_vUN=w$LqG2aAaoO>ZkimoTwPY6N?A4KC9P)>6$oW zJG}BZ^Duf${#=_eyCZnxI6cJ7to{!#--P=ucBLK@`;>@ z@bk~`*HZf@{DTw`*OBD98a8x~^Nx}WBtAkQ6z;_eRoIPYmi$rg_d0&e|bmooag~zPL7U-V&a=>bE}K?!Hn!&Frq;V z2bMmll9ZsMBh=Qe`*C^oY49EMyLzm>^Aatfof-YthYuq>N%{X-s{W^DL3v86F2!89 zSn^f7dpG{7a-!7GUvkwaKm>(`oSu{3d;cKZa9u0A92%N!e^UqbGr<6*f&^4hAd6ENM$t2`3)nGww-5g@vUpt1OT;hH9cj)H?JsUD8=iOI(XmJ*F4Yvnz)7W%Lxk2-5^t0wnKLGt(N@~h){y+LCqY^EkO?F9 z({0PXzEW*y=ObX>qD4ljm=?0AF5}Wbbjep|;UUuJ93jJ8vMJ`9VB~oQ-X%k8^DPm^ zMGmLJCM&8?&T>HnYEGd^Z-SmH?tyRS$=TX-?Vq(hwYf5^0V+q8l$Lc!SiAxT#FB%5 zRD!xRl2j(E~OSy}Kz+bI6`>hI^`lW(0**>zl{gJ97Q=HsExk&fqoQKwg~8U{pS zS+kJId)ULb2V9ETw;i1mvXkN6o$K_M4><9^t!#?+$@sf3aLw#`jHfMSiz6${bNp0@My3 z?E$sn=SH&c@K`EU%$XktA2-T_BZ>Sp444TYAFsrb=|(bV z#90s%5QV#+7z2yP%Mm|vskH#q2`HL0(+vb0p zpZw>Jeb2tcQw1DMKWVB21#3$h5gkvg?VI5e#;W2JXeGg{pv-ko-Ce2O z4^c*T(UM)8p&I?A1A3CrclUG;ME9>BJ4$O!Y5zGEam;?H*+>tIX)Jk#b zVgr@511QC>e5G$&aZU)JKKPGA*Sb6xe_6Qr#^Z-v!%a*rLE2|q;IXDkOD6zmvb5x> zJaP9N7&-w`Yb(}BX*b0&Bp^^$F=_pRsBDs%AI;SPPZlmL%H6O`<4e3 zABlgSru`px6HW-64C7Igexe$whFWHg9N2aers@)RXAMntliMg+;#_#R#QVGhCm{lp z4JRvGFXNx7er5t(qw;n?enPYLC+(Q)$f111-XFqk{2eO_Z-&+X#34Vgl~i`+BdX(< zKz$+Wb%H49N7#QMixQ9eoq*carH(Zr@o9L?f>({&hde*%1Z)FY2W)5YFMg#Fh1IMN zBLi>Li2@Q!3&1%aw;E0c21jHdE@0QY*+05)?rR5#W2+b$GdsbF*y}2Pq?*2VxTO?j za5t})!i6JavqccH;jxNnH8DkLOJCWZe7~-i7|A43!(p7_(28TqN;a!KxIUfmP1Gwz zzidNe$RN~ShfnXQj3Xn!B^x1_iNdZ+MT<9fzzG%yjT4acKA|tQaDen<<$XRSFhaNV z>46554a>E}$J7A)!A7ogU*mYMl`hHeOE!)bo*2uMLJ*uN1JSmELY*Fw8u9qVrS6OS zQu<*FC3SiJT~;cU*ziE>O5vD;vYUvwFqOW^)O;WmiPc<|SDt0(L6^igOXbjhka7YprI4yUSFZcNoOheAZV|u{O!1UT>1^q1)Y6rcH5^GOzNP%wh?^x z#l=gm$?H&n9RA-!)8pyN6K`LJ3d6^u1?cL~`gO-;zHESTm*NNGP*87WF$u!ICV%3s zF&F>7`S8FW9@^_I_S$PJtz^BB7tW=#JD9oL?Yl+CJ>kJpwRACi6|(H09GD`tSju>* zHMcrc=xYorU1zG&EF!hMtKEJa>%ZfKq>Bbh2QsUZ#e zw&)mT9Qy$z{kWHANPf6&mQhauH>=WN-BO<%xcYSuWwDssvD}Ao$nH4xV&FA|5ISIB zIfOEda8p+l>1D~6D?5d`*4552M3hU8%@M!SQ%rA`(e01eoeX)l2w~NQc4It>m2%)b z#3&iqgX~_r?K$I|P;CU~L=lme3cdbRyHIUUA>4U2TbG+*+Z9l>OP1AztHprFr3lMI zMxri&P}03MQ1}n5Hw2N0kt>dH-i!BG(Z#0nYHw5#WT(7c5#h+CL}k{pu_8(TOsz$+ zupP4}LS85fabV|`(L$4k7?)@G{9_xQsxB!R16J_9A5P=yxfIQ6^7*ATNKuU?0{(B? z-v5D6D3|6trlRikf6Y>%4Fj?G$5`gSjUm(M4R#Nu2&X@Ujs z;(a(QM_qA&Lta6;&aO_0s~FY*b01auq%(SxB4zYB?MNB3)1`wI|S#K*~oXlcGR zTh1k&2bM}B-`QGhLJN~IWd>bW){zp80}_>pOiss8z**B5EenyiZERPn8YE|`rI9$D z^GI(7_@>-Bmp-(`joAR>&`*G;=&s;XbhE&s-wnrKXxtd-3e4TOh>9$mqG?+?#v)>& z*~8S)RjBs<^#_5UQYIFJ3(}|+APlnZ5fW`$ zjIb76V@KJFc8aBYN{}%^BIS#>!tHq)ta{j=zl7eH3rdA9u7ei9zNB)M?flf7a@E&0 zpNoTFSOWd7%PL}Bt37Ah4G|c}R8xb!a`tW(>av@knB=%-rSh(uS`JzU9=p4AbIPx- zzTvL(y=m#};&Ebf`0&7GQj8%Jl-FUP`LY+1cOXmhy8T1MSihG_`Daf$E1zljGbdeg z*7aAczu^Z6`&2VcFm(T>p+p7=q*W*P0wBL$c*^zL8bR1H{>`dn{d}+djh5;SBVmJ| zoD6L}0Yd@tPXCTK5pLPpC$_n_FN6iVCN;_lduhKDkCILLF#h#E(zw^T3I+8a8w8h7 zmnmdeFGBCmtwK3?xIDPxm-|R%YQx!8ie1Qf3%4ePOUJdz5~_O!YUFX9D@FjgL{g&a zX9_7dnA29uOawgnhdMe720LMYr}vcO>_ZkX6lE@QO=)nP;wdr0E+&M>1^i5gOcmj+ z7f@~_|LQy57!J;06J-TJ(Ub@!(MG9q66Xp0qNB#oNX`&$y@X^xIwQ7`(IfRjfe_n7w%-h~ z#Q3b4dN5z@V@BWI#b4otj5O!8%=Z+mkJd! zZ9n1i{tF;8AO4fTF&nW{1?6|h>0hcUEuT*x4UN?Ab&JBvNIV^dG!OGo`I#eaTj?TY z@LMov!@;e?Rdm9kI9(BASF<>zYe~|NkI{$Gl22?aEKi5${uY37I*QVeyhNPgVmxXZ zCyqvxEaet=$wFfwa;)kP9tRgUO<>9bo==9|Ue+!h5_PW}_Ug96#_s(j|LXZ^U*NglF(Bnx@Y6XMvT60DH0= z7mSA7R#C&N@t4aZc!1`^0iou;@O+vXa3DvMcpeD;_L92G8FLEhJjk1Ifp6f6S}~R2 z`ja#O^#A9f{xg{d(i8Z-vFcEr&7DQTso*cj0WDT8`f4|MhFry4j#{&$LHKu^loNBV zLdQ?1m{FzPp(rjW%zd_hdM9=i-q*jHVtio9xPGZVLr6~~&LAQpb+xa_WblkrN5Z}*Lua`@I?YAC>g;e6BQ~-6C-Hq{X}5-=q*uY64l9{fB-;93t8i4v`=@bA z$&K-9a<7;;yzAUZIe^mvqr>cVoMQJ-EbJ@|Xo9$*_N}>ha{Vw}2aWB^y@T+GGxZ9# zdAyhn789kt^(PIQRH;Fzzf*5OICZqjV+yp~dKQ<;nia8rWv@FcDBD+KZG9yX7I(1K zuhM*T3a4tj_nI5yZ_o{gKtmkqGHMoc*vy%29?$1`qtGh?M{x6~?Lo)$mvVYxhZ(K( zFGaBh)Mk8uEJGHTvA{+YjMhbPiSuW;3mMnl2JCwa-trJs)Y!Y|t(*u^1OxWyY77=D3M-+SP$ft6ZKft=P>csv%x` zEnBvVM4=%;nx3SA7})C^ld*K}=a>NIz!U_TKtwWEK=G<)*Ux7pM$r_1n#t-s3I(pm zFfE5wxvUtngz^_`IS2obDCY54f1yWC>KbZVI8t+>OPSgI4`Imh@X1f-j*;Cc&kHny z*v5eUs3Lu@&`B$SbwObXx*a`~dLECN2soN?M2TTbNzub_TJ?w~wls5v*^y(4o>aq|NrVL!;x^PyA@JiTosUMw@lby6h!au97p zgku&b0@QFB&Z@1Q5n9K}I`2)frePVWM)t3r&hgq`H}Tqa@WUwN{bdvn{_=B^-lXnR zlU?I^rzQAD{1%01Rz@u2kE&9b75mr;du5-UXety0xkZ6trhryf7m*a__1{uG`7Y%A z9EwBVXIz7u^45H3i0%@dZG;~8<3+)EKLq-zU9D+R$=B2@cP=ldkfz*TGfifBR<9Hl z#(>ED6DQmA-?Iu^kJA1j`N2E>8;}kMCM5O~96{gnlsf;k6KWXc-(fj2x&jez^NjRj z^PJPGepehdC8RZ*v-L+ph|eR54{VqHH>PxiDmX#Egk)#ryXhN&@%h_V11WgvFaKZO z{J)UYYUDD%njUwH`ez$_HyzV~l?CUIvH5)x(=({uv}%dV2YgbPzB8w`^M)HP)mwg* zCt5s+s`RuX#km9K0V<19EE`Ex{R))^TcL;Dl=03)@AK`v#p9wkTH&W%2^t4PL5ttw zzD#p0GUG+u?Ck9PIeRNU6MjSW-^hN_ttnOJ9(&!bcpG9q5hCdI^2j|GHwAkP9+k8| z5BsO$!(!(dnmha-@8155o@~6yew~tqyJ@tU>32ESpSyvq8UtYQToa6m)=E?#rv;F1S|*hYM#gYn^R#{Lsm7 zReer${^s2ixuFPVdT`9o!d>3jiaeeejsWS)1FIIS{{8vtlNB_ z^h6Le2a#6@k<_!Ao!i~3)qXn+lfk^q%Q!|hlx*(@lcvJRjGuQMmV!a-r-`xZbRLi> zf$hM^4C44sX;Q>!H7uB+`ii~InK=Xw^pn}@DY7p66%&>a!0p_AcJ^A<{;L14d_K;9 zFZtCrW7x!R@9ibQB)R+iUapDpWpwR%bnE2AR?_YC*h9eYTlCiogXLDXF9b z3PwlPNXp_;(ABLJ0>)@6Y||mJFr%>$q=&c=kCl#m*+mW45c3O=uN&Djcgccdj+&Sk8djmT=4)&q(66t> zc!}PyN5Oe=haHas7kTZrjc~x857qM#wjp9Fx&;5%K>I(ZLD)OnOpWd(kNv_A*gCoc zsID#H^?9}rhr6>9W$EniDh*kXYzq;Z$A`Bn4CFP+bqh%?Z2vG{TE{ddI1PAyGAC<{ z5!5FqA^O-HvZa6FW^n(7Kr@G`fCzW8klMBcLAoHFCwlJsA0w$-L^!vL07AY;U<`go z1IrKm9v7wh`n>k6^H$L2_5##P_HJL2`!PgvTcSbo$b50;CJ5E|VD7Ro<)d6p=3odS zhgK`x-(IhE*S6bhqLtl93WpDj2QjtVR(|!@o-!B$#TU--zJ$??TQp`WL=xcdnfPY4 zy7umS25laLIrPl{Sb>{y(aq^x+qgLVo(&Wsa+0SO78IN09vEr?9@GY@O{bLbKlL*x znPr(VPf`1UxXwf3FKJr0LM%~H8#`i8*2|~sweMvQH!-STXM7Jx();EgGLw8KPKery zj#_ESufIfZNu<%DaQc+~uKr+E&1A5)+>@^2UvXN!smpx}(&q?VuRbxJkR_p5oH zj1e~Y8Hk&6D%5~wlzH=2oBUcAiaeD9S05GJ9fp@STAeO9Q`0TozGSSnahN7Mu*o8~ zNcsKUzLrmD{C6~&tM@y-5y?)pF}!Ow^?R_3;v$#FZ}197iCH^o(e6m0t>2vp zV-10oteFTg5Z3hlgAY{MDEI=Q?Ac{QJv9rv{4aD|&_Stxb=QxQLCf(5i+##25<;)e z|MaJ>bRckU*Pe69OJW|rT@}cltqYw|gadKn5^Wj2&t*xM5Pb!7F4BP0KqJv+LzY13 zUwM!;Ll%l4(jeTI9gXN9i;|n%he&<&-ZrxBXOm|1!cXf>2rD@)%?b8q#a;eL=n}^Bd9K>#GB} zeV=C28*K#XXF$caBY=JDpnPl=TXnX8d&L50J)??*bOg;PL_3FW<|DhB9EhnX?`Rg% zd|I^0Exp~jgM?eGbLv;R7+|-+*gRPCWSfn4Y_18o-$Xt*e+-OSMx9xb?ku#bF)d|g z?*g}dsa)cRX3@(!n!cE;`&##P_ z1jGce>v2-CWM)E?PFqvsVnkqPq-_>f?lzBfEL}>nu4pF(une2%4V4Y;>pz3#w^t%3Ac~5y`gt zocpNffbHeHoQ3k=C6G>Kw8#<|4J(8|8b6b|zoT4fvAU(XENPaJ;50_4W>#B7avi>n zeL$xgzv&G*sg*y?$(H+G^yTA<>pSj=K)-TZ2MIfTc=t>9lnOZ3zg)KriwedNmMNf1 z6(A)&ILqF8CX?#HS$bdJ!c48AZs`SPs1#VMm&Mmt`Q1T%BP1Cl+E(>0b_VzstpAGt zKT_)ym~mFkPEs&->0-^}Q&t43E&gr9sGaxXzBvudQSt*emOlCgCZ2!=RR;hckmWZ0 z*dJ|pZ}VGdk{hct+TJC&cRa`R9snbS5vYUh6V>BNHhD@m>8c%Gy3-lIp!IUv8^lQ9NXRD@_>8){PV0Uz!w*kM46#WoZ2JAxQ#q9q7->1& zB+*u0h1PP@7QI_&;DA&gkK2dN;CqsGWPDP#y{WfOe}`@U-bw-Q{CaKl&qT}lP(-Y! z*6i@gtHbNzZW2CO<3t1;SZC7BI`OQGZ3I;ga<$fMaFV3wy8Uhr>Lw8uzCZI_=Nytz znR4=*g#kP6%fRU*9zM!a8?_KM=rO2|xLTr>`sqGsG5;HCytaEfB8W2|yXS5JBhbTH z1gN8{>25OL(g#q)m8b{BZ)>IXHCZ)4%Ypfp25Ow{W_q)(zI{GM@WcfN9`$MP{M#x9 zGL9uq4!_&7aIP`w!V?U_^ap!?C|c9|opflB1$g#|Eg~rEaiLD)K95HSymF&Y?A0|S zeT)C9p-e(&m9@7RZ(7gK(EMG8=dK(@NJn4>GPa~YOg)7F?fIQ!E>pv0rd3mqMTcgQ zpyKsWx8oP!+-ounMr{;3bjPoR7Zh(ZyJiZ25?iFL3b3$+P58X!BjZ6UCA1eDut!HP# zxJ`71Hl)GOA8;u2DDyZ{?J`r_U%->etid_?_n~BG1O15#LMHV&=k`nn(r~d%m;}~ zqh3k^r1yIrUy4s6DVjMOJxp@8ab2>|#N3J~TRuO(sU#tf!P%9EVihGYtXRi#}J9^?8U(o7p^ zqtYpS(ZltbFrQY*y$_F6oVQvu$T${fsZuq60FtL`$EAb1zz+$urAiEeDGhqIXK&<@ z>&ErCK{AghP@_5mAF(g~c}?~> zT=K%}+c;m)PC9fG+u-Qg5d^t2xBARZOtjd%%fRuX%7}|EK_TSM<)Ul$B4B`iIVw(CqdSJ@U%DnyX>9bf-Wt)Vk{RUsGnCXIO~a5m4Q|@N>?ZDT0*H zc{D9MhRBD^`}XX+b3U8g;ZOXn&n)*7o%2rO5^)^pyU;D!q3-bteZA=$06qn73Uqb2 zv75!@+?6^2dOGG8Z2dp7Slg~U+bx^s!Eu1K=)fQDJ^wJH1EJR<#lU=!nCa0}H+f%jx-(lFIZvh6encN%{7r=8DWSG8Ye zYIwh?zd@4-4;HOeV`A1z&>$eKT;}!SBk1&-<*zCOo0&Gg7*yPsNmfScI|!EJBe#v# zA4>ChWA)T3JM}OVU_i`6fkIA;ZqyXbJ= zD&lgqxoIVxUNS80dDhXS!^rVXd{b0|iLE_~PB()+vGUdwHqg(z3A{0EE`g5rw!S?b zTA;IdqY&TEEzXrNsJ=QFSbRX1D4~2h!0qqCFcc$K?^BvM7n>j<&DR4di^8skST8umv8 zbdasvXFoB3%hEQH_q@s4OXt&BshDCOuJY)|F#ETvzI=tz1sSx)l0xL4*jUBbSr-lM z0Op$GyOL;fwgsq>V)H({HU+QVxnIEuVO5yOyLE)^cA*BShuzU@Gc2IVJ)3>OiUA=% z5bxNPL{_`f7cboY$v+&j_5Z0R_jp#+NSYG7Dlo1`S#p;J8UlU~8H9%IP4B`l z!2NhFWX6%(6wCf)lN9`ESImZ6{RUH<7a=CE++E+IYn-$={roI4%lHjvDBXYD%pcOVax5 zZHR8y-s>aK!qKVte!OD^MQL*fJT1se^sVnWV#gONMDsqk%<)E#eJ9x*-tgu}l171L z6b>u|Ksk2Jv>dzB&5~7?#GD8>M`0wBv3}Jr39TLhMR_;FKvL;L2OfPTyZ5w#BQ3W@VWq;Ser)Ve^iysv)Tiilx? zgn{V|D05GdoAOx!$8$`?B0V>oP|!o3?C%V6-%lftEbHFPzUCO`c;icTU<|t z?a*ahX_+P1%uv#_YOSw3C(ZYB^pUGPVhb*&-Mv(>rpdDw9^g=D0*6(wOcNsatuyjS@SWbtpCQ2z^cML%R+FZ`E@O$35Zk*d7W z3aeIL$w~e2#petuXde#ToyWt|hqW>1QxBC`}6r3-)HlfADgx)6FhZ zua_CZUv?CP*Ai0I*l-FlCKWC=gArB#1VIA@tJ22Y|-x|G-BYmV&N zUz_?RUfSZTl9KTVlgi&E@h7HYtocrQ>uIO>~SbXY&CE^L?=Ezun{l@L-$JlyU&2qem}OH|O+Fdkfl#4Ak;MxuFy z+cWP75Vd;E&Gmn1L8jmoEPxwf^DjiufOg~Xz%#8Ek7qf+-~Yx>LC@!-1(xME8GQ^P z{2ITRq4@>GhbG&^)0i0fIfk;y&4jj?pRY#1_l+M`_Q?u3HHqPs&4{~;3+ui5R^|xdsOoQTw9em z-+D|ruz`V9?Nb9&j9KZnV;oJGW@nb}d9=++A^m}w8W#XjdzS}pH2mE^i`EqPdcEac zD4h7keG4%eKvtoenjiU~67=&^zUp4{qy>v}dp@(~Sw=JQwDw$oU1-K#{9DnRsoXdr zy^&^TOuzBsL4Ia8iX80TE+D#SY&lGRoEF0C1J;BT&dW0vf)`X=nM+L3@wIo*h_*m>%D}gr^&@(v!%Q%MsDjp*jC&dot zl+5|V+=wlLaEnw`z%K1mzlo8#UIYKQk=4{+5HKo*0~Y2!siv&<=po?bcx20OE*~gQ zEtK)=oe9oIp+wPK2?M6UsnWFiUL7Xitn*`29c(VNqP8B~$QO2sVh-7b(=uT%H^-L? zSl8}kw*JI|r-Iu#?_%ekOC%y_HuJ=}UM2L+w^H00V^{vKwAF^YG4iFvXc5d-GOjK= zii;OD=RM=)dviE@8}oXv$idFY;W`Q!bVhM4>)dcS{*6Uv_z|CA0 z(Her$i%fKl7?IC`_NY?51`BUb0YOB;iQO$S3H!q_!cEVEm~Y3~H0>D3 zmq*Cv2cYTe23;mbY3tQ>q=zQn$i`@Ve2ZUm*{3@5t0cgrwOIYar3)+u@dj)=mAwKlh-DmQxg9XyV!MuR7jHb+~} zoz=R{>;-Xy6H8UTFOy9zTJt%co(}5OZPkqS2(UD)1yyO(p1?6#!fkO{FDK zS(5TOIjBo8DxWh72veU@KJ3)DzZ^s`kumg6oYRhMwWJF4EthT?=}=b+bWsZK)2_ym zowtmd`!iq#2x{~W!&ddpz!5!L2RQ~zKh|3`JaR`qWuE)VPny>6VypaW*-^Mg z`x2;A6eW`)Yws>G--<=#&K!zco;`CurTBlcY?i;{VHLtH#RfQ zcY$qdj>cYZ?91V(lFpR-g^gD#bnJm?$iEI#_n+ja##vxF^Z$;jN_#dORIiKBUoIrk zD1d~eJ>H2MkdMVadNAmul4`G1`X4R13c>$9y*#_YIJ(R>yJmv4#Z?TC18<#zXksgU4`fG0XS} zr+JBD4vvcgm&nB5rxYI8H4bp^Sy-U)G#0d19FTqoXp&>5kG8*^z3xPLd);=z)`9Cj z)0u<6?R{@c_HP)>0##Ebr>;yke9x)~+K$i;0E7SY2tsZ3t#OMzf z1${;>Gv)tlwimlK1hUznYEt;#!3^RQ6MK=*{M5&ug? zzoOSha-VrN^2*jNySw_gySJ;Su$R|Zoc^smh9MY;adTyqjcSq{MMS3OBp3)gex{Lo z0gk;CCh2{c^c1*L>8uXiDnD_i9e-gWQ8OBeLw+zgVgp6Nf%ji%nb_eh`KuFzifVRr zRpsZyO^EO*%sTQbhIQY4OakhX?|RHk3T8EypO+83toMASnpoM_4I{vQB5*c!6yz$V zu-EEVh#VDuzOXQ^Uiw)$$w;hrHXCT9huMZNGFeO4!JScw9<`d(%>FOJkHpL&n8&O!ZwYVr_DJ0~ zX_u&P&zL__ax3m<4PI*5WL4v6c$aw0oU_;LkzX!z#B zOYCogrgyno&w}|go)Vj!t)Wk=jjhd$u@xAE9ua?JXyUIL!*L|t_?i386sGx$Q#{JJABd=MWrLk&j zR)Tr@vTU9s3HqG>o4fCf{#hu~XKi`;a;BctFAk`!!AQeNYNM`gC63g^?lWi1}dGlJMgj(IYb=$r7@8-Wp^19`Q0Pu3$k_$PW zuEtm8odr}0`nw#N6Fsj8{zCYlkEMyKgB?~AiNW~j-byaG3M*K)WL>>bP6lP#xN-Dn8KEXj#K+9hLuas&;|3aUR^pu zMwKbXJ3w>4vulL!$o^?B%Ev$FhOA80ArBbc?@@3c!XQ50vqGU@kf-eHX4=*!!?2qp z7Wus^09#x~QkvAb%l&k1^5RM|QB^GsD)DQU-<*Y3*sqNECCJCSQ=(RF1?oBr#W^n8 zUQ!#!pQD`;WKX?`^0N~d4J!k$EdC|@Q8c-4RBdiFY5NLKT6=`{eiDH#qfN~AE5Jrp z3Jx8R25zATO*44k9vfd&3qKWUyd6vV3-nw6^LcAt!+3m-#_{~?Jobm){Ce}+C)Pft z$dy9S(psR1eS4&Zs+rau49Y@XpaF^LJVUe`2q}o9E@-0@s0#XOO|DoL+UluXUTU%3 z+4VE ze=p^qcybq3GiT07Q{b326BYfZDLO5!pTE~Jjd3cR!Ud0-J-YRji+|dabOT|UZt%TF z)V6|u(h}RdmB!xCW3OULS7=7?qSYNJjfKT+{sAzo`jpFI%)%Y%q)2UGxqG?qmyFX) zegG!(SP@f~|90U^o%8cAUW)=8)6x1w4#3*7pp$)7JJi4asZ=<(6P7WRj2lc_i!c_;jL0@1xI&<{Fo zaf+9ktf}!w)m;-<7And)TPu|dUGvi;H&JYPU9s#O+Z;Z?k(QM3(pGQe_oHS_n~h+9 zEH3|uWtLAY(ay*;T%`=2l1)ohVFLzgsT#*oJCaD$t((h*f4aT{5xJi8{po%ucc^L6 z;~3_bn+O+bP4u-Cwi9y842JeW;MV@WOcPaDHB;(&P5$d9qgD&YENa+b4X<^s(~P#? z*9Z0XW~U2`+YJWjCS+^>Qlo+7jaX%?nfe%Lket$_$vJqj~&}QhpcezF0oZnm?ShiNM$F zgCvpN9EsYp=lo@3M^zlt!DeHc;`=401kN9EED7WffEXMEeH8)^@r`x9@hiQgLaAa^ zM3+(m02s#cjP9@tD#=JF%Rdaqxwzu@52lGuK%1E(s!G>2nUqvzR|v{!<_@5}+?^f9 z->Wdp(Dr z9Z24;O28KcxeB@@tqT?q?%=A#f}^;ZqZxKui2_B#ntM&PkG4A~fHDGu$h~N>dBhED zqx^gVrrh=6b*WM5dyk>!d=~6(CoIUOl55>Doyy_thF~MnY-4z9ysc?0I#$#?&e6US zQQ6Ny@g~^O$^F1qzpEY3EfmLzF()J3Af6e+fc|2fea?JQDTn zjJ#@nBTAG%diZps5S+9AEeGn|!hS>4mP2Umbh@_VUMQKNlX-g^B1lac8l7l=Os|m7 zxb@Jjw})VpUvRg$w@J?-+SCsdU-RM%G9BoX3~n7C?2zh}zsr%~U{jyF{tI@7LdZ}z z-{2{jy^y6>;cc_z3j0C!P{HCfla?Hf(>22p6 z8}CQmBGg$az^vqAf*69Sjd?Y4rXu!nyYs*NAK&DbEgeckgM2J=UH%(H5M9;JIw{*Y zbK_rKl4#8Wm6RW;DdR*mna3t_!Ava(pj<@GjT*ELMj`^dy&kzM@$2FI6Ut#5I!*i$ z%E$3z;ZCzlLiw>Liv>J}OGQJxUrF*3=bKT5do(X*Qeh57OB&$fJ*^~Pg&2EuX&KGf zL?8p2=P>uzmH}=4!~8~Zx?q7R>P0Tkw%n(h^6Be3hOe#+L%LJyiMXVi!0C)-mwvP1_)i%^FZtJYmGxV#4W{zYrG3g}FteVv|L5-bE6H;nC z+m4vnzpG6sBW+#C-_K`$L$o9gsvQ7vIITAV!DEzvL5J!noB=S&a9qijGz`^J`Yy^A z%N5y|UEm(f$xe|Yfle~YKIYzE>kYyfifnG}qXx$75yBBS?B0v2a_cJF0wwGAAcRt- z@15%aC+A|3qEZ|MZW?k(Ns&nL#nsb7eqUtt6D6(>gwd$d@YCQZ6hF4%w3^(8(&wVCFYhre6f5E$#yD5IM?RKB7-RjtA# z&N`Jy`$HISJD&nks3fXi-R2KPjMH}l-n&cEOKnDD+xHJ+K5=(e$deLDO}-;Mo*zrp zWk?fv7I(CoMz36C@pseon}4R7aSpqAW|31B$RJ5a1bWf|klF~znfZk6Px)5rts8a8 z)NoD?n{qYf+VgBH*T}5{pJce{2iB@ji!2GuDq~)3;;27dr$BA6;~E8aO}~eu5DX#H zAuJ;Y5WyjtTS8y1E|3e5 z!KMh);_-zy^yC<~XN&{ZwJ`B8iNAf*z0PS2VldR$?hvMEh5yp#Ux)kSW_m%eFWQQD zSpZ*G?dX{^!?+qBA)O54e$*()yQAEdG#OL+uwmP2=|+^&mLuGUAnGoPNr9r?r$>ZvEKuqXsp0Vf@}tN{6-Y zRa<(8%B!jBist+k=ZhE2;Ngwbmfd{nNux3)YzR13^|rr3*Npi%#TaxBzxe3`VRztP zQ)>y-KdHQyqn7l$;I~yc4_Q(iU7A|EfJp(c`zK!7B`jQqP+E})9L)#G|MGJyh>RVZ z7@vKaOWR=H^@R;WX+HHeW7I7W{qoc%v(_1jK<17Xl3-e|%fzjWvo__cJ<%|Z(VxHl zRKoeeOTM^7h980BOC6z_uE${z42&-;^3{f6%fu9QCG*PKKoSRD&6=$6aJn-{F_4ET z%py4Yxg447usS7SB8uNmgip(EHucB#j#+zzz5KC>G(CopsR`oc(LaQnr2E3z)tLmj4 zw)S}N%C|Ueb`R@DZL)psUpU0tIUH#>M+M13GiYy?zH@1Rew5ZWx2G{ zN=Zqm98$#=Ili3P7I7eHjX<@2L7*PQZih$aF*N=Bepiy7FQlcSOnu=j!_mZvB({S$ z3Z|H5*yPclQlrl#w@m$hN~HPu&2#IYKwUJw0(;0!#wy+4bBbLpXvbM1yJvG{(=xrO zR3mD&w4PpufzQPS-FO?dU`tX9u=aB%FB9Id_B-{RdZzI#L1K0gq59Eiv}2Ak1_dF7 zD^4LOZeqewz_o)${A({S`oe=q>}tjUkDAC2Lcnxl9Px2WzUxKxapOKP6*Vc%k^--WW42t$F2o_uR%E@7l2k&RFfVYzrLJpo z@&E=2N>jC~od{xnBT-Lh)&{H^w8FcO-#H?%;JugO2uX*v9n*l`m=V@zn9IuX8?SUz z5s9g{o16Ln?v6&JZy&$#C3l&34aTXMvYp3G^T3U90p6J&-QiKFC7e`9i#Ghk-3nRp z-Y5@Q^jK*ScUa_cOu0hDZ04}tcdY8clceB1zYJb%6e;?YeB*e{k1kzOTu@?0s&Y)@ zkr@OBj*Gtc4Ek*{8w5>E>25{#^f$K+643mA*MU#D|KUkmQIeWb1_L7gn{$1;Olobb zRl~}MhwQP-m^A>Z-qdNPrM~>YvV+)~1p3f{M+xiK1;mT_clPOZbVm2)8B7yHavm*w zQ4!taB+;@>n%mtZ0)0`&M1*P9w=X|D;^)4|F`m$xO|yegG^fc8Xh4t87Jj>m{&2Q; z-FVahhi%H99QC!|nudvp7%5`$FMi~4$4L>rw8K;A*4yyiI$nYs{Nsr9YHIxewbA!f zI)+~W8D@ek@Dt4ROChrQXdG)6LYU2V+50$~0n?m1fDvYqTR^fA9C2?4C3JKX zw!Srm$~eTK3-8uNvE?7dPHp_OOVb4X)%&?}gbzM1vL3StXSVrOuM>0V<|?J}7766y ziis%x!xKHs0s0W@paeUL8OnRP_%f9InO!^#A<$e}_KkJ_zQ^$Q(@<5CXOjJ|$T?V6 z631cW>G!G?n?*7fZPj09ad?QTrmP%%S9Zah8Dy{a9|&C21g*!)qeYdR`A#L-gd%2s zr-a#(1~*kpat}kcHF1vHx%F(iS;t5cW{T}mdtvG-fpVX|lLb4-r@=5Y-AHLa#)40< zoR5^|+Id&(+ER4A&&KHz0cVOv^jI~B2JBRC97-P)h#Zn*k?COZ2PS@fmAmJfCI-^0 zw%QZ+7ABfNTwJTP(*geq;m7-Y4RXQe2KZQRnITSg9AzJ$k;~GdE2x`~oT2i=?z7Js zAbA+K*X$zh+Fk1Imvc(w%df;Z`XL$8XbDQpe#Cxbm+{3;XE!RO?C3d9lq7gv8776-U23!^x}-95OwyE8Zg1ozO+6X`3G}~) zhlp>Vt{0sHpGOLwxW!-K-yEfb4Hm6|XDk@8vSK)f;i#8ILlO2A;%WO)e&u7CjQvG_?MC>o@^o(Or9kxJe-O?HSbY5 zT52@4nEg?v-9=|XFkOgoIL6Dlr&?$AgfMTBJgBh~#`<`g_@LHzx+J8?>X(lCv6h88 zEC?f%6rwj}|M_<+C=GbhPZo82*8=C8V)?Y(_vHL)z%JWWc<3}e)aiuiOLATR!Q9v1 z1t6F(`REE3SKaebBVivUmLvV0u7Nuyt2c1<^Q+)k>P5X?;W^3|YkZ78VlmUBUR!<{ z@w<_W@99{`FCz*^Q0m4?syi%-3A|m*HJlJHGR9Z!PDkln+2q~giEsBv+)NMzn={RK zAvS0fg(@4Swg#THcsA8qJMkX7xK&G0TsHiPS$UK>e8mY!fhE9LXoY56jbCYt9J)HV zgk(6jV{vb;d<=d3agH$fm%h*bR_ZJKu>Cr;om9O{)b!a=n8Z3Wxh}=NW&LtFsI8=d zg7s%cTHG?6HG17;uoz>f;3lijYnx{8W%bkWlhZ%$@}uskhhd?LqudT9_b;mvfGRO^ zbi>RhTvi39qS@%9fc?!-X$X`f&r!dC85`1AMhh>P3!xg9ah*!hL@k(aR~?rEXTPuT z2ywxWu_{zqmkZ?4dU|22>Cub8mlQzf=$b}PTfIswIx=wbMf*(LPUl`6X^ppWD=3oT zSdkdAIEgFs=Z6W zUWwL(p=EN;Vo$GI|DaG;k&8u{%aWrRS^+kbX{nItikXBl3q}MlGlP(4%mQ+^LVhZ? zA^u$l55z&|YfcI;H29tJLBI=HYyHn8>6`3> znO518QE{Q^OoT6Z*7@@#m3_fB1_+~1tMWp~=5VRR>)K~Z%W9)YN24gF)@o=HiX{gs zwh4M=|2YbQ^Tn-m+A4dk|9JfPONW|kFXN%LFye$l+$}y|w1SAyOH{{%v+V3_5O1!3 zflJ|wnt5$)AZq_=k8T*y7KYrMMh2l_}6Ptz^ ztsKfF4;exbUQPYAZ91!+$+OFXy>3% zNH>R`2D7n>pZ5RkApUo7_`mOjv+C3d0{cR@A0)K3OO!*ss)`9vKky#~TEWN4$NTy( ztFG}(!75wgrjR1uQO{|Vd#fGe1^0hl*s2+Z4w1r_lU)~XtxmyZERX}&lWYc<{xhir zgV{L>s-j?YchN5{`X6JlK8lr}W3-6IG_r;@BwsARODedsr##bM0Bnj0F!T8CA=h6wPznSxSZYOw7 z=z~lkz!s5ocvafU(^H^qkxOUYg;`MsP(WolNt(t`*+`>$%>zgmf4{P3o@Z0l*))#& z@##sXR`7Yca@wB527jjhi_o8x?9{&%;Ejc~wPDxp=_Uv1U>`}pAeHB)@u{*6G7QX&RVo_qO+>em-i9R9plk4cEOf&xSrjl=$d(v{DIk~fV)?h8YM z6Ni2wJ|>w8N|fQ?5q5+cm%#-}*0!&8TOO<}JaD;B@IF1+$Jnk?n#l+4^s7*1it0>3 z6@~{iNj)eeUK0-aDTIohzJy&+zuc3-ZvKo+`+KRI7tbzTf+Z#Nx5?RzY2faXgQK$0 zglJV7v2%BG$52&k&R4d#g#B2*hLH9WYL4ffU~KQ|YgP`MYFNM< zl)8r&A=zF-i-9}e+|%lOG$%n8VaxcHT_Y<_J6tNI?<`qB(ph%aWYNaz4<4IM(vzx# z2$)E8Ld!#sLBpA@yx^rxQsQ;q#oUn5%TO(=*%}j%*?U8EHTes><$v8YZ|fDr#I%GQ z-!{pt`50~Wc&zATNYj7$P7kD{Fk(F8&4}y=*ol0ML%nZ&ot+$Oo#q1AE*{BoO2k}e zC_qdeAOrUGryhzAcYpt(Qzoouo9V?KYM`v2r=_<8RZAD!UKL zDb8hW8tOGBpT z@?rz9Lpy=v_xdGQ>^U(SuR6b~O)itM#ZE%1EvYQZ!tt_Ilac4@{#uVw><@=KnrWfu zhAii;wk+`&cOxC}#!ApHh4+x`tvxL2*Q zSt_8w7<2cF-^+z3%h&B%<6=Oc$_>KHV8w=orY}@p#I}1?j0ZU;@8wRDdOKX|ag(;9 zw)NMtV)N_jkP1mA?O4#Emm=R^j`C9yCuxz6tln-J#DqgcBSBP5jN7iEFas;1*vI|$ zPUJ)0c|ezGWw^8T*<KPMHAP<_iqr8)OF;93@vv1vKoQU(Vc8M^hD|TQ$vA8$4 zgr9Z>+cSmBCm#cvLUKs_{31(ivBO3de72meRXo$KZg10nB_;K=V! zFs}LDCA6(wvt@%cUqZ4f!RXDKq2e8xhShTX8k;^4CzxOt2&&rZ7;`Tm#X#)X%uZkd ztah8pTD){K#Bj$L5OP=^kUJQ2QE)%qbdmpqBMZSse;Rm_fWucBZAH?9;0rKie65Q_T;_e80a0Zndlb3;X|5-A_Nl_W$21 z?|wVV=^{n8N;-i8VsXp5w%zTUCtKTQ146W@%QlL7YO308v)4RI<&rACpCH%Bx3JB5 z7Leq>p#6Jwsx~&c54swS1V36Q82P4dFOpd?p^GReVSN7A#g@HKhh(U$IjYe;+vw0` z_%{bK0HtF)fwfKr5+EXyE=oqx-%@-xpDY`6*S_<1L5=q_tU0t|h44UVO?o!++Ct{W zlVuPM*X2?AddNg^X5sT66!v$cWx`reZD_6n1t{~0`4erb!}zR3r@DJ*Z35a45xfm9X={3Yw`6k%W+rXK3)%82eJEf zb#=W8OYl)0+nn2OGgQbcvn8`~CNH1?oc$J@%$!TY`UA5|yIJSnx#?f2%qapZl@meQ zwNhp$X}8#7*uUCS>kSPyj+C?un5tYIjZb+y?Aec~BM~OhD^tX0=r{p8DLyl>NdRbn z%E&S)?&`*1B?aJe|IZc6_}z5v+9k$RXVM(Q^v?=c=v`lpbOOUnhD<<}#p-FMwO>-I znWKw&W3SJYnmOGN#UI{iG1jdu_l-Gmk3Ku7hDV9YO8Br)*^%Bv7;!7v{mhvkB@h)1 zpwaV3x%j;@&^Ei<{11zx!9;Mv*Y_%hgzPM5WUosS{7!zzap!3U69R&UL$KV#6Ue;H z2%CN?L30HyJS=`z`nkZVicWT+!~vGPrfsp?Dt9Q+ot%Kvg~0l^w60MEbSVDt9K~-= zfNh-ZhG_oBuZ!LQOf7~;iAJEm0wvDCItN)b%07j zLT!1-GUn%*;KBZXHUP;9qzn)HxdoD%X+%16yuAa3h?g$!^Q?Y+BfEd=JE9=k!uzk2 zYbNK%Tj#}9F~|%#UY})kBTCuG2ql0I>s7w~i!EMm^a14g+i^?2P+|o_D8d8L^z8AL zKP^J+GrD9{HY;W#{%597oLh{CP|l1Pk+86&np`>&JLw1yz7RX~1I{SJTV>!H7XVt| z4}g%9Ghsyy^E>}yXn8lC*jm0yCRyGqTvxghtUF!;2Zn$*=VM`nL#r7GB8%ga*G!(+71$XS?MQV4JX9JKU^;CvZFFm&-jl}<0E2Ka zGXPco=omlY|B0N{u!Dd{Zz$zZXW|ic z{WW~y5XrXtNWelcQX~T~hexGOr+PmYLLVI;dYmyob8{;;pg|JOcvjDO*ZQ1wzf69& zKP)6!ECr%kIn9h8IJ7I|2N+_>YuE}ca#7RpmoG!s&B0LCr}vOc&~n|<3s{97yy=1= zjJPPXIJ{MxOt4)H#14zHqO@}Dj*Q2NeGFOIM(qaztF``IQANr4}N*EcDtOQ7o z&zAqczZ!G0;wNa2&=ed9BqqX5PGNeXQ$2AKe4%}tvb`yaZ30VmIAbrS@aCHDT4fK0ErwUt{*wNG0Iczq8fe_M?t+f{I=kJmkE!Lyzg_B#~FtTF&*LV;%EP-=* z#A6z$QL1#@suXLfX5l}TfzKiO&Wx+N1$lX#DY)TbhDo@+^_?921s<-h5u2A@dVlz@ z#1vC##u>{@6k`s3dsRbh{U9eKXz{CO@;S%v{;)p(ba6DY!jTxvAS55ASWRMW&zeEXks z?f=E#9|b+$$7wXbR=zo8Wr=?_zud^uD?izAXU7(*h23q%EJU$OO2P~?>5miCv{Int zJjJ?6#}t*|*yEI>Od_P@G@UDvQ=p`xVJn_L_n#HKo0xcu`V^c=WS`a5k@H0T+3Sm? z_Sw{(wy)gT8dfj$UivP?N(*PyJ=JzRY zJ953{B3zo1rYnchO)J zZ)f)XY*tEgM^c7tt?%t#n6(PFM`+Oc-`-QEXv0-8>`uA(-bJDFhOduLij}FCOEz&-Al+{0N;qfL z9tru2&*o95T|F@#24csIC|JoY{;3MZS_)A?A4M#a-p*Uy@6XYIRJ=#$WfZW<{nQ8!o>ok;>qb=doBRu z8D4#)7C};4A`jy7Dt6D^`wVnwh*n=213EmlY;3rn*Zv+V=&7}-#M5snCY1uY+KBLs zTPf2~^w$oE;+XRgC<^lXiK~rP(*mjeXc?z;x;Z%B zy<@(AHVfVl)S_r)?feAFrQ_>M>9A^I{J%e5>o`l3<1i~8ch>O>)c1@Sz9!@9M4gqT z)sg%iQ>G0Nqg;rD_f6$dadY)_wyTfz)g1QHfA(Me8%$cwk$UPi;gaPJ zuCi=nZ78UoxNE?~0=E`Z16+e{PBSXka^tlAXpk5j?mJ0_ssmqS_O z!wx#^Bdk4{udRIo!T4@4YVKwwtaV21ZMlHvkmMbK{4wg0r;yJLNC zZHpp}O-NSP&dU@qzhyp1ypQa%PzkMUeR>=8yZs;wJ302|F-++1lyzOTR%e2q#I$4g z<*0NUBOpc_Oc=+Ue|o?O$(zDlc)Bo%F;gk`br<}OudBXZ+CFjK2_(s8pezG0q%4GQMf<;iCVl~ zAO|#~(p0wc))xmvo>Ai{gZ#{xxyy@jb&a*mSforIJ-G={N3BUb{_{sEAxp6Mzoj2k z{P>)e^AddXBEEoD%j#1~B%&zNs}Z(cLvFVLoL{mf3>#T?@q?8GbeCL>Cg>KQ)$vF6rq8Ee*!y1b@URx- z$Qdy;G|(KEc>3PXbs|{LE&o7dGmexPaA`Ds$M?Y>`ybLdu0BSaTPfmDc1%pAtr12V zf!o~NS`LDlS4F$=BQpW?UL>g{pG({UvQBp2kDp*T3xj*Yt)D}JhBloGkx5JIG(j*7 zSI|r}-GRR^$IHgkBa4;h@eV~!g~jxs#vZfqwGF>IvpLqYN%04n0M6I{y%-mZYQ{|1 zL{nC^{4V+MXNlQ;JMHSa{J1ap;NHFsa;A6^A6k8R{>T227fj`0fzkH&a!LHv3{1fFqSigl#*OuvO8q0T%jX`P)q?DB>Sfb#E*j87ccm1DQfXI z;uG-E=D2|W=&Rn>`3 znWOiLew@EdDB_3!7o7rHRih-p?jNar;PR#$*AAObvhKL@mrq62c{J(foNAzJJ0?Xw zaD~*cZaiY8d(vmNAf{ZDr6KW$+4m=D3X+`nf3h(meI}Og7sd8?$bWbGUhCg;K64B%AWD1H{*BV_-* zVrsbf+d^AIxoIpzojMBbcK|x}?X(l(!^1=D*|)APVZQ#v5YJ$2yrsc4T$%|#3)@D( zV&oE)7mdblTiD%%uc9w}W%18MACGSp7Xrn3qvSe{vhsjpkrZNr3MNBMRA*N3RyMno8c*RSUQP9#USPu># zh}`B&m7F_%QgF)KN^MSiw30Q>F8_H4dFe30nG7SQhQ}WK(@OU*^hW%S343kLV7t?e z>b)xkG*T(!-5mZXy!hEd8xmnbeoPZ!&ve=k`_9z{a{GHYR^3}y4k`uKubgBZ6ZM>4 z9jVU!Uqj7x4((1^X8H~a!G5oI1+Urn+Ewp1!-qvQX|EU4Is9hvj5Pz7!UiFzdl|yH zd*~AOzDvU@w~9a~z2r68%A@SxT}L_CEMrzm!#8HCv2<-CJkX|0FhUmw5+Lgr`e#x6 ze=zqlpan2Yd>^w>pLoOerAf5w<-uGc79F5R*ZHxAzGtK5C=u=p$ee>P&L`#%s~|%7 z6+jsB`JV<4>GN0aI}{&x+jA`nKN`t(D`o$UsSI8v)-QKzt@36V}J#!}y$Mx@OK*Zk)pD#vwCEdsoEm;Tw8RBIwU zETFac@9yIGY&BP$OLr(osff?&!Sgwtb;|Y?n?9_xOIqsLwnimnJflKLD~h3Q=rtak zO5+3VIwG2WF~JE!L7mC{ApO@01WabFJjF!$3niu|e0~?iC}cd*qGa4{8prNur!r5? z?Je<*?YEu8;EG{|3V}8xq}*l`annu>vRCu%yA)*n?ghA8CLE7HZ{0sB=C?(eYRRtz z6Ib-E{fpW;*vx;tvYN134U;!V^366?vBZjQ1^;ZkN3@uFP+2clvf)$ZAvra)jk}XM z{!?OZ!poE`!$-Fe_Rj0sRg`4ymLPzy%l11Oxqfe@nZsM!>5G8q8_A|5{GLQFnS)Oe z3_Q3!w9aIIeY))NWRjgf`1N2to-i8r3i1Qg*>s|KpH!u*y})A8M$u>sse#E|Na%>e?UmO~XQ$X9xRBF*%JsE*uU@L2zYsa%Y z86o?*SU#uInysCvjgW6rfHVuz)WExa0@s8+RcnK|f?-{HJ8|1RQeEE37G2J0S$e*O zeV(lkvL+FKrkGu%q0Wav=9p0DTM5pFFB5L6{(%fJB0Rn}&z?vVq8N_r*z^45_9%Iz zHDoL!c?JfCSJIk_gTtABQsv!GPe8}n3^k*uMMyJ`bO>c%%4zns`J)(Q?SOLG$u#g` zNI=xlmQgEdh1)UlAhhj(n>@J>S^Ii})nxERe*V4p%iBA`UK(q{Da=?W45}7XmrW$|LYOt3g`X{NI`bi`-#>-Ne|y(71^xe5-$ zY7IBu=%$Q%UKW`Rr?*Aa7ZRM~VO!~MkIwM>d*ef*3Y&SvoK97py}khfes^Q_ePZ{m zlG`XFP93J8rAZ2;IE*mk7(Qz>Z-F#34#ENsQ^BqM%I^cF-GBeBy>O!M{4Z*cGeO_( z^FK_(8&&`nqn~+WBqr9^wzSEBKtRxmDqWXRx1gjgB|WQ$#98NvtWklq+z${^|Jlr0 z#-d7ovhuza89@dEODN?&KTS_@4CQsmz}TxC-n)-F5E!aL2^f)qrbFuP-r!CGXaCdn ztVyV~Wo}NZVy*(VKsV6+11E;Qd1$7(dy4rkagwOCxL}pDU=l= zkJr&f%Zr3P+Bs8SX`8yPs=)d{rvpiE;fbxY&X~WJ@2N518}Z4oS60q zhUaP)OUc&cs};TH=kOJ5!(!k_YCTI{Ywe;azrSB_E)sd32XT$2fb)lSD^{W!l0sd3 zdfBKHcTzWZJ!|>ay%JYnig*tYZ=tufwd1!AeJpCZYa1R#!ruq*j!Kq_^YTu_UP=AG zmlm(J=v(r;^8a|vh3E6^3_XiQ>#2z-EgM~lgzBXhPC$%zX4KIqu++n^H{MM5)K1B^ zT3-)%EK&Xpsb~8Wk_((;91CJ#=k$D^<6Nl^2qJ818?%r-;VNz5bC-!jE!7WA`dh|> zk-4xL=zQCj=qT<9jT9n5fj)}s}(RFZ!! zBK*hDBTi~h! zxbCIqBAQ#0_c|yHMxT8>w}>~o*gQcr$=nFE_IusPr}^HYQdbp23=I`TA9Nm;pnPh` zn6y@#I2tDetl90Gh2@~lv4M6gWNyuGe^!OkM3@1zi@HNhkm8<< zTI_~>I>>PzdD!mLWaYIvx(1PuK6q!QbPJIu@V7W`nW6;bX1A!+?W?X!U7)o z{KZ&CZJaz+5b$B@!TdwOC4%g~3U-5VUnITPDR^u7{lcm-@sfk=l&6N*eCp!eupStV zKHQ0@Mw!eU8I!<2`%<=Nv&*(Dj86eT0mMu;w_?Kb$$JNJEP-FMI`hCx_tt`NInSX| zIl0ALcTwD!-G?N~VYx%qv(@OUCrn1(aD~)Lnn0`98cR)nVPLY~!&}$CJWR=$``yO( z7qjkL)buL{n=uo5NK;02Ww-7}McC6sPM*DZ;{0z>7r?~k@FuEhAzt#6BXcqVdW1+5 z>T5mQU`!FAEOnHX9WM{!mCN_lIHPU1sD$WZ+mn})D`Zgn$a6|D4$`vrzz4ojQF&tXx_w+Zd*){82Opug%VKc!q)V=J4(wTo-)d}Gg_tLJ`+peQnlb(UtgHAkyYo&N&(C5GaPlfK(w=rP#!pGP_B)z zUvJ%)YUiC5{hX*70w~*NoYu>FL`l)7-@=`S6_P%iOYo6RpghbrXMcTNs0(!6$vpeP z&0n2r=UK+glsao^uc!WFA`IqjH-vKHt^-I;uif0Ba2ZgDDYCcIQ*yE13!~oN;kobgNF}6 z!!nG|ZZ&5HmrRFXd@xO^mNBkx$1f+o$2^DUCowvOD=*Y=S5X{eH~ZgT%#{y?TT6nO zU2B}@DK=JA+zoV=(J9z#8wADbdJ=FUF4VgYtm{oudAxX)yu3Z%D~0S>u-wXqb7W;V zlKyJsfl{l4L0VVM>rUkSIoIUkm*K$cXxuN?pX=dhZaQ$3VFHov#d1z38)X*xc~$=+ z0zYJ4I^_*~<*TASHzl-}^#OW?mV{L11HX`ie*T+#^NWl(*PF2v`=Wl59^{3T_0@<9)en35 z)o?3gG^oCfObuImAq4?I=H0cqwY{~~J+>_dl?;ZeU{vg4R2o}-dsg>arL73UPy`;0 zC4dv6RuhPS5%w9k&EZj1jMjC|N`w)oYi(@A%1$uYvkBlA+%#&l0y;%g1_WHFaALbxAz`4W zy5@TFK-H}t+L4F}F;g3fQuB7a`s>#*bL-K2GwZ%$mwR)sV&ts|oIzIvzQOdRU!>8=l_r zaiAb4|49xb$#kan1GH40KF!4zCbna`ag$LADb~I3(`1^zw8l22ATxD&XMX?xhq579 zPYh;PG@9mLnYrzJZ65+H<7dF8w&yY<0BKDz37%X}NAD9(j8@eC;Uw;!+?15{(VVvK z6f-$aUR03}3U)EB2Ye_Nbg4hrrv9N-n3jf_)HXeS_k9f5&GxerwEDbkISu@-gwd+A zlM@Uert=pU;ADSLu54esbZfh>KD+E*5vVw!Z==l&OfF^{u@PS(*eY5k5ndT=Ha@t0gyF5u=?W1c}Fbqt$kgmO{vqQtZEj20gBj!>Z`v`A^V+9|XVgQ>MwX_M43CD-(O+}<4gwi!*tN9s zV#5Z+hmqsl6S?vI(1NGlezu`F718;z08#9ijeL%+;5kY_$1D;a)Bq;objvG`rghPc zjkB%2Jq*fya*Ivre1F=?xq&=9u(8}lFApGQ)62SIAe{F;r1d6h<(j-eqm?xwa!A7> zNDW+s=2Q9=i}O>lU<-Y2uC-u%y%E!8B)9K)bRwsbfUd=AO%3fQHPB~TM&<1Qoll>o zcLmmV8|L*>u)UmW=Y7)|lkxSrpr0+^OPrqGD?Y);qLHl_^S9(Rn5n8O9;Mw1fsTr3 zKu1n5?{}N06#oEAuLCLIRM5FoS4-}!F-2?6bhFI{Gk^?ZIW%?W=(nQy&lg@Y{u+)! zi6l3t;&gs`1|0R}kJf}vMII(z`~RDn z4~eJ#`1HQK`}Yed>m4HR0D#SXyBGQvmnWT!6_w!)WiCNv#m-xE1tYbifaH%)(ILVa znaTk5k`JPBNbX)si)CJ+77bupL(y*dE6+@mzLGdaN=)iWD%kFI>TYj{n)jk|Sm8?| zo})Ogux_WoyLFs~6zXpof)z~8dmn)_`_F)#hkcf0aBjQ>IRUuzSvY;E#Nko4?NFjH zANs4#rXf8&xRMrrHch}_n0WFEwY~d9q(%;Qs>WsIkHRNnYQdm?*zWg6o;7ESI9DYi zk|&7s$zYpAP#W0Sjc$?|jRN(}!p-jg%*>H1kWp9bC-fb5JRL+JBz;8;T=Yl52sHvT%1Pc>=@Z;!1q;pRNof$XKy;+iKqzvK-{;mpcfaQ3l1|N-m9Rh z=uS~>Z{0olPEAz+`@^&)0`o5e^>SVg`ArwNc?{@7%eeTvvu>v_py+ zS9Q03ZoZs$6DRm243z57#BxJZdG2-N;qU+Qt@S!;^gXKeS|$c(Y>$%x@V#MZWQl!f zb>xAHz41Plee280Z_(!h#8s>ikM~VsPk|M`zIx=^1Tg+DxQ}+3ROCb>#ULTsD&oL& z1D~6eM5rGDef&QBDW!=q_&ANJ;p%8;D|gtSUjJGh5KwVnuT1IHT3Tk|ohDy>eN5gk z>8GjTst5nAio^#3;f!tEv75!zs6G%fMf-fDFIoO>ofZbAsux+jR3PjG6}P2)k*Q}? ztc_H&HOk9T*z3p&4$qoR$)}+uxwRN`?D)0a+ZpI{^PFHVQjZyph*ezqD8c|>ohFGp zo!Ycs+~3?Z^4}Al)=W4dkEWFE2+&^swYO-IEGp{A*f+qP_5Gizx(PmIz8TB~?Dp-x zz9zxjNGOj013)860^5~1xNLVL?@LT7F~?}K0K}W)N{9iVqBbo!J%+59r?ldli()V1 z)c#?%Q;XT)&}cFVZ3Ae4xyg~%OHYJM8e73v)otQFpo$RDvO#e6I^^xyC|j8HjX)EQRYao|ic(+2Dm&)w&yY@{z`+>boL(JSGjbV4Z3 zu)xMJ1CF70YRP|&TYY*sTHvp^A1=m&5C%+Ws>@qE$*^ryL)A`W=p8xXP^Y$m2&=2M zp;HqClDWKiRS-Ap)I*7kvf*+p{;?2Z)*-*8FroaqVXn_LE@B?$-aVt-2EkRh$>5rm ziLY6990=3}n?Cytn4arAcU|7wI=*H0SGM)6`EMf5+uitqcptEo@gpt!6>C~>wxpSY`ldaf#5W!}*}ogN_O>X+1wPQPiu7L;)Xq$$T`Tux@J zb4Le56a{0i`~P?6Y8l&2$J~@(9Fr||v zxhidX)aQ5p3+b3Dv1X@{5oc=_C?#)O7{Lh&WbkEwLLz@c3|Fo&kaGt@9cNO?%8~?? zoOCb|*>_;>?d=tOtoNa;BpEh)Uv9O`{EFx_WMZs-C#G-}Aw@IZNhbs@%+?mVe^b|(37k~LgRjb_*2iey zy*JZ)kO{(8w{O#5^o{KgwGVDhT;I)E3$^0Q(cjH0OS5NTW2_EF>4x(SU zDU5l^*?x^65$je){o?X)k|4wkH-eYF&8Rcm`*!idwfGC&aVMlzj_h^4621#XLFpfA z4tW7awYBa<=*q#&YFrA&`PBS%aZCK}$MV;Ei&BNuTbc3Zj z(EoT3icJ|W*mo#?C2~bOAidUJf0!h_u#kP_oaC60>E{C@GRCAhE^ z4~~`EX5jg>(^&5%asy=7<$&w&znyNax6BCg3B$z_J9p5tW@z!Q9)j*S`uoe&QkM2E zPwD==_&}wy-ksoa_DDvC>Xr!zW(vEC8y*(@0^0o>Ht=Y3vvlM3_Hi8iZnE3`bI*EhAd zXtHz@J*PBkR`JWkO>h|HE{CS0zOK!L+w^($CRrAqcVSB{E1)cV3N z3vO<|zftT17|8(0VC{ScT2@Cf4XUiBR@?YWh<_;fo4UFHCV2Q*Kk^ZlyKZi4D6dPW z-iCUn@I1*4*XU5xxLKa@bWnX=&O&ybxNkeQb!Y0I8{s*ZhhiMUfbJimY{{eip5{=Y zD&^?mh)Bt_idJ~+<>5I)KQSy#%bIcoD94{|Wy>nm+?A9Wlr_8&3dy|{*xO``RSdOh zhD7l-d@jp!Xrl0Nst_~&!9SO_O>dB^^i!5D_DN8u=T>ap&wB=78qz3Agw}BIy*t6Q zg%*t({NRMXPG&1@X&Nk^d10Roz;cTDa6mmIdr9-##VLSlBt|Xg*O9w|HWa`_KZH@z zZb3)TH`sj_T0%sz%mPsafSd?p(j`b{tO%B#+^QE5(6wa6_+npmHmO{GhQ}m0sQI)QEaoJ)qpdS| zqj0Cc6Tb9FwW^rro9>T&yJ`|l&|BZWc5OM_g|2GcV4ojf;9hA_94(MH>jPp&?Px9^ z<4r%?(z8STSHb-ja=5Np#HZfS0IJpEGEUkhJWveTYc^>iL>{xP!M@5^IYiyB_sMe% zuI;iX_b0|g5#-1|Z)lP}hKIfuUSiCT}}$v83srG$N(BedW-%W1>NHXq`}HkYg;jI%R`m&}wgJ82ip)lJaSfRmkqQ{2CQrZ-9q?7^4|Q_|l~x z;1?yk={=%7_vB1A%mg(02rt?noi!$HS3N~;jWF^+6W-#(+t_AhoQ?*|o+_I!FlK*n z0d}&G+qBcNFV>lADq5(h>ae}`5 z)?>}5HXOMRib8RV$JDUZQovINKOG?IoRdC<$P~l+L2LL!w+1f-sL` z8uJy`+{I0n>#d27_UK0>Oiaxe3cx=?ZdxXx8;ix8;Gr6vS$3D8{AVraMVDXP@*pp@ z`jhZ@nPUw6Zmy8QU^fwtd#v?NC#Q=`iu;~7#P8^|KK>sIx+&HOqw=r`YE+}2Wth^7 zyJ^;)+8{PKN`SBOryLRXH)XcmajrZEyjogvG>j&>Hb#_+)6{~Poa|&lONGG+b~CRn zSdk|(Yk1z*7vm;cGOC>C(A%#mz!-(O29Or596Z_R-@v#i>oM%)Mk$lB(`UwQuaC#$b$wu( zTlRrBDWcZbjv_h^HMN&QbR=^ci~d+boQpxYhwMh2%xWTeN1`E)e zanyayK6S_5JfwYp+>LGP+MISW(B`de!&PlIo^PF%r6e6qbj{$i{S^I)<#e@!7W;*W zejg;vX~yKF-1PU%biqLtJR!lxQvT_LWyPknT4eGe(_h3fW>-?8Ts={(>YpI%s73nW zhAb#m2Q>IJwEM7X9-wxmxW9T_6IdLs^#?$vr+yZW%zg9M^O7JQ9&`6k-w ztBe4!!sI6>CipO=qeM``KcI9HZ_L$Ej`B4onoI#J)A?)o$8Ey}-D5kJ4!0Mo>+gRH ziu-lsvg-jWJsSJdiiqCA;vJbVZkE6E>;GSmWvFqy3SZIxGN!1lokn7hVtu|M*k&K! zQ(3jwdJF(Qw!1lLfwJ=_XuF*EtfpRBWr72~grs>%;$H0;p-r{dwlxoMpRprM@Nj;E(9)zXfyF5E+{X>VvR>$~l`O;Kmqt*3y4s$vS&cKpx4fpSv0NC!Jx8IDi zFtBBk>taGDgBKgrzg0E(YimxvRCkV-I!v_>tYsXiq~?rH%SWXy88&12B3ZN1UDFCN znG3~jjO%+u#RPXfSazW)*u-nh%^is5!3D24BIVQUr)hDKA}T8Kl5IH5vX2i_Ylf%N zEpNL+2Jp9S4d$m*f~U9X>1hlbH%(f`FWHZ9Nr%1+Jm3!HI}A-T-lqWCREc>}BtQyAyg^WypB~%}W^z1!LK#9|iJ)4JUr%^+ z?g1^OtegaOZ8k#K%YA!9lr<+lbVt!7Fb^%2SZCWoonwwD?`uD&v|xgY8lqBmT5(j#^{hMFy@W6U0l)Z&*P;F^_CR-iNQ=?9GBqIP9KmFxEDHsWf zq>QX|BR4rsp9ivQE9amNOd;WrER}QnF>U$^jCC-#Oc_j6ngGPS|8#cp>OzyU#-LTm z+GFNk=XLn^q#z%1E5j(+R`i5=Zr1?(En+nlY-(v7yPcH7PmzoGrm=Bp zbx4L+Rt5=sL%@p9jtaLCO8_@@59-^4fsI=FFAd-JsAoO!KSfz#Q*+euvhOJDmq*7x zG5?ol{x2&<5${6}mkLzZP7@~gDk{cqhZaqX1^^-V1v#yi7?F@x zMTAG3eWc4=qlZ11y=85feBKv$gHYec} z6Lztd7Tm7zM4}>4I*!A;`BqsLP9vaVSkuWxtX+^iF;rQiO|ljtpYt-<$>l8=b##oE z=dgpk0R-VWCX}s7YI%~uQD>PJY?{p~DQ%

k$`}#af5N!&==qbe$T1#D1%1VU@C> zw{3{L5>K28Rc@&QVwN;&bu5!Rk|`3X!Jkj2M3!SLC9U_cJ< zu5{$>mBW2|0LZ303%b`GL{Pabw8ozmhCK4cN(7KlaC|EUZUuwn<=~XhE0u8r;r;?A zU=~Rxyjvghv_8`+jA!gYsns5SYurDrP>fqf&L=@1Jr%kV6yiUm`@R?WX(3b>7~dd+ z&V%^6xi~#NgCi5_M)DZ|-K$MNr!Z9kZv|9At{ZtAR*n|ASECvJiq@r_nbTK!yDc(1`Gy^^<8t# z`B^Sn5gChrGd^D9zrybbFA5Q7jf*d#f1t=U(KDJ=XrJNEzYb)bK>9<&Sv~C! z<54tEo@HUjr9mtiQ4vRuxElR$x0$x<@WfF`uouceAs2huX@@Xl%*(q_mT5&g9hnU<<`GMN9h(`>{UWrl@;vwO{+`7i=r712Zrc-CSfn!dMQ&Oz} zfjY>}%+!X&pVZ%P{f+zPa^y@6p|~tWoR;TJ{o^XQf4|i_Wx%&C_shibYsuj#iWt=D zpG6+1wcBlN$*monm3Gt7v|(#>(WsCf%dZ^h;q@X`!`l@`P}!#+j;&Cb5I_sDz7vW8 z(I*gR7&Mu-9I&)F;>^%;We||ad7EFjH8}_t2Z}qjB4pIme~J*<0U9kznUthre4cO#(x8P138i1rU#(tjerktkYFB2Ge5`#V{G|WN ztq+S08Hck|Uw|(tQx)-76b}Vtlm&wJjc;gmR$Ijbf$%yx7+xF?IMX$+UJkIzl(@w3 zDl*mKiqLLq4ZA&TU{BzIKB^C_y`2Ja_2vECU%7#or1S)Bp?$;r)(RF12l+f>(^3FN zUH=4)r-csk9tPdj?Rkz>fcBOn!w8b5)LsSuIjQ3`cdE>6I4;#T=xmrh?4LY!n$ep7 zvL{Tp>KSHG`zwm~kp#m|mzCj23t-RhS9Q!hDjiE^l)Gy@&*8&|dR(S-nPg9hW|07S zmpd8mnpjt?SAoZ&kVWqD!~r>?qY|y z=i}8c(*yKFqQX)Xq2I6Tu-k}Z71H3MDJip%3j@;ZE!W&z5O|^PGC>8mZ`su7I;{H?$8|a+Jvug%B)+Z3OHtCp(V$ zJs8|}Q0fe_4umLxe+7`_rXh(~K~h`r-d>nvaH|C1CQ_18GH&5Ma~fdb(TVpbomoXz zZI7s5$|Y-zMB}m#pn^CqJT63>Z5tJ9MD#GwR|?NO=%wvF@9*zl0$LdM)l)bG8r1#s zsx&6XM##{4l<1-Z^AZ0}TU^7)#XpPyMaW=Yg>qUB2T&m#)A%N2+0StBK-;p)%&I#d zU+$!|&{*z2a7?Y1FP_D-B|ef{Ey4f(&P^fWA$%Q#rbgzZ6mVNQuOGUB##f7>2*uZB7h&NixkvAgqJ;NO2B~~1 zIb@dT4o)${Wlr(VE9v~#yk0znsZOo>_wEh2Jm;@+tHfl_{q9pE8s7%HwVj*$S`{r~ z5N*81g~vl1v=y5TP8fcA#wF=O6@{az)fc?xp7sSy`KPF&lS&1$|8$b*CajH5XnR>^ zmtE#018=0ZsGWLduk_E*Dz;_Fnh%Gn3S0es6aGi0;&)EiL~-`}@KE|Wup3NyleW>v zq38T8w7K{|LSi9L4;?52<;-Pcb-iKBsASS~(*;D2jU!NS zgKUGfEca>QU&!V7wmI9bzH!^_Vo@QjT}9AEomz3$LXvSsU^R-4Fw@B z-aGOnvsBB=4MCA1O`R%~?C6=muh!VItHkZSg?jCo)&o5xo|e&+TQVR~dteJ}yjT66 z3499>#nSixmXM(Rss`Qk{kd({`~8!n$JNQ9L#V?a9W2)@sQOlAMazU#p z)2seW8WcIF_b1o2S2PQicxYw#U(ocqy;cU1@o(DpJ6R^?7Em$4eN2nfH()dI;O6=} zFxu+pquyW9aoG$fMh3a@Q}p71cE*kVZPMfnV%p*~E?%VU*l4KB`s$~Xwd?Ll&-6*R z(PkPb3|%OW5duN`*G$)7%SgjV6p-Y>3LlYfs@;yfAxYsi&76@d$6zjx(b#4*I#pUn zz4OYEgp6WrsA>nS;s+<*9{ezt! zEGq@eRuzk;k>gY;W&5 zc)3oAE~bPUM=#i<3upN}@(t_lXZ>Yia{i|CxJUu|)jhU<;?lizZCR7}++Z$ngLonN zHjV}faOd$fV|6=M@sI!iAFN_(tKumNKO_>-qn6j!t^D%w^=UokZDi89hpgre)<};{ z6hB3-m6_=J1UP>ZvG)>P(iORh_0o?FDv?)iXLx(?$?CO>`d3#Eq44Lnd3<p=Y-Gc1cV3&QlW|f#evfkE6mJ#d*K+%7%>28d{rMGtWS+zh5d$TNZw$Zc`+1b$~{avILawz1A}=~e=p1L z7!BFuK_(TzGRpmTSeohWTM&4ot&6WlcL3=K8yb}w{`(X)mK|vd^|D%*1FYG4DEezg zzO0g9;HkU<k%_*tSb3DlvW=)f%FSVBntY=rZ3SuDgKYd%)TigkNG@7y z&zFf{V6j&A(T=md+=e>gC-3;BOk(+BK?6p?q|a4B_#pXO9zJr!NJm?|(tG-Vyd-?~ zchz~>%Ie9O$i;UYe3263TOU;VC(j@Mb^o?pW6j=pM}<|-e8`Poojkt-vx6CD(7LaK z+rSHOLY(p;V4#j_N0Oq{M05X46rF)ZEabJqEw3=~ zW~87UH3*r0;_ejd(^mO5IP>fJ)^?qlq9WCc*@b3)jb*!+htB}jEwn01bTS4cOQv2A zV-v6wG&fa*L~R%JgK*;pKF3)`2r_|y?&|IsIAOU6QE2?tjx zfrOn=VSI(yYP=HSzcix(5j~nU=4TVm>^n;;X5tQe^`qKLCi%~?#*o1pKD>gg1Cz`j;U_cn-w=i9 zm-LUm|Gw%r{A%L#)Qj@nDLu~?Si{LpMjE)VT24-xR;VPc@%n6gU&I>s&%04*Q-~6H z&}wOEr62IXX2D-$XMcYGN)^4ZvF|sP1e~}q+$|~`$lUB3shOp zJ{1#FG!dk#JrlM(yLz^!H%5o}0*0o$D+MFyJW!V>d0JL&;GDj6pYSsjF$UbgA>@RL zz)ExW^o!wIAvVq$1AlaVVfyLAVAjV)#&f;0z#>{guBO8S7Fx)hN|&ypn2G|lMo)Ll z@MFBwjw0@OLJTQw%3d_+lcqQ!1(b{Lz8(Fvm_MKVG%!ShDw^>tw^NOnom}VU!nDvk zTKW-eK0>|B$&3xPVgmfBFZh~R5ev0S9Sjz}GcA6`+IyFo8s!GG5Di>D7NFpC!z{H2 zg_Nlwgz%8Ko%g88q}Z}FD$0+p7~QmN-KLkqp{+p{+?_v4RJ6p+)A z{oRMOj~>M7La5+LPKU^uU5^wmM&9l{882)#ySXvI_gcD7!Mk7C*}<*Pc-C-L2h zrQ)iSKdz!s8jotc5zt{0QAnBJm(1NdI{)jYV#zAGGdb9-3{eRnJR+6z9X>4o3)+uW z`}y_qzEG5penDk&mk)X<97#3zg9yDs{iMZz)fyM?$J;ZC2qPI8P~~uWxq!;d>ULTa zJyfZn@%v%@g9H^Fj?lng?sxli5PL6Vt7jX=fh$2(((K6?U;0OGT|}-a-sG0PtpG>6 z>c;L%60tkb=K!WyS|-R8DILp_iJ7e;7z!)qEE~f47qW4RAGC1yzE6%tWwc^_f^*d_ zh9`c*N8lFnPK775eRrUGlB!zI+d`VSb7qU*MAz_J<)Hj(-xC>Wn%NV(qz$wZE9f^x z(9p^)Hnz`HyAid5wxh7F{dh)%Z2VO{S1|Jb3D5rDPnYJLLksFE9bX;tyk0bjy-$FQ zWh{DEcG^bnZC+XFMm1HHcA2es*F&I=hW(ALZJjhaP^NX(yHT%Gx3loaLdXt->p-&QjQ%PH?V$dLbI!>oHBX) zfxGQ$6|92%$7iCQ&TD7bP&y|WFpBVoG^4LLM~{oxmdjN73EW(2xuIGyri)0Gp`cjS zWGS)u8Sye4Rs+uueNlL!n~E`bYWH*BxeUM&Pbf$a2-m+mBU!ND{IYgeR8kSoW*dk_ zL#Qs`6*jDj=04aI$5O?p z!So$ICk=AlvXBcEdA}74n6tu~V9`*#D^fHR@s8Vr!=E`Vb|>h0S@X=EE-oc)c?TzI zjCEO6UBZ~ZMepAoyvS9m$|~y5nFV1-GV3R3lb}uie8rRdqhkK*O7IpkL#CHE@ml5n z-tB~E_(L=yZ83pwH1JJ)=_@1mZ>!LmQw%uK9(HKX>5X(kL_+=YgE<;U)PM?^Z+R{49Pu-G^M zm>^)MkYR79wD~4$FNw;cy^!@x&YGq81eg-8b>Trxx{B`|eO7LPAn%|eR1}qC_|LN# zpBu1BA{o8=7gIt<&wfymVp_>#Y-Xg!uj8c%ZIYb2CJLZcs z?vlYP6t6Q~eAV`O3(=xR9!6$OMvP}i1A!Iix zm>nm*DqMrMZT{co1n|;9t)?KYQ^fVM&{ecnJi_dDBR>0z zSS~pR4|2LfGc^rytJZtAv!7i910EPeC&R!3lsMj<``7*JCt4hICle=rAj_Z{mo|cf zp0QY4xAdLP|M=5wJDd9(!@X|qK4LB$ApH4d$2@p3L-x6XM`i9n(#fJ`hQ-CNn{)Gt zondD&>a~_iRTH;{OIB0By7WDSSqBCUi^F>tZ%XbpBazkvI8zd|X(bB3 z{HR=d4TfyhToa&?>@-J2gC@Qx#H8Yz!EH{6c$rX&XJ=pcMsRR?hbib)yneC2#7;K# z-1@#ajt}Ro+ zv{ec-kf-{?MKO40|2p+c`d>DwN?4EiV^zC_j zd)2~RpRTMTLhp@PTaU)mIrzL;xW2;|G7^&rmp-3M1q`pl`2I~3l+LDIwDgsXDKNMm z#9k;pDu~ka+cyCdToR6*qQ~de_a?t+C$DuBTWNar>?3~J*)Dg}K;`v3-dhrXXUiUS zH1U|!mB}wy9K`Uj=r$sEa*oDV@?bX^>%TJ|#S}LLe_k#U+5o}~jLwu1ke!r5{x>{p@%C<{$9y;pYAG z8E!%2Fr$uy>^rE*vRh`%YYx=@=C-c3=9LgP1efeJ>Y+P&!T^Vvg+ zwSGXL6c=AgLBjmDgmWs~(S?hB-odN=&8DDSd zFxyE02ngDj1#RPn+gt3pl?9*A3L^;0cz4(C*&wiAz(;$o0~fBZE}REWznn<}jt`^XJKq(5#l4qQHe(F$VBn-lH8jhK9N< z@v3C5G=2!OeH!o^CJFDAFgqd*{ca}12}xcnw%_PndLt8&F`MD<-=eZJhENEQ8P2nF zPFslVQeXWI)Qv6v(f>s7=NHhGVWgQ3oiHuklyhplVG{UfGYb|jA$#UhF1B6DwDPhU zp8#MQD$}E7{eJDDtVtx8f-8&Om6xrhmQ2h2CEU}>O%*VvzD}HTivit~IUfrQWg3$A z87FqtOIFN=7W!DW9)bBC-t!;kJ+9K|v`hX}{c20A zN`szVNO##U|D~1-o27}}c^9unqzp62L0HTF)?Qe{3?#ieMeBxvSP0}*lJEaWkUMlG zr?D88z%R<`kh~IN$IR}D*yN=;6)Eb)<~22x7jpC*K;B(PAl@$pC}96O;4A*jZ?|N0 z$u{@P6JyH{KFy}0%eqQJKM_P!R3r;yQbAOJE4lBtPNXxWm(#c{-R@++5PiQN`)rfL zBJ2CIN%iDXWa!2;P~=e8G) zhy>kpd;GLCsF3|=Idi)F;q>+4I={k$8Z#}#?z454bB<12edatm`wzd$78iO-YUK6l z{AP}d+Dn6l!`CqcnyJeJ#4cyYIXQW_t9JiVrQx8@W9?QLg7bz?MH-Q_*X_kb0iC=p zgk?@evUL)>c3iX6jPwuUH8p=<`bKP9_eOlYl7Ne0(mpn{vBFg66{;$u_@_Uy$r zoV5%|c0@VvNgSRskssRV6h`XbxPchaGKkg-{#rZVxal1xe3e7++71&()<|m`2rIab zxL}4tw*%O*9`5*Oss=Zk`>>67ER8jUhQ7kf{_GLENNHFEaEgVB+Vl=Q{R=)ay%)e@ z_{)a!N~K^18sq_RX?xfmnL57}hUJxZsjcqmpYw?If9!tViPmJXj1~KQi?5ZdEOR(N zwtw4r%sQ#j`!KTps3dPeWhq&m0v>C5Fn7bJUk%ZA9L^4q6VSN-Y($>KziHx3AyJP=A_ia@qdJ zCnj+ML4CSE0uEcsTnPQ;0t}A=<`2J5fA`$gy@==%_$}rXo+m13JlORxHt&iw#g;It zx2b-{kRfLTZkK;YMHZh)V9!Kyg5+YdeCyx!t1^djzwNZ(&F^qbSfcoHOvv|Kn2RDD zHd`|J$aP>EHzUN*Gn5;0X~YRRcIN>^jQB~~$av9>#*BBsrXmi*S#StQME`f=3Ib&d z(?GD0Bj?-W6`*jCG#S>fn}YEAXS3B#vKWZPA(VKd2h^u_yZdnU7Q2u2-GvX$1m@}= zk6c3|-Z!5y*BJ6SGF|hZXl3FGzV#*<11A!;0+~S$7l5Xi2Ae63YYi$PlX8{C zf;9IMm-c4Y3Gp=i?Mo)(s0J6a0xFK~_v=44BX=PKMADbazAYX*=d0Bwa|tL-4AcBa z`T4Ws?X5#Z4^o?+qFyDW#fd+qiO;^LK(pqBHH94#Rfqxw2qn;ENy2USNx%}a9z(|qUSnd z`1LcpOOD)ZtgmO`)cdBuEc0SVfv;Fb$HnRP&Qe7uP0{9M{K!9b_9ucWN-ZF6Iy-I^ zL-54YiH{boT!84BL7>)g<1+?GVUeN?$?ztcy%kjiO~2knoMVX%eKNUjzYEKsiUsWB z+7ILwIO0VMvi9mc-x{cS(QRRG z(t|scowoF7EsS1<$4WJFYkd@$fCsft!8 z7;L1>uSiHRFmlD7$MerH=q5z0sqfzSn^O*7jSJ$XB_R42e>5LBl7e=Cs5jH!{7%iB zW;1?FWReZS@i1IiEBB%>=s9xtF%|fU4&^`()QqxHk@G~VMA>n-JfhyhT=*vR29qge zJrSKTWjQ7X&K6kz_ny>VuT_BbKp`{M#$L}I*TJ~NBJ#MJN~J;;mERO)hne%_>njr1 z^IOz0>1E73&6wBe(NMr;>^>uAkFsH971>W4P9A>kFzlNV=ybOQ(rp2cbp#Fn5;gY6- z<_$ktS}~DGUrfyV2VISsp-!jqvZ|`JogGctSGGeJyS*j&zrlso@l1GA=~1 zQuyZ;U6A+kSPB14_t&rIA3D_k__pYa+H!Kd!Ucn96pOqvH6%xg4N^#x@Yk+n<%45? z+R)*d&*lr#E2wm^wHJM`Z5Ypc{@vFa7w-r472&le&*#{VUBA39s_;opK|g;dUW7!B zg2cgGp3^>M!3q^#vd(j3!(eeXs8%66fHVC1P!mAo75N`TLO!T8rCEZ|zCFwxIn z=MRBBOW$4X=C#kH9vX-uSWFoYO0l_7M3vx~)PeTkZaVKAv{q8!WinvF%e+4UlV(qt zAC~2!o0g=@LDrarjP@^jK%CRKUXNO#RHky1a2xSRP%Tq9t6n|07h5&gbMYB^F)do9csF5Knw{6m z!;@Sbh8BDx09o|508`MoDRQ@%uTaQupv})U1Ey~bv+Xa8U%@XGZeFc@)#2h9?pI?R zM{s{^#F(aIo{wH-1~3NP{B}xvs|PvPfG{_)fHAO8KirUW*HTQ6WmG5m6OXO8PIBB~ zu6O0BDkC%fLHlaD2RBX)L0MjUtP?M6!HOi_l=O1D4lt89d1sfzG3R=bU5j+IIJqPa zw^yIEe@Gaaa;scg%=;Z4k7XB;2^r~=NpqHBN<=$z-A}+KC)7JfM#Zo_&gMkh5XE$P z57vKp4Tx|F2lfFf)(P+Dy`+yzP44C6L13Mfc1Ozi9iOG)PW>5PwGX31*l)T-H4mejwVVfT(zBn@;6XoJb3>>@(l2j@BB{>BcVbo z%kwg$N`nW~1Ad#4d}?IruKMs+4_;;TtZ5ud+M>L7F?P^^Lqr77nK?0`d{p&!Hs|HM zqwF_I4Y)<>eHqGkh?ump;L_txrbJj8JQ{>7G0FnKZ9H}>=?e?#=I@B{N z2&p9(4+CHDHtN%Bk+kovDNKat{Ok+AIv~BL3NUn(GdVKxqBIoFci|R}eS+THlqd-b zX?QZTI0UPlXJ!(^uvr7U&hd&JyO%(u;_sx85I4dvU`==9g(GbMoK~r9b-2?|^`nuL zN<_U}_m4juq_r#bAYy%aU`Nwzc;Qt(??R&MNMiwYl18^!IW6NGnmt^8dzbSb5EwMb z-=co%E|mBt0XN@4{GthuA3w`idZ!@8>TN?sSW-5#C+O5?RaP(qHwz?v>vG=LH{83} z6`SUB-UUn3hr+^m$^q<=`0HXDYzSqzrH+Uaa`fgfyNpa_eJchEa)nH6h!zJFDp`-W2Xb|R6rGeZCq%VFG3SkDc5~Y@#oyD`Iv6|bvc;7E> zA*vOcFn1GrZifR!XvRLj>$V%EE=%`p?`T+pVIqEp^uqPYUl3Rbr!*b}ON&U<0|N4C z9Y3AHadqQALxC{6N@VOn%;sWCnNt^x;AnHpii6hfBXKHxz({hm9T{rf(;~#YkZ|Q$ zY=T0oe;gA?@;!I#fPo!>XhkmXmXlTUr|DXgQ zqk!za>#-yZcYUYM$9Dp2*({y7&EP)7(KcR*4ot@_)C@>>*qXNQ-WvMcxyD82O6@a{ni_qR5UQr(XubT%mvfH=dK{mTOYM zKiz2y#h)fN>^QuU9=~FG$43B)ZpwXt#nNZ|e!81REACJqmsv@LGc$7zoEHxHOov41 zfbB*bqF)BU)}NIK%Sd#QO0UguXparZ)8G}gk-}DZ5cFXb(nhR)L<7$ z+%L@1mpZyKHQG4``o_gcE6$_9jt1ymbDKMC4`OP`(*^}B3`X|qKc7#L_2oqQh^!?& z%q;V2epw~nUEn|O_Zn!5iR}q351{;$N~q(cWuyBoiWku)vMrzIAS2?Dp1MfGU)gw? z%yBaDRvNQjSf?|afH27)mevdh5yn=C%h-kWngxKGYO?k?`v7;VO|KD(d=sKt+A$im zsHB;UOl%Ad>OiXIHg%(51q?WGsltU>^kk140gnPZ!@Fda-=Q%X3}BJQvs-?024DXD0hPgAfyC-c>RX`hbWnIEa)I_FsM;|Ol5{7 zCHW6yV#+LQKv7GWdc~UeWmO@hFSbpQ)U<#_UdO7w{T5I^5*48oVKA)?0)$Ahx0d*% z3mn%s<2W#K>Z!Sqn<9)a;*v7_u2Tk>@#3b3xI8 z%EhL{!+yH&YCh!Qdf@JTJ`UUw3V?iSGiVMLCcjdq8dOL`cR2lhsA|>$?k8Wo#$ICo z=QK4`6}Z9+wAc*`rAK_#$XATC>usgv1OxDl)&^j;&p zjtrC#NXJy|(84w#;KH)f%15WbTXW=@90%oY2M)Uei@=*tc-KiB-QSaznQBhl8!InF zD%$&`I2FMiNBouPX~ktZoVjhC5+Cvib};kero==w6n*)?U!e+d-qT(+MER2f8&mjp z2#HDrGpoBoU@m)wptW5gH%lwiA3WZ=BnA% zsURPS<`5%42+J$tn^+?zMa?(uJw86-Ia$afb7*-UOBs6OF2C)7XK(Rp92-Ff(=0{r zmrB=CiN(|6X5kFX=?A_O#>$&<74%+yLxLkz7PX+k=cL}d!K+MJ`pPjFEX^ZuEQ|&V ziQ9HrT|^_AWV^<%R4-S!Rm}U?Xv|=$5J$6jHRk)e-=yf5-ZO2#?W^lhsuV-lw~Tn6 zskVLzSC4KTYm_1FiK)!&djhl8Mx!_@++uW{?OpWJXo^tn%$<|=k)&Z}@k6@G*pIMT zSeIJ4l0tUjz%k1SQl&Z8X8gz5v}@kLe@jJ;&!plmJ4_iZny_h-4Xe=~*cz~~Pme~) ze$%D0M>5%hb#hjKpXcq2E~b5HSFBdKRS`iG9yU#TXI=oMQTilj?Hj+WC=7}>P_ehe zeg$T4^6BR4;q6&VD!<1Bnt&gMPI7eINg>yz87Zze*rJ~kRu(m+cG+$lAm^K3qJJR( z1mc7_`5j*+d(EUw=9Gb_RGjjfMLab{97On(3{1cJt_Vd+M)`XhGin9(N&ReH*ot&E zzQl{^xKQcf$|MWSid)d%)QXkTPkx>4|GCNFBz+zaOx!D!wLO~cp4?)WbiT1cwb9)s z66;ozcd(0bkt2Q&z4j

=B^)xu9M8_woJUrMyG+Q=l_qo?}IW2>dI+}C@2|aCtrY@IET-Xy< z-(1>Sm;pJMGKU?Dbb8;e!DY9nZOjae_@L#h_Mv^QROxA@g}Uve z8&W<>gTHjX)r?fNGw0{$^8-;`Hm4R}7(xE1k2L%&`sFhCPr=H3Q?V1-LQ9RqGSK=? zT^_gXw^06QGg%@DbmyEsK4L%j_jDxxmdPU(SCrb5+K{-#oe1o03<6+G#HBGa{}wVQ zXIC0Fd{VcA@2Fl3ZywH^TGkw|I+?DNZQG`Wfm1PtO-XpBGEj?mL2>p>PBA{|sD>06 zwrc}J<;bQ==i-ZXCq5nF!iI}TQMU63*!^!+pTyP~&Z)a#xoM=qsYP9lu+g~4Z(eZ= zvkIkv&m`~QzMfUUzZ9;ULro?B2}?iSubCH`YVhYIp4b_UW#K~n!9Q4>fR;fK|30u= zmaMHeW6QGk=tr@%&~_gt>kzFZ4J`54e4XOQjO_??Qlly4^W)4x`;GX5+U(uY)6^FT zIR3LRVk9xk;qC|50MSPg;NP7%TQjbow+<~Ge>)_- zNF&`CMDUul=kW`%_McCoL07qweWgK@hManb5klid<+le`vxh?KV9oVu{89z3Nh%Ai zDiZXDrI#-9L*+@*h0Q5`wuz@Qr-V{>yFdCQUpbfuYMEwGs8AhFphq9K{^-wmbnxJQ zN4<&9tl{J$iFfFIc?52~&7fdC`v!V6f`Q8}XV}QXDSZYxC0|Bsyxx6czU#UER$DGS z$P5h>OXklp9Mvr2%D~Z#9I*(`EQ*THDR|meK(V=(7dSsg3G~jh)z7nD!c`0Yy+9np zIydXJ_KY9=z<4@f4RDJvgZ|ACn^R(LWE5bqL~sE?Fpq>w)lXm-mDuwSwh1>5dcJxa z1m9WUw>fAT2h|7c;;a>dGLOLyShz+6-&M$1a$A~zb^fp-`SzIa9Qa#J2y>4cd41nH z%eLp`-+6Op>Lt?3G6$;O13>uP{CQOkc%u-3V!K=I#b*e=^pl$reR%i3)raT+0y`w& zg;LM)Fl-}!22qM@%pxV9JqR6kwEtx@j!O7Wv#KVdIh<6A1SV;(n@~`{x;9sbkqDT| z^+7hYfk{8MtkNgy-}Bj8htH|$zd6&CW5rrHYZ|O=zMTp1Cwk{hf2(fRiC9sMu&+E9 z*`eE%o{<#vp!~LRPKdkLG{IfdpbSok8oiDBNBn`VZpzI00?2*1GIy6I2$T4r%F}=L z%sF`1CE80*M2>`QV~Y^|18Q+7NQ$P4Wt=K2YGO3o=3BIvQNE(lRn& z1c`NPR$}Dz{CZYf#SvMkS|=Cg2;oB?e+m(EQd5Nb=5=Kgc!g*iXihCP`G#hgi@ELQ zx$7ALS2coe1yS+zj}_20eUqot&;l2YrIsQp7X3U8xa|BW`CI3T?T8CNIyt%XB171% znu+4YUgJ&FuPYs>p&=abmVAlR80h-B7YIzw{L0{q9SvU+F3Jv&MP-lzZ2AP{R%K2+ zW7fRjA@v1s(#ggK&QF3{BP%6zlU zrDbGp+JOsKDXI)lPm}iM{@|rbozzV>5H1ksbUE4gV&pn6-)GA9^cd9J3;;`x=4HM3 z<>smS40CYW+2cchap@R!Kb6?4`>cVrPos;C>s#!b(^YnF5l15Heqtc*Q@K&vhlS2+ zJ1v*pFheDU?McpG#n`WX%|zt75p0SI-MYWzV$SF_3KhHS&UEAyMP@hYba%+Nayqh~ z7`8C9;S7@Oe0d-{+(9v*8-HB+J?HH2@87sWfgOdlPm7#VKjjD62>C>`lN)!>oQhka+-Tcf+)k`4=u!PY<-O zD(4;d`eXLRDX(4J}B)lUJ>D3<9XNU!NM~hQx>Het~hJH zBT77gyj#(IpL2-Vp%=<;ZzMR@fid7)$S3rm{Hy7G*AVQZPHhY|@}`T6i*s!Mmby$m z)o_SzeyeIHf@ulQf5;8JW*65ZSM&hEfgw!v?tklz3JuW3q{80glvYrablOYFr;++X z-ONKWoy#J^BAO-E6Uh7m`#Fi_?=(c(>m`jlabM?K@#(@07OUgd75?0PJ%V{hc}>_` zI!^J9|2M4d>=UdoZC#j)~PW6WV>AF5|5cD4E`DI>YW z_-Z2x!{o#zfv=&es=AhpP15GW18EW_m4DpA)7t{~@L?1V-+3R}C_53^R;vuZBbB~; zE`k_L_+YTb?}>wf*%n#(ZucopvNN?W9^qi@opqMcEy%X&2^S(TJ(8~7)2r2N2yJEMs0RbOX z&P&_3HW6d8jG`JLBynVes~IsVAADS3UbDCNa+$odA)ir!1iBUPbxyf@`Wf*PG1hPeeed{+1dxZOdpj(elorh@t znWZRv6qQwm!33NG0rT#Nk?G#)PWA{rjqFY>A$zc#U@1fMb^FMqB>i-qM>0&yAwHe$ zEA(vA3eJ9yFx{MuUnTbQncvxa<~;~i7Q?zphf6Z{DvS6H{hm4wJMDiEsz9a?W%%oj z(8zc5$fA_7LJ~@PB=b|t4+8|?*l-dEv`E5#U%GD^?V2T}m&$m^*Pvj3%}QlI;$J^< zoFVXp+$j;Gl%)~@n41dk=qY4VHk|o$jPnn26sfMtQe^UBY2ltSgy+1}`j=j*mPNOu z-xovJM=WKQ-#G8aP58HAHZgtU6)H%rG0ZBmTi34AR$K@?s|Sqqbc2~PeNl-ZYe4J5 zhl`PKiIkk<0wInF*QQ1kiFuL*fE4vk`jV!!`I-2Z$Uv;>2rWi1O0 zukEA=xM*w4UB|!q@rv%pSM%O{WU>f0jK5D*GBzz`PZ72U6md5Ao=YH)?GhO6GqPZl9%+Y z)CL(@R_M2omOa!j-$gYI6c z;IZ;wTF}sbM3hfQlb_loZab-D|55|udmX)W%0^$QgYOH4O&d6sx#fDVumyeA;{23O z-fU8%lQVAPly6&$<>28wUz;mT7x>v){9b6>MFfg+Z+uLldKww1;%1buz&z`o%)gHG z*9LQDUAmLJ{8Ao(K|`QSIxW7Gsn^j|!O36V6H*AP{i|n2)Y1<<7vLf@mc(_ijtJNZ z_?xoIh@}>?gzEP9J8tR6tK(46oUON11M+)R2`%`m6>DQ?#!dbC!#{gHrvxYeCIEmi zV0M-h8Kl0$yVWOZZp6edAmBt_S?kZ~BYG?a8D-l;Zqtm`W6i|pMkXi74OpKJZS&sv z14G?@rBwA1of^avZvtT^(DbJVpX2J&S5BnB_ieHd(84e6^#GSuSJt=p_E4BqObT}l z)``?c+m2gCSNr4Q+;@+cnj5lxDkKNiqZvzZnQ8>ME=Ws{e=-#~!_6kau4)7B0`&5Ggsyz8>Z&TtZ^5QHtz5 zo`9|5gEzOE>Q_JIG-1u_K&l|m(_r76)>ScQuywhkJhmDPkufWmlpP^@I>c`zok=!{ z%QWkU_E!L=hb2{TqHJ#?+s`oev)942cZQ(Qx>}*w~WsV?+lsx<|Bt{q5T~(~)6b>@wgP%KZ1sT-97fsMA7f zs@dC)e$oS%kQBHK?1?g4yHCjoBWL zx9O?PL;w*Ao?9pNlaA(4omhU8`Tk#;y6w$!t7(698^1YIu!YUmdpB+fANy5S&CzVK zh@Tq>=C#PBi-i?EM*m6lPB{iEsyL!1-I1WRtORsTT%JF`=kT-=5|Uj zAaC#WWzoL+9yfe_bF2-yl8Kh1LV=-HD{yT7_`1~s*$Jn>6|#jGH%ZU{a{RsL_I_50gcLWBpbSH zjL-_c#rdhFv--usS3iFpl7v!Y$3afiK)bJs&{TR!&7xxc4S=xfP7h{)7}qoUYQhR% zLyp}JAKAPL1+epeF+=pz>SJE$YP#t4t41i|2NU^v?P^J5Cap9Uj8BqMs&ivs*PIwE z?=va-5Uga>`0|X6*6canwWi&3}=*& zlgUL_)Q+nj5iX9zkAD-*Y!Y*Yq&zGU54tA8}2VZga=9^nbSj@CDOJeH(b|d9l>xx4%@Q4A;tt?tL zw)wSobUYqkDHz)KS(%J7&o#bW(-QbABqbTHVRvzh_kKv18l5_2G8L#in!r@%N@ck5hO82VxhqIp zPgW6nVuz@d%>7N-pmHQY=L>KGtI}>+kPtk81?ZfH|BO2cwGKRDGMfm7zvH_EGk1I8 zA@^ME^vs<|?2wOPD+Ne>AuT4Tj4C|gt-TJ^f+4zDQVOf5N zm|1-h+jN=t4fEFDNKykof4NL_vW-NZ@Jr!cq)PSIv?a2VH5QJasdg>!(>QtGnQT8!tpJsV{d_uHAdF7dyjA&9Jn* zlYUN7lE(%inH5+G`(g66RMOY|43 zVQH?4DZ-;N78E(Yd}RT(W)7vp#fw~2eXoA*R_K^p(>hWl-k#K6D|Lq(&vZ$_r$_li zUfny=gNKw*T}u^SslT*bU34)_l&3H>HQ9g6JL@pXsvlQ`+9pz+orS%h$#P0H?Fq;l za&sx;@bYg^gwlhV_n+PGcS8u@xap$zX;&CwAydl9E@AY zrQe4CPos)Qe00M!@nphmpja$-E1s{J^{NH#Sg9*eb8awSqGRTNKwvz5Z>1`dcKLo_ z8zoa7<>pFu>44x*&d!OHX&L6;B7c)<+=U(^v z+`(Te)J46z`e%&)5+LYyJ)&77uwK?q4H5JVhvipBkmOU|f+?wphMpgG4#wTv?@f0{ ztI25I^J;s2JZHRLFv~;j%f<*h-!d?qY^TwYDZHdgt9W#NI;(-Obn1&+eh`RJNc?=; zEu)y(=LZ`hp-F0^JpGPvr}7;p1wugGbjsC0vlF%TSqACBvJh$o>W!=2bG{PRd=MZc z+jn2Ked z;@N91I^jU0SyIbU#}Y=tUUh=exne!=Hy>{tRam!J(t!xxW((pCo2J=sY*aiGI?nrB z2)u5*O?e0!$p)1wm_bPVzO=C5>pN3N>N6Ca*WNJ4mS%nKF+;FYtlg~WJcrp%g3NJf z#9hdH8G;C8DP@HWHA|j@`&Rwxv!G+H4b4TFI6+bHKQ5#Jcq})DvJ82fi z;85H*6zBH$6Sz!o9T+t({3Ps(`OFi$dS~nyn;eF|7O5@0#*Mk`B7d)YhheK68oG!Z z4r`j41*3C2a3W=)aW+d~4j62gN<8NLhz4<8`ZXZJ>|dW{M^%vK_(`wNoc`v=PTl@0b&FV{_T0~d8^kKTs;F`X&?;8Aa)Z`B_WP&? z2eZZ*O?oRq9y89;7pC$l+s(ul*~zQN?v;lt(yT@ws3^IbtpAGSce#uD5{MR~ym#Lz zrdxQxGR}S}6nWjbWYsa9$%Qw$ap(s;b}DSoDCu!5dcp=}AXd zxns!khOE?u6t(N61S{u@aw;kGsa9n7enJ&19)o?mkZ`-B5Ff1Cqu8?CEHSM7D*Q1Q zzn(X#%eGR|PETOmJ)Ee~m#@P2)vZEV)N;-VAhzEDtWQAxc)6#iX9uLR$26Ou#I_{- z{X8d>4#3!3QY~1qW#nyO)CadvuhWRk8GW0_H4ox&>FZ4YBQ1$pogZE+ispZ;z$8!d z*}jopApnS9x?G-+4H!kt-4D9XiS~YP>p+rp`nzZ*7K`FrQhrdCDpiyPKNCsOu&g+H z3JnfHF(S0>SdwNQkB6p6OVV9h>h%rjBrA$4LB9Yl;cuWEU6*}ODMMp!QcNBEnjR-! z6@CqJJzq^HT9;rB6Y@P&mkEv6^-KdZuA08T9G9w)G!&i2Ch%nq{$N)6?2J~VKK#I0MY30KT9V}^b&BY|-OdAMWWbBK(EW8v8j0tJ^TENR zr(U6Q3X6(`vVK#Fa%veOEWwjfqNW{D61m`#AD>+3D z8KluI0fay7nu2lLWP~F%*L=o^BnDKNo3bJT3Bv#1wgBea?|u>!=osVmAW-;e73vT` zDn|N=eD{d)t&RS@`eBDADME@9b=#{+A{NQUow(lZbMwlZ+~0~AAiUzLG>G7@N6WQX zMwqqjvb^(=%~(o;0KZc*T5c&6>e>}LIJj1Vt4=oOReIdcwpFA4qX;?e>mRp)!;EebVD}A4AWkBq zY-5@JBvS9^2CSOZiG;V8J$aS8|NH9u607_eV^rB|PT3|6-9I3l=I`#zmv^(5pMz&<**4Sorf zv;UY8RyD*{-C3;v>nvXMt!D*PletVoxZk<~RbQ78+(3}a(2rEE)5oq7+usa&Pn`(i z6gnvSXJJ%^gZ*5OR>jG)JWhv^&n30*Qi2;J(f~PMj-ONk zY&)UVy%Kk@{y?!jp%ap%*7?!hKppN}hmDAx6Mvm+E%bM7vs!%m4-Wdnn{Pjomcc>g z(97Lx6Fj}h-B2>>&hR3auMnmUw?3D$k^)31DkJgX9hQ7T5{gv?Osy037gf`&;V8y5 zS6;VP<&e;gkAcRuy=@9ytyNkYgoW#@keErR&b;G;pKDQAvmf0(S74ldkfuQOB}YJ? zB{s!nKP)#K_KB{^@B76L{1Ca|q>bx?0wcI@m~w5*~=y&c>v9Dk|PcOqPoSsBhB1kX=SU57n(hDnke)3{%|bYhX|GH$tLWPNg+gHH;*%i4Bont3-2R+pXJTn6W3M4&{kn=5#+5TfkGpp zunnpjR+2N8!dG(K1iv}LT%v4#&Lg&*o%c}; zm%ZEW_s-ok5=u#3^W=g9!I&(RP*zpZK2aW1D-$lpps3EoMkSOH&5^!yCwT$rb)qxW$nm%7{00F#FoY=7mYDo%OYXYlTmr49VFV zO}?T7niEV`b2Sp6IW4}ljdcF%{1-g2a32#*JF5J(6(#kM?%$nd;1gc;;)0vUcuUW; zV-pS*4~f3sCrl-8i(p96moQ)c6ijGwPm68A`?o3$ouhg9f6lEM{Ox}_C--->lu$d# zz8Bwsj3y5xI`C&{JnIshq@y;ZG-|f-thaReD=k4kKOy76S*#{jsPoyCryMsfbk0=FhWnO*g6&1ael80g8aT}_M^t$#%wH(@o0cNcYJ_;-Q&us<3eEUr>VUL_NN7b2kqU%y<=vzO{P?(2 zH4H0`iA_z^d?`t4@D_l7`H@;Nl*^uIeU!_9X9Ai|lXcP56X5!q=e3i8eB-IfcCz054 zZpI63!eXj%R)9@AA{8=jXT9v}&{C4)>(`%0P+a?s?3VXi$4UVv2{>W7rS_$dvNBpo zvsmYF{{CX@YuvOb7GWcIduh4u@`5#KcQiVY29!f!=L_ zKC1T#VCa#1d)#)JIH`zRrFzx48F1aUOAozLPM}BDb7h}~%)Qmdh+N&1e*sP5CMX#} z?s{R(IU6aFZ|ga-YfxAnV{NRWkvmjD21PRwJlNW!@qZ}LP9*Q2O)rY3zqvW3za!;j zMtsDCt^3?v*v{fSIxG(7+$uA%A>;$1usAX`ZWRwwe(0v_)x|Kg~BL;gXG`wy0qi!@4@G2t4@(U#%d z5av8*@v1&yVX|`CoU5xn!+;iwwn)Cvauv!h2T`W;T^C7Vy{oJj8#QyY`Jc=0$GSVviSSyexcnBR|Zb71{O#Mo3 zNh$#3J{&~mZK1us$<}uGxNHPC0NWL7C6^wRE)?{+H@`)vq>vchrv>X1RA6=qBl$a< zZ(kUqus?-FO@<9b8DtPXugFYqy!P)YeQ5mfyHd;F>`Oy4>n^gY=n&c~e%pGf_{e?K zt;@qCMwG;pc2-e5+LLy6l;ZUiCcOWy(%x}7c;kudnLvxJ3X@d%z zP67xcOKp{lSKe98%F=2>@27F!yYg~=XCd!cG+GbJ!+pfS42CJD3gkh(NP-#{;ny*5 zN=x>{mxN+F(s@apqLCTPjct_yKyp1JRRVPc3$iW>LOm0AGMTmk-gI}MK6h!f&Kdc8 zn|7U@?Yb>~%a9~+7dUNk#xdnIUT3$+={FSSLLMq^ve;x=I5%%jnyHYGq!$QCn(ne- zMV`|jk%N)U#UzhKgsd;*)ECR~t8^ zhCO$`N9KWI9$x3F08@%@i_UK*Wzn}io0W+Rj+R9#--6LXdSFjjl?iEzD#bn!{aL!2 z`SVi)=90S8`mJC>EtTJat-GZp)|V_O+nuKEYfZ^;h}`}*pYk;m_Sbp+su*8#fI*bO z!~8C-{!cUx43{KTUY$PxgBZ-fp1PXGB9?MEn&eYyrJ8OyP7xzfUY)4PytTD!b$;Ic zY~3v`2en&@ckw%A@w=@w$%iPwfEtgn91HT};i6PxEtB-`^o zNEaR=6Ofbay^1l-G?qxD+rla898{wg5xzz|y1=bIjJChGv$lF3_%a-mbBz97sS*aF z%rX*SJNBHR6s9>o6{Kxq76&QJ5=krk??B&5UngK`nF69tv^_x}YWB9=FY?Iq>B&<` z$m=|!0DDo3M34D%XBkiaEtIZ_XqS|5 z+UIAe!@67?oii7EKY`9+8B(kk(QW<5J=nwFn4bw(9%ko#rX5Uv_bU!+(^Z8;W5ifq%I!J1y%eD{{nGHHYS55)x|w& z#!mstGc;B8Bm2A5A1c?komdx8y!OwEGs`IHWAmNDs0PLJL0=!*^lBsOWRSq16WoYB zKsvB|@gv3j%X%XOCbMz<{+t3|@AovN_x4ZK-zdtOCVgWfZ?4~(?Vcy`9Gd=o8u#n& z_Vx5!^?iECm_SRo2p4J+u=tq2y2gE}5A32(t*`%qc$#vdH#{}D3IFu{gt8quPCe$- z?Rek*O_J86D_N?LuE}Ny+uNMV@XudKK&Nc8;!_(be;r z#`O%ZL}1Z@8tU0rn};XG`&~jpr{mj=S1xUeo89M%B%cEp<1GRx`wwhK(prSp=o5sl zAm`Vs0;NAgB-~)`JxWAoW?Wv30=4NO_#vsXshW2CrM8Dqy@}J8pV8yY+I{U~cC~%E z;;sZK?>@zlM0UiJH{Nom9KNfe)nZeoynlV$(wSV37*7#i#s`GC&$ZIT7;xDZ6W(DT zOQRyc73HF({e(1CCwN*LjfvX^o=ozR*JL5jkoMe~D_=!puCT>4-MIeS8-~;4WG4*F zZ*E`yBu(h&7D(l8uF%kTgQLsKf&*@%Q!x6XD6;(DOGEE3eJ%}P(LC*!$2o)uS%|2p#_MD$)U-`t=`5^@P|m=Muw%fw9#$P-{sB zKr=YIOQbp5xSy{L?SAs;rirGhKMOA7$15Ii1~m+Lkq`FVki%`*&T)~{2{WBP3BVm2 zwBrS*gl`L=%JgALVuVoxj$6%lUQZpKRIIx^(pR@-zS)vw9E@rjq0>Hvogm~q=hfpY zZ_vS%R38=WoJ`Jcp^k8gD>uC6;CF{Qu2L|q!bp-nKEIGb7WWMhH8N277H(mbdh z#1JB#-I?{y+{Z4GC;g;-{m8uq=mb%TKrOJ3$JPX`#_*vPCc;o7N5_k&&YQ)dl5GC} zlp$$2sJK$`4&met59#7D`D6u6{T+rC=eOMdopcHLC3>h=YK9OBug2jS^(0Au5n7I$ zeP(Ue&;Ez?Zvc4m(+kxpbHsWde;a&?tp42*ZCzUhGa?<+ZGz?qSU7br2g`pcB5JYFy(mO_`6>rd;;%8ad^OfBTt{Om@!B;meBRC7c9dX&=wH*rTI z1LWLfb&`HD(n{%Rwp0p-06P#1qwE1xHv9!6z z6gwbVphUr=0fQB$l-hQ6X{11>&j1F=XFvnhGdEqrGhHFdtfDxye2XJI9K)+12yI|;Y9a)7 z1q(7h!|Fc zzjlwLd zu-&JB8{#z)TfS(@*o&KWYyon0lb8~dwGs;%af;eBL zK}_r>I$bU#u110(#pTMz)m%nD%M3g@-Oi4$v^mZ4NkL1 z1Vh1$PhATL6S~->`B*Gw8g`#)}K|yK<6OWhze>JXx+A*PmDC8chl=xMF6#x;G96 zV}&C0vpDe}u=fNnm&Bq+N;m7T#EnD;@cz33&$N(gxdR5pf!u1w32yegu}{==7?m2x zI3i2`r&$jGaikUNK=KqUyz{gQP);Lt4LxPS=EI$+&hf=~#c8suT=rcYp?9w*w6mPy}s%)m+3^Lz^Gbi*x5J~2lcfW>fU*A@>k?%5tjdzj<*g}BWi~HNKfo5|W zaufAm-beiE`%@n#=4ZdV9{H3OnlI0t0M|!Sru~b8Tcn&@+*Fnn<)McpX$zjeHvY4x zNjRAIMYkY5+Q7Lvx3f8r-4hijsqbRQh7h5efvAsfx_f&L`QUImBH-;K=TR16fn;k1 zEeHoJQBgq4IZoW8zf?1n_A+7@gaXp^xLT^)@ zqk&Op36x!PmEqmZX*t4v*K7!T1=)eVB#XnZF98wHmWv+p$%rjoywFH9XI-d*jl{>l ziSiH|L@9Tv44KRyC`<+y6t7_F!ddcTj1QYW=p5{sF_)?c*39le4KlHT0%Ws<&Q}Pw zU@s@Y7XV#Jn*bQ`9~WW;nc?UsB@*svzJ{)lab{a{8=9n>KhxTiAkYpV<=VvS>+h>y z?j)*eN=S5c9}uC6w@_Np!s4XCQlmBJF{uo|8M%ws4lO?d)T*1=yeENpcwrtmb$-tSR($@9b_DvhNi{Dy?rf+EbXFQNd;2D1htf?)}a>}3`xms(_;klJ6w4$ z!*TJO3YCiJp-|mxBMD57>0zYu^V3arE{ZNRCMaMHVlx5xIOC)LHCC=9-TJ50DsT6HBWm>wm1az6?^m`1XRFQ)}?%_k23n z8PAl-Le`s4B%%ophsc*K217)!-91a2DRM8Ka3Ei}y0RIrr_F=ZhF2 zfxdKR*9vJTlVbgYKDQypO!cm>tn9aWr2`PAUbm(yQtakr-%mTDa$ufW@)z`hw$OU! z+kFBKO&+X8FbBS!zl#nCu16 zEA{PFGXsZAQJm*;zGwP$opjp0_R*MtYzVT>D3!TT{<{a6;ecqIKh;spYCl~PL zru)3-wCAShGeDs{+P-pLJ-HV6RN6!m6Y@i3Dki~&J%>hpB-R;_uS+5Zs`)ko=hl9E z0|(_S^ee1$fAK);@5$G)o3$+c0lNQ=qF9l1&oz@gpiO#m82Xah#klyh>lcJW*ncB! zo`~k#UXHN7R`KXeaTx`~Z#)v&ku z24tl=h7 zEJ`%ecRpUYuqC{2ij5y@K)dlxb>24x+(8Mx?WRxwE}lcw#|mzTx>MD!A*!0 zXcW)r&BH<;apYcF`YqZ8S@h}yoCP;#Y}K`T?FCb_&=NfPj?1+;eaNaJG6-9V;ypLe zTORxPr)}PDer;pzx_xab~ZKh*5DWkY9tocONAs^IWP8b_BG%8fxGhQJ(BR5Uhk!v z`~k|mX6}~TImC2+KWFR)CxFcZ++=P5q5o23%L2oWvBV6wX%tY+nG7cozl&*$Ffa*fZ@0hu(J zez#6kZZFjzDmR|4*au!AoxB0xGX-2`8>8w$G9={pas~S6;$M9{-{{*Z_WHv&(fgKg z(@xiH1E=l4`;DeG8ElSHkqy&gIA*x9b3u(!1fzCc(DMs=y~0Nh325Z?+DPOr%f5Z z$4w1uR9(jsqPEUfcSf(B)C`;65B;lBY=2w~!k{Ky46kSBXbcJ2%~!Dp8Xsle8*X~J zGXcK!$>=8J?P(SG4K9Ci#y6z(6eu^;#+-tdTI!W4XIn09BQihlWXhOBlUaWfOjvdl zhSd7?^NL=lzh|ZS^EbVCMD1N4GlB``s{{tE(1~u4UhEz>#9rBA?x?YZ&w6Y>a zGx#2eCLtY=v%V4U&&by1Z%K`w(9>6ZMNJu2a^F78HaWzOhQF5o)24zC-M_kOrjVbH zN+j(+59{$<5gHiaQTXGRS+*2Zjgw@|crN0VnXwc|K+|Ern``maw>q6(GE1jkAz>ac zD3r5>=zVnw)FdTIS}5atETw|7hE|dO3>+PfTLM{@_94KzNG4?8CN{VSS15-Sp!W2m z{#$Gq@;_a)_@i^9>_|tvtLGjZ7dn>sXyn0M+`)bPeOX8y&MJ2r#s~$+1VkgcwWp?! zxK*fYDfC%#i#n`T%2PQtxGSOkCjG?fuQY}^*wS6?(?K&AWBfZ+a9NU0p ztss@#KkqJfm#hx$qirSG1WTvIYLLfJO}J-lM3R$d(lK-%*!eDvd&$`C)frd!^C@4* zu$~jBYapH7Ru@(<9@|PGiK`!c%UkLa`o#eD)*k9ywBy>Ij_&bW!>OYVo!{-nkyr)V zW9#S}44^(KN($ZytVeL@j>3q+zGP0N7jRL&Ozt>8xLp7$SMz#Zf9O%Q1>F5JdjwWg zPXYG7;zRVyQ@Iq(-NIJ`TBd*|aT z1l3UUV{Y6Us){k8Cv~QX@(jt`I2mowVHO=?&rw!DvwkMtZm$_|IKs~dxTHLF1}CPr zDKAw^5qbBK&Gjs2}(XeW8E zS8$R~78zVi2t5!c;*JD--OeFge#TuY$c1xo`)P&*cy^{HIu}P2ZOWi(v!v$xlX%@y z_)CD1bXNrVec$D7;d%V8p6G1p>FI-%@DIpFPBsdtg>YaaBHKJu8x#N}1#og^L?+Wa z^N|Z%3{AFC%4Cc{`dIRiuOd=q7|P;jg|5b@(10EoVC24_YUa^X#&3aP5u0!cPq7(wUh|NqiS}5E zt#uAHeb2JB!e+9GHz%hi3+l;G=OK}D%fKV91LFcrr}%4!lm^Nz@51gx1%BxW2ck-smMM^q#?JSa>4txKGDejIs7 zGXT6Iz;m>}-l|L^`JD;@3`GOT<2}=58g2Q& zDxINU9cW!4C+UNBVdQbQhNAq4XwF>vO|It#-|U)*E9Y$}EcD#Wgq5{-qESi;Y3BL{ zDtz=rWd@Qj4~7eL3X5vru28}vdkpn1#mb>QB?tFemtJlbUH37Nahh`!d8K6YY^=M~ z>3|cn$cdiSpJ;J_Xp@G*j?>X5D>$*TeO6}5#bN2!8^~p&&a&l#uFe?wNcCSMOI2=+ zsV50Sh&fPfnpwhyChc6XhLD}CukK7&cx->|)|kaH$PDe}d?v1W^X?Sk2LI4&1gP*i zGiIHw8}Jf^jM10V86n3=&5S6wEP2JJJ67NMqk=D%N0+}wpEzAiGIPYviDL4+IDJ1= zBg_G=+yOTXp%D8mP9?!32hGR}{xI53c#yl*eoSIuBqp!}ysag+ zGF5Yt-B^V4H!uo{N*xcUUc+ewPTWCzNnmTu!Ff{-+LTUo7tQx?SQY_H*yA{g$!COM zl4j#73z_~AD?dBt@bd7#M_mrqX&yjyRwWZra!c1_-+#S#XAWn;l?|L^-rEAcTs6Hp zr!$Rk`d@HWZp(A#E@IRCY=JS-F70qczv=BeplY{!myfeUOU0A ziyG(=CR6p3z(M>qq4QlQ_qMv#7vsBcS2xy1acIP|D;SVfCx2yQ87JaY*!iyjhw(PX zsncP&04CelD+Sp7<2t5dbwKl@qzb}_Y}g1A0{VCDlxB~~!J(y_D;p7Nh{xMT#+Zx~ z?sVrj>EEmzCPtQTE+|}$9mlpEzicq|NoTiP09mrnIapJ|7fi=IpheMzWvjd1kFANyV z(>f-jB4EP9^z9rfY6&jtNR+;Yo!q*js1&T?{nkMpuS64z$_(5J6o_`3eyn$3O>lAb z39FCTRY=*8BC0F1^4<4!_>mMDnsaOS>MC87<@*o^Pn5AS;zJu2N8IJcp|G5EHmI}g z)04b^KGfFZ)jpyKRxcp>nb##mFLwLf?hizLVM_NR3CTpaRzrvi?FH2d2l)n+>F6v+ z?%kn!{*86oavOVx3WX@RHyqq`G4q4BX(F;2ie(MV;s6a?g_6LWjG~)myB+X{$*Ly4 zaXypjCG@edM(J|yBkaY-gH90;ar|=;KGw4Z>(#pS_m(jLpRtvcN~q7&So88-2LtoE zK-%95Z1>DRxF>*}3QCP_bYX-V7@_`OZRL?&V4uyck;rqLsFN6I05VVS)*0DR5=n3^ znzEFVRUbqv_+wB*%V@PIHasHyAr;f54$rXtR*}u>TTzRaH{I{#zG9{w7R6{#;+$Ql zVA?Ez;u947d3A+-e0&THGZC5-vLi`vyg9sE|Lk<(X()u)3f(;j|9H4n1Xc} z&y|#|d-2*86&qVwl9fi z5xL4#2wU46a+)m!|cEOf(9I^9HN%^?<~=H{y1k>`-Jv)hUBBz9zEK! zk8Mly7X=2H^m^RXne4gBlHoC0x#N7`a@E=H(Lu6N3`k9UH3cBzA0_T4pJtD1{qD*M zm_MFdcdQ^}s=oNA8Z{1q(mxo1i%{4P@IZ|Y9&K-Eq-Bj~ z4zhb<|8U>sZtnvL&L|im4PtF_-TnzznEQDinDnkgHj6~S(L&;Q&}`9dWVK`IbS&ws)HVVkDC`-bHCC$+g83Byj6eGy6rK5XVR_Fh=m&MPmZ<;A%P(dh2G zTeq#*`-m6T&-H8QA8icsL$pEiz1)$-3>jj*G*Qc-T4dyPlu4+}h3; z6N!p$iIdM0K>DwLZ9}|1b;jE?ui5?MIX$bEp|tpvkQ6Vh6>Fn>22!wVw4^&1XI zz%4?H5!u?q;s7Z`#e*(a7An zNsDb-THl}m8q(7&gu;I9HS96OXvh=F!-UAq!6w)|Mw%@0kfANbs`x2HBN0$xebBs(n`EF@Za4z=ZzP!K9xMOZt{3dGHR-Fqxn_e z;r^09ZsaizaI7EP6UitHRXX%~rkB`aO2cyLLv(oFjtI+M7R&mTU_St5^xDS#y}$V3 zpmWtO_9WfE1x3v(iN^~9Va#MyqbS+LyPKQV$5ZF_M=llm1USUZj;p3Eno$v9()2cX z?NzYXBzQw;7KgRmm2Ypn2N^Mz!$kH4Pt+E2?fU|>x3mTSYl_?v^NuTyGQN)XlcMNaC(tr z()JnyWpoP9{DjBD>`7gJAm9M-UNT`5p!^(CJxYBEa^X z()nWlK_#)LRhJzM)e|;_=%KLm?xkvVs7W(sw{ra70wayGAtceq4~Dgsssx08%)eM? z`o&kMb6tP%{SU#+(4}>-f-&m%;~bsF@JO*Rfv9|Y!dC@ov|}_$P8Rj;6P_wlB$|a> zEXhPNLNrlaIHXa7lG%ccMd*7JH?c&UuV9%18n2-hfGby#5P=DjgE#e9<2Sk1^AMVJ z*p@F45IE07N_5wSaW2f+oR|F8Qjv|2T=6r5K!`B{I;$6caOZ|;A{Q}^nd2cg1@5HJ z05khttEmn$rA}zD9w*aZJAyx^iF4)a6hWt*Khvf01P6Eq8g&Dc7o_c25l1^d;#Q}= zZ~Yzen!Ma;=XcJXnyz1s(fUiQWvxZJsC2m7m4G%PKccHBa0?5J>BV(OloJ#kC(@1!a~P zY=0sMsA$n7EhAsG_{whSB^dEDIaB902AQ3F_BM68+5aOOlZ{~Ko3(;bsD`2)K($r8 z-NY;D*>hoE8W8n56QK+G+QHxQY|7D1e5R0L!-3*LSR(rIp;1l(Lx?wy@C&;Zs3^c< zf+)B=A3t=<~F$e+3kU=^b}BN0uXjyaXMZZO*TRG&?a&6LHf zwRB4!#q5G@=Ha`nusz~eNP&ci1D#?mdUW2dUjFbwo;u z<{b|94O~mus?z^T^Of=(!jY4wKAcwkTdU$h&dyg@`4bd3q*HQq3;h{i9OmTZsIr;yPp(~e{(xpZeSn&p0w&Y z$(0+Hsw0TPzoXQ&m*(6jFjyK7;vlH zm_WF0A28x&xQ6^k<#SWacOsP?S+Mcz%AMez8)IEqU$G(xqK-#HHT)*0E+Rgwlt4$u zMwBj2PTy$7IwU#fe$>3Fe=X)mT(&0s`?N44xc|LKZ#1%f)izgb5$GLZc_lf0*R7vs z;{JU>``4w9*B&imZxS`d2R)xUR%l$VL{6RlaR^3}Y#%IgN8t%R znci^9HlHGaA8cYxGA6qf>(%M`aWglFfIKenkFURQ>pqBZ?RMI^B#%L0{OYXXUx9FN zue4Ppw`1(jK7^WUQ?xX!7lP1lv!tEpgJKErF^tRR7nvHqK>b28O+}Z+Ui6T2v_qT> zM~WHsCOGkVxce))&4UZ?dIhYc^&c+T?gsz1doTyeiIcj;gL;J8Gq9?o1CEp_D#>tU zN2*z?73mcUgvtv3XT_%s$+YSZY2Hi|=*$MDibw@uU|K8FCYlX-o_ebe9CPY%q-rvTMwcip6kf*_43*|X$|CQM37;^vA z`rQ^s>~uUXWpH78Htux)kwBf*+eMG$k5=~4F4!%jF>6!*&-&_iILtlHgNXrIv}}?# zZqo*)Z7ZGe8~}m&N+r(tq>>7^bxmSa5&*1JIV#6uy|c!W9hK2$JVQR)CvLU*3C%Z} z)r&p7&6oL3E^|!G-nyN^Y!1LEGa8lKHjyh4SY^&RfoHqvCuwm=x}f(fJJ`%EPbx7s zSTa3sA)_*nsgpjn<@f1S`rpfg?NzJ!F1lY=$M53mI@|8~jS~Rg@QPbg^Lcg$3{$4y z71?~kfb)qEOdY!4&;%zYw(hR-a3Q^O>@@bs%EBT_axY{^b*^$Xmf(3-`KQY8#G|IB zw$Y;V|2$V%_|LFI+`V&$pg3Uy*Me35A0sbKt4~NL4>TXESzAC**6|>$?hOPzTJ^4v zwCmxXXiyvx4=+3B-yhUYlHu%nWBgM^DEj2Z+0iGYgzyi1Cqn2R!YQXJOL&7x-w<5U zKK~%1Xt;9OON2=!xF_Q4tRxiuGK7venj;4iG6iaW=;gI67nM8l_oylwt^gop`#@5+ z?7qQt@u&4j!qn}g>hCU}0wDf^i{u=JwuPXnSe()ZB~9*VaiDBlmiNOE_j5fY4G7yYPUE{ZYu)2^&vGxO{N4APg=-F^KP@S|;an`5xt`(zdgA!AUxL*g9*d;q z_VLAftGOGB zH0bGFrR}Q-RXDuz7E?}QhWU%t^Wr;05B~ezEA4LF5?02?+%Xg~{E7rzfHCIM2n*k_g&pACm(uXFsXdaQT~@0EdGw25*nETCw0QBFcnb|er;%$sOfgK zsYVK{7)b#c_Qt>MDw?r%=UE0gt%0r|EKRa{TJc%>^iUyZ&xCFbB&6+9)E5!|?@EH@ ze~vl89GHI@hqUVX8*iO%&?{7oUtivE9X>3=gkEbIqUYSS!GcbqVk?97Wn3~{4&5wC z?Y&n`ZfALN18u(P0ew`J$S4qHGu(*v7aKSwqOAD{=XxCu$OQMr zR9nC5;(9g<=*~z_`Uw9iXgX>V-U_S&DpbezRpV`Y_dQ75-wy$%B#kM{_~Ln6;+DDh z`IN+;`_R2T1?Ck4L3JFv1Rg~uG222)3hF_^5uO#3n++qabxVkPNu=SQEQJbr6R$}0 zf0Z_xz;dG+%nU*DV(Jd)GuESM>gSWY$De0{dlI!oXh<*{YUe>9Mi2p$bW(TUokvJM z_aVe(3M5RG@SZb5t2!K)4K`0!8~yYvNu1cznJ1*J=+jr4!OLxT4-ubRo(!iBUYt(e zxGuUBFnFgn+-?wdD(dL zFPtCu2u}T;`JR6mf>}H1wJDtA6Kc6OdeEP}P!t|m)alS8bNv_#P?j(|bt>aZW0mbY zsI&P>@-$%^_Lz}JszLMjeRQ$Ki2A}wGrF8Z>akU%L!s^*OHZZ8`8wAy)`C+;uu$VG z7-f21LACY!zW*zx{wvs9LBaYA8Ac-;K!m%@b?wuoLir#O9>*Mff&B6NR|70J#tb>9G1310}f;z2ZwW)`H|zo%m|+fq4t z^7^<(T7r$ACW;mIp~emdx?hNA-d)@HeIK)PFK@m+gE+IBvIi*+4OHVZceTbOSq{*) zgDpAnFBS<|Vs?16cH+t>gJSa*eq?+SKD^%cYF|@ z@`)YWc#3P(@bT!>)dN27-YcC~>nrAl9O?IhdB>vHpJV|jQOK}d7#-R4r;SNgsI7PS z)YgmE0Q~>DS|mssK+j7z*jHC+#K;676lVmue3s=~dBjs+Ldmp){Ru+l2YY0!j~t1G zjDy*8vrLu;gRigKb6E11Mu|W%Orj=~KYD%*?W~b{3(;lvrM;%|{PnVDnry+7KUy}y-+P#!Qi5~|2H2P`#a~*?ibhp|IaaSk5mc-6rKRdxWWppnGB!& z{k!+!jjXV3Kf}1$R&9mqrdw~AT{@Fkdbh~A;QZ?s+R9ZfH@;{s1k=kOPBFnlLo%lt zW-6ZOwWu+1CIVsa3yKMe9P@JGBW+TiV?68nb0B?Xcfp!okW-q!8()Sby1o0vXkQ&o ze<*RneWmb&k}{Wc9u^(=AQ-m$2c>F|oG>n|%9#cQU;FAl=ZiT^e1;Z?PI<|pCk z?bh{UcnLw_9AD;%mvt<~l3QiLN!Ym6+U;#@AR27X?n^DYQsNu)SESJx6wA@{*2t<3 zW~0P_u$cYg{~_wDqT1Y|b#X1O#T|lck>Z}ai!44k-X)*F9Y*Lml!NqN{xUWfsOFIpT2~X$IP22u|BJ#w|Je2lEGC17V3Y-?0q4*9QPuBXj^jTZk5ifP zTULGA=B>JWa^g`+)J&jhB60Dq!PhsjJYq#NQO#+=fKW#PSGsF{0oWpL6XJ_%<&jH@ zZM930i=MD94SFetrV??JD=rXdlpg)DKkw!76{V0E@I8#1w4@bX8ivuExI zA>2uw^Y!D3Gc2Zo!F!o@6onuXWyR6ya4^c0Io|0Qez90h%XmhQwJ?HoSG zZ@L}dw6a-c@IHLEu21sUDQW)=%Mq7YY6F^9iGBH2r* zEuGARl8#CoXb`4>D|Uu=lis%6prfcHFvj+Vlek2?{P?yo{vZ86imEAKPyekz$FL{X z;CII9Cbshi`?vO09^$BbqVQTMTeGw5TFO(iiVyG`d#Q{TE~M4-pv|6MqPlT#ju${9 zUj=PKbpaiOWl!l{W4MJa8eQSL`uV_2d22~v58#y1h@pKJWJqKptJkw55YL5q4~?A9p@;#Y3Io_qP{ z-&t5swYTlEd`x0{n=nj;oXFd3h(E|ilsxJ_LhV>@d6|LkAdM+cg3++b&VtbUuYdgn zmL#p?{yV7g)PL_T&TltvghgX@e@wI0Js=ztOj+3kW=7Bb@lj0wI4#p#^;{vQ-4&b2 z;5zZSOW0lPXgVMb|I<|mQYJ5IP(pu!awi_zALu^TfIlW)pJFI4+W1*QePxv@ zniRh0k5M1Zw?y(mb9n?EkMokpG!<{L=C&0x15m@@Oc6LzSvj@pl+0fbaQ6gYp~E)T zb3L8|-T82!?vS;WxsZo%Cb31y>drS^hTZyhsXx8Q$4n+I;r)^M7i+%Ftq*&d>i6P( z`6Tu%-CEc47m?IuVR`lE!_C7!Wm{(#|4@}v^8DZo#aNXe4jdssD?;S*aQr#*`_^Pc+`V|WXD zXB1^vK02f=I;sI#yExv8>U@f8X(VNwHHs)S4Go4Xk@i<+BBp=56GfF!s$q_(-8+Q;4pIH8?+9i0qwg@O#RKZ&-qD%2q4snvXG~6Iwe+xR{wkCY#mh zGO8MP7gqg@!zl-Q-nsp{*=D#?k}Gfc`m|y|sM|`G1@xiikNHI^vIvD8epj0>nqn`- zan(oBo=!GkMeIMIXIsV<9+0af@!QXb6|Fmzewy-i^LTUL%bj1I5M4<&Axp-_sK4f{ z9K$~n299hdO!oCEHlY6mLl=bBKi8Cgp1MQKxlEJ;_inTQ`G`^IE*P(W`Nbk~p=ht9 zg$p`>lg(L+&+3^kC;N2AWwO2aNYf>?mtiU$+AEq`aV^M=cTqaT8O-xK43v*UrtQTM z!Sq3YOJ{OB&gAW9bv}7=r`zukPh$r1dv+(X5Srz>BThT%>#60m~n4KD7_^+8ghZK5QFxtv{ z{0r4=YD6(8Lc+zx!`u6Qck~kt@D!kTC0ETnVARg?&m+=8;dteK*eIU#r+=~FZ+R%< zm8xfFrezI4`I!3;K$cHE0gVnyN`*?+W*2`6cGRcdLq#;I6a24HffhR`^;^BQI2%7p z1fdubIw*2PVh6j!jv;T(vj*p~c&Ma<5(ePPP!gp{B;r?bURx3}7{!}xH`^nL21ocB zu1}uvj(PBY2u^|h8K3dpk-O7tk8c*^>h#xCJya0g6?kwGF9c4*1@Gs(h|G1ktB9=< zv9^2k{%*&XXfNTuDr?!5;71y3xx|81(D@vLHZXFk9|{l9QT`~?Dp+&lJIo-NoY!a4 z6FTkd={9ISPw9-|DQxLYu7*a)`rwp8VD#GBO)7=+6%1i25{j#$v4&PGZB9|x$u#Wa zDo1X4zf3HD`uWTB1*MaB2VM39eQ8WqNZ9bRjSI!?+~4Z?h4^EABT`>gL18`w1}imW zBc~1H!xMet7;_Qy1gPLoe6rYu2}vLJ_d&UJFleOe_EI%}u&*+G zu#cFUYI1PZfaSxI_-k($kV#Qm1ug@p)y5C$4URam9sPinu|oq(EPvWuEvOfbt5+xyvcs~8)0iej{lt=z*~Y%KTDkX0(8 z=tQXFj(9KL4r7Fd@VLzEyTO%yF1H3`1(U`#%*sy=lJ7&DR$ww2Os90Sc5X#TRj}hT zoXVZ^b!!KwF7vXP8dpiFIu1&{6E;#6R`JAmY~bq}caUcR9MU9@aak9x&?@B?F%iZ_aJU4G28wd7!q4 z#MP>f0YrV$wgKdBhsahJ5_lL@Nheh|djSvvHL?}0T0Nkq3;EJ^ zLiFLR(5e!61q7`!#d!}@@1cJuScDW0ehsQdXAeG7rVGS>c%~g1wIG<9OFSv~r+(UQ zgK0eGn&XG>-VA8;3FGzN@I zHo{N${kOh`egeM2Tq2@nD5qKwsy9yqTIvk9Dq&vl^r{jdW42~C&uhEF}DRgEsx?~a46fVd9$&ng8jX&B{wQCYpRr8IE zWd_tkpCV!h;30tF1-r%_bh|gHD(|H^5Y#_sn_$X~{KpnCj|4${;h1n!JM|I$%N8I~ zsiSOcgv@-ckD3uLW~KCIu{e?fp-lc>%(YE#1qu~3iQ5Wb`ZeKtgH-QA2T+M`OQ8u0 zd{JzL<*kl^_ND}7V$ZKwGhW0RW}m-C|5r7^UG5$S5to$lu!!*4KqYl>7|)WY#fY09 z|NI5=?`0fLtZF{7e`U8P(mHcV#tsU-=sD4o4#P`?OX{+HAw1BW z#Mz6~;FE~F>q2QHN_p$NJ^xm{Q~GH7eCQ&DipNBuBayYGrxp#Sw+%8FWhH()T%x_#wGxeo7U#e0 z{`4zlIx;~Sn@_?LAQJ-%HP=J`8LgBG9m)UmYY;5QJaH{`99CwAQPoniE|$4|v@dap zXW#ORKhQu0k40uvCqR0A9@ZO#``S^s?Oe=iKD!jI>Kiv;V|pL0!X4NJ7D<{P4iBE4 z~&7NYJxrCXldbR;J4nnzOMGxtqVY5JK)|0m5ny zxa%JOA!BQbV6;nuUukR@qI|sw+dF-G@Vhibt6^dsAG_a1_}Gx){-cFh3b{E{-xlk8 ze{}D1p})yEmOFZ9&{PjCbj*f^5jr4JszgT**p@=fjYwvIYQROCqRHq{K}EilQijXu zyVWsJbM`KTKhl*CYu5ejyEbKIq%%VW-(A>vf2ui+Iu7E9%K`vzeb#Y+Xt)a2g#4Q_ z)z^;haxJy0)BKZ9PF zHiF8pePa!wD;ixLc%h4O-GU`W%=&vPmiG~r@e4~ZJVULAF6_C-U&)=^eTlB?L~Vly z)){di&Tt0Gan>(?_N!@gn!0x6Tbub9QLY;d&8XdoHiri(mW7U3j@0QVt(u-q9Gsxb79-Pi-I%aFyn`n zH)~hgU3$O1y4^ou=~oRC;K}Evx)?E{cDRjL555$2O2oE^Z9-F5Hbg91VU+}|6^RvO zXu0bLKU$^z(%g7H9w*jZzss82kF9gmB1G)EXmp`K%)i)ZC}&q`xw(@QE7@j}ppFlX z=0c26PF4z(<&1hy^!v1(aE*D~FFXDIczn;P!(hd`QXrh`l2X7@Q*=b$xk#RTdQ%hPrhYbf8qQ?=P(X=2?2l9iuh3{)!Ti z=|66UW^f_lbcy*lw1jrqn~}08nWDssWN7NIJPjj|OD*yrnj%9#H~x^Kd9^Ye0iOck=l9K^vZe?euX7vk0(DP1Gf2W;_;ln>X>|~Un64k@e>3RZ?uxq7Nbjz=>3D3~E- zGlsYJ;Ivr+OIiXQbY*=^it(wtRw6q@0Ta7_Pzcn};~Y^^1U$HUOAz_l=sig zB2W-?m;{M(sS;5;`kLlTle1H@|0-${kID3tZtFyU`b8B7vbtv~8%N`uBnY8bm~x8) z18Wi0UCX{*CV`wl;h#B6DwFK6Q)lgSGkdk|?I+PrCP~szO5P1WboRwF!eqUwHxHo< zu8ArnMV_b2ip^d+HUY8&O>8m77d5Aww4&(BGRGgCq=vk1p#ro&j^~b?cICkzEhueq zxS_fW_9OxrqRhd`F-&t%THCe&(ycvvfqA&~yY98qs#QA=2#BUnH2C5Rpt%5UqaNdy zPav%-{MWZlJlnHGYcoO$;cM*rg7`eebPBZA6{!!?&XA;OYZAF8%KOAi)Z#G@lgweG zcJ4=G%R#O|1Eo@+3>y7mXTZ9pz|!2H)Y``8XBmFE6x*+$9ukBic_I1J$;$KAgruFO z7cVZQowo1ozshf)gtl*1-oj~j8zJoOQPP1)QA(<|t12J*c7^=u_D@c?eZQFC|2y4D z&AqNwok7SI^`QxIQ%K0KQ}>vzRg`x}$+$S!O+Zw4EZm!* zyfg+I1#B@9&n!H&cZWq5Ls54bbrKf6eEOTFC_<({L!|Sl&f^anST(EA^>IJaY-4MS z&$^v_=(2C2#?bHiu%v70Nf=HaSHc>5RUqn8d5ZFvlgq}4ksfC4E&tVf*b)kpmnl8q z5eNm$#`qp_|M7j)sCW>a8J1x>su~cKM2OYkj}ptuK-4VsLFo1WGIyCaem1q1Z3{4Hbm}uHn@N{-(^3~`(8;$dtSuco zAGIm}n$R1Gsb1;Q4-hby1_UoAaB&rL9+NW>{sMD^BVe9&adi%;-fkBQoW|LI{;HPt zrZrjqQCcjEEDV)9L4(9Fek-}TL>S!teV6~cklo(oMAYs0-rgltSg2*mmQ^P*9L(YZ z#h&1AuMYlXIXXC@Mj%kT*P(V}Dh{P}3BCWrzCbY%lbJ~h_&Euah~|x5ZIwxmguBPW zEPK55YghpzF}_?lI{SW@F^RoDm6B11`{) zK<1rULm2TWQNk`rf%(b6%H+UoB^kv z%H6?i^~9rwrWfnGsP`^$xDZPw;QQQl08p??Rg%mv_3&2EIVt*ftz6hGw;pjrm&c_ds( zQ0kzW!*>yP9EAIH#zFTh=@q;~4#ifF7tra7@8ffN)+-cZ!3+ged>uN#S}F)J3+REK z!>?ZZ1}T~e)1M$z13~^hEv|~;p00SwkZ;cvk(^W-J9TaN$eGq*yX@HbL}R}I zIL}NwEfUv=J%Y@p(9HqqBsJFW3q^!n-ko`=PIGcFPwkuizSV5pAy{w6>04hPwliGy zt1u+V`D>n(xIq4(PrL}c@k1*p%}nA2N}K?fiqm12ybhEjkMSOZ&D^S8EqNcUF?6QO3| zr7gYX!1(D|`{w=P)qguQJ>Xf87qhUa@C|e;)-*_pceDQTcG95`Yc~3W0-x4o;&xb0 z^K|*?F9=De9HSuVAxgxCoOxd!p8o+ZJ@MJv)Qjj$b-fo`8QfYYQu%eg6xsIC3APaTeQK&X1N{ ze#Bf=GE*#x_Cs390qz9XX(X0*BXUSXs*gFN`l?#HNG&IUh{SKmjiAcY+KO$jAF(8{ zjH;+#3|ZlFQq_+_n8zR|{WJQvBqif_G^90`?Z4R~{p4}ZdUd_sZZO}*Pci2BOXnqE z%CaM=01@c}oxtOXN-EqRkr5Fh-eLO2>>yc!veZ&Kdi0L8#cRG}^Gj3K9T=N0LES~% z-#K~2kJ8}^Q&J_Y`mpi-C!SD_A4RqR6}EZQ(f5sP+T zd(7NDUU6%GXSIZ~k0?Pc9|QBK2r%K~LkiE^>dt@WpFf$dJ1A!=`4uQ=C|5vbU~$O% zvlL5vjaYSYOwhPh%i(~VB@BEQiGB!QZ)`-(g66IlY;@IW7?> zjx>`;Z6?*{C$T|&ha&+ttk%;LQIIL%fjq`K+$vc<`n3r#U~k&m20h@;_75=Tn(pNKbwyEt?Fb~aU)_HSrk zeUAwLbN*sZ92(t4Ar5~1y8BYHP_o(BT-Z@NVON--p=z!Y5+>;9Lv8ultK^EaT1blEvG)jHBUEhy!fi)M{ zopNBmVTDOb_HpSQM&To);w#5@DV6G6eVQjmI#3nJaX z_odoU6uH9f=s#GPQ}IbkL` zatNno6(fDwP`P}WN<5j)wY#9WHS>-{87aH5-yULDL1|wv+V9A2wRubHv}sehX!Y!T zqyt)5$cN1F9!jXVB`KyfQIFaL{Z@t}YW@Q=zj3m{M(&Rnq1f-a=99exr7&lxco)w= zKOm0P^O<@!F~tLI_XCmdVtR;K?{Fm@0W4UpiWSHdhHPw-D2>=ue$|?rIj%hRr3Cdi zk!TnuRoN*Nb@X%X+=Jis8af_0OaNi#E881q zH0y$1$dah0k{KhY+tP{qiUlG-Egl!|39<6nJhBCcuE>aNOyE(w7&)8@pq-J6g$5R# zFz))Ke?GQ-W(sS#v_%a7A;Ltto#ivfn@sxnc@SoFhy+5OwWpxiUDyK)yVOamVFV&# zD-o2)spT(mv+qv631?qC+^B>{{1Y9lpRe=7j8((>sBu(7h7jKlOqywNmS0_k>?)hQ z_gCqe+phxNSSaru1AzMBRhkH3YIu;u7^~8|r3-v-u?4)V zS`x3VdR`|_BtlhPBsnMgvatS08!j{S!H+bghRm75o$&j(Ob-{;E3!g$~A(&x>418#=>@cBV zPnrg%pupsw+Hj{p!8BAIS{#%3DBWlaEr9o=@P%<{8Ii3o>qQ#P z`S~VpZ<;ZA=j6f@1_T>IT;-+_e|7m1Cl1bfmbkmzx46pu=lgcpjz+2-K}~~~hpsYa z3u!jrANg%@yoRjVH!o3OhMigtoeChS&Q3h3tQZq}z5Sh7|B&`uO?R>f6sM8oEJ_=w zx@D@^(F7U(>)Afg`jY!of6i%I z$U7u`PZ-BvdX|rEdDh>Ahc>AHO`UgeJ`&mxZ6Z88?|AdZi5%yFUVCowa^5#laV2Y| z4;IO-7p82tX4|-b4?9%^hmxiDBzXcuKbzkC>^xt0@L2J9T6@j@@VGHKKn@Ez`2E@z znTPYocF|uO4I5FtJwek8ze3YDG?7Zsf3j2T^(vGl_mIaQV)TJ+jxHyJHK~}--`~H) zH6061_N9_W`{=`qzBqWj&A5Y3VvbHhX|~#iq|6M%GylUN19Tm<8+&$jRlj22TzoDh zlBV|2CIH+0@=X9D8LM(wT;`TpzlHB`0c8GacH7gI$zTjI*?z~Jhi}0%$YDrinM}$U zmqdPfcNPN=$x5g!Ok4caJfQ&s@1jrv)Q2euH6SU;dlT&NvHjc0#@565Apc5}b+J)Rj7&`~Yj}@h^+XWAII-^+tOc4s*F2_g8r+Zks5MmAf z)bPwifjKfVO%e0uCb&dh#1vzr6oj~uVBUKn%MfYI^t_oD|BUGv#QWTqrO(`)AkUJx z&VV-k4*EW%*UlOK?={FOuBKsglni_Ix_S7>u}aO`>)YE48bdVLSd=#_t2K&xhoK_I zSlFaE4OW=W$~e+;kxpxz2W;^Szdb3!0*0~gUoY+tl@@N->^*d@w(yKjFKH%COmbI* z=o=qw)(v+w`*VK6KW(vr`4XO@NXQfL&dG03sSv4qP-l-2j+SU55U6hJT*7{~IU@~p z&RyyvvRiKz?m9Bb$D<|<*HbKoUp(_;r)7U!;u+|N#+T2~veLVj9?2au0ZkmRO~!|z z=3;D5(i;+Y)x)AvIpss6U9JS*4Q~H_>}Nk)?Ei_008_Wm&SGpbQ|T@2`^)QnjX~FA zM=VodMvNF1P!pM3-WoFoPB{ljS^MSBr)#y(sq8y!2VX~7l2FmNB#<@iC}FmgIGqtNBN`KOLqMomYTnuZesX8k ze*mq$N$4msQX?4nAhiXo?E_0>%Eh7e-eLU2jhn2Z}6xH9>NrXX=J z{u^|+2@C9)MKM%_#tHoirj7{njn?C+qwS;bipLruzI_#K+^jqr7$IR9yDY8&Ru%f% zP?N%{7SUR7DEOdq-oL4p>4=(+Onng6iM;?hj|bqMp|- zks-Do5i`kN`9)QZ+XTtNep~(9Dpgl=`pw_uef3|Nt@~{zQazDzn9+Dz-<=3p|AzB> z2rT$PFU2#kVe#X4$W$R25^j7<5~Q~#Kr1##5GgKc{$t^|c?0UPWK29ZvDC+KKfoRO z=VtC8LxaD%j2H9h5EUOEJUpBBC?b(8uD_oe{cMPmAW3z~1oQy;2!~KC?7Sr6@1;fo zhq{U*7i`wH9e-8&*d|`9Y7Anwz{gO^K#* zbH2e}e?<0{+U-fp1a1@?>XL9QWX4*=1QLLTY68jNREM9TI2eQ~0DXlsBTb)$>ErM} zB?xUZnuVShFTs80INEOKYjH^Z=6VUIQP`DwJ_mV+f9GKh=kkFC4o&qaB(o`Is7g-> z1Wy+M_jRw2R$~CRDYW*3>tK{f03?cHRu2feyWYos6lPT>{HJg9e%0Cx7*=6$Oo}D) z@;h9EZ8&YS-+%kzCLv{!h?bff{QLAk|A1nLi_D;Goo+`XyA#LAb5jB)#Cph{FM;h@e!i=2-DB|fqJ1t(p z0=`~{`IM{wh7b*UN?&&t=}I=yzx$zW^6TB9SH_wvSrk9UH@(R9fTZoxToPHKSj6P;QAA!JaqMVXH94Mp6FH~26`bL6BU~!6|XsM5+Wyx`U>3L zFNc=lrOcMNxE!!dAyh1R|1@%orXLKPP@s9SAibIiDClk#%~yz|)HsuGjFRG=yIC1z zSpFq1_Y|_m^D1x0e;a=Y`j0!xwc6 za@a!q*pivk?6-4hIn?ZTXVKOA`MX7_(<-shyI=*oBtQLKGG^<_Vg>MV-Z2yAvwG}j zO7b5slINtbsB!+@Cro)A!5|~bIDS+u8z~oPp8PyXz#0=;Kvx7WKcZ931OuhaCItJ$ zm?EObRf7|jZJUeNZ57wugih?1{SiqIS2W@*)u^e%5j^jWG!1oBz zGl|ROKa4j-&Xd2f`w;GdjG!*g=R_Nm=YKYPNtmuB6;EsixmY4w5E8CzGz;nG6wkaZ zSbQIEURSM%v|X-of71u$Md;0pYtRfBlnOSZB+%5)iMJf>hlip{zbOW__E8c6c8)4-*L)tuu?NmIVsg&wF`QnkAn&3t-$c*pK7`HxnruvXUEN!*d)U$l zoG4V%uP^e$N91+luziO%?sR8ZMv2a)s9K7V*f`>Z@oL+-rfKP} zfCqEq<}=6j0GyaH zoF5db)-f@ABu#)Oqz9u)B_QXIiZHKCc9+}LRZsF}7EloWl8{MK>1FV%=d*(9I!Df( zuzRildGQe~^it>U57MZ|qaB#Pd%CaE)#=R{;n&wGe64yr!lEyY81-T<5dts3={{*t zu;KY{t)g`AULyS62xa$mp3FT6ixlAULJdCJTUIZT=MX`e@cCp}Rx!|n_jGi({3X#Ni{la-i&I1{ zBQ&Ak;bP3+5kZFpsWxlvt(^Xpm0Yw?Rj#ghzISY%^R-?tn>J&n=-i74TM8Mt_w6(j ze}ACRxt%~I$==+k^Q`q zwJ9@B-PGp240qv6=mDEENwU__?{2)$&J-GMmS(ope0{ZRElfZWjVr=)3~vfP1r7UR zOsm1lAE{ynFrWv|ZF_tB)f(ShmS06;3qEf%BA@V05^AuAT(iM^;^qDjs8Hnu2&|nk z3En-CFzyvOM*M*0;rQBfAHz7`f15UjX_53D1a|UUsBc!#le}wwoqywnm!Fb&LG-O* z+lHsCpBC9vla9K`>!GIyWF|pcoU2RaMZTYYP7&gQMqTUumekjlj*gkI?fB=NKSSuB z&(%+3N{r99LpuCsW9*-X>||}AV)u7D42PBe7;O`#ylSQz6%!*owP!c9wBfQwOt+|X ze8xE!0WqlO@dMu9hFa11Ay_f$kqk&GEhpqDPsV_)fg>g;d2;;YJty2K0Fc~c^cO-l z0k8|nJ!nvg-#|zUltC<~&s!$>YNz&45vh>NIsTPiHut7>I_p;#>i4>y{8(=?IH4kP zY;0!vzSD1dwZ}q`(P+Pe!Ow4*>o098b+Alo886FSrHhen%(DkJp;WV$FE24))hvsp zrbQ8key5_L(=<&e6k&u>f1y^WqvoDv(GUl;TToYIYS-B^JB2AG1RgqLT546fN-py{ z>ej~nVjXlS2_G7#^lB_8m7Sv`u*c9nUQhT!!~aU%fi(9XBOQoh1{3MiXG2vHZx+Q{ ztVW5|fszw=sKz;t)4ArN`hs_}DjXQMAA(>31ElAcW!8K3%?b1yvCTDeY77dR z0!UGD5zHZai+y<9F11=amPJP(n@O3#fSD=WJm%phB0SC?Y%RmH6>BYy{)tl2*Q`4~ zDr8u(Wpt)u>7}(i&u#lW+lvqLtgcx47yk=YKRFWY5)y86J!dKyR!{MSs9}9AiZ{&L zJ^0~S#o*4t&*fu#8~Mf7j-%A|nhEiY)0*K{&+y-eeh}O+z1x1T*CImwwG{>r+4N)k z5OSqnZv9^~kl{x1xJEbJsj1L*u_RGb6*l1eD~rRu?%dc9?!$kpdxLtBJQ1MciJ1=6 zJ}#+T(t?l*&wPV@lrM(qF^XaNLpuTjir~7hqe7A_?E$4+y(SNF26g? zV#iz~FBP(=6Uz>Ts$03R?}_pObS87^9-{rqBHFI|Len(Lpm;y!n6yt*YKgEL(sHOX z@_hvini;`pJ@FX;aYEVE8-Yi|5;c_HCr_36rM#C$`U>O1rs3%CaLVl|tT+-W(5K%` zl|k~Pei65L<%}MEGvT1qv&|U6-!^2^tMn9bmLtkz7Wn1$@G#DrC=&Jh8t;#-J0m;~ z9UYZx8kfJD@XJ|Dn&<84$?9cw;rU_r;Apqp!}dN5Xz`%d9*Q!NZPg)7G72 zx7f)Joqlw5eQv7lB9*(n<(1`(#?iA@4f#PKibd{7U+>Rz`5;&nIK;M6TkEwDIYXS; zXDOy44yYYGa4aSH0yu8`cSyW=UB4mW_xA$!tta<&nJgx3Twu9Fs{O*Ldv{Xo<#}1R z{f?`z2&rQR#-|*k6y#BOLk2r)N;S$=`5-(dWpzZ=?bTjEhQvMuN7k%SEtPm|WVKA_ z%o_m2jiamZY8Fv$FY)OhNDzsA2rfz%d9WxW6*)?|I*Ix2kh9Et>GBX_=ws=#_es&H zp3i?ym%>^^tSOI5YoH;|T5XHtja+O&XEO3Okwk~*(6fVUhhwy%uR}Ti^15SY(DrfM z-S6=9Hgloo^@j3QghmdMaqycAm+_nVM|j5Mb{SK0BuT67wx+ z;7X~$QBi&gS%9caPAAM9;Y6AC?)6WVSeBo9Lh$NeId^E|bkp6{o<}**nH`PcXtt0o z|D-`Yji(CY%Zul}&YPeA<==ns#RQI7bo_D)s*#M~dQ#$~%Y$Zu?<8+ ziR<%YwHc8?8G_4#A|#8L!km;Xi{d7s>` zos$PK$&;Vw2)o3a(|G0hbmi=ue2-3(nQGU7hb-5$SdNO>t(l&=NJ_-u4J1T_fqh@3 z5#=(g7f#vN8)L060Xo?|6FE|Y-AeU*d-4ZUKKUT9-bVlYc)9K$8_5ioAFr#X46VCg zu%gw52mbQ9leEpsD9x0$=0r$}G}D@$a7gn#!IlRNfDuOR8j_6y_sTMMo0OgRc_~QS zRJ*~y{c+=_4#tVpRS6;-c2z15+h-L!uwm7pCu{@l?eqZmdpSeD3D@p!)D*X+LAa(7 ziP2X=OId>YNbEtU0_Ye-W}H|Z=df6N&>9Yyo=0a!4O0Yn;53h_CY~`~BLYk9G%#Sr zgpaOk{i;q|kbze>zE-ExV60iA?Xq;WKh(5Mg9n6K>>g9{PHB2HgsQS3sByK&O6U5C z$J5L1tBIeFcS}CuaiJKdE%~p@z(tORl&-JG_))?S1Rq!VFvcKs1bO~x;^0~@cGELv zzAh=NNEPoObi2~Hj6-AVF}vo(R=UmJ+5UQ;T3>^3T}m!wE|EC+wy%Askm$bVi04*5 z)f%q47M=aEQoa4j9wZd1T@>cLAFrOIzkicfMa6)?j)TV7cuyf(LVAA~6_%Ij)70TW z#&ad=@g%8&aINP>o4=*MCD4ktMQ{ENcr8UiK_nCXB^?_1=b}Ahdvw}bE_gE-7CVxe z&L!w^3{_K%>EJmxdL26)sa!*xTNo9r3e(O+`U7|QQzfP1hZ}SzGb&ZN(7)aNZT4C) ziai5*(eH?U97o8xl;f&6w|=MKt6{H^&-q8vKQ2&)hmBy16T_#p$!>;`WDV~-G#jjY z#maXM&r=e;wu}tPl&l*Ne|4L7G=^kn>YvKkpexy9K3rx%V<4!W`9F#cb z{cjPOYKx9P?F{ZWD89v$FNXs~7n9~`B}fw173&q3%jYr-;c0#I)8t|M!0uWtF)_lS zbd4XTM?g4uwaL(g)~^@b&xhmBj2#B9|4zpnL%e9(e#JE&Vgvl9W0$jF5Qz14OzJhp z@bH?MFNjf*oHEeEJC+0$ zM7^oHnC6m>_qYTA#V7j=M{VM=g`fUv*~WvDE@;IaWS(r%A~dWa^e7W zfXoOd?!P;IZ+F7>p!K&+x#z=u4=>`uXcv1GX7DRk4W>sJKh#16ja?pymXM&)H)hzq zpl%tidGIxNnit}$xxuWkyFSZ5Y~WG#r4_%Na3LyHxXe3zPORQv`~f@U27Ye#O@NPr zeZeEc1^m`POewLevZs(TsY4a|b);Y|7FMoG#1ECKUqU06;zhHRGyj&E0H7UiHwlvv z_tJSC@B8NY^}tgXKAL(G_YbXHe=v&C(K3chz@8ugX!q2)?$G>mblm*fnpoN6)wsDF zNk01v;N`ieVrQ5l{s{yE^C@7zAhR=6pc3QP-$~p3sP}DfT}-0nY+VuNE1v(Xp1fvA zHx=YHsF{hIvLQ@(#3LSA0o=5mg3&zHL!Au(Ib ziOq*`{V0!s(J(kg__VXgAed1WM~XJ{3}ZS}0=y@g1s==>m1BL zj6O@Gs@nsRVU#4vHxFADx6v)$_KgyArE&bsk9>zJ-PUk0q?2xhzl*mh@CO!u-E-6g zVCb)!kX56_19A5-(W;^o@JklFzdXujl;-BMF@}hq-gXkIT1Uul=jXyt=%rr1j${1J z;AKElkpu9758H%|jrerjQE*-GdgKv{gpSiu^M z&~or(@GI-<;LoQKxM4XCM@I^lE+Y_cU%yu~LJIEFJ9F#$uy^jnchMY ziTKKhNK&Kb*-ND#odN^=dFK;3ucx>NghjEO1@t?l6aXRsN+@8zEKT#VW`c9~lhYeW z$`U~9%hBs}Cx_P-NZJa190+kxjL2VdlKu~qDN@$;8|kUZyp zZo=7}QY8kShTGGWAHfM=YL+|_Gx8R>O-;?#xI6cPfE>UY%y)Vxz)%0HM0G9;3h+3YBxi%Xr44XZ#4>1e-4@fo}r9=MJ6mWUEjQj}f zf_Z2VluO&2y1`O6Wt?O6i76i(=cn7arrDH;6Evqb235$sxoMmI`hDhT^>KA}g8~d7 zqOi-7_$iL^&Wtnw^3{Y3p|-4VZ5JfiPirYNnOY)T<{4Z=t6i(;$NCwWj@V`ipV=%n zSq2hcV^fnV5|ji|okXoDFP8v|07k(fMM}&^3@}CVkNwP&3a#wLyx3#{<$-o?FhB{) zwBXgs1Nxq>#pX?rxaMKT^Zz62E!f(OmbGEri&I>SLve@V?xc7rP^?ICFAl}s3dJcJ z+@(lxT3mv=yE{qX%iiZb=XQ5Dk|MLAAy!XI_vP$5xf;Vp0&Ya_(R75B6N<`{C3q@c(Yxs0 zAtyqe#9;oa3PCRzfXceXAcCd!G9O&{irrw5QS%+7tq~T3&eJyV7;!3ksoLLMIFE`M1)5 z!uC4aKfN8PMGwLbOl+K*9Ny-?7#aZSw+`=Cf2J3!(&M<$e9EZ8O$;Ibt9o^C76~-mHq#K-wT~mu8LAG?K1WecaHe zL7GP*SA1n%C?*{nJELbYonV63jdW64)z`_@Dh^emI1RoEb(DfMkme^_6lFNQqwK9g z754{U{;hYxmrwfqsO76uIB^zad?1J9U}Z^ZeTN}l=-v@5Q^iX z*@;k6kggNyI8;K8J7}z7e(N2OZ?=0BA8I68ymWXkjsCSg{{9IkpeoXft3Vs}sgFc* z%M$E8d$bf~*M@d2F?Ipv6mL$L`Km~pb%#Id;e_%3j#O<=0A3^1ap6rI1v@(b?K7_| z8M9C+7Kp*moI5IJyc69ft7oCfh!ct{GiKsrQ~9#GLUd&sHTI?SJ_jfg!rRYJ=P_6m z8Z&tPZIzTML|^7irmN@cubU9>Yd*Ho6hHG>-VkO-ZTPHQ-he{|vq0}ZG@$yauhG&h zyy=I^dHQ)z9lGpvg>`{WW@u|8XYtdDJ8xW$c3N|pnScewYrz@BngMce40i&*VWpW~ zY|p;_xcRFfSiwzxu4~9e1s1?QxTu_LXC8h5{xGUWau6; zN6RZ&b7WN8y>gZ^B%DA9$H|48+%#OnA8GlF&6#!f%hms;qgRJc7M+*C*km2)YI zkd&Jse56PW`WgK`-Q^uF@(r*a2;X|P>RT!JO1cM1xV~YLCa1CttFl6xt{}Io<43Oa=;g^iy^PK54GyTvbTPqK z9nY|jYtz8^S6s@V^Y2J<&;S0=@80CU?$_z42V&3E?_0qD?*3;t(A*%G&|Ztj8g^!T zU{Y=Cr!JUFPxULqCx^MA*RF>JKQ(DKKoS-^0EOmbFo^^eGn-Lfy*0aa4|pU=1xvHG zf8peme&L=58Gl)u%b=rdgU6o@*v*=GAn#>H; zPTm{!#YhhK%I}witrppiJ?ZMB$R8ftQ^Vcoc6@b+XBmQzYMP10cAg@o(@EpW%GgS> zmPuUmI;#Cj!>ELT^%4FRpoPV03UC4zeR>wy^4BHfejvd;8EPpyXu@)6nFr?>zN&M? zSUiM)U~8B*NH-%R!#obrCjZ7SXzcVY#JHa?#E_m}@W%P+)b~3QzqT*tbTlXXeMo__hCPz%QLzA`j7AK5?VpqQbv1YRs4+&);V6G3MaV z%JBc*^Jm*x!~9Or=liUNXX!-@a>>uXTi8tg|1RiuvW9>egk`Mu8X5+tqd%~D`jwh8 zQ8ycg=PoAr12InIo+TB4g7_qpA8MuY6)5X!lAC!zd2vQ&+J=LpgRw!I@Ip`d#px1;@)T1eH%~H)HQtjzV z8LrZ#HHsyLiOS-4Ub7r{*3Ln4L%q@FAlcr?j;lpdW^!$}2dRq%bF|^^khAaB{{kVs zSsjReuK~g`?^Op!4V?d6I4j*PueEasZ_+5OilFDdi;=v<)tJ)8d_<6qW5(ia0c31Q zfzjy*E$+Wb+)794Q(L7=&U~#{leemJGAyU&F>?k1zWjE$Cz$B#DgR@m?iN?Vl`e@y zgG4#i5i1_K@!Iox9_T3Sawy(*B0$w=N`9cL35AQ9$9=0;EG=&b?FM3d+p(o4xnW{j z=D^XS5(s$00zt-$i};!D_Xh7w=kF2X+JW#>=Z^AcMJDPo)o;-RZxPuQA%uSrEt}O| zC=nLy?87t3R#f)r1;K5xbR*AVVbc=Xi{fZr9$M2sZ*w#cV^Lq<~}sSrdXtrVA5*e_M}AC3TYLJubt9-{GH z{2kjr-EB>_U)1N%@Gp8niF~l&q5c^7m|(D$`#M=Frn}@%-T)cZ&qL5w5#4t><`oX8 z79Z^MXyDQ6EyJ^}LGJGEc5%$s7mlnrk$FP}6unnl!t2<4F*}UIuReNW4=T|#JTO+3S(FEyR+h!d0Z+wZCbrJ{7A9unG z!MX=I*jUNKVH+D#t267GZwkmU;2L^(=dl_gRL}r&o@rZ5+-Z|L5Tc)F3hf(cUHIAW z(?9QTR-?qn6At{@gwMAFuS}1bGF>)@_u6f`WJ|sBLh~ISga?$pi@&Bo| z?>li?R!m|0tso{@L?jtUcvx0?^ZhbJHKT0S^&p_Ax>^3`{q2-k)M&TSeQr$F$E$zr zc!^KOJ$bdIdS>^qsiVn!7L&`{@C31O__0p!gN1aJX zz6yCaK~hK@+jTIaQ;EQ4p2je@qS=6{Rua4acI|X(Yd*m%NP>oYqbun3crxgrV^vG7 ztGQJ*t$3h&CQjZ2od;i;ouwNqst<1~Oe!I;K0z9Y$r!Fc!DVxP^t&fUbz2fBpdi|_ zO1jA)UGbNH!8mN{5@pY?(hmLaJQ`;r>y;Oh@*az{eG|7dFlVQ~SU%h+oxL*8+&3pr zGB*Ja_aXG%Ie$-Z)I5cmC&dOuPxj6ft^OeD~R-Hi)QulefmrxjF zZQUQVw3QvEx9~z`LdT{`@R6e47!9^yEQ>0ljU^iEVLua!%bdgx|HV~2U$_`_-iTT# z`0hr##K0&N9$LC-5UY%v{oN2b_43|e$6MLq$dEPiJS(ddl@+ZVg8GHa2TLF~(PKCe7c9;FG}w?kgZ6das4cS-+%7GIoaqFjZRY zzuBqhIh_H};wPtlt9x+}J$R5T1AY+KAyAKQ%iX z44l8Vq`m2!*%nE#{AKxhA^ci73-bQ&QN4NX%@~~pXcJ*v%ul4`?X=xNj_bsIzsNVO z;o0;1^ZOawt_SR`iPua@5Y{uj#qHdlbw}PmXUN#u{yuXmC2BY(Np?B6qPXcJJzOZA zCcPl@6W8?%P_|i`$G4_xkuILhl5^W%g+2a*Bk-%vpN20TpHVz45mk9!nLAxkWJ4Yz z)%8;v+VLr0OLg`K-Ir%hT-$d2EW&^P@m@jJZId16yl;;atU40mq-14s|72dNJ`aN&zz z&6Qo#pRw2$wmwIz9h^2lq;qKw6Fn(X0<7*=2k$wJSWfB)GY z-5O!D3eMaON@u{vQ_uVk&!>Q*(l($j<@_Ux4uGlf&t2=@K$s{Lat4sw;8Dl0E1IDq z2m$~PNwq{8W#SEG=RxH&S=Kf^i0f3VY<6yZe0-~{yDZQcj(5tH>6qx)>T4Sk#7(uG zt4=zSe(b9PSR61>f{R7u5P;KWv@A)wdFjUKUcQQ+Z$poGR@`w;!Ni)7Gy0GuiU}Pr z$51s5c)AXRLe(@Fja1p{()F8`}3CMmU-e^;V5!X}gNw3|0P5YR|1)=G_5WXDBxfP7_ z*;48os-0zKS0PZ2xfAOZU=Hp@VYp9!v8B_7SQ9EA$6a^hR!r2k6b5d-S{@^IOC+C)h_K? zjk3BvzSVN{%wufV$y4=iOvvSpinzfl78HvonjW|I-6ld+-q4plWw)+Y$1V*<69cs!X z;=mGjx_dn()|NLw_;k8TLj&Az7bHT3CPi}}*z*mxh@mRuGszaje79hbhbeD4;>GwhK8%B!*kCM2+$XDYS#>dD0C5R0ZFrNC%s{jT&Pf9Ot zvHL7VIvVw1XYj;D;u;o^IyA7JurmP(clA$7(KV+IpZnk@di!~OrA(YGKmc8>`A#qx zviI&2t?(WRJe%8o>7YO$G=c5_uK6WU9`X2J+;T5b?Pe%e6ap3;hF4f8%BUrR+A8;j z$o{QsfgG>mzt6kU035pTU;d))=7lPJ$e(4oM3A&h&ZD&^Qg;S;RmT-DbT#zLuy_Ii z8+*;a8;yQL+7+#KUqX9PE~iponOD8>x*RkMvjhFgpO3)|+V*VRe?zqK5DheZe<@ro z-qioA`pT`I3inOrtR?olc(!$k2k=BZ+X3M+_$&-1#)P+!-;H>4zS8CkT68g00`5o} zssN!1Wj3nlD*>R&R@&!KM#+M&*VH{&`^koX>l2>AEeZY+b8+mQxJEQ++64fFGeWrs zg*u`LwTQM{Q-yI91UJ5#a&_w;%q|B>tx^EwYIl0Sy1PUH zo%Ei3ah&*D+EINr=KFtM(Bq#iH4B6$&24|)b~Fe5sUHe9A; zrPzSO-!SC2xXqqcoT2-dLu8NLCwG)X1fc>%qx~(eoGo3 z$~79H>X2Adnrkw@g>fhYT6H{_ao)hq_XLd1)2)=qCh%1jfpAdv6YefEiCcmXUrWqp zk))5Uin73)Nx>h*PYyu&6!vH@q?mU(!=yl!FEOL=MHs-U5HFM>5$`|C@PQZZWXx-kfpKLoq-8KYy9o4D$lzW{~RuBWqttqIFuze0*X=Fa0-V3qL#8zx0%}=_bv^Vn)4t zpDW-0D&Yq5N^~a7sBzKxapW$jQnL2fX)_KlpIt=GVK8xr#uV`E)i0JkpS)RZ70-_) z%kj9)cw*&KMZihcwhukQ+??@YPiPlXweB1wR{|BHzo@0LliXFxwbY_x19sC4 z@2g`$wT4~qkuS)S!q~&P?rqTt-J3U4^P|mHa8Q&?$_J!LPady@e~V|M$0y%}XAlM|x>CO@nW2B^cUd;MN!< z<-&L6_~-(KGdcwFh?dg5eAw4P?c_8|8~ncvCwfTLiNHU!fAum-m2sY@4rrJ#_N{un zIDH5}ldegvv7rO1>ewSGDHL+X!63Y^6e|y5Gh(?YtvC6{Gu(8wMSaqS!-w)&bpA-k zyIRlHpu|P1CVM7zMp3%^^iwwr1P)c#mo`eOb*H!$2%;aBDgeuI*DB1Ijn#3+x<8M8 zW2IPFqFG_a&=~PBr5uOn?I9F)As$~_^$wNIbLXOie#&y>hKSMj*6blzn?4uRY~iiP z^LtlPlvX*VRkiE76?Bua;ehO{fnFOQ;$VG?YTtUr(u+a@L`p(J)f?w4Tpe}GdO*$f zmrzYRMroZwWG%7tl`q=PE2LEVFgoi-0`y-4M#Bvq6qc3R{D?dZBJAwi8lJg0Eulw* z9!q9C$R&MZcrQ1yx+#F~mu4?u&;7cU%nnBo+}wes7VD){MfmF3@P^e7i~)*c`Wdx)nR`R2>k$s=nD#F4Q~@YS*XU;%9^%B_ zQ2YHC7kMa0U+8~S)&G53EWmh^DevGVxG;jgAWiPaDSsMqtqcOPYN48ri?5aDxu*R` zgdTfR_4ioy*Ii{&oFp>l_FO22Zd*$nJVFZdrG`D93Fy+fG1;tp;z_5k{5VqU-(c8)*A3-GOYzZvevA`MDh2b~ zq!cg@&b^PAg?0)O9bxwRqekzKbZzeHuMIUHEGe0jbSGAHfPDUrss_Dz|>-5rEQ@RV@%y6iSx|BtKDb@6(le zo&6FnH(&a6Gi!sT%h9omQekoOPDmPv#EID4G`41mlEN3*bzbec@`cK4cqe-P5+~(2 zJ-FDkDp!-G8opv^6(t1=;i-!FU%GPr-Fr$p;vkfs50ZAk$(?qRTTbl?2~u4K#@zh{ zoZ=`?mtu$XzX(YdbZnKy5&!cy-BZ2yVOA$GFzL4dtLSXy)02`;TVe*%6yNE2;TD zVc6)#q%w%J5(qT7{jx*$_D+)sj8>`|yd!zlm%R7}IR1$08pl)sKCRtVd_0Nfi7X!u zYqoD9%V1t;n%fE zWFS6%=7@7a%Z<_o+k@FlqST%|;@8ZuILZ<95K_S$}698$qw(+hi-$78Fjdm9&T z5Qs98m53Yx%^bGvLq+h~1%iE=gE$7MMLw8tL0$LEu0T)uuf;M}Lnp7tQV$uDqg1bE zLGk|dTnn#}uO}!$K0$n~=w*pvvqdGy&HVf6uUDp!{73QEy4IIzsmI}07qZodFg@q0 zP>$DD>d%Dq**USrUBf)F57k@_DD3B2EN~4^D;$^xO%0evlflvrxG(JUzYa3Eyt6q}!C|2$NoWEUDx` z_-p?HCWy|K_>GX&Cmldrn5b|>#3E#5PVgN&E$eDa&&M;zzinx|lcp0n?|>rSb7t|< zsH@g~u%wSVd2R#J@iMgeF!Zr@l!|+tG6*`cz42HSKj!&=Es_F%t`U}O0H}GXOwE%N27s!j=Flj+4785Mx<(YQ&qvJx9Ui4q5 zgRb-4l619yT@LSE`TX&iKZmg{!Y%rNNbKCzUL{7psE2W*`QDPIE;?VCGD43fKEV)^ zDr?RMowzEeB`@ie``_V!)Dv-BkVEOXGD|PsTKVjT5+FYYL zYONQgq7PjdOxfDKg34eSJQ-=<57h8$TJ1f=c@c`#EG+EtZQ}Q4gN2cn%nqqUX`3dH25V`w-DN{(4qE87C~u+xWUEchRrE%I6eUZe^?YI}lPs zo9VutLn9Ur^yWR%MI3BJ%LMz-EisH!qki8xj2v>KOATu=vvOAag#BN?rT%TMiHi;x z6Os#6zYr&)+d%s+GN+G`dTbXJ_xYQqeUFOjIJWzHIloU-i0k0h$DO&m(-VWSaH0;u zM4wRyn_&IH&zk^>tmpe8GR6~k>g8yXo?^cZ_M=7-J_pb0v^IWV8G7!xKZ!RNOrM}2I z(w8gS$!UPI(wI4Kl5?tx@JxL2#@@o(xY17HUtIP_)g~86c`PeLO2I?~W@7uC zDlJHXAjf)GLW=LVet$E0u?)+7My+vjcIJj>X{}#e=m;XF*MyASxcm_2s6j0G2@TZ` zzmN=`1Gw*5^jjw<+e;qIoc=Hj*0P7QUFm5I`Mc{0bx=sL{Cv1K8jn7}7M!k?L zR&$3P-|asie-DzN!o;a>UU#NZ5gjoGSez-U{g9o2fHAbN8Bx{8F)b>;T~E`gpjw3! zo~(Ah?f-jf=%82a*70_(N>9cup!9m$Uf;Gx6*r%%C^kU78%=}^tDz$&>I2Wi;NFTl zYjDD!Je`4HwRRQvn4Py)F-6v;-YKx@rmH}quMUo zO^MTTe`{SC-@mI|2#6{BiNZ24(B0;VR)?g;s_Gv?Q6&coVuOE3*|g{#1g?(8m%%H? zz5(WQbd#kNm+wPPrh<*v`0AI8*n`0W(UolDY@ZMotvh6X#038MQCJ}vgof0CAg>L> z1Z&1`;tR&T1+L^7rZolipi!T~MolnL z|1hKHUEja<_@LDiG|v(e6UyKd+}jh=e!o{LBxReuWSPMe52{&L<%}ocNA-M36vm}w zjL282PoQ;^$7Vk%u~j@FDpI9N(|pcA;or{_uV><*8qi%LoRkTPRwgC@PZ+uO&SfXr+jF|m67CQpzw*?tx99Enx%@?R+ugKbX z0H!KcA=Fc`1zLrDY^0BZs7NYLn40KMAZeUm!E$BnNoiy5{|AHo-V2geFu%&4jubKM z&7_6>!vw5Pk?x$T8w8hZ>aL=`;ok8#4}_m-N!R59M=1=)MeiK zm@*LnwGjoVvviBA8rQTrRIE!=nD9q^?LY9=qnak5OCGGAl`NS0ME*#hb66bp7n)oh0c@q0x}F^Pa+zA{%!+`F!Vh$5Yrq*w8WCwmtKrLPJu$o1jlFA^%fslG#^A1k$iT{J2&mqQLWHR-bEs z95gKdA~~AQIprN&+UH#IcdK~w);phurOlxbVRk;5gSY2FbSR&_RtuB`eQ4|{v)z{qwJ>DREbaz*wUc6z6I2woiSGp6aAwWlPW1nU2nfSb zsh_la&PmZlP-c3&(-yf^+%}ZvR}rACb*zg@9yg}u7n-1N;lZ9DjUo$=-<;q^T@Ya~ z92lRjz`iIj@uH~ln_8eOGUW)n_#RHn6H?G=nG%vnPNv`v73!Kj$lH-%T`E^`+@A@` zbBH`1p|-Px-rPB3{Fw zS9_2tO^}p{<%Zw8uz*p8;3Y=LgOy`k8I6<*eDClOx?+@{DQg4Ig*#%Me6yJ(ZwhC8 z=yXgQ)1}}nDK~Wpz}8bc#qEXgXH5HcQ*?lSKd>+~=KaDE-CX&CQmpei8sxd(c4IaE z93E*T9C-fRet{?b>hlK|Zs55sYQHIJ^G1PB?ARDwGzQLQm(LBenTavYy7B3dRhmPd z1>Vc(U9PfSXPen9B&Rc{M&*vE797-FpOKW(8%w<<0%wE>H#Sh!e~{BnZ5W6-lEOwT z)}$6eRQZof?7z>45p`YEt@ezhAo`@V2bH70rzaSzkwa`7WB`FUy9EALnUbn1teaI| zo7SI2AX(#j3u=z=o|I73o94oa{M`e0#m-_zctmgl#o*9%iiIb$_div@!w zDLKRB%2shx;N)Sby2UT|C+rx5uca}n>_jpuBl0DfCkJSg%_`kz^in%FpEYASzM~0J zD5l_~a65lC+s-sr8L?twBS|mTMN{UDPp|NGcC_1#*JY~k8Q6@ROP;EU{fHS%&{03i z6dL_)Ad1p5qn=iCnc$}Sbs3&SfC&HGCI{il+#hCq6&7x7XWrmIOA%Y8=7JNz>%*O& z05QcPJ}KX;4*Ih~i9zcx;@J*Fwn6nXHBZAhdsBJtL4u{^%QUXEo+Dd=JRJCmlGx7qWUC8s9d25r{zMbmS&1gSxl5sp zf$T+5@rPrBoarAXu+DK<2e~sWHjJRymwyu>Ny9t7wB+rf`plH{OhCgbkBQ@6G9=}* zj+&L{$VU-7(2`RNIivNGE(#Dx8?HU=g_n4zM4fdU=C3SX-j#=K=+7;gnVtA`;3W&#;zn@=}#Ruq4 zp$8Mo3LAIv7Tmuad59fdFZ*FTL+?JtAJ2>3O-LQ&2E9xwXQ+0mVWydCAg}>)+f(V; z?tKG9%fOu~!*)n2g^jv{8cMnoTQ?|=g?*i@?6gsWSt7pP7+M!N*2Cd;iP~PZjaU!6 zJ)dG>o8Up$fs^&allX{-iE;ykeYx5>zc}gP6@egU*FS+p%n`ddNi+FR{X{&CHw&z+ zOQoKBBSGiFFE7Gmi5G`6Da&I~wFzP0Jqy%=auhw!g666}DxaoznKcW)aCqYf%aI$V zaovcW*bDxG(lUOjOryVR^$3~Lw^DmbaZ%i~pzGBuxu%P$>uQpmJvnZGm?L)7h}sb; zvkT|%5na%6=o}R@%$0m)+`#^}?1n+5fVU6`-)jpHV^>k(g)1>BNj`3OPS61_pjSim zfx=?`ehudx{fFHh)MqQ=K}V$5xwLVgqy$?6Hz8_g3#hBl1jnD#dQ8h;nL>f;a@yzP zW4A^GT$3lkxaz%z08^|H0R3S;7SGAdq}sF;vJr`J-MYfHNjOqcs2+1VFmL%WGTlY5 zh!vHSLc@V*O9pmyX~xZ|T>prD4i0&nl=k1E_@CojRK>67{^egiVPnHXK5R?YonHRe zK34Y+L;jX6qamRjSU~(6t_JebPWz8O*`^H5Pl6PqHz@M##MeI+O^;QyJXBOh%qSwc zLW@rqAqUn>Y}NB~2$G6S%Ie<)E1cy1DoHrMVVy&Iw91k6c1SM5U+K(kw4o{rO)EL^ zDi}J-rBBk`Nd8VE7_GgXOi^>wnEPEsTJ@BYhGhKCp1Q58V^oz$)PVN-`*8nNR;F&X zXUCL0wVP*!BK3|xV)k01>e|L^U6Xw8wXo=pyEJUFD8f;l8?8Y1+wb$ZyKX@C#|*(+ zo&GaBPKqdw9Uv2kW5+qfg38Jcnls((1tzSF8slVe9pCO2mPZ0VcFo{F;F2>l(% z==Q;DcPuIsD-t(*#W0{2KraWA}F+Y;xgqexPBB3~5+wMl`sULJXwv_zz1 zvRp4x!r=LBOI124?D1m3WC(yl8T2sc2nv200yh$l37e_~%BT~EcrSRg-y?-zcy$Q4OiPh(?#BD5UZHestYdG8qfU84 z+14LNFJOCk%IFoeP_2iN+5?0BC^$H&J=cT}U#>=K?Wk=v)QXu;QM5zzoyzVg-8A{( z5KTQ z!lt($&`-J_!d!RIE3ak;3ve^CYAB^pZ3_!VaCE=`lF1+8xX4f-$}Lx#7|aHNzd$!H ziRrSI%Sahd!)idens_ZyR8XD82a>hh$1?kN$Cf3~B@tab*-F}Gboclg zx0U^|SqDW-`n=SsLn=ynH&1_PBNww55Kp3%T)%HNmOZ| zbl99?zTu@boAH1-l2@%71QvC4$mwToYJh7smEhr!eh%+>xtycGtf#BLN-|RwxBI0u z^{s|c$^XvCSNq&4u3;zGQ_xHVLDKDH7enFx%VpaI1tzu{0eauN$Ww(Wj9ZDqGq39C z*J)LjsL;&kzWIkyE4rDi!n(e2t2RxIh2oBPLi4(u&RvidkMP~0f(*BP%0ShH1cZ30Ps zo=gZrTnhQGaMIL@(U=fv7qS~T(6#aU@Ok5c^>r^GzS=t-o*Cp6@#RqV`#ZvXxzn5J z%QP2&Hk60;^A!OdYO<2#5cbQTcVD?Z0~Jw$z*bD7K~&1^2ViB4ch$Rz3GtiPeH4+T zzn5*e|8l&I`%-I<)n&g>X!itlkcst!zoLJnH4*XHn>4Uu96NAM6aqC66RnbR=oVRp z*V(he3K1(;iYkw&sK395kJ3Dk z^}nJ*iP%(@Fp}I*Zq-D&Iid6-+0=9L&WA6qz#oQ=C@y({)!(_sTJWj>sJ@!|pRWln zdlBda^$StIFqvasFVCf+_DJ_vsGmM)jp!-65TD`zMIKeoFKBoL78$PI+_C7wdVzub z73VO_j&ocMsFlp|k1Tiesps2#*jWbw1A7RHR=L5_nV8G#5U6d?JvJkAG37`^LIfiv zt6S?O5a}`(=_e@+R`{`-6{ywt=NH9PntjHyMP3%@cV?4a$P-Zle+|XJGqLRq>pRKd zW1RAJk!JFoW$N5z%1W-Pp{}5@K(M`5#YdJRE+@t2k1{Ekga9ST5{Ufe2ISDMs5K)0 zL*cUaphkEEc2isR>~fN3=1qUCI*fWq+eN$e3OOnc@)JJxRw{EwE5mzt{Q7(Wb-E#~ z5hBf?bgvhD{w?hC1%YdQEx|dkvtDYIYPA*@`Nf3_j29M83qAN#Iw>^srGIbFb}Lk( zc60kA=`LUU#J9RO7)PZ=FgUB>cnl@(&1;DoDJ#Qj=sQ!j9>u-xqx-bD@TM9E5|Ro4 z-EZa|VonsaOem}EJ%jlvUU+@Yu~x9X)JLMcjS`!d=?y9Sp48}@uU0pzR6_NGo8c?3 zFNuLaQaw>eGp~~Q{pH;ccmXE|w=2_T&pYB50v>kcWk97xM+}T}fp|I3QsY<^+a@+r+e6yl;o%(vF z8JI5W<(lxd;6H@v|32rFAhOiYGci#k;<;g-ULNzPb-FmAmNzadOou+jD*{?`ypdUJ zrkZ#ybnJOQ!^hJ2S>Wr9$13(kSBJC=BtlCKJjqCkhg{voXuRPKh~KM#J!4U*J(^l? zUt|Dlc1L7#fN+gnT=%e@hgD=@(XFPe^xEC~_&K`xV2OgLN+!8UjHZo}iaI=eR5q zaZB3OgGduEV@@qZT=f$HBdPUDKpgYAXUP*GQ&GC^fS(hN9k zR)r$8|K^-=)sUZF5CdK%vLUt5`%`DNi(y#AAIt$%Q%59i`wt$0i4R2Y7H0CyKRA}W zd!v0%`yY^1PwEi#nA}7w`yyFsg^rX77d|^&$eR!tj?kh6Uj`;)ZAM8)OpH=r-qsDd za7hfZ2Qm@bFsL!;Vko+iu{!Dy{h|sIH3!d5VJrym`b=+qtT^iDcC79kB(W#Nay9LK zj_L*0R!=!gqkIyfwos3Zwn7fU#vRzGuR*maTJ0nj=Nd#j@f`5mju2Gv)2wR~(PH*0 zL1E=3*60A2o|mDp@jAmq7gP8erz4`2?m@7%3oo{fB(t7>B7_k8cJ7b62yG*A4aE$5 zNXn||xL&qMdy4}xM2VPnT>3v9^2q=)t}aA8Z%I(hSBol=alR2*WR+|LKRjtVCuj@K zY2}-6bD3DrvTAfXE$4_BVl75yGd9V}a&vm<+z*%+!-WngxgpeIZ36^V;!jI~MPt>2 z>p|ACI`)>V9ca%M*i+6(SmLDEXkGznTkQ9Tm^Gup)U$?Sk2_A_!Z^|Hs4x)){9};(C1&!;^8-V&S)7ccW%k3vNw9(H{|`#du|V{tA{Z*5fS(RMuQ83HksX z08ZF;@6E~VGii=vJdsb#gr?hxtUE%(u3h7|>#G6n+p3nVdFM<`R4^aBJ2F5s0OG7- zRax~K$a#??7J|c{uv9qaiRy4d_smN!PP9H6i(5Q1{EXnvcbLL-wRx)g*|7nb+CwFfDd;J z&;Cp*`yWFS4T#*h+%9v_V+yBy{M}SCivdZxT9$1^$&!oB^@gX2uAkAXkxS+w`lh~O zpWpRh>r+U$y;&wn>=|@pHc@}+8+`3lx9wTnzFh7CsHBmwmXqVG2|3;z?^W)fpD!}s zwELPnabHWoVjC5`KHuG+t=Mm}AFbk(Uz%LyXbK?;zH?+?TtUgKk}R`UeLKaWJWOCh z@sU>ZPa9RFS^+q9wcM9C{|*82@a_$94gSWQ2KD?bv(Ef%3^5y^SULGC_r~kZg>NdK z6)g(C6<&Yr7phvjpviB57DIfP(s=#$kG{Q2oUXQ{3>133eMNIrrZ0 z{f51EcXf4j75^F!Jm*D)zoGCY3!>5=y&a5LWoNa~i#x(ya^I`Sven>E@#wY}63Xm< zh1YJ2-r7G>dkEY>QbV|oj3Y+^D|a5pf6rG zlUOgpOFq>sf&AQaG*OE3M(Lu#K%i&UZCl-`_@mPD`^&R=^Hx7cs9+-V?^-HHKf;I8 zpj~eKxu15UndW(n)6^{J1yVydT((NWDVMR^-J)pWtF|%t4w2l=6nb18Iq&U2G8Ej7 z^sm8s)nCzskd>nLHvXZVVyA{ktPk7}qttkqF?c1YO&?Ck2%HfUP;W^2W^!!&{Q6C@ zT4yKD&UtXdRumoFksX!mF@th(gru$22KsL!@)=VCSKpqZgd4BNQp6+as~rbMUl>mHlO~fm|MjN zoE6LAm{q$!3?KFt&a;@?Oc1TAEjqp%VG)g`hwj6OFY#$u5_4z?oCU3p-O6Jd<~m@{ zZQf2r!;LL*6&1dP03%1pAx!_)r2c52D|(lUq?3fdB~Ka%$I6%pnn>?M+8|_cOH|sC zZ_F?XsE6qh(HKzwcHUZ#9fosaU6lKV(p;DHUc?PiLU%#?mon9XA66%T$rl*SW+_J~ zG{S}4A0K3^N0Ag-dKqOYoR$PN2F}ENIS;X?co zQGZQa+S9)6nvjsicBk{vfH@7s$6qubz~WJtsjc45U$A%`khRq}dzw?+o>+aVWWDZT z8$u2!dT3dg%V%Ptp?6iaO(gp7gQt~`$M?@@#I{!jx~PN~s)}PkQ%+m#F|<{=^1B=V zqJ4~K#rPP`3TT8GLM|bk6r0ESJe_#p?Z^)J3Fk>UiTYVE_KU46fUfrikEho@3JDqq zX-6KtaqOM9*%N4g6x+VJ<=DeXPlYm#pDL9$`m`crVy5&n)4B*Pi?ukDHoFTwI-D2Q zbfS``iPmwgZPf*uT~m3At*rJnhvZgP9g&A^rTvrq($zb@FkJdXpM|&)0I-OExZ~^1H=t z@fK57uBq2tY_69j2_Yu;9jitucAVnuzBD5ABPNiUePTvh@Q2~L)%l+9^vj*DCr}N5 zh0l*ZeV*K9bguo$YhG{;QPv-~_FAa!z!^P}X9T_sWBTsszy}!wC)oF#d5R8paCWvd z?9Jc=CRqjToK|AkU^gm>PZY$CxA!VnB5Q*-oDY3TiC(ROIroZ{(*FC=My?OkY(Q3u zVP4t9^gg`m3o7Dj%Qj?xVfUk7UKK<>U_CX{7yGIAdd2fxf6mdUx_(S@xM4kJKvG&1 z_@#Bnzbc*NENk>Cyj%EUr2@u@pA>1ZfggpYe(A4~2FVaU?W#&p6lQ09dHnkr&pUyw z<-yeFcO)NqsA3H!jrQbCmK9CT3d%~;j;E~63zQ$r;WwPSVK8uVe3xvQ?$?mua7!}$ zyqMkJ*)=$C32+uMi9Pt2B3nGZBNGo9Wt+$35MCfV$SA((4e<5N9~p}<*Ah8ZJPsK~ z4lL5i#5q+38acQoeC0Bq@XrP1RWV3KF|KS#nbprVu&B!xva^OvxWuoPMvJYay;_%}$K6I< zIk%nmV=gX?s({+9I&=ruy@UK;4i2LXxv!P5w8S2XEIT9%`8l+d7;cn4s-&D79ZbcL zbXcY6a^x!D($+%J4^oNK3KJ4ghL8=wuzYtysN`514X1U}AMvvQRSi#(5`1^=B>k!T zqs)1QS4lagf&X{QN)Es!DU`yogu}F99qv`_a26#=psCzdzFxPj?YKmYOzt2XpZZUc z_*Bn;o>c_C!A^gLx@kUw%&BKHDqhkQtJPy6p%{XaJqlF_(w_%=T`byso?U!9xtzKw zjG}ARs3jj*62mffIodFTIF!_;?ogaW&Al8j->dy7_VGzwEe~TQ@UCghnx2^MnArXG z^C3KXU|;JqQGQUUK|;wTh*iK$IW9MTuf5~whhN79y*RAGxak?p8>G3?b& zMZs`81I8(H>HBFjUq&2i2rZU$LIKXi3Ljy_#*@_-SsZi0BQ}ZISEkUm{huF#1-LJ* z)ii;o>2Hi%0niWH!d~D@8X-QbYKVYEo!kD50W@&}3`KDg`N-1{j_Y0)itu-gZ2s_r z1&%(|Yz`E>Bm#D#hU6Z$u*VSyewSCQucnC7Oz#AWpc7#^U zl=_~f#q3K7&&>pVNo=-5=GVqCTlpwMl18XQ{A!D97u`mPg&u4g(O^2!lha(t%+VDm zEbu5?yJ;6R$oCK^r6YUMj^t_G;>c7-E$V37f~5*fsU6Lm>B&{QMA0SCgxI&q^hP0X zgFXKca{4afhqy`usrh*UEV`I{j3T6XlQ{tZ@SD8%BH}h#@S5>E4d7**LGgDl=RizZ z{TGpMSDox_jwCo7O3Ja3fRmt8i)M-I%1E|X7|cq)m?V!bReZg*>QgHnY^|GKv0P;tpR4COr zG=Y?_aAu;BJW^zqCT?Czb@h`JOB&8wWPVCYBDeUTdoh)Sw}to3#i$NT;{0=OQc={b zf|VyUQc~}*pDiy^W_(wPVJ;g>eJy>74`M^~h%kr46WaBZ+;mA{=$md+Dg_%wIQ?mV z71IM8GOF5?E5&>K7SsNvka8b35T!zxw4DC*ek`kkosk0a1$ol&p@Vpmzfd=zArV&A zs7um6*5&;2F&ymL8okZLw`!fG?kMqx8zhwb^o!vX|5qb=!5%Sh!Q@oslcNj&{;zl( za`i(NI=FS^&;tdJ4Dq4Hf`6>CxM{o;9TPbACn}Ea>?L`L4hr~t#hz6R-mYh%TFPjE z2T~74x9`K<9wX9G+Q(J$5^`-mNFdS|1nnfG)toboa#bK9{xQ=;t~?s=VjPLDF_PN* zO7s$^5O0lH05$6;G%WIHvW#{vXcIotW-$-COs2PYD&cq1lV|Zj%4Yy zWOgH_+QcYTTujxxE1fE)>R88C%r=*Ii zrEU@?qW7hmWW%a@x3O6vexN77?TQrD7m*$oW^9Xf(!iS|PtVw9^|vXuG-nfYbz;`# zO!yLuj>KjyHmkkGPIlKt=6d;K*>8c&Q=Hh7cmA`?9I(Q=ztolQ;B@t9VC)Im3P~(J z!wa+4lgHR{pao!#cf5lAIV-WcSYfJdwi9i*V9fPVWJ;Mca%gj-J)0S*M7Hi&(b~BGJ8ZNk?yDonyLz~?QkUrf@hi{B|Gjqk_-=jHMHKE) zefU|*m|#-%iePd@bWqcM8HXB{g=!u%HVm$P{StvEuHhe{OIvZfVu;aT^H_S3+{8V- zUCbN%=X@p!YojviS{)wFJ$6Ey!MkF)h)nw_t{_h7@Vf{sD?3mCl~R=w6n3r3h;?7T z5f?AWvi*zNs2Pj;n11s6I|`jrRb9T?21K-WI#XJe1Uypm-iz_gKN*kWZ#p&%%ceg4 znuENTg}hgYYoc4?z*2Y_MzgAedU+NlJp*M|;kC#0%_u8rz&A1WgRQV*=LFihUqdh% z$p9yy@*3Kt+QO}C7r^?Y>~e*ZmY76&hltf1g?eb=N}tFu$^-V2_4N_rn{Nd-%4>^GNgMWGF`jZ%8rLSQbeQYJKi*9&6D&&cdr!+or`ZZ*=pfp4p-^x}i+>PTgiql&0vv8iR85AgXx~%T zBZKBBM8KjCe~YWv+Ibt)PWb%%v^+Ln5E*!QGKuq%L1|G&=I0K%zPep@Dd8G1X^R52RpGe^(^5$& zjG>PW%{QdflmL$94+Bg)2Z=y?=hWjG5}{Qd3w(Cx_o)fGk1L!DL9$k%_`D$*_KMPY zd-@V1hi33O^~4}>5H=PLssoxol1y?Qz*zj3Thz`AQWLo*cR~W~tKeMRIH>=FhZK5h zNuVo&vtoZIkJ`jXFIBUjxT;7) z7TOVq)CigfQDc>cC`Q=am9nMb3=eeLl$wy~o8{)sg&7P*X-Qv#!ThV44=|3>}WZ+X$4D>mm4eukbn7wmyb!A%>gYPV&L1R$|9ExlS` z^<3kJPGs^#ME?B@llN@g5#Y82?zf3{cfSM-kc_AN*9$HO&qWf?MQ1Sw*qXk9V|?d0 zfF!M$eBPB5i}?=Co@&0~?%&wR+;$F%^j?JXDZXKNSn{K}fAXs7%qn9CagY#=Rj^!% z!A%!v-8)s-k{6ZWKwE3Om9XQo-{S6Gwwo9|m6oBR_C}s@UC5C*plLE0-rH#KxS>9f zoAUtL?f#3n{tLY}+1BqxhRlI`5b}#a9}&tJM^bSmw9b9y{w6n?QhWNXMQtCvdGXW%WyjR zv9{#XvoJyqu1(J-;9^VYGY|F198djDdvGUOnW3YQ??hS=Kt38R{gdzAotXYlJo^YE zRer~{)&PgbcZR$?@(!DTD*-rqn3D`cT>1zwxcrJsPr(2CS=+iRsWvA7Qe<*%+=Wp6 z<%xxqYB}3WN7v}v2h)AhEUm+Ndx69svkscV~oiKn}3LQJ~C%Ej36IM zRc1`H*I|lm(c+upSAP*^Njk%O5+6j={%9Jdw8U_@-0?;$?DTmN_V15USX&)}9I(?2 zA>irv6W`pS%0ZYe|I_Ax*1p#*_k2mkQb#&2tEIG7bS~J*E~&itTVk~-ie&;@4-7D? z*$Xph0MigwTJjrmErG5fT-p4?k|O@WYWfj~^!Nc=|KLg>M>si7u)*thy2K000u8GEP<2 z9{KF^!?NJ=g;`I)wq0e-nYI&kezo*P1WO2ND^Ah~_x@B_VEgN{cp zD8M)MviM76J}+6r42`5vbDku^_@A6U%2#9i>-xto_Y1dnfxcu|=&Qo`*{~xcLvl%5 zDZ(S!mC?)Jeh4ph62$$LLFxqbHDts?)ZC!=+;_k>%;(Ui{P;>02qrU<{~@E?S|M0h z?ua%#5m;}Y!R%0&B93st{*XX!VP2s_V539&?KW4Aj_Kqf9b{FbiDALAct-dIJUS4e z+5e+=u10voID*d_bDR)P}N}chyZ| zL5sAU*az>m+c*~C$W^LCqxvRx$`X-lsF|YR9Wm0o%vH$v%{?$I6W0QRMDv$3 z#Z%Fmx2`_^ziUQ0rtnV<9}b4GrhN5f-m~8qddk$&dUetM2EK0=t#3cW(aQIJajZ%N zumTBKJ-ifj{go7|2KD8rp z@wmkeS=?t9vbmz2OUmNgUh03fr}ZUJaO^_8^k4GrurFA!_ba1FJ0aOT!C9nI(tVV}F=PUcQ3#}A zhrGu?js|zQXk>6A#tlz0hz&qD^q;Cebp3jvshQ2ng*G4L$od#Gc@CS={1D zf&)<%MC2aYMy)uHq8Nb1geYZACNf_Ne>`{9P%X>9K05)!`5&U0R-V$4=JD~z)K~dl zk=2%%24`Tzi}wjr?erKxPI+89)+u%VcHaSrH{p>)a81%7f_%YbIXN~|z@x}v%=b;Y zyB+t|S|*&v(Mi299GyxPMD?MB=~sg9UwA-m^*F~;BVFB*lc4u6Li!fvD!b+lNXV1# zyCuW*6ld4F`^)J&kJ;sbp7x+}%}w(|wxmJfrHkZa_>6Xl_HnkkNqz6iu!r7SypdW` zdPVyl)cx82=02KeaJazxSHi$`8WZO|#GX2Pbf53O1kgI*i34Qely+p=NzikzryW9_dLpn51Npcx_rCRs`wYf zRn7KV4xtHN*|onq7>`olLJyl}T{fxk9CjX`B{<=;U* zc*r_?fAs@eocAd<(zO@(m?Ybgi!)fnEB}|hqio8$xfOh0dS`t5*Y|jlDNtSxZ{!^{ zgJtt<^6=`BQ|{=@M-*;pj0$e%82UMoH1qqhkdBP_3tU^S$raIk1ipJGP9ns0p_}3P zP;Ii-b<;&4U$ATAJn2@76&r+WX+D(aC&X0AL|vI_50?!73H7qx%J+b!wmqt&yFkYt z9E3?6tjNywi!8dE(|AM;^yae)85u3Q4gBC0`4e36+9>XH-Q4Kt#GTv<-*mTlr@_}|558rh!2WZOR0-0b*p z64B36R?l!*?$nh&>W$Z@p1UwxCX-&5&A!?cPn72mN#9%TwhL7T%$HE`pGRw%d=2>G z6T-9g6{)+d(;a-s(}IVQJ`iTtB|UY!I9dA^E7CG{lf~EJDk?DDty|wnrj?lYjxc+h z+}J}!?nV*CbeR~mX=<;u;&+uyKY+6zOL+QT-KJ9K?cNF z9tCuZIX-@xy#yh>@r(EM*e1`HkYFy6%G%6pH(t)ZxZN`C#z_|2tEbRQelr!tD6N>-}=DDiqdUi^nK+HJal|Q7o?!hd0@IetmR$CNeY|b69Tz;x#J`!CQ`2 zQACd#f?KsR1@HLHSaKh|5#Rrv++sD!$~|+SU3BwXsu4utbX=!(Ud=d#THZZTf+XT$ zBl^32IzC$}Wv+DVqxn8)~)0 zecnef0_PC%+Z+&{3GWvTqKN*_zpNOkK%YgkEfcRML$+TDcwl+7P5q=E=KRAmSxLU+ zA-t3bg7-6!WK7D7`j`FI?BIX=%nzMy_PE(5%;rRz`{31SKDFnCE5FyD?!iKz<_LFo zuQ9N8Y>e+dj7q0%rM)}}N+OK><0`spTRm$NgkX*CIf^D*Pr|V5;8Mx(?{>1T7|C1* z7QT?O4W*0li0RCcY*v?FVK5_l@)KGk+Ngh!w)@)nT9}ZWhUu{-Fir6~-FOEFdg{12 zu=gE@@oOxqy)zbierY1r8$bD%LCXNe-latq;HC0~^mAg29oQ6EU0piy#BuHek0|N< zZQDt!nLh$IGA%TfP6I4=ku;Zl{9!jkUmJ|d>~HeWJ{HT&qk-B)!>ueZ;K4Ba!Q>UE z7a_0tqTEd0BxyB$ME;xC>r^2b36DWP*Q=@?e01G2o1|E-H^R^h`wNy4?5WH7Uy(T$jY*B>=`iS&+YfpI`mcanXQb}n4F&?!6b`D(lZ zj>ms=np)r)O2B`Vc~$Rv{VDk`=;((!=1+r#g1&E^MY*2$&|oe18pIip@iOFSwR91`u6 z1+xFDlwA(zZ9sv5$4@%`C7Tb1jMzi6L-hNyoBv@=br>x!t_Yktw9MgxNh%ywtjlx1 zr`4O1S+}cw7s-QgX}rXYnr|Au-5Xj)k~2q5Y7ZcWP}sXQ9aWfcAyTLQ>i9ZA>_VdB zfAZt!dizBHA2C`iMNglu6i_~xyOK~DV)v$@KrFBBrcJ}E$h|%_PEGE3`P-4sp;$&O zoyy(+tXBY4OJBr~HVKqt2roxZHcldeLsXQq3%b+3XATuEqR1u)`=yNjO@&BdObMwe z40C&i2o!AV=kK~77+e2=Hk=KN!@{I1Kp|6*>p+>HNONorlL;1)~7ZYn}vpT~TaH$#wq@81;ZD)e7s0vaM*$zYL)3ur4 z9OM-?ILbnFoNq54eRY)L&`K~1FX2{?4qZ(u?W9D0Eup%l=;7}lvKf};W!|Gj3J#*)h*&PpDd)vUN4S1A=UzwEC;nt3j9Zr8nE``S@HnS#%@f_8Py<4cVt+ ze*IKlwDg)v%@tH?jnIh)ZGlQ0$zk3uNkTPL?gHDZTX56uwdk(|dW4bqRVvGn;aST( z$?v9%J{5Qv%w-)-^trpb+FI)u4+S1ePky>o1pYzIPm+vYB-8Xr>_(%6i@No%47m@E< zX^YYden5@w{64NnFY(>omAhiAnO=yL83mMNayZ!$X?gZc5Z1A!+V0*|Si=N#taG>q z%0Wq}VI$P1R{A>CX!zIR?RdIX8Q*+YGH9}S+7Egy$?2LeOL%G%pTdcb>wbv{`!UXq zt`W#fNP$~~KZIcd;x%2gHJECB6tU~mVZ@tvmvs3ryGJlYqF;#2VzxQ-_4s7iR~%%n z*7~E<4;{rea+orA{*uLk{m6FdrnNpSiL#+K!LkT?gMNOmIBmb!+(;m&Ud--& zv>k)S{Y_n9xY$EEHzt?yq77YsUiqr5+~Ji%ugq7X1UHU>xFY()?I*$Be!kg}6#7qp!M=+ zhS_4zK7TbkmS2qki-t+PelApF{Yb(kHE}4j8nhHQ z;w+~+U!|##(+ThL7>9t%$dYpwfU%8@oeK5$jCBDTQmCgj!z-`*iU(#OE3MucO=>xF zw-93tGdLglN06~ z={TFPMSy>*nH^fbYAw^T&uFK6FqTd@BDIq+vJf^)Z6T2qaI%xOdA)lfTM-AumbA!$ zD`L^188x5lry3!?lzEhgmb{L(dFIqnw!ArS?{@rJ3U?$xVE=srE|v9vrv2coA@Dd) z3gKe_vUHOq##cvDtan!hSML-odLNDC19AVas;-FzHq1=wdG?Hz!uL2|AS!X{{`?)o z$|EQC*VeoOv6)_yx9~J~OWc(S78rh`n_VB7F_q;<`ECQrM9R}Lodok;zfXvZYy~76 zas{cLcf#ub>Osm<9?&}0uwZ0)8dBoCrQXq5zT5cR zZk>wHq~%+}1qkxTdxHyXTC%^9h+O?kk`gnCWRa#p=c8Mu3LN+-xX1N*LCD8}eWg)9 z9?Fs1q-HEBIC8}=Q@J837zCPp8~d3Pu~YO6uPRDa0)M#c>D8{B`#H;Lxl!p+26UAd zDJ1Lh2+kU#N1UQZ4179U9^Jd5w}aZxA4Uw)-yx!`y0fI~zyQw5fPrNWZu;@BM$g@9 z27EEC!AP_bk~s z{UzYB5b zb2u7Ddd4PJkR%9OP13Nr&*L?JNguiK?uus0K%3B=iiMk--a?ZYVXZ7j4MI1`ls9k=M+?H#h==I?CzSLaox+ zVGdiEE_5E^Gxr&eztFaPAv^X>h>~17dkmJxO*yObn_#oAh57 zM8XI<(?Pk5B$>T2V)alglV8Xj|xn@MOBR!)Alx%So3O!mNTDxmPU^HdOm zE3pOT^;=+OSL?J7sgz)M@D;(O&ppRd1oRd~&+rG^#-HK!OY|ggxkGR=JFuxMA z?u9p~LJ&TiuMMM&?j7FZ%LOG0*Q>%%OIf7hN{^CgB=SXM;tGKR?|YwG5S8RN$S_!X zC86`8UP|~CL4Cy9u$bp$xHxSr9Eab8qIr*aX2ZSZrbV2k5@Rlj6&czVOGxZ%?Uz4D zFH1BW#38iepC-@IH3rL)2p3See*2U>A4@ddlOU&tGkBS&)$UX`xU3EqCOLFY_t)*gr;hq?pUDF~k|jt@ zd2JPf!*r+1B(FyM(WdqAtkB!FspHPdvN;cMJJwbty$fQn{F{VfZB&Cm^uL;0TI@%? zO|SvIW|n`;7?caT7W#eVZNJtwXX3w6yrcYg3OA&_wHFVI8gYS-rOD9QeDY@_Z%hs? zJ9@xAYB_HpzMTv3Fmdu&b9nLlm`DUI3J9kYEe-r)+X{@wn>J$5 zU7wZQvC2#R%}ck$##+}&RmKdtxhv&-eT4|!Tc?a-3t@}x_-4F43|HUzzti>(Rq`Zn z@i};mHxe!vHzH}H+K}mmklY;^YtU%wZnZj-~mj(%NqDfp@U?|k2&qMMd!Oie0_XS zRDlKfA66LSv0kmpA1XjXW(6ixaN61Xxf;~@nAMFP$>jGw%RUvBp?( zjv>1Yw!p}mF^3x76n0tW16?$tJ&h{!z1${$(7?Zpk5MF=tdiBnC6VkAUcYV(s^Of1 zxY0o+ZDo@8`+cUquQ>gMVJz1{)JL>4a$+b(apd$q@~^=fH&1%pFr+p~E)Th2D%hz% zg|MIcEfB6P;q-di=ng@uC5zZp2KJ z^sSqM@z&#_`&^N9onC4-+g+WT~YJ>~G`={>gjI@e6l z^(f_sKmH=^=M$X?wRgE_j~M#v(|XH3t_HK|e7`rgNJx#B7>XOUH5OXFR_b3)F~Ahz z#QxKqLZtjF(s((5maij_z+@a5rTw*&h4?0z_h`7p4PoWQ*chXb)?Y>tqA zI0reolrR+vA$3?i)vsdSw*_S&%BpBJyz8YZD3bFXJ=vSQ0vM@0CWsj~f6^z)a#DaEvINPRZ@PfH|EGx}jnxA6}x>YV-v#|b! zweOPaR;F7Nm<$Oh4@!CJh_5G|5`{eF!O+8Az75i(70CM5pltl^0pZUTAfwf3N9N{oDyT9BlQfNG(Iul0llrF5UKz;r_q826A;iw^wV?(d(Rh*-dFj+!Doq!0^3jEAthjG5vs+l`ol`@WR8g>Yl* z(SG;e;X8pSQ#=Wk3B(zd&_;@-Fx&rXKW|MCS_1>dsDLTYBSHtv=*egnENx{4Z9WCT zh0N0n^s9gH>$S*}b)y?mkNr@pP)v?Qs6QVKu2gp(4Ls2>GfoHHca}ToteDqI@ z7(e?NNpZ!wErTW*lZGF1wlAgqI6BKQ$o~+2-8ZsTa(|w~5me?**I9JUJ9%rUb~mTU zTW)Pq1=2{DmU^kSBPBAk|KDsib6txw$!D}*O@(5KUcdFS5;B0ze8hhuzv;SgoP5C?F;P1~C0m%B&}NdMjSP>r-EX81~lV{9DXO{S{rx z`l!i`k&nig?F(A-NUPE2o(z_CbY!YreuwI$+!0Sc(`7H1Xf_+3NgPg?$&j z)FyAc^#^<_8Ot9vU48vbU@~AL94xe%ZI9D&QlnuE2qQ4wmR;QNhvOGBjIjNi-RT3p ziRQCnPlwobb%xxX*d`9dK>?X}^>ybv{?OuiTY(lHv6MkM`}TJY_N}02<=Acq;`P1j zjV|Af*$0Y8rer`B5I*TJ%;lLI>A(|CJ%MlJ7$2VqRB5r%5l{vDsD#-Lqf};&)eE!N z1i6SJcUPfjAb7O?Tkop0ZkP%Y!suDHRpdT&!cT<|QKv6Cs{)Zv;HMbny}EWgdk*C7 zM{!AJwS?gFUL=*EI3 z=?F?NKoG2U!6rXXI)qPcg9}5EX7RPX@1eSMne1Ecd^(hj+^tg-es>BpR{mIbTkkqO ztrqNiTJ-IEEtPyKy%^_wic73tzU>Mr*?JEsC|{QBeLJo|zCEbuWS{EGfy7r?aZ)8a*nXIWyasBX@5SUON&%z8jQC?}&*avl&bmylk&~TRVY0~-c zHLvkm_ggG?5ca1mlk5oRTyV=$G`iNU>5NE(C$nS#RW(bncbYk{O26LhD@p77997?l zLKI3gMcNvV49;K{XJ;I80O|j>j*@n4HKifl!)4x*2`U z+rNH<4ELkMHd~kN_H?lRpIx%ZF!|u({SKG> zP3z5mAJ&+R5`SLX*w0LPeJkny=x%ugAlAIqXV9D<^LZvD|D)#FB9|YW564LpLJ6pC z^Oxk|m(Qw1fr-F}NrM%kS(UNzLkpQ9&%?PSi<5}K|M{WS)uv0>`gYR2sY_?-FeA}) z>;PX$(M=jNmbMuXp64PGF$NIPZ zm<$m6ENJ|FH{@MNVao~_MbpSDwQns-RTk`BHcp6sI^_0>x*#{kYi6cuMIFZQvB3s8 zx*fgl{%O)M_xGwl8oc-$T7{6d-5g1F9w{`~IVq>|j@Wf-yQRa7bV(c0$^m@j%kP~8 z>P~fJvj_-;=Cy}~9ANnWXNdamjYEwyKT#etaQ_sv_5Qlp%KVb>J`fU{Kh4P17bEur zYInUwkwU>-#>x_wpy_JTc&8F2HNSsUfoRTkb)HVDiC&yxex$^MmMy>KS=MB7FHW;F zyX?tjk~-{xAsb&MgHBm|;Q-Dku;>VUJvcL)N}#5p!7D6wi+`!Rd;9rBSvV^%x&Lx# z?nlFNX+Nbx)ij$(b+abP8J6OgUzi=#{3O!P17=SeaLV)M#rS#eebNAoZ?49M+=HJ# zS5>t=Z@AqEzGX}9M4ogT5wuFaU%VL0w+Go>|HPK8!3yoW)_>0{vHv}5(q4AeTfyW& z@@K0z=%MN2$=ME1)>@#AdTjzm*j~uLyH09|>7@N(74wk$Y1Dw!OMWd?4xz3CT-(UP zt`t8amom}_aP?h>NmaJ?ESm|D{xq&9ep>OC3jXUDOz4qttX_YDlWq=onDB-Eaj|_1 zJdUH~nEG+gQ$(-o1dx8GP?;_*b_^&oF_fE(XQL-ZY!Jx0YFZ3UX+@G8jO6$fB+9G! zHH51^m|{z}4z6t#YtN%Yk}^ineGAijD(%NH#j~dgPKhho-N7{3FBbXhD}?E16ZwLd z;t4lse~~wGO_5bB!qW^qFV`y;(uaTET_%DN`?#N4cuE3L;g{?eS@L#N!+oa(6wv zeRw;I_8*;Vy=ocZe*oqo+xs@vODP}n#DTPrslKa#>m>IVxdqVcj8sh4L2z3ff#CIB zCbH7N{7|TU^#_j~0_s(Mq0|j8+ULW%jTZO+=$N!km#0XFU4*7c-M!G>FR_E)|7E}J z_L+<5e7GulsdyJoq5H-AC(`wvqT^C#{UPY4tx|BG?7Z_|YU&F{}I%Wp?1$}z0hb6}&u#@gLMz6kJnkJbBlu|rDf>^u$JV>1Uu zrwUqRC!hWheWfpV!2IGZ22m#c`e;Z;te*(iTsJYcSCtrhbwM#QdQR%*Mc>AP)G7x% zFloUVVJAly)Ea4Q~y4^~L_aBRKO z=hicS^6m2%;t-WHMba)&SGihD2pGo6PJjoc^@9oQ3Y-^h3HNDf=x%34hIL+MiZi!N zB0;`P*{uIxS-9jpEm54fWcJ5N)u%xh?kOsQ0pM?9UKWC7y6sE4M=H?H_bFmq6~gVQ z3wqp8?LxAB@50u_p7UmOwH+hze*-&Z)UfK{KA9(8f6d68fe*B+Bj!b+rPQ-VW&OhC zSV<~xLPS&H@`O@Gj5u@V)<^m|ut2qN8D3iRL5~^OCV%+BAOL|= z|CkH!Ey9Sh;Vi_x$II(a^>al$6FfSh#; zq4aJ;)A5X8^<^Um*4G7Pm0GFt1OZ!@|9|fHc+|AoLNk&#V&Nt|VXDi*Q06k$w9#Q* z^B_hfF0J9w{T=r;w&l+-cMfbBf>~@7ioP@g*A5!8Cm}TeR-4Uz#tg>pN>4q%4kkxe z*6oJ?R=xYRz zUwn*Bu!O#f+(D%}>&bg9ydx&x2C^Ztz5Wm9=lNaF&-s?bSZg_$cj)RRS(8Iyqi}Sb z++q^<&3*z86@RbLYcVc`k}~Rjkv#Aj2^=wr=)I^}#1j`FYUvVyRFhct8a1TyxDxoj zmI1hn>Anz)rwA#KjCC~LE&HWTTz9A;tnw>QdZF5Phdeu;SXq8uisK)6NvEQu3!)+p zD)prhQj6X2lq>*ub!ZlB@(_rWP=D2p$~i-PGb3D;368#u&SRPwp%JoFGwK*z54|Z$ zJ>TjJ^c9_pQ4qOIzFiZov@q87DTVM8%{CMG9sAjl-y(96J5;R%&B#W*J}vj@MU$j{ z5dL;0m2!#b+UNg#fA*iidL^c_qj8=UQ|YEQtZ_VrMuU)>n^O@XCqT|i@G@-*W=)9Q zM}!prok7JUhK3p~hPSBR7SnEn?fGUg0WyWzl`l!fPTG|C70;;5&<<%G>*Jyz<(=2lo^;HIJDwuF zd5#b1YAd43e}=?9LXBn}Jy3Ypj83&;4!XFkt?(YwE- z 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 k-G)KD$K9yV9gi460Ry|I9GM2P`>& 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 diff --git a/api/core/model_runtime/model_providers/leptonai/leptonai.py b/api/core/model_runtime/model_providers/leptonai/leptonai.py deleted file mode 100644 index 34a55ff192..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/leptonai.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class LeptonAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="llama2-7b", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/leptonai/leptonai.yaml b/api/core/model_runtime/model_providers/leptonai/leptonai.yaml deleted file mode 100644 index a246ff5356..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/leptonai.yaml +++ /dev/null @@ -1,29 +0,0 @@ -provider: leptonai -label: - zh_Hans: Lepton AI - en_US: Lepton AI -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#F5F5F4" -help: - title: - en_US: Get your API Key from Lepton AI - zh_Hans: 从 Lepton AI 获取 API Key - url: - en_US: https://dashboard.lepton.ai -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/leptonai/llm/_position.yaml b/api/core/model_runtime/model_providers/leptonai/llm/_position.yaml deleted file mode 100644 index a85e8e65ba..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/_position.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- gemma-7b -- mistral-7b -- mixtral-8x7b -- llama2-7b -- llama2-13b -- llama3-70b diff --git a/api/core/model_runtime/model_providers/leptonai/llm/gemma-7b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/gemma-7b.yaml deleted file mode 100644 index 2d69067a23..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/gemma-7b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: gemma-7b -label: - zh_Hans: gemma-7b - en_US: gemma-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/leptonai/llm/llama2-13b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/llama2-13b.yaml deleted file mode 100644 index 307f1ea88f..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/llama2-13b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: llama2-13b -label: - zh_Hans: llama2-13b - en_US: llama2-13b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/leptonai/llm/llama2-7b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/llama2-7b.yaml deleted file mode 100644 index bd471e59cd..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/llama2-7b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: llama2-7b -label: - zh_Hans: llama2-7b - en_US: llama2-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/leptonai/llm/llama3-70b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/llama3-70b.yaml deleted file mode 100644 index 9c20eb6cdb..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/llama3-70b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: llama3-70b -label: - zh_Hans: llama3-70b - en_US: llama3-70b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/leptonai/llm/llm.py b/api/core/model_runtime/model_providers/leptonai/llm/llm.py deleted file mode 100644 index 3d69417e45..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/llm.py +++ /dev/null @@ -1,40 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class LeptonAILargeLanguageModel(OAIAPICompatLargeLanguageModel): - MODEL_PREFIX_MAP = { - "llama2-7b": "llama2-7b", - "gemma-7b": "gemma-7b", - "mistral-7b": "mistral-7b", - "mixtral-8x7b": "mixtral-8x7b", - "llama3-70b": "llama3-70b", - "llama2-13b": "llama2-13b", - } - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials, model) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials, model) - super().validate_credentials(model, credentials) - - @classmethod - def _add_custom_parameters(cls, credentials: dict, model: str) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = f"https://{cls.MODEL_PREFIX_MAP[model]}.lepton.run/api/v1" diff --git a/api/core/model_runtime/model_providers/leptonai/llm/mistral-7b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/mistral-7b.yaml deleted file mode 100644 index f2b46ff917..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/mistral-7b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: mistral-7b -label: - zh_Hans: mistral-7b - en_US: mistral-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/leptonai/llm/mixtral-8x7b.yaml b/api/core/model_runtime/model_providers/leptonai/llm/mixtral-8x7b.yaml deleted file mode 100644 index de788ac256..0000000000 --- a/api/core/model_runtime/model_providers/leptonai/llm/mixtral-8x7b.yaml +++ /dev/null @@ -1,20 +0,0 @@ -model: mixtral-8x7b -label: - zh_Hans: mixtral-8x7b - en_US: mixtral-8x7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/localai/__init__.py b/api/core/model_runtime/model_providers/localai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/localai/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/localai/_assets/icon_l_en.svg deleted file mode 100644 index 251a37fdc7..0000000000 --- a/api/core/model_runtime/model_providers/localai/_assets/icon_l_en.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/localai/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/localai/_assets/icon_s_en.svg deleted file mode 100644 index 9dc6e6276e..0000000000 --- a/api/core/model_runtime/model_providers/localai/_assets/icon_s_en.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/localai/llm/__init__.py b/api/core/model_runtime/model_providers/localai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/localai/llm/llm.py b/api/core/model_runtime/model_providers/localai/llm/llm.py deleted file mode 100644 index e7295355f6..0000000000 --- a/api/core/model_runtime/model_providers/localai/llm/llm.py +++ /dev/null @@ -1,674 +0,0 @@ -from collections.abc import Generator -from typing import cast - -from httpx import Timeout -from openai import ( - APIConnectionError, - APITimeoutError, - AuthenticationError, - ConflictError, - InternalServerError, - NotFoundError, - OpenAI, - PermissionDeniedError, - RateLimitError, - Stream, - UnprocessableEntityError, -) -from openai.types.chat import ChatCompletion, ChatCompletionChunk -from openai.types.chat.chat_completion_message import FunctionCall -from openai.types.completion import Completion -from yarl import URL - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.utils import helper - - -class LocalAILanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - # tools is not supported yet - return self._num_tokens_from_messages(prompt_messages, tools=tools) - - def _num_tokens_from_messages(self, messages: list[PromptMessage], tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for baichuan model - LocalAI does not supports - """ - - def tokens(text: str): - """ - We could not determine which tokenizer to use, cause the model is customized. - So we use gpt2 tokenizer to calculate the num tokens for convenience. - """ - return self._get_num_tokens_by_gpt2(text) - - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += tokens(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += tokens(f_key) - num_tokens += tokens(f_value) - else: - num_tokens += tokens(t_key) - num_tokens += tokens(t_value) - if key == "function_call": - for t_key, t_value in value.items(): - num_tokens += tokens(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += tokens(f_key) - num_tokens += tokens(f_value) - else: - num_tokens += tokens(t_key) - num_tokens += tokens(t_value) - else: - num_tokens += tokens(str(value)) - - if key == "name": - num_tokens += tokens_per_name - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_for_tools(self, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling - - :param encoding: encoding - :param tools: tools for tool calling - :return: number of tokens - """ - - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - num_tokens = 0 - for tool in tools: - # calculate num tokens for function object - num_tokens += tokens("name") - num_tokens += tokens(tool.name) - num_tokens += tokens("description") - num_tokens += tokens(tool.description) - parameters = tool.parameters - num_tokens += tokens("parameters") - num_tokens += tokens("type") - num_tokens += tokens(parameters.get("type")) - if "properties" in parameters: - num_tokens += tokens("properties") - for key, value in parameters.get("properties").items(): - num_tokens += tokens(key) - for field_key, field_value in value.items(): - num_tokens += tokens(field_key) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += tokens(enum_field) - else: - num_tokens += tokens(field_key) - num_tokens += tokens(str(field_value)) - if "required" in parameters: - num_tokens += tokens("required") - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += tokens(required_field) - - return num_tokens - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - prompt_messages=[UserPromptMessage(content="ping")], - model_parameters={ - "max_tokens": 10, - }, - stop=[], - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(f"Invalid credentials {str(ex)}") - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - completion_model = None - if credentials["completion_type"] == "chat_completion": - completion_model = LLMMode.CHAT.value - elif credentials["completion_type"] == "completion": - completion_model = LLMMode.COMPLETION.value - else: - raise ValueError(f"Unknown completion type {credentials['completion_type']}") - - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - max=2048, - default=512, - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ] - - model_properties = ( - { - ModelPropertyKey.MODE: completion_model, - } - if completion_model - else {} - ) - - model_properties[ModelPropertyKey.CONTEXT_SIZE] = int(credentials.get("context_size", "2048")) - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties=model_properties, - parameter_rules=rules, - ) - - return entity - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - kwargs = self._to_client_kwargs(credentials) - # init model client - client = OpenAI(**kwargs) - - model_name = model - completion_type = credentials["completion_type"] - - extra_model_kwargs = { - "timeout": 60, - } - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - if tools and len(tools) > 0: - extra_model_kwargs["functions"] = [helper.dump_model(tool) for tool in tools] - - if completion_type == "chat_completion": - result = client.chat.completions.create( - messages=[self._convert_prompt_message_to_dict(m) for m in prompt_messages], - model=model_name, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - elif completion_type == "completion": - result = client.completions.create( - prompt=self._convert_prompt_message_to_completion_prompts(prompt_messages), - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - else: - raise ValueError(f"Unknown completion type {completion_type}") - - if stream: - if completion_type == "completion": - return self._handle_completion_generate_stream_response( - model=model, credentials=credentials, response=result, tools=tools, prompt_messages=prompt_messages - ) - return self._handle_chat_generate_stream_response( - model=model, credentials=credentials, response=result, tools=tools, prompt_messages=prompt_messages - ) - - if completion_type == "completion": - return self._handle_completion_generate_response( - model=model, credentials=credentials, response=result, prompt_messages=prompt_messages - ) - return self._handle_chat_generate_response( - model=model, credentials=credentials, response=result, tools=tools, prompt_messages=prompt_messages - ) - - def _to_client_kwargs(self, credentials: dict) -> dict: - """ - Convert invoke kwargs to client kwargs - - :param credentials: credentials dict - :return: client kwargs - """ - if not credentials["server_url"].endswith("/"): - credentials["server_url"] += "/" - - client_kwargs = { - "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), - "api_key": "1", - "base_url": str(URL(credentials["server_url"]) / "v1"), - } - - return client_kwargs - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for OpenAI Compatibility API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - raise ValueError("User message content must be str") - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls and len(message.tool_calls) > 0: - message_dict["function_call"] = { - "name": message.tool_calls[0].function.name, - "arguments": message.tool_calls[0].function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - # copy from core/model_runtime/model_providers/anthropic/llm/llm.py - message = cast(ToolPromptMessage, message) - message_dict = { - "role": "user", - "content": [{"type": "tool_result", "tool_use_id": message.tool_call_id, "content": message.content}], - } - else: - raise ValueError(f"Unknown message type {type(message)}") - - return message_dict - - def _convert_prompt_message_to_completion_prompts(self, messages: list[PromptMessage]) -> str: - """ - Convert PromptMessage to completion prompts - """ - prompts = "" - for message in messages: - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - prompts += f"{message.content}\n" - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - prompts += f"{message.content}\n" - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - prompts += f"{message.content}\n" - else: - raise ValueError(f"Unknown message type {type(message)}") - - return prompts - - def _handle_completion_generate_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Completion, - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - if len(response.choices) == 0: - raise InvokeServerUnavailableError("Empty response") - - assistant_message = response.choices[0].text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message, tool_calls=[]) - - prompt_tokens = self._get_num_tokens_by_gpt2( - self._convert_prompt_message_to_completion_prompts(prompt_messages) - ) - completion_tokens = self._num_tokens_from_messages(messages=[assistant_prompt_message], tools=[]) - - usage = self._calc_response_usage( - model=model, credentials=credentials, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens - ) - - response = LLMResult( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=response.system_fingerprint, - usage=usage, - message=assistant_prompt_message, - ) - - return response - - def _handle_chat_generate_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: ChatCompletion, - tools: list[PromptMessageTool], - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - if len(response.choices) == 0: - raise InvokeServerUnavailableError("Empty response") - - assistant_message = response.choices[0].message - - # convert function call to tool call - function_calls = assistant_message.function_call - tool_calls = self._extract_response_tool_calls([function_calls] if function_calls else []) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[assistant_prompt_message], tools=tools) - - usage = self._calc_response_usage( - model=model, credentials=credentials, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens - ) - - response = LLMResult( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=response.system_fingerprint, - usage=usage, - message=assistant_prompt_message, - ) - - return response - - def _handle_completion_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Stream[Completion], - tools: list[PromptMessageTool], - ) -> Generator: - full_response = "" - - for chunk in response: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.text or "", tool_calls=[]) - - if delta.finish_reason is not None: - # temp_assistant_prompt_message is used to calculate usage - temp_assistant_prompt_message = AssistantPromptMessage(content=full_response, tool_calls=[]) - - prompt_tokens = self._get_num_tokens_by_gpt2( - self._convert_prompt_message_to_completion_prompts(prompt_messages) - ) - - completion_tokens = self._num_tokens_from_messages(messages=[temp_assistant_prompt_message], tools=[]) - - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, - message=assistant_prompt_message, - ), - ) - - full_response += delta.text - - def _handle_chat_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Stream[ChatCompletionChunk], - tools: list[PromptMessageTool], - ) -> Generator: - full_response = "" - - for chunk in response: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ""): - continue - - # check if there is a tool call in the response - function_calls = None - if delta.delta.function_call: - function_calls = [delta.delta.function_call] - - assistant_message_tool_calls = self._extract_response_tool_calls(function_calls or []) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta.delta.content or "", tool_calls=assistant_message_tool_calls - ) - - if delta.finish_reason is not None: - # temp_assistant_prompt_message is used to calculate usage - temp_assistant_prompt_message = AssistantPromptMessage( - content=full_response, tool_calls=assistant_message_tool_calls - ) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[temp_assistant_prompt_message], tools=[]) - - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - full_response += delta.delta.content - - def _extract_response_tool_calls( - self, response_function_calls: list[FunctionCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_function_calls: - for response_tool_call in response_function_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.name, arguments=response_tool_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall(id=0, type="function", function=function) - tool_calls.append(tool_call) - - return tool_calls - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - APIConnectionError, - APITimeoutError, - ], - InvokeServerUnavailableError: [ - InternalServerError, - ConflictError, - NotFoundError, - UnprocessableEntityError, - PermissionDeniedError, - ], - InvokeRateLimitError: [RateLimitError], - InvokeAuthorizationError: [AuthenticationError], - InvokeBadRequestError: [ValueError], - } diff --git a/api/core/model_runtime/model_providers/localai/localai.py b/api/core/model_runtime/model_providers/localai/localai.py deleted file mode 100644 index 4ff898052b..0000000000 --- a/api/core/model_runtime/model_providers/localai/localai.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class LocalAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/localai/localai.yaml b/api/core/model_runtime/model_providers/localai/localai.yaml deleted file mode 100644 index 864dd7a30c..0000000000 --- a/api/core/model_runtime/model_providers/localai/localai.yaml +++ /dev/null @@ -1,72 +0,0 @@ -provider: localai -label: - en_US: LocalAI -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#F3F4F6" -help: - title: - en_US: How to deploy LocalAI - zh_Hans: 如何部署 LocalAI - url: - en_US: https://github.com/go-skynet/LocalAI -supported_model_types: - - llm - - text-embedding - - rerank - - speech2text -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: completion_type - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion type - type: select - required: false - default: chat_completion - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion type - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat_completion - label: - en_US: ChatCompletion - zh_Hans: 对话 - - variable: server_url - label: - zh_Hans: 服务器URL - en_US: Server url - type: text-input - required: true - placeholder: - zh_Hans: 在此输入LocalAI的服务器地址,如 http://192.168.1.100:8080 - en_US: Enter the url of your LocalAI, e.g. http://192.168.1.100:8080 - - variable: context_size - show_on: - - variable: __model_type - value: llm - label: - zh_Hans: 上下文大小 - en_US: Context size - placeholder: - zh_Hans: 输入上下文大小 - en_US: Enter context size - required: false - type: text-input diff --git a/api/core/model_runtime/model_providers/localai/rerank/__init__.py b/api/core/model_runtime/model_providers/localai/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/localai/rerank/rerank.py b/api/core/model_runtime/model_providers/localai/rerank/rerank.py deleted file mode 100644 index 2b0f53bc19..0000000000 --- a/api/core/model_runtime/model_providers/localai/rerank/rerank.py +++ /dev/null @@ -1,134 +0,0 @@ -from json import dumps -from typing import Optional - -import httpx -from requests import post -from yarl import URL - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class LocalaiRerankModel(RerankModel): - """ - LocalAI rerank model API is compatible with Jina rerank model API. So just copy the JinaRerankModel class code here. - """ - - 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 documents to return - :param user: unique user id - :return: rerank result - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - server_url = credentials["server_url"] - model_name = model - - if not server_url: - raise CredentialsValidateFailedError("server_url is required") - if not model_name: - raise CredentialsValidateFailedError("model_name is required") - - url = server_url - headers = {"Authorization": f"Bearer {credentials.get('api_key')}", "Content-Type": "application/json"} - - data = {"model": model_name, "query": query, "documents": docs, "top_n": top_n} - - try: - response = post(str(URL(url) / "rerank"), headers=headers, data=dumps(data), timeout=10) - response.raise_for_status() - results = response.json() - - rerank_documents = [] - for result in results["results"]: - rerank_document = RerankDocument( - index=result["index"], - text=result["document"]["text"], - score=result["relevance_score"], - ) - if score_threshold is None or result["relevance_score"] >= score_threshold: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - except httpx.HTTPStatusError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - """ - return { - InvokeConnectionError: [httpx.ConnectError], - InvokeServerUnavailableError: [httpx.RemoteProtocolError], - InvokeRateLimitError: [], - InvokeAuthorizationError: [httpx.HTTPStatusError], - InvokeBadRequestError: [httpx.RequestError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - generate custom model entities from credentials - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - model_type=ModelType.RERANK, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={}, - ) - - return entity diff --git a/api/core/model_runtime/model_providers/localai/speech2text/__init__.py b/api/core/model_runtime/model_providers/localai/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/localai/speech2text/speech2text.py b/api/core/model_runtime/model_providers/localai/speech2text/speech2text.py deleted file mode 100644 index 4b9d0f5bfe..0000000000 --- a/api/core/model_runtime/model_providers/localai/speech2text/speech2text.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import IO, Optional - -from requests import Request, Session -from yarl import URL - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel - - -class LocalAISpeech2text(Speech2TextModel): - """ - Model class for Local AI Text to speech model. - """ - - 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 - """ - - url = str(URL(credentials["server_url"]) / "v1/audio/transcriptions") - data = {"model": model} - files = {"file": file} - - session = Session() - request = Request("POST", url, data=data, files=files) - prepared_request = session.prepare_request(request) - response = session.send(prepared_request) - - if "error" in response.json(): - raise InvokeServerUnavailableError("Empty response") - - return response.json()["text"] - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self._invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.SPEECH2TEXT, - model_properties={}, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/localai/text_embedding/__init__.py b/api/core/model_runtime/model_providers/localai/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/minimax/__init__.py b/api/core/model_runtime/model_providers/minimax/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/minimax/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/minimax/_assets/icon_l_en.png deleted file mode 100644 index 5066b525f99c3f36d2f96b3b905aaead8cb263ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5767 zcmV;27I^82P)4ihG1%Y>eBeXACCiemV`emGcfXhK^^C@{4j)LiurJe z57IMRIGse1^XBn#@=X>@-U}3639uDo?_MV90Ji-oautAjXSX+bh3qw1BG=s>(#d#; z5xWJuYw)p{L_J1nl&j^8J>ci^s>t16E2X@g(Q;A&+V$3+fNilEjQB+WmM)3Ci&Oqc zGX67Y(q(|8fHpkWVPX!|f7i@)ig{)OgmZw{VkYDUXwY9`_m8pfDSW2{$(goruJ)#R z73JlOmXoVN)1Cl8SP2bS39v;qo4s55N#rk-*8a={Er)UL#HWZcHohZad2;m_)uB!Z zpibcTR_=(m{RPG%krHp9ohD*ZL$QfWqX6aQjFyuMP|d4*V5SS;JNmF`NkN}7THplN zz8JQI+yn4-0f1g;(bAw1&<^*CRY)nZpGBl(jbrca zqfDo~oau6M0h*V*3{mm`&C6F>@&L`tS6cD_&C6F>zEqqc@1Cz6T)#f(_`6L*aI3;B zjndkmCJ?uLC9*p5?z=V8+e*pcCMASJUQd*Mw>XZ#DxY;a&E$jux@*EeRko~Kcdq32 zWG$fwkb6wFSmAxn*^g~ovwiN)*>q&e@>EOY-ug0IYn#p_pUi|PCR13Fto_b?mv57E zliS&gYe%$yc{wpTA%GsNe4@Z++MSY|ZzIDq34CP%azP2OMf;IAUp{_{?*-b1ee?)t25CjzvK8^ z9C*EU^3b6}Q5r;g_gmc?)%T8$4s~z(^}V#TG~hUnCWK#8_XU3(l#TRlv$nR@i~W98 zPHh(y$T;qItmpV~1GLnR283=r%p@yeQgi^E&uNTpKpuTE_T2|?o+r8den;KozmyF! zda6MhZ7m6Sz5F4PP8l?N1PuTGq7R?oM%Dn3_W+ZZVvHW4$1nabDOX1a18F%#NcL0I z3!*^W7TTJEf{Hg{vDTKpW%uqqaGtJv>veDc6&R0+Pm$Vni_9BCGPcj>2j3wW8!s~X3fvq z+Z&sztGA6!+x!urb^;VaJdtwNVxV6F5R;b0uQh%0P8=&BS=bbhr~ZoO-oYo`r~4du zsYf5LGCZsf?IcJVs_aVx-IqY1CTJM)3EhC@%n0;=_xNuT&FA0 zk1}*W42*n?{l6o3g5`vDb;YL`W&y^B)u|nhBj2v9tbD5nq{j_VPeSupI?c|W{}0j< zzb)JQtKGY6AFV3zn#h?;wUDDkc;$tAY9Clddnie#P>Kq5Xe%*Wp^i&FU$dP5VXrUU zDIdJNh@{p6?Rh@5X(5a0XVXn}8;KfH1GOWBUcxxL6+g#jmRGSWyfeB$GttHYAw)a%0F=`em>$=vZZqOx(!@l} zi|_SlSS871QfJI_0VdMdQ8tLSn=tvFQO6QC3u54|$IlCK4yBd6u~_Ukne&S2IN$B~ zS*j+DkaR(?q9WSS+WLO)>#V4#m@b6&2rS=?*iw8vDN|lBr!8T) zCQ?L`%0m*AHJRwj$Z@N%%kDSSNJ8To6Yld!ax?x}ldkC}3Ia4hQ{Xl{T3A?Fp>|YO z*38CLLd!}uI77T%#?|0KlYA#wRdo&xuc&JcFec{cdcl2g8{EY;9*^&4+^UN)dX;qS zNr9qf5iWCf2Jo)*Sdwzwn>uLYn!c6b6Q=;s6;NIuU{hHJ4;rfP?zS#fzIA!|_8GXR z*Wl#m0mMp~G?U=J-nDC&@_}>iSDvUpwx|IN;?EWEac@@|z!BnMY@drmCJl=yACsmr zhkb{J|0xFrs^$^8;j55IHY`fFM_x>A;n7D z)>aQxzaKy`oo?4Se^N;60R%=hG^$0>WpWKhK@Es<+)mYo9xS-vG;N(B7+1Dl%Ao@V`N(u}LVG<2^Ku zF9z|x5B0vlN11IEjg|m27Y^1A0;Voa6CYqQetbm#ByGWsT+-Ioyk@YvgF49)u(p+p zG0Fu#mTg&b3!KC!vFQ#nZuDmOT`x#Yd?p5YrvJDFD3T5XXDEI_Sq;O;`3B4?2>QdE z0t>5>V|FGDH?jgyQzc?3~3lz-InU-+4WiNpGEP(k7Amlj^!#@Wmj^#T8 zmtrz}#Iv)T3C8%!!w0Q3oXP#rf&iB+M%x!gBB`+hHB?qsR?V1xC#>>iN1lLYFpb|3 z3XL07`}X&9kbPioB7{OZcS_!^d(1q?j>5`Wh+V5|&2{)8L#Q($rKHsj6yv z6T!=3_=pEPnhz$uW!2+om;}K{1lg*WHACD7P=?&pA9Goh;fUy^&!_DLz4N?dIC{9H zWk2>gp-}ap6%<#QPSaE{pJ!(oZeX1Dm6wm5={6{xQmN9rOlf_lXt#X`bxnX$yMtwz17+H`!}S=W{7^Ls@%x_Q;> zl*To%4vN}R*5ClJ^lKctOHp5#gJW>)YF)S0I)Fb>9|PSVpqy>QOxX<;IvqOxdgO{R zgG3UuWhQ{BLyNA8Yw4Yns*4jNx3KPv6s4!qAjRKw9IklQ^If)JWP~B+pMumev>f&L zl`0*1L9Rf*=tOz}=JP3&_Eg+3uBWUJgaKRxai#K8vjvx<+6-i_fVrM!IyDOpFi2GiYK1l;llkA0+56eDd85k2pFB}R zeOOPlw6uJR0sIT-mu4Ed_fB~b1kJ+P&&qBmF=6)LdtIN;1{FAEk{NIY7@b{QTv1b3 zSEoSAN=vm`0Q4Ftyh})7jZueigbq?0F1Exr=H$H74W@_!_a z3jqq{i=J|-4?#^x3gsKRgg&~#_F*190~CJLyBug9zpg87FTRgW^%^1{{PSz0(desF zrc8nF8NLepE`=q4yCUOgX=z7C^N08-4ZATEs#=cg9}gPrBA$7h%mWiAPW%}8_#E{$ zAQ?Q4o=zZ7uu#&xMGe7ruP#D7QP!TA-0*CQ@-pnA^@-Hsx9vdSyAXq?g0LBMibV)> zJlCYsp5Viog&^jDu*HX6oh_UCmMt%@hP7r_$qYat>=p!jZ#z!pEXMiQNcpS?-;wq| zAUXYtG6@eQw$SA=E;P6iQeC5(h_{AgsoAx)wJX)yR#oVz*$PJU($taVsJdua#lb%{ zD%s^xlu7NL1Sv2NuVhl9v7>5##ynwItfd%~GE!+yA-6;#zEoz}<&k1pqEj>YCeSa7 zP}9N49u7hT?ZbeqgsnP=4jclV@wPIfl8gk$A|M8;;7yX6CY42dy3fv$`z}7*D zn{GRtHfHXT0ByvSi`N41MZnv93{G^{KyAe1&UUX?dj(*0+*O~Td4%xjXj|pyyWE$= z5$i-8v$Uh7`G`f?oh>b!OG?Jh*L4-!WF{gPa(pZn%cVU>?Cmi=&HG8pN&Oi&Wed#7 zUmJ=#<8(5lwmwZe-`&i(_hm`WM3UW`>6wN!Jv(smLx<+H7F2xrg!;|mToe_l3{1;_ zi?74sbM^;=pTC|ojD_m444yP{etB(e)ry9OhV`~-dXz`y6C!tgb~^r?dc;BaY|XIJ zsgjbCWLqC@0APz%R#v`*lfSCkW*{BATh2iM(MVILPHo(@Y15AqocmQD)ngxf+A?b> zJsQ0iaU9natNsKlecf$?@D39u)T~7xx1~(;1-0GNM^mTei9~#rN+Aw%P(NgEx6KkcMM(ggTqsTJ5qr+9L{nYE)weHiS zIbJW;>saQwrEpJXnlL~EH7>J{J@l%Y>DIDk?(<7g%C&G=36ACE z6t(@wb3WhV#W22B;$M+ot}@Zm6rJ@FDNTuD?$TF(bKs zX$lz-bSC%kcGJ~ZKfUBPhQSo85SS3Z0jXEbxuzUl1rg0p~z?| zC^5Axs~B)P2oH53)9mNZU9cek;;y`$kPK54e~$a758 zl(yspp3y49#)D?GBgps9-BqH;Tq&ySb~893^+>FGElsUt>90ouR;ig3A)(0`2)4j* zsywo`j~)5Ksl1FL88M>1&`bNGaq)>@Ts?O%n@jNk^zd~KlVx18v54>wB1iv`ZKa1H ztjf#jDkJvl8@BkBSlUUw4iEFU(4sqJCN!U<9>7AB)dcowAyT~W1)@!RDKDc|Mm+jx znstjmV3tmFbmubU(yOIL7E}KVvduOXmD`V5zK002ovPDHLk FV1nFr8|eT5 diff --git a/api/core/model_runtime/model_providers/minimax/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/minimax/_assets/icon_s_en.png deleted file mode 100644 index 30c71e9bd383ca475e64ef6843b2bfedba6905f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2007 zcmV;|2PpW7P)}@2+j^P#drfu?WT_Ev9K`2+$N4l$1ouqd!d5q^;T?AyU*tN@<%YQia<} zRV&dFl^>NVHA+iW+E`IQk?N{V6a%SCqEa~l93HkD8)F{E57r*ve#&1suatRvdsSrTX(nP1?Ho& zqxUu}WY64Tv|pt(r=Z5+*y_4AZfrE!zIRp(=)e7u<=W7*qReV$6HX&eA>?~&pWP3_rRIkp=FFU&F{OA(ive(mkFstVeV(9qf{0e zDQMo?X_(r^XERGEOXO^Tr!0+CRe|G%B9!rUV@047xmaa*6i>dxkXUfKV9bzf`PB6J zjK4!JmH8yjTA!Q^__Q%+z^4uC;{>F-v7pB|(P&}9ZE3GhZlNtceZr82Tf*d)#|Gm` zbHnjBl6;sZ^UAaaoLlgtxED1m&xJ($MeEUo*><4l=Mq^Dc%rv z)!-_l&031c;$n`Z{987hkUD9h8#kT_GWV1Q1gqZ1_Iiu-V+h%65kHEIq9h_qS5lcG z|BFxVL*N^xJVLhK7IMc1zQDeE3Rz<yo`3IwSl^Uq;JoM8FzN3a z;=7QHIZu=GU3u*Ru zXbXC<+K;6}!jon6{rbq1256Efgi3LHkv%z5(e<0|G3B?xs53AqAeak!J1&JNkBFO) z1=lIVN)L(XBhlRlx+vuw1Jmt<@?eWHkKANV*~sq4;nV+WdY zE?gvYW!-F>5pYH`!DX@@2#5#0+vw0Nz_QJliJNY)$qrmS73GoHsp1w!T^h0bpzJBI z(z7&_^gp(wyc(3W*&{>iieK`Ldm`;Qr;7IPN?-8@pqAqq$-Ga|HQ2ui)|JKc$3r>q zYEH>_);DADH2Hj-n%eEW7?5$lRtnC~jLr~vWv03_LW|_VP4lUIIFY`@H^NPdG2BZ^ zjZDtH`#(MS=$UxVivd#oy@KJ2^a|GD6jH8Pu-C?aer~B#54M1UFE+0sy573Tr-p)Z+SuS^$&lmf}F$!-qV!q`W`WwA5 za0Ze={7>!Ue<}i{R`REL;EM6Fi z%U2pw7dr%{CrWJ++jZ1fAghZ)7O|;>J*cgD@nmmw8gO%SV(ftjp21?ghP7FXLMnzi zJIV46MkD4Rx3qYas{z$*>qgGj{`VbhZ33&k$ua(~oRgnRL-kGL{Z2ZRTs|;JZ;i`? z;o>iVV6$d6e{A2r(`ci-;Q02dPTEqTLVGMe+%wG09apSTW^4eXp>;_=x!X~4GakPQ zZ+b|^yRjO+bAT=P*HukTIfY?vY6^H)R@^JAlCrq{w?oFEpL)hTg?{uuHS=rO?wyK5 zKX^OZb&|((*Tlqd;SAf=6%%6;>{O=CMcAAjmYY=+3c7L9As1=c6Ms5yxqE}LkUi>@ z-kP?**3>eYtRk+L;*c7`VTs@zEE_FKE|vB7cPE5bAvBhuO>)ZkDV&$aoQ=pjjAmMj z9hr!7VREqJ!!kvFLBfvMt>9LD*n@gRmOUfkxUIz0VuCoD9k0iQ`#!j1SBt!KC4xWo zhP-qu{^|QUVs9rRR3$X6MNCI~()@+Q`!l_|cZg;Ic`YM@gk~?I8aI@_Jz}Y-vHTN4 p&Rd25T6K}BF+VJC4s-a7;XSA0PRj&!3y=T+002ovPDHLkV1hX)(k1`^ diff --git a/api/core/model_runtime/model_providers/minimax/llm/__init__.py b/api/core/model_runtime/model_providers/minimax/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab5-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab5-chat.yaml deleted file mode 100644 index 2c1f79e2b7..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab5-chat.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: abab5-chat -label: - en_US: Abab5-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 6144 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 6144 - min: 1 - max: 6144 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.015' - output: '0.015' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab5.5-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab5.5-chat.yaml deleted file mode 100644 index 6d29be0d0e..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab5.5-chat.yaml +++ /dev/null @@ -1,53 +0,0 @@ -model: abab5.5-chat -label: - en_US: Abab5.5-Chat -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.01 - max: 1 - default: 0.9 - - name: top_p - use_template: top_p - min: 0.01 - max: 1 - default: 0.95 - - name: max_tokens - use_template: max_tokens - required: true - default: 6144 - min: 1 - max: 16384 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: plugin_web_search - required: false - default: false - type: boolean - label: - en_US: Enable Web Search - zh_Hans: 开启网页搜索 -pricing: - input: '0.015' - output: '0.015' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab5.5s-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab5.5s-chat.yaml deleted file mode 100644 index aa42bb5739..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab5.5s-chat.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: abab5.5s-chat -label: - en_US: Abab5.5s-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.01 - max: 1 - default: 0.9 - - name: top_p - use_template: top_p - min: 0.01 - max: 1 - default: 0.95 - - name: max_tokens - use_template: max_tokens - required: true - default: 3072 - min: 1 - max: 8192 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.005' - output: '0.005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab6-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab6-chat.yaml deleted file mode 100644 index 9188b6b53f..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab6-chat.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: abab6-chat -label: - en_US: Abab6-Chat -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.01 - max: 1 - default: 0.1 - - name: top_p - use_template: top_p - min: 0.01 - max: 1 - default: 0.9 - - name: max_tokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 32768 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.1' - output: '0.1' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab6.5-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab6.5-chat.yaml deleted file mode 100644 index 5d717d5f8c..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab6.5-chat.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: abab6.5-chat -label: - en_US: Abab6.5-Chat -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.01 - max: 1 - default: 0.1 - - name: top_p - use_template: top_p - min: 0.01 - max: 1 - default: 0.95 - - name: max_tokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 8192 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.03' - output: '0.03' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab6.5s-chat.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab6.5s-chat.yaml deleted file mode 100644 index 4631fe67e4..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/abab6.5s-chat.yaml +++ /dev/null @@ -1,46 +0,0 @@ -model: abab6.5s-chat -label: - en_US: Abab6.5s-Chat -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 245760 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.01 - max: 1 - default: 0.1 - - name: top_p - use_template: top_p - min: 0.01 - max: 1 - default: 0.95 - - name: max_tokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 245760 - - name: mask_sensitive_info - type: boolean - default: true - label: - zh_Hans: 隐私保护 - en_US: Moderate - help: - zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 - en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.01' - output: '0.01' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py b/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py deleted file mode 100644 index 88cc0e8e0f..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py +++ /dev/null @@ -1,166 +0,0 @@ -from collections.abc import Generator -from json import dumps, loads -from typing import Any, Union - -from requests import Response, post - -from core.model_runtime.model_providers.minimax.llm.errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) -from core.model_runtime.model_providers.minimax.llm.types import MinimaxMessage - - -class MinimaxChatCompletion: - """ - Minimax Chat Completion API - """ - - def generate( - self, - model: str, - api_key: str, - group_id: str, - prompt_messages: list[MinimaxMessage], - model_parameters: dict, - tools: list[dict[str, Any]], - stop: list[str] | None, - stream: bool, - user: str, - ) -> Union[MinimaxMessage, Generator[MinimaxMessage, None, None]]: - """ - generate chat completion - """ - if not api_key or not group_id: - raise InvalidAPIKeyError("Invalid API key or group ID") - - url = f"https://api.minimax.chat/v1/text/chatcompletion?GroupId={group_id}" - - extra_kwargs = {} - - if "max_tokens" in model_parameters and type(model_parameters["max_tokens"]) == int: - extra_kwargs["tokens_to_generate"] = model_parameters["max_tokens"] - - if "temperature" in model_parameters and type(model_parameters["temperature"]) == float: - extra_kwargs["temperature"] = model_parameters["temperature"] - - if "top_p" in model_parameters and type(model_parameters["top_p"]) == float: - extra_kwargs["top_p"] = model_parameters["top_p"] - - prompt = "你是一个什么都懂的专家" - - role_meta = {"user_name": "我", "bot_name": "专家"} - - # check if there is a system message - if len(prompt_messages) == 0: - raise BadRequestError("At least one message is required") - - if prompt_messages[0].role == MinimaxMessage.Role.SYSTEM.value: - if prompt_messages[0].content: - prompt = prompt_messages[0].content - prompt_messages = prompt_messages[1:] - - # check if there is a user message - if len(prompt_messages) == 0: - raise BadRequestError("At least one user message is required") - - messages = [ - { - "sender_type": message.role, - "text": message.content, - } - for message in prompt_messages - ] - - headers = {"Authorization": "Bearer " + api_key, "Content-Type": "application/json"} - - body = { - "model": model, - "messages": messages, - "prompt": prompt, - "role_meta": role_meta, - "stream": stream, - **extra_kwargs, - } - - try: - response = post(url=url, data=dumps(body), headers=headers, stream=stream, timeout=(10, 300)) - except Exception as e: - raise InternalServerError(e) - - if response.status_code != 200: - raise InternalServerError(response.text) - - if stream: - return self._handle_stream_chat_generate_response(response) - return self._handle_chat_generate_response(response) - - def _handle_error(self, code: int, msg: str): - if code in {1000, 1001, 1013, 1027}: - raise InternalServerError(msg) - elif code in {1002, 1039}: - raise RateLimitReachedError(msg) - elif code == 1004: - raise InvalidAuthenticationError(msg) - elif code == 1008: - raise InsufficientAccountBalanceError(msg) - elif code == 2013: - raise BadRequestError(msg) - else: - raise InternalServerError(msg) - - def _handle_chat_generate_response(self, response: Response) -> MinimaxMessage: - """ - handle chat generate response - """ - response = response.json() - if "base_resp" in response and response["base_resp"]["status_code"] != 0: - code = response["base_resp"]["status_code"] - msg = response["base_resp"]["status_msg"] - self._handle_error(code, msg) - - message = MinimaxMessage(content=response["reply"], role=MinimaxMessage.Role.ASSISTANT.value) - message.usage = { - "prompt_tokens": 0, - "completion_tokens": response["usage"]["total_tokens"], - "total_tokens": response["usage"]["total_tokens"], - } - message.stop_reason = response["choices"][0]["finish_reason"] - return message - - def _handle_stream_chat_generate_response(self, response: Response) -> Generator[MinimaxMessage, None, None]: - """ - handle stream chat generate response - """ - for line in response.iter_lines(): - if not line: - continue - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() - data = loads(line) - - if "base_resp" in data and data["base_resp"]["status_code"] != 0: - code = data["base_resp"]["status_code"] - msg = data["base_resp"]["status_msg"] - self._handle_error(code, msg) - - if data["reply"]: - total_tokens = data["usage"]["total_tokens"] - message = MinimaxMessage(role=MinimaxMessage.Role.ASSISTANT.value, content="") - message.usage = {"prompt_tokens": 0, "completion_tokens": total_tokens, "total_tokens": total_tokens} - message.stop_reason = data["choices"][0]["finish_reason"] - yield message - return - - choices = data.get("choices", []) - if len(choices) == 0: - continue - - for choice in choices: - message = choice["delta"] - yield MinimaxMessage(content=message, role=MinimaxMessage.Role.ASSISTANT.value) diff --git a/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py b/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py deleted file mode 100644 index 8b8fdbb6bd..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py +++ /dev/null @@ -1,191 +0,0 @@ -from collections.abc import Generator -from json import dumps, loads -from typing import Any, Union - -from requests import Response, post - -from core.model_runtime.model_providers.minimax.llm.errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) -from core.model_runtime.model_providers.minimax.llm.types import MinimaxMessage - - -class MinimaxChatCompletionPro: - """ - Minimax Chat Completion Pro API, supports function calling - however, we do not have enough time and energy to implement it, but the parameters are reserved - """ - - def generate( - self, - model: str, - api_key: str, - group_id: str, - prompt_messages: list[MinimaxMessage], - model_parameters: dict, - tools: list[dict[str, Any]], - stop: list[str] | None, - stream: bool, - user: str, - ) -> Union[MinimaxMessage, Generator[MinimaxMessage, None, None]]: - """ - generate chat completion - """ - if not api_key or not group_id: - raise InvalidAPIKeyError("Invalid API key or group ID") - - url = f"https://api.minimax.chat/v1/text/chatcompletion_pro?GroupId={group_id}" - - extra_kwargs = {} - - if "max_tokens" in model_parameters and type(model_parameters["max_tokens"]) == int: - extra_kwargs["tokens_to_generate"] = model_parameters["max_tokens"] - - if "temperature" in model_parameters and type(model_parameters["temperature"]) == float: - extra_kwargs["temperature"] = model_parameters["temperature"] - - if "top_p" in model_parameters and type(model_parameters["top_p"]) == float: - extra_kwargs["top_p"] = model_parameters["top_p"] - - if "mask_sensitive_info" in model_parameters and type(model_parameters["mask_sensitive_info"]) == bool: - extra_kwargs["mask_sensitive_info"] = model_parameters["mask_sensitive_info"] - - if model_parameters.get("plugin_web_search"): - extra_kwargs["plugins"] = ["plugin_web_search"] - - bot_setting = {"bot_name": "专家", "content": "你是一个什么都懂的专家"} - - reply_constraints = {"sender_type": "BOT", "sender_name": "专家"} - - # check if there is a system message - if len(prompt_messages) == 0: - raise BadRequestError("At least one message is required") - - if prompt_messages[0].role == MinimaxMessage.Role.SYSTEM.value: - if prompt_messages[0].content: - bot_setting["content"] = prompt_messages[0].content - prompt_messages = prompt_messages[1:] - - # check if there is a user message - if len(prompt_messages) == 0: - raise BadRequestError("At least one user message is required") - - messages = [message.to_dict() for message in prompt_messages] - - headers = {"Authorization": "Bearer " + api_key, "Content-Type": "application/json"} - - body = { - "model": model, - "messages": messages, - "bot_setting": [bot_setting], - "reply_constraints": reply_constraints, - "stream": stream, - **extra_kwargs, - } - - if tools: - body["functions"] = tools - body["function_call"] = {"type": "auto"} - - try: - response = post(url=url, data=dumps(body), headers=headers, stream=stream, timeout=(10, 300)) - except Exception as e: - raise InternalServerError(e) - - if response.status_code != 200: - raise InternalServerError(response.text) - - if stream: - return self._handle_stream_chat_generate_response(response) - return self._handle_chat_generate_response(response) - - def _handle_error(self, code: int, msg: str): - if code in {1000, 1001, 1013, 1027}: - raise InternalServerError(msg) - elif code in {1002, 1039}: - raise RateLimitReachedError(msg) - elif code == 1004: - raise InvalidAuthenticationError(msg) - elif code == 1008: - raise InsufficientAccountBalanceError(msg) - elif code == 2013: - raise BadRequestError(msg) - else: - raise InternalServerError(msg) - - def _handle_chat_generate_response(self, response: Response) -> MinimaxMessage: - """ - handle chat generate response - """ - response = response.json() - if "base_resp" in response and response["base_resp"]["status_code"] != 0: - code = response["base_resp"]["status_code"] - msg = response["base_resp"]["status_msg"] - self._handle_error(code, msg) - - message = MinimaxMessage(content=response["reply"], role=MinimaxMessage.Role.ASSISTANT.value) - message.usage = { - "prompt_tokens": 0, - "completion_tokens": response["usage"]["total_tokens"], - "total_tokens": response["usage"]["total_tokens"], - } - message.stop_reason = response["choices"][0]["finish_reason"] - return message - - def _handle_stream_chat_generate_response(self, response: Response) -> Generator[MinimaxMessage, None, None]: - """ - handle stream chat generate response - """ - for line in response.iter_lines(): - if not line: - continue - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() - data = loads(line) - - if "base_resp" in data and data["base_resp"]["status_code"] != 0: - code = data["base_resp"]["status_code"] - msg = data["base_resp"]["status_msg"] - self._handle_error(code, msg) - - # final chunk - if data["reply"] or data.get("usage"): - total_tokens = data["usage"]["total_tokens"] - minimax_message = MinimaxMessage(role=MinimaxMessage.Role.ASSISTANT.value, content="") - minimax_message.usage = { - "prompt_tokens": 0, - "completion_tokens": total_tokens, - "total_tokens": total_tokens, - } - minimax_message.stop_reason = data["choices"][0]["finish_reason"] - - choices = data.get("choices", []) - if len(choices) > 0: - for choice in choices: - message = choice["messages"][0] - # append function_call message - if "function_call" in message: - function_call_message = MinimaxMessage(content="", role=MinimaxMessage.Role.ASSISTANT.value) - function_call_message.function_call = message["function_call"] - yield function_call_message - - yield minimax_message - return - - # partial chunk - choices = data.get("choices", []) - if len(choices) == 0: - continue - - for choice in choices: - message = choice["messages"][0] - # append text message - if "text" in message: - minimax_message = MinimaxMessage(content=message["text"], role=MinimaxMessage.Role.ASSISTANT.value) - yield minimax_message diff --git a/api/core/model_runtime/model_providers/minimax/llm/errors.py b/api/core/model_runtime/model_providers/minimax/llm/errors.py deleted file mode 100644 index 309b5cf413..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/errors.py +++ /dev/null @@ -1,22 +0,0 @@ -class InvalidAuthenticationError(Exception): - pass - - -class InvalidAPIKeyError(Exception): - pass - - -class RateLimitReachedError(Exception): - pass - - -class InsufficientAccountBalanceError(Exception): - pass - - -class InternalServerError(Exception): - pass - - -class BadRequestError(Exception): - pass diff --git a/api/core/model_runtime/model_providers/minimax/llm/llm.py b/api/core/model_runtime/model_providers/minimax/llm/llm.py deleted file mode 100644 index 4250c40cfb..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/llm.py +++ /dev/null @@ -1,271 +0,0 @@ -from collections.abc import Generator - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.minimax.llm.chat_completion import MinimaxChatCompletion -from core.model_runtime.model_providers.minimax.llm.chat_completion_pro import MinimaxChatCompletionPro -from core.model_runtime.model_providers.minimax.llm.errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) -from core.model_runtime.model_providers.minimax.llm.types import MinimaxMessage - - -class MinimaxLargeLanguageModel(LargeLanguageModel): - model_apis = { - "abab6.5s-chat": MinimaxChatCompletionPro, - "abab6.5-chat": MinimaxChatCompletionPro, - "abab6-chat": MinimaxChatCompletionPro, - "abab5.5s-chat": MinimaxChatCompletionPro, - "abab5.5-chat": MinimaxChatCompletionPro, - "abab5-chat": MinimaxChatCompletion, - } - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate credentials for Baichuan model - """ - if model not in self.model_apis: - raise CredentialsValidateFailedError(f"Invalid model: {model}") - - if not credentials.get("minimax_api_key"): - raise CredentialsValidateFailedError("Invalid API key") - - if not credentials.get("minimax_group_id"): - raise CredentialsValidateFailedError("Invalid group ID") - - # ping - instance = MinimaxChatCompletionPro() - try: - instance.generate( - model=model, - api_key=credentials["minimax_api_key"], - group_id=credentials["minimax_group_id"], - prompt_messages=[MinimaxMessage(content="ping", role="USER")], - model_parameters={}, - tools=[], - stop=[], - stream=False, - user="", - ) - except (InvalidAuthenticationError, InsufficientAccountBalanceError) as e: - raise CredentialsValidateFailedError(f"Invalid API key: {e}") - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - return self._num_tokens_from_messages(prompt_messages, tools) - - def _num_tokens_from_messages(self, messages: list[PromptMessage], tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for minimax model - - not like ChatGLM, Minimax has a special prompt structure, we could not find a proper way - to calculate the num tokens, so we use str() to convert the prompt to string - - Minimax does not provide their own tokenizer of adab5.5 and abab5 model - therefore, we use gpt2 tokenizer instead - """ - messages_dict = [self._convert_prompt_message_to_minimax_message(m).to_dict() for m in messages] - return self._get_num_tokens_by_gpt2(str(messages_dict)) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - use MinimaxChatCompletionPro as the type of client, anyway, MinimaxChatCompletion has the same interface - """ - client: MinimaxChatCompletionPro = self.model_apis[model]() - - if tools: - tools = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} for tool in tools - ] - - response = client.generate( - model=model, - api_key=credentials["minimax_api_key"], - group_id=credentials["minimax_group_id"], - prompt_messages=[self._convert_prompt_message_to_minimax_message(message) for message in prompt_messages], - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - if stream: - return self._handle_chat_generate_stream_response( - model=model, prompt_messages=prompt_messages, credentials=credentials, response=response - ) - return self._handle_chat_generate_response( - model=model, prompt_messages=prompt_messages, credentials=credentials, response=response - ) - - def _convert_prompt_message_to_minimax_message(self, prompt_message: PromptMessage) -> MinimaxMessage: - """ - convert PromptMessage to MinimaxMessage so that we can use MinimaxChatCompletionPro interface - """ - if isinstance(prompt_message, SystemPromptMessage): - return MinimaxMessage(role=MinimaxMessage.Role.SYSTEM.value, content=prompt_message.content) - elif isinstance(prompt_message, UserPromptMessage): - return MinimaxMessage(role=MinimaxMessage.Role.USER.value, content=prompt_message.content) - elif isinstance(prompt_message, AssistantPromptMessage): - if prompt_message.tool_calls: - message = MinimaxMessage(role=MinimaxMessage.Role.ASSISTANT.value, content="") - message.function_call = { - "name": prompt_message.tool_calls[0].function.name, - "arguments": prompt_message.tool_calls[0].function.arguments, - } - return message - return MinimaxMessage(role=MinimaxMessage.Role.ASSISTANT.value, content=prompt_message.content) - elif isinstance(prompt_message, ToolPromptMessage): - return MinimaxMessage(role=MinimaxMessage.Role.FUNCTION.value, content=prompt_message.content) - else: - raise NotImplementedError(f"Prompt message type {type(prompt_message)} is not supported") - - def _handle_chat_generate_response( - self, model: str, prompt_messages: list[PromptMessage], credentials: dict, response: MinimaxMessage - ) -> LLMResult: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=response.usage["prompt_tokens"], - completion_tokens=response.usage["completion_tokens"], - ) - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage( - content=response.content, - tool_calls=[], - ), - usage=usage, - ) - - def _handle_chat_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Generator[MinimaxMessage, None, None], - ) -> Generator[LLMResultChunk, None, None]: - for message in response: - if message.usage: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=message.usage["prompt_tokens"], - completion_tokens=message.usage["completion_tokens"], - ) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - usage=usage, - finish_reason=message.stop_reason or None, - ), - ) - elif message.function_call: - if "name" not in message.function_call or "arguments" not in message.function_call: - continue - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage( - content="", - tool_calls=[ - AssistantPromptMessage.ToolCall( - id="", - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=message.function_call["name"], arguments=message.function_call["arguments"] - ), - ) - ], - ), - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - finish_reason=message.stop_reason or None, - ), - ) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [InternalServerError], - InvokeRateLimitError: [RateLimitReachedError], - InvokeAuthorizationError: [ - InvalidAuthenticationError, - InsufficientAccountBalanceError, - InvalidAPIKeyError, - ], - InvokeBadRequestError: [BadRequestError, KeyError], - } diff --git a/api/core/model_runtime/model_providers/minimax/llm/types.py b/api/core/model_runtime/model_providers/minimax/llm/types.py deleted file mode 100644 index 88ebe5e2e0..0000000000 --- a/api/core/model_runtime/model_providers/minimax/llm/types.py +++ /dev/null @@ -1,30 +0,0 @@ -from enum import Enum -from typing import Any - - -class MinimaxMessage: - class Role(Enum): - USER = "USER" - ASSISTANT = "BOT" - SYSTEM = "SYSTEM" - FUNCTION = "FUNCTION" - - role: str = Role.USER.value - content: str - usage: dict[str, int] = None - stop_reason: str = "" - function_call: dict[str, Any] = None - - def to_dict(self) -> dict[str, Any]: - if self.function_call and self.role == MinimaxMessage.Role.ASSISTANT.value: - return {"sender_type": "BOT", "sender_name": "专家", "text": "", "function_call": self.function_call} - - return { - "sender_type": self.role, - "sender_name": "我" if self.role == "USER" else "专家", - "text": self.content, - } - - def __init__(self, content: str, role: str = "USER") -> None: - self.content = content - self.role = role diff --git a/api/core/model_runtime/model_providers/minimax/minimax.py b/api/core/model_runtime/model_providers/minimax/minimax.py deleted file mode 100644 index 5a761903a1..0000000000 --- a/api/core/model_runtime/model_providers/minimax/minimax.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class MinimaxProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `abab5.5-chat` model for validate, - model_instance.validate_credentials(model="abab5.5-chat", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise CredentialsValidateFailedError(f"{ex}") diff --git a/api/core/model_runtime/model_providers/minimax/minimax.yaml b/api/core/model_runtime/model_providers/minimax/minimax.yaml deleted file mode 100644 index 0a97ff9bb9..0000000000 --- a/api/core/model_runtime/model_providers/minimax/minimax.yaml +++ /dev/null @@ -1,37 +0,0 @@ -provider: minimax -label: - en_US: Minimax -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#FFEFEF" -help: - title: - en_US: Get your API Key from Minimax - zh_Hans: 从 Minimax 获取您的 API Key - url: - en_US: https://api.minimax.chat/user-center/basic-information/interface-key -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: minimax_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: minimax_group_id - label: - en_US: Group ID - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的 Group ID - en_US: Enter your group ID diff --git a/api/core/model_runtime/model_providers/minimax/text_embedding/__init__.py b/api/core/model_runtime/model_providers/minimax/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/minimax/text_embedding/embo-01.yaml b/api/core/model_runtime/model_providers/minimax/text_embedding/embo-01.yaml deleted file mode 100644 index 33546eafd3..0000000000 --- a/api/core/model_runtime/model_providers/minimax/text_embedding/embo-01.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embo-01 -model_type: text-embedding -model_properties: - context_size: 4096 - max_chunks: 1 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/mistralai/__init__.py b/api/core/model_runtime/model_providers/mistralai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/mistralai/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/mistralai/_assets/icon_l_en.png deleted file mode 100644 index f019b1edceac20bb838c9be16985b4e8767344d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7064 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 diff --git a/api/core/model_runtime/model_providers/mistralai/llm/_position.yaml b/api/core/model_runtime/model_providers/mistralai/llm/_position.yaml deleted file mode 100644 index bdb06b7fff..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/_position.yaml +++ /dev/null @@ -1,11 +0,0 @@ -- pixtral-12b-2409 -- codestral-latest -- mistral-embed -- open-mistral-nemo -- open-codestral-mamba -- open-mistral-7b -- open-mixtral-8x7b -- open-mixtral-8x22b -- mistral-small-latest -- mistral-medium-latest -- mistral-large-latest diff --git a/api/core/model_runtime/model_providers/mistralai/llm/codestral-latest.yaml b/api/core/model_runtime/model_providers/mistralai/llm/codestral-latest.yaml deleted file mode 100644 index 5f1260233f..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/codestral-latest.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: codestral-latest -label: - zh_Hans: codestral-latest - en_US: codestral-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4096 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/llm.py b/api/core/model_runtime/model_providers/mistralai/llm/llm.py deleted file mode 100644 index da60bd7661..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/llm.py +++ /dev/null @@ -1,36 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class MistralAILargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - - # mistral dose not support user/stop arguments - stop = [] - user = None - - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - @staticmethod - def _add_custom_parameters(credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = "https://api.mistral.ai/v1" diff --git a/api/core/model_runtime/model_providers/mistralai/llm/mistral-embed.yaml b/api/core/model_runtime/model_providers/mistralai/llm/mistral-embed.yaml deleted file mode 100644 index d759103d08..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/mistral-embed.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: mistral-embed -label: - zh_Hans: mistral-embed - en_US: mistral-embed -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/mistral-large-latest.yaml b/api/core/model_runtime/model_providers/mistralai/llm/mistral-large-latest.yaml deleted file mode 100644 index a0d07a2bf8..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/mistral-large-latest.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: mistral-large-latest -label: - zh_Hans: mistral-large-latest - en_US: mistral-large-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/mistral-medium-latest.yaml b/api/core/model_runtime/model_providers/mistralai/llm/mistral-medium-latest.yaml deleted file mode 100644 index 7c7440894c..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/mistral-medium-latest.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: mistral-medium-latest -label: - zh_Hans: mistral-medium-latest - en_US: mistral-medium-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.0027' - output: '0.0081' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/mistral-small-latest.yaml b/api/core/model_runtime/model_providers/mistralai/llm/mistral-small-latest.yaml deleted file mode 100644 index 865e610226..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/mistral-small-latest.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: mistral-small-latest -label: - zh_Hans: mistral-small-latest - en_US: mistral-small-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/open-codestral-mamba.yaml b/api/core/model_runtime/model_providers/mistralai/llm/open-codestral-mamba.yaml deleted file mode 100644 index d7ffb9ea02..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/open-codestral-mamba.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: open-codestral-mamba -label: - zh_Hans: open-codestral-mamba - en_US: open-codestral-mamba -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 256000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 16384 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-7b.yaml b/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-7b.yaml deleted file mode 100644 index ac29226959..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-7b.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: open-mistral-7b -label: - zh_Hans: open-mistral-7b - en_US: open-mistral-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 2048 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.00025' - output: '0.00025' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-nemo.yaml b/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-nemo.yaml deleted file mode 100644 index dcda4fbce7..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/open-mistral-nemo.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: open-mistral-nemo -label: - zh_Hans: open-mistral-nemo - en_US: open-mistral-nemo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8192 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x22b.yaml b/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x22b.yaml deleted file mode 100644 index 325fafd497..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x22b.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: open-mixtral-8x22b -label: - zh_Hans: open-mixtral-8x22b - en_US: open-mixtral-8x22b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 64000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x7b.yaml b/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x7b.yaml deleted file mode 100644 index d217e5e7e9..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/open-mixtral-8x7b.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: open-mixtral-8x7b -label: - zh_Hans: open-mixtral-8x7b - en_US: open-mixtral-8x7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.0007' - output: '0.0007' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/llm/pixtral-12b-2409.yaml b/api/core/model_runtime/model_providers/mistralai/llm/pixtral-12b-2409.yaml deleted file mode 100644 index 0b002b49ca..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/llm/pixtral-12b-2409.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: pixtral-12b-2409 -label: - zh_Hans: pixtral-12b-2409 - en_US: pixtral-12b-2409 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8192 - - name: safe_prompt - default: false - type: boolean - help: - en_US: Whether to inject a safety prompt before all conversations. - zh_Hans: 是否开启提示词审查 - label: - en_US: SafePrompt - zh_Hans: 提示词审查 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: RandomSeed - zh_Hans: 随机数种子 - default: 0 - min: 0 - max: 2147483647 -pricing: - input: '0.008' - output: '0.024' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mistralai/mistralai.py b/api/core/model_runtime/model_providers/mistralai/mistralai.py deleted file mode 100644 index 7f9db8da1c..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/mistralai.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class MistralAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="open-mistral-7b", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/mistralai/mistralai.yaml b/api/core/model_runtime/model_providers/mistralai/mistralai.yaml deleted file mode 100644 index c9b4226ea6..0000000000 --- a/api/core/model_runtime/model_providers/mistralai/mistralai.yaml +++ /dev/null @@ -1,31 +0,0 @@ -provider: mistralai -label: - en_US: MistralAI -description: - en_US: Models provided by MistralAI, such as open-mistral-7b and mistral-large-latest. - zh_Hans: MistralAI 提供的模型,例如 open-mistral-7b 和 mistral-large-latest。 -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#FFFFFF" -help: - title: - en_US: Get your API Key from MistralAI - zh_Hans: 从 MistralAI 获取 API Key - url: - en_US: https://console.mistral.ai/api-keys/ -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/mixedbread/__init__.py b/api/core/model_runtime/model_providers/mixedbread/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/mixedbread/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/mixedbread/_assets/icon_l_en.png deleted file mode 100644 index 2027611bd5e8b4c7d06a5d00e515a0db70e67a17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123637 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 w{bSIksD4HJ__9z1UXH|stboLk~^m(z={`Y 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⩾!yGZE4c`i(M)g0d#49`e0ztyfP1Ot+<6qyC}b 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 None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.TEXT_EMBEDDING) - - # Use `mxbai-embed-large-v1` model for validate, - model_instance.validate_credentials(model="mxbai-embed-large-v1", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/mixedbread/mixedbread.yaml b/api/core/model_runtime/model_providers/mixedbread/mixedbread.yaml deleted file mode 100644 index 2f43aea6ad..0000000000 --- a/api/core/model_runtime/model_providers/mixedbread/mixedbread.yaml +++ /dev/null @@ -1,31 +0,0 @@ -provider: mixedbread -label: - en_US: MixedBread -description: - en_US: Embedding and Rerank Model Supported -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#EFFDFD" -help: - title: - en_US: Get your API key from MixedBread AI - zh_Hans: 从 MixedBread 获取 API Key - url: - en_US: https://www.mixedbread.ai/ -supported_model_types: - - text-embedding - - rerank -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/mixedbread/rerank/__init__.py b/api/core/model_runtime/model_providers/mixedbread/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/mixedbread/rerank/mxbai-rerank-large-v1-en.yaml b/api/core/model_runtime/model_providers/mixedbread/rerank/mxbai-rerank-large-v1-en.yaml deleted file mode 100644 index beda219953..0000000000 --- a/api/core/model_runtime/model_providers/mixedbread/rerank/mxbai-rerank-large-v1-en.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: mxbai-rerank-large-v1 -model_type: rerank -model_properties: - context_size: 512 diff --git a/api/core/model_runtime/model_providers/mixedbread/rerank/rerank.py b/api/core/model_runtime/model_providers/mixedbread/rerank/rerank.py deleted file mode 100644 index bf3c12fd86..0000000000 --- a/api/core/model_runtime/model_providers/mixedbread/rerank/rerank.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import Optional - -import httpx - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelPropertyKey, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class MixedBreadRerankModel(RerankModel): - """ - Model class for MixedBread rerank model. - """ - - 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 documents to return - :param user: unique user id - :return: rerank result - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - base_url = credentials.get("base_url", "https://api.mixedbread.ai/v1") - base_url = base_url.removesuffix("/") - - try: - response = httpx.post( - base_url + "/reranking", - json={"model": model, "query": query, "input": docs, "top_k": top_n, "return_input": True}, - headers={"Authorization": f"Bearer {credentials.get('api_key')}", "Content-Type": "application/json"}, - ) - response.raise_for_status() - results = response.json() - - rerank_documents = [] - for result in results["data"]: - rerank_document = RerankDocument( - index=result["index"], - text=result["input"], - score=result["score"], - ) - if score_threshold is None or result["score"] >= score_threshold: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - except httpx.HTTPStatusError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - """ - return { - InvokeConnectionError: [httpx.ConnectError], - InvokeServerUnavailableError: [httpx.RemoteProtocolError], - InvokeRateLimitError: [], - InvokeAuthorizationError: [httpx.HTTPStatusError], - InvokeBadRequestError: [httpx.RequestError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - generate custom model entities from credentials - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - model_type=ModelType.RERANK, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", "512"))}, - ) - - return entity diff --git a/api/core/model_runtime/model_providers/mixedbread/text_embedding/__init__.py b/api/core/model_runtime/model_providers/mixedbread/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-2d-large-v1-en.yaml b/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-2d-large-v1-en.yaml deleted file mode 100644 index 0c3c863d06..0000000000 --- a/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-2d-large-v1-en.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: mxbai-embed-2d-large-v1 -model_type: text-embedding -model_properties: - context_size: 512 -pricing: - input: '0.0001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-large-v1-en.yaml b/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-large-v1-en.yaml deleted file mode 100644 index 0c5cda2a72..0000000000 --- a/api/core/model_runtime/model_providers/mixedbread/text_embedding/mxbai-embed-large-v1-en.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: mxbai-embed-large-v1 -model_type: text-embedding -model_properties: - context_size: 512 -pricing: - input: '0.0001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/model_provider_factory.py b/api/core/model_runtime/model_providers/model_provider_factory.py index e2d17e3257..1370676f0e 100644 --- a/api/core/model_runtime/model_providers/model_provider_factory.py +++ b/api/core/model_runtime/model_providers/model_provider_factory.py @@ -3,61 +3,116 @@ import os from collections.abc import Sequence from typing import Optional -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel -from core.helper.module_import_helper import load_single_subclass_from_source from core.helper.position_helper import get_provider_position_map, sort_to_dict_by_position_map -from core.model_runtime.entities.model_entities import ModelType +from core.model_runtime.entities.model_entities import AIModelEntity, ModelType from core.model_runtime.entities.provider_entities import ProviderConfig, ProviderEntity, SimpleProviderEntity -from core.model_runtime.model_providers.__base.model_provider import ModelProvider +from core.model_runtime.model_providers.__base.ai_model import AIModel +from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from core.model_runtime.model_providers.__base.moderation_model import ModerationModel +from core.model_runtime.model_providers.__base.rerank_model import RerankModel +from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel +from core.model_runtime.model_providers.__base.text2img_model import Text2ImageModel +from core.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel +from core.model_runtime.model_providers.__base.tts_model import TTSModel from core.model_runtime.schema_validators.model_credential_schema_validator import ModelCredentialSchemaValidator from core.model_runtime.schema_validators.provider_credential_schema_validator import ProviderCredentialSchemaValidator +from core.plugin.entities.plugin_daemon import PluginModelProviderEntity +from core.plugin.manager.asset import PluginAssetManager +from core.plugin.manager.model import PluginModelManager logger = logging.getLogger(__name__) class ModelProviderExtension(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) - - provider_instance: ModelProvider - name: str + plugin_model_provider_entity: PluginModelProviderEntity position: Optional[int] = None class ModelProviderFactory: - model_provider_extensions: Optional[dict[str, ModelProviderExtension]] = None + provider_position_map: dict[str, int] = {} - def __init__(self) -> None: - # for cache in memory - self.get_providers() + def __init__(self, tenant_id: str) -> None: + self.tenant_id = tenant_id + self.plugin_model_manager = PluginModelManager() + + if not self.provider_position_map: + # get the path of current classes + current_path = os.path.abspath(__file__) + model_providers_path = os.path.dirname(current_path) + + # get _position.yaml file path + self.provider_position_map = get_provider_position_map(model_providers_path) def get_providers(self) -> Sequence[ProviderEntity]: """ Get all providers :return: list of providers """ - # scan all providers - model_provider_extensions = self._get_model_provider_map() + # Fetch plugin model providers + plugin_providers = self.get_plugin_model_providers() - # traverse all model_provider_extensions - providers = [] - for model_provider_extension in model_provider_extensions.values(): - # get model_provider instance - model_provider_instance = model_provider_extension.provider_instance + # Convert PluginModelProviderEntity to ModelProviderExtension + model_provider_extensions = [] + for provider in plugin_providers: + model_provider_extensions.append(ModelProviderExtension(plugin_model_provider_entity=provider)) - # get provider schema - provider_schema = model_provider_instance.get_provider_schema() + sorted_extensions = sort_to_dict_by_position_map( + position_map=self.provider_position_map, + data=model_provider_extensions, + name_func=lambda x: x.plugin_model_provider_entity.declaration.provider, + ) - for model_type in provider_schema.supported_model_types: - # get predefined models for given model type - models = model_provider_instance.models(model_type) - if models: - provider_schema.models.extend(models) + return [extension.plugin_model_provider_entity.declaration for extension in sorted_extensions.values()] - providers.append(provider_schema) + def get_plugin_model_providers(self) -> Sequence[PluginModelProviderEntity]: + """ + Get all plugin model providers + :return: list of plugin model providers + """ + # Fetch plugin model providers + plugin_providers = self.plugin_model_manager.fetch_model_providers(self.tenant_id) - # return providers - return providers + for provider in plugin_providers: + provider.declaration.provider = provider.plugin_id + "/" + provider.declaration.provider + + return plugin_providers + + def get_provider_schema(self, provider: str) -> ProviderEntity: + """ + Get provider schema + :param provider: provider name + :return: provider schema + """ + plugin_model_provider_entity = self.get_plugin_model_provider(provider=provider) + return plugin_model_provider_entity.declaration + + def get_plugin_model_provider(self, provider: str) -> PluginModelProviderEntity: + """ + Get plugin model provider + :param provider: provider name + :return: provider schema + """ + # fetch plugin model providers + plugin_model_provider_entities = self.get_plugin_model_providers() + + plugin_id, provider_name = self.get_plugin_id_and_provider_name_from_provider(provider) + + # get the provider + plugin_model_provider_entity = next( + ( + p + for p in plugin_model_provider_entities + if p.declaration.provider == provider_name and (plugin_id and p.plugin_id == plugin_id) + ), + None, + ) + + if not plugin_model_provider_entity: + raise ValueError(f"Invalid provider: {provider}") + + return plugin_model_provider_entity def provider_credentials_validate(self, *, provider: str, credentials: dict) -> dict: """ @@ -67,15 +122,11 @@ class ModelProviderFactory: :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. :return: """ - # get the provider instance - model_provider_instance = self.get_provider_instance(provider) - - # get provider schema - provider_schema = model_provider_instance.get_provider_schema() + # fetch plugin model provider + plugin_model_provider_entity = self.get_plugin_model_provider(provider=provider) # get provider_credential_schema and validate credentials according to the rules - provider_credential_schema = provider_schema.provider_credential_schema - + provider_credential_schema = plugin_model_provider_entity.declaration.provider_credential_schema if not provider_credential_schema: raise ValueError(f"Provider {provider} does not have provider_credential_schema") @@ -84,7 +135,13 @@ class ModelProviderFactory: filtered_credentials = validator.validate_and_filter(credentials) # validate the credentials, raise exception if validation failed - model_provider_instance.validate_provider_credentials(filtered_credentials) + self.plugin_model_manager.validate_provider_credentials( + tenant_id=self.tenant_id, + user_id="unknown", + plugin_id=plugin_model_provider_entity.plugin_id, + provider=provider, + credentials=filtered_credentials, + ) return filtered_credentials @@ -100,15 +157,11 @@ class ModelProviderFactory: :param credentials: model credentials, credentials form defined in `model_credential_schema`. :return: """ - # get the provider instance - model_provider_instance = self.get_provider_instance(provider) - - # get provider schema - provider_schema = model_provider_instance.get_provider_schema() + # fetch plugin model provider + plugin_model_provider_entity = self.get_plugin_model_provider(provider=provider) # get model_credential_schema and validate credentials according to the rules - model_credential_schema = provider_schema.model_credential_schema - + model_credential_schema = plugin_model_provider_entity.declaration.model_credential_schema if not model_credential_schema: raise ValueError(f"Provider {provider} does not have model_credential_schema") @@ -116,14 +169,38 @@ class ModelProviderFactory: validator = ModelCredentialSchemaValidator(model_type, model_credential_schema) filtered_credentials = validator.validate_and_filter(credentials) - # get model instance of the model type - model_instance = model_provider_instance.get_model_instance(model_type) - # call validate_credentials method of model type to validate credentials, raise exception if validation failed - model_instance.validate_credentials(model, filtered_credentials) + self.plugin_model_manager.validate_model_credentials( + tenant_id=self.tenant_id, + user_id="unknown", + plugin_id=plugin_model_provider_entity.plugin_id, + provider=provider, + model_type=model_type.value, + model=model, + credentials=filtered_credentials, + ) return filtered_credentials + def get_model_schema( + self, *, provider: str, model_type: ModelType, model: str, credentials: dict + ) -> AIModelEntity | None: + """ + Get model schema + """ + plugin_id, provider_name = self.get_plugin_id_and_provider_name_from_provider(provider) + model_schema = self.plugin_model_manager.get_model_schema( + tenant_id=self.tenant_id, + user_id="unknown", + plugin_id=plugin_id, + provider=provider_name, + model_type=model_type.value, + model=model, + credentials=credentials, + ) + + return model_schema + def get_models( self, *, @@ -142,7 +219,7 @@ class ModelProviderFactory: provider_configs = provider_configs or [] # scan all providers - model_provider_extensions = self._get_model_provider_map() + plugin_model_provider_entities = self.get_plugin_model_providers() # convert provider_configs to dict provider_credentials_dict = {} @@ -151,16 +228,13 @@ class ModelProviderFactory: # traverse all model_provider_extensions providers = [] - for name, model_provider_extension in model_provider_extensions.items(): + for plugin_model_provider_entity in plugin_model_provider_entities: # filter by provider if provider is present - if provider and name != provider: + if provider and plugin_model_provider_entity.declaration.provider != provider: continue - # get model_provider instance - model_provider_instance = model_provider_extension.provider_instance - # get provider schema - provider_schema = model_provider_instance.get_provider_schema() + provider_schema = plugin_model_provider_entity.declaration model_types = provider_schema.supported_model_types if model_type: @@ -170,13 +244,11 @@ class ModelProviderFactory: model_types = [model_type] all_model_type_models = [] - for model_type in model_types: - # get predefined models for given model type - models = model_provider_instance.models( - model_type=model_type, - ) + for model_schema in provider_schema.models: + if model_schema.model_type != model_type: + continue - all_model_type_models.extend(models) + all_model_type_models.append(model_schema) simple_provider_schema = provider_schema.to_simple_provider() simple_provider_schema.models.extend(all_model_type_models) @@ -185,95 +257,82 @@ class ModelProviderFactory: return providers - def get_provider_instance(self, provider: str) -> ModelProvider: + def get_model_type_instance(self, provider: str, model_type: ModelType) -> AIModel: """ - Get provider instance by provider name + Get model type instance by provider name and model type :param provider: provider name - :return: provider instance + :param model_type: model type + :return: model type instance """ - # scan all providers - model_provider_extensions = self._get_model_provider_map() + plugin_id, provider_name = self.get_plugin_id_and_provider_name_from_provider(provider) + init_params = { + "tenant_id": self.tenant_id, + "plugin_id": plugin_id, + "provider_name": provider_name, + "plugin_model_provider": self.get_plugin_model_provider(provider), + } - # get the provider extension - model_provider_extension = model_provider_extensions.get(provider) - if not model_provider_extension: - raise Exception(f"Invalid provider: {provider}") + if model_type == ModelType.LLM: + return LargeLanguageModel(**init_params) + elif model_type == ModelType.TEXT_EMBEDDING: + return TextEmbeddingModel(**init_params) + elif model_type == ModelType.RERANK: + return RerankModel(**init_params) + elif model_type == ModelType.SPEECH2TEXT: + return Speech2TextModel(**init_params) + elif model_type == ModelType.MODERATION: + return ModerationModel(**init_params) + elif model_type == ModelType.TTS: + return TTSModel(**init_params) + elif model_type == ModelType.TEXT2IMG: + return Text2ImageModel(**init_params) - # get the provider instance - model_provider_instance = model_provider_extension.provider_instance - - return model_provider_instance - - def _get_model_provider_map(self) -> dict[str, ModelProviderExtension]: + def get_provider_icon(self, provider: str, icon_type: str, lang: str) -> bytes: """ - Retrieves the model provider map. - - This method retrieves the model provider map, which is a dictionary containing the model provider names as keys - and instances of `ModelProviderExtension` as values. The model provider map is used to store information about - available model providers. - - Returns: - A dictionary containing the model provider map. - - Raises: - None. + Get provider icon + :param provider: provider name + :param icon_type: icon type (icon_small or icon_large) + :param lang: language (zh_Hans or en_US) + :return: provider icon """ - if self.model_provider_extensions: - return self.model_provider_extensions + # get the provider schema + provider_schema = self.get_provider_schema(provider) - # get the path of current classes - current_path = os.path.abspath(__file__) - model_providers_path = os.path.dirname(current_path) + if icon_type.lower() == "icon_small": + if not provider_schema.icon_small: + raise ValueError(f"Provider {provider} does not have small icon.") - # get all folders path under model_providers_path that do not start with __ - model_provider_dir_paths = [ - os.path.join(model_providers_path, model_provider_dir) - for model_provider_dir in os.listdir(model_providers_path) - if not model_provider_dir.startswith("__") - and os.path.isdir(os.path.join(model_providers_path, model_provider_dir)) - ] + 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.") - # get _position.yaml file path - position_map = get_provider_position_map(model_providers_path) + if lang.lower() == "zh_hans": + file_name = provider_schema.icon_large.zh_Hans + else: + file_name = provider_schema.icon_large.en_US - # traverse all model_provider_dir_paths - model_providers: list[ModelProviderExtension] = [] - for model_provider_dir_path in model_provider_dir_paths: - # get model_provider dir name - model_provider_name = os.path.basename(model_provider_dir_path) + if not file_name: + raise ValueError(f"Provider {provider} does not have icon.") - file_names = os.listdir(model_provider_dir_path) + # get icon bytes from plugin asset manager + plugin_asset_manager = PluginAssetManager() + return plugin_asset_manager.fetch_asset(tenant_id=self.tenant_id, id=file_name) - if (model_provider_name + ".py") not in file_names: - logger.warning(f"Missing {model_provider_name}.py file in {model_provider_dir_path}, Skip.") - continue + def get_plugin_id_and_provider_name_from_provider(self, provider: str) -> tuple[str, str]: + """ + Get plugin id and provider name from provider name + :param provider: provider name + :return: plugin id and provider name + """ + plugin_id = "langgenius" + provider_name = provider + if "/" in provider: + # get the plugin_id before provider + plugin_id = "/".join(provider.split("/")[:-1]) + provider_name = provider.split("/")[-1] - # Dynamic loading {model_provider_name}.py file and find the subclass of ModelProvider - py_path = os.path.join(model_provider_dir_path, model_provider_name + ".py") - model_provider_class = load_single_subclass_from_source( - module_name=f"core.model_runtime.model_providers.{model_provider_name}.{model_provider_name}", - script_path=py_path, - parent_type=ModelProvider, - ) - - if not model_provider_class: - logger.warning(f"Missing Model Provider Class that extends ModelProvider in {py_path}, Skip.") - continue - - if f"{model_provider_name}.yaml" not in file_names: - logger.warning(f"Missing {model_provider_name}.yaml file in {model_provider_dir_path}, Skip.") - continue - - model_providers.append( - ModelProviderExtension( - name=model_provider_name, - provider_instance=model_provider_class(), - position=position_map.get(model_provider_name), - ) - ) - - sorted_extensions = sort_to_dict_by_position_map(position_map, model_providers, lambda x: x.name) - - self.model_provider_extensions = sorted_extensions - - return sorted_extensions + return plugin_id, provider_name diff --git a/api/core/model_runtime/model_providers/moonshot/__init__.py b/api/core/model_runtime/model_providers/moonshot/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/moonshot/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/moonshot/_assets/icon_l_en.png deleted file mode 100644 index a411526d3d69117dc37e7859666e639d3cda433e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13654 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 diff --git a/api/core/model_runtime/model_providers/moonshot/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/moonshot/_assets/icon_s_en.png deleted file mode 100644 index 58ba4b462376265f9575769b69521f9c9ee8606c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7419 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 W|6f6TT3Q{U04U49m8+653H?93{_?s2 diff --git a/api/core/model_runtime/model_providers/moonshot/llm/__init__.py b/api/core/model_runtime/model_providers/moonshot/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/moonshot/llm/_position.yaml b/api/core/model_runtime/model_providers/moonshot/llm/_position.yaml deleted file mode 100644 index 1810ec61d6..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/llm/_position.yaml +++ /dev/null @@ -1,3 +0,0 @@ -- moonshot-v1-8k -- moonshot-v1-32k -- moonshot-v1-128k diff --git a/api/core/model_runtime/model_providers/moonshot/llm/llm.py b/api/core/model_runtime/model_providers/moonshot/llm/llm.py deleted file mode 100644 index 3ea46c2967..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/llm/llm.py +++ /dev/null @@ -1,327 +0,0 @@ -import json -from collections.abc import Generator -from typing import Optional, Union, cast - -import requests - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContent, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class MoonshotLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - self._add_function_call(model, credentials) - user = user[:32] if user else None - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - return AIModelEntity( - model=model, - label=I18nObject(en_US=model, zh_Hans=model), - model_type=ModelType.LLM, - features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL, ModelFeature.STREAM_TOOL_CALL] - if credentials.get("function_calling_type") == "tool_call" - else [], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", 4096)), - ModelPropertyKey.MODE: LLMMode.CHAT.value, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - use_template="temperature", - label=I18nObject(en_US="Temperature", zh_Hans="温度"), - type=ParameterType.FLOAT, - ), - ParameterRule( - name="max_tokens", - use_template="max_tokens", - default=512, - min=1, - max=int(credentials.get("max_tokens", 4096)), - label=I18nObject(en_US="Max Tokens", zh_Hans="最大标记"), - type=ParameterType.INT, - ), - ParameterRule( - name="top_p", - use_template="top_p", - label=I18nObject(en_US="Top P", zh_Hans="Top P"), - type=ParameterType.FLOAT, - ), - ], - ) - - def _add_custom_parameters(self, credentials: dict) -> None: - credentials["mode"] = "chat" - if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": - credentials["endpoint_url"] = "https://api.moonshot.cn/v1" - - def _add_function_call(self, model: str, credentials: dict) -> None: - model_schema = self.get_model_schema(model, credentials) - if model_schema and {ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL}.intersection( - model_schema.features or [] - ): - credentials["function_calling_type"] = "tool_call" - - def _convert_prompt_message_to_dict(self, message: PromptMessage, credentials: Optional[dict] = None) -> dict: - """ - Convert PromptMessage to dict for OpenAI API format - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(PromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - message_dict["tool_calls"] = [] - for function_call in message.tool_calls: - message_dict["tool_calls"].append( - { - "id": function_call.id, - "type": function_call.type, - "function": { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - }, - } - ) - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = {"role": "tool", "content": message.content, "tool_call_id": message.tool_call_id} - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _extract_response_tool_calls(self, response_tool_calls: list[dict]) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call["function"]["name"] - if response_tool_call.get("function", {}).get("name") - else "", - arguments=response_tool_call["function"]["arguments"] - if response_tool_call.get("function", {}).get("arguments") - else "", - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call["id"] if response_tool_call.get("id") else "", - type=response_tool_call["type"] if response_tool_call.get("type") else "", - function=function, - ) - tool_calls.append(tool_call) - - return tool_calls - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: requests.Response, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: model credentials - :param response: streamed response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - full_assistant_content = "" - chunk_index = 0 - - def create_final_llm_result_chunk( - index: int, message: AssistantPromptMessage, finish_reason: str - ) -> LLMResultChunk: - # calculate num tokens - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - completion_tokens = self._num_tokens_from_string(model, full_assistant_content) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - return LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=message, finish_reason=finish_reason, usage=usage), - ) - - tools_calls: list[AssistantPromptMessage.ToolCall] = [] - finish_reason = "Unknown" - - 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 response.iter_lines(decode_unicode=True, delimiter="\n\n"): - if chunk: - # ignore sse comments - if chunk.startswith(":"): - continue - decoded_chunk = chunk.strip().lstrip("data: ").lstrip() - chunk_json = None - try: - chunk_json = json.loads(decoded_chunk) - # stream ended - except json.JSONDecodeError as e: - yield create_final_llm_result_chunk( - index=chunk_index + 1, - message=AssistantPromptMessage(content=""), - finish_reason="Non-JSON encountered.", - ) - break - if not chunk_json or len(chunk_json["choices"]) == 0: - continue - - choice = chunk_json["choices"][0] - finish_reason = chunk_json["choices"][0].get("finish_reason") - chunk_index += 1 - - if "delta" in choice: - delta = choice["delta"] - delta_content = delta.get("content") - - assistant_message_tool_calls = delta.get("tool_calls", None) - # assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if assistant_message_tool_calls: - tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - increase_tool_call(tool_calls) - - if delta_content is None or delta_content == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta_content, tool_calls=tool_calls if assistant_message_tool_calls else [] - ) - - full_assistant_content += delta_content - elif "text" in choice: - choice_text = choice.get("text", "") - if choice_text == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=choice_text) - full_assistant_content += choice_text - else: - continue - - # check payload indicator for completion - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=assistant_prompt_message, - ), - ) - - chunk_index += 1 - - if tools_calls: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=AssistantPromptMessage(tool_calls=tools_calls, content=""), - ), - ) - - yield create_final_llm_result_chunk( - index=chunk_index, message=AssistantPromptMessage(content=""), finish_reason=finish_reason - ) diff --git a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-128k.yaml b/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-128k.yaml deleted file mode 100644 index 59c0915ee9..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-128k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: moonshot-v1-128k -label: - zh_Hans: moonshot-v1-128k - en_US: moonshot-v1-128k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 128000 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.06' - output: '0.06' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-32k.yaml b/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-32k.yaml deleted file mode 100644 index 724f2aa5a2..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-32k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: moonshot-v1-32k -label: - zh_Hans: moonshot-v1-32k - en_US: moonshot-v1-32k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.024' - output: '0.024' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-8k.yaml b/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-8k.yaml deleted file mode 100644 index 5872295bfa..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/llm/moonshot-v1-8k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: moonshot-v1-8k -label: - zh_Hans: moonshot-v1-8k - en_US: moonshot-v1-8k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.012' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/moonshot/moonshot.py b/api/core/model_runtime/model_providers/moonshot/moonshot.py deleted file mode 100644 index 4995e235f5..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/moonshot.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class MoonshotProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="moonshot-v1-8k", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/moonshot/moonshot.yaml b/api/core/model_runtime/model_providers/moonshot/moonshot.yaml deleted file mode 100644 index 41e9c2e808..0000000000 --- a/api/core/model_runtime/model_providers/moonshot/moonshot.yaml +++ /dev/null @@ -1,89 +0,0 @@ -provider: moonshot -label: - zh_Hans: 月之暗面 - en_US: Moonshot -description: - en_US: Models provided by Moonshot, such as moonshot-v1-8k, moonshot-v1-32k, and moonshot-v1-128k. - zh_Hans: Moonshot 提供的模型,例如 moonshot-v1-8k、moonshot-v1-32k 和 moonshot-v1-128k。 -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#FFFFFF" -help: - title: - en_US: Get your API Key from Moonshot - zh_Hans: 从 Moonshot 获取 API Key - url: - en_US: https://platform.moonshot.cn/console/api-keys -supported_model_types: - - llm -configurate_methods: - - predefined-model - - customizable-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: endpoint_url - label: - en_US: API Base - type: text-input - required: false - placeholder: - zh_Hans: Base URL, 如:https://api.moonshot.cn/v1 - en_US: Base URL, e.g. https://api.moonshot.cn/v1 -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - default: '4096' - type: text-input - - variable: function_calling_type - label: - en_US: Function calling - type: select - required: false - default: no_call - options: - - value: no_call - label: - en_US: Not supported - zh_Hans: 不支持 - - value: tool_call - label: - en_US: Tool Call - zh_Hans: Tool Call diff --git a/api/core/model_runtime/model_providers/nomic/__init__.py b/api/core/model_runtime/model_providers/nomic/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nomic/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/nomic/_assets/icon_l_en.svg deleted file mode 100644 index 6c4a1058ab..0000000000 --- a/api/core/model_runtime/model_providers/nomic/_assets/icon_l_en.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/nomic/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/nomic/_assets/icon_s_en.png deleted file mode 100644 index 3eba3b82bc1e3fcb6b27cd9038786e19b7c2adcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25814 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 j#hd$5Bz3DL**g^>vTwt4UwW3_V z9g*2R>O21oCZBnk?{}ph__eriGFJ8I<;}{+$~)xER=s-Z0Cez~87+G?fIZxBZ>{9Z z-cXO2v*0_c{@Tq}5a(1|?(f1t-_T*p!7ZS?oh{(QQBZ3Ypjhk3bd8#M+^w)!-@?8Q zx`v(2uJ?D|iC>g*TuKMUI!5@(mobju<+%wzhOfz#%t=L`l?M(cq33a-SMsFoeZR;`@M#t*)#b`jq7@u+jXr)bhU8noYUdfC4+(5|EITW|A#6K<7A_< zMN8STjYJ}3Q#x5O)+LqLO-!G48J9_oq*+~D<}hw;YE?t`*v6tr%0x4U#v!6wng|(& z&QOf4=`_TgL&MCz{)zqheLm+rpXc*F?{oXU-*?)hna(B5r>}uTes#IS+xpwSROg31 zOoLCkjvGU@i0iH~Y|p*etrRW8SjVTR9C1RYlp|3-B~K^z8e;J*)Gb{av4``)a<(dY z^L1+Ciz|)-u2SLrM<0Xb1v{H{b48pd4S?&BDbIq>&Quiy9 zxEaxyTGi#ZC1=MNf^3(#ENigg^TaODWDsvC&Hmy+|9RIAgb~y9&||e^U+5xE$ztqr z!fsvu66j8&L@+2|JqWf5_pO%ce=hLk5vvGe+7kok@=^rdw~!mXiczWv0x}dZ?r2Kc zu0BqPz%(J@;Qq!^e8}g=;7P2qqj@@=&Q9Runa9op4`lI6a$hcN*5fY#M9Wh(fjy+- z!X16tjR0`(5sE#?A)jr%V!sI1zm2?otqRx&BQ$+#M~YiuWEpVyT8S_7UELZzd%wQm zwb+_W^hUk}R?;RGc!*PUJ5KTLj1R?s>jfX)oH@mGDTYc*@h<-{1dUnm5UV@T+iz8jT>j`~`_2;7NvKD4LkZ=HaF^=O+pbeLIBk+F<^y`X(X2&pT^fdvME4;m5cJ9mrWO(yMw5T28?}RWT3{D*_2)NHjWQj zVR`hOFw(8j@)G_1?D-1Nz4!KgWO=f1K@1mCHMUbHGiHfTk^unywv@c4GQ!xzR|gh_ zW?RJ*=$dNl*~sj$;h}Mwc@DdHT>F{V?lP9vNFR! zJEKpLFEI^H2U0ee689*)bb7+~bD46bkuQvth!Jv5Z4Mc)^Ow zHI7+{^Mk=Njw+FR#CgqcLdhnOITq#)tndf&H@E3!GgybP3wEIA+L@0tk);B?b!=Ix zw7gailx6Q*>z36;YJ->^Uy`*%synCt*|dtx2fY}dB3GbmHp_64YoqW4*N;Nxsh-8p{~bnJJUYSp)SZ{xD=Au)hy9vq(sbd!`!dQlMcj(4<=<&jVmPYjcDH$22;RV`KF{;)-h%9}ACh=h`Y=U2n0 z7aOK6V4Iza!(P(XN@z!9_al<4lZHPCQkTJ|^ z=W+^HPlQM{akHN={{93IH0WXQB)I8G@`J^C?fY8_Ny2y)L{ekrGf=7~t^Rn4K5e#! z47xqdMwX+v|J) diff --git a/api/core/model_runtime/model_providers/nomic/_common.py b/api/core/model_runtime/model_providers/nomic/_common.py deleted file mode 100644 index 406577dcd7..0000000000 --- a/api/core/model_runtime/model_providers/nomic/_common.py +++ /dev/null @@ -1,28 +0,0 @@ -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonNomic: - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [KeyError, InvokeBadRequestError], - } diff --git a/api/core/model_runtime/model_providers/nomic/nomic.py b/api/core/model_runtime/model_providers/nomic/nomic.py deleted file mode 100644 index d4e5da2e98..0000000000 --- a/api/core/model_runtime/model_providers/nomic/nomic.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class NomicAtlasProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.TEXT_EMBEDDING) - model_instance.validate_credentials(model="nomic-embed-text-v1.5", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/nomic/nomic.yaml b/api/core/model_runtime/model_providers/nomic/nomic.yaml deleted file mode 100644 index 60dcf1facb..0000000000 --- a/api/core/model_runtime/model_providers/nomic/nomic.yaml +++ /dev/null @@ -1,29 +0,0 @@ -provider: nomic -label: - zh_Hans: Nomic Atlas - en_US: Nomic Atlas -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.svg -background: "#EFF1FE" -help: - title: - en_US: Get your API key from Nomic Atlas - zh_Hans: 从Nomic Atlas获取 API Key - url: - en_US: https://atlas.nomic.ai/data -supported_model_types: - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: nomic_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/nomic/text_embedding/__init__.py b/api/core/model_runtime/model_providers/nomic/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.5.yaml b/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.5.yaml deleted file mode 100644 index 111452df57..0000000000 --- a/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.5.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: nomic-embed-text-v1.5 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: "0.1" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.yaml b/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.yaml deleted file mode 100644 index ac59f106ed..0000000000 --- a/api/core/model_runtime/model_providers/nomic/text_embedding/nomic-embed-text-v1.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: nomic-embed-text-v1 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: "0.1" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/novita/_assets/icon_l_en.svg deleted file mode 100644 index 5c92cdbc6d..0000000000 --- a/api/core/model_runtime/model_providers/novita/_assets/icon_l_en.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/novita/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/novita/_assets/icon_s_en.svg deleted file mode 100644 index 798c1d6348..0000000000 --- a/api/core/model_runtime/model_providers/novita/_assets/icon_s_en.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/novita/llm/Nous-Hermes-2-Mixtral-8x7B-DPO.yaml b/api/core/model_runtime/model_providers/novita/llm/Nous-Hermes-2-Mixtral-8x7B-DPO.yaml deleted file mode 100644 index 7ff30458e2..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/Nous-Hermes-2-Mixtral-8x7B-DPO.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: Nous-Hermes-2-Mixtral-8x7B-DPO -label: - zh_Hans: Nous-Hermes-2-Mixtral-8x7B-DPO - en_US: Nous-Hermes-2-Mixtral-8x7B-DPO -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0027' - output: '0.0027' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/airoboros-l2-70b.yaml b/api/core/model_runtime/model_providers/novita/llm/airoboros-l2-70b.yaml deleted file mode 100644 index b599418461..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/airoboros-l2-70b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: jondurbin/airoboros-l2-70b -label: - zh_Hans: jondurbin/airoboros-l2-70b - en_US: jondurbin/airoboros-l2-70b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.005' - output: '0.005' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/dolphin-mixtral-8x22b.yaml b/api/core/model_runtime/model_providers/novita/llm/dolphin-mixtral-8x22b.yaml deleted file mode 100644 index 72a181f5d3..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/dolphin-mixtral-8x22b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: cognitivecomputations/dolphin-mixtral-8x22b -label: - zh_Hans: cognitivecomputations/dolphin-mixtral-8x22b - en_US: cognitivecomputations/dolphin-mixtral-8x22b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.009' - output: '0.009' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/gemma-2-9b-it.yaml b/api/core/model_runtime/model_providers/novita/llm/gemma-2-9b-it.yaml deleted file mode 100644 index d1749bc882..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/gemma-2-9b-it.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: google/gemma-2-9b-it -label: - zh_Hans: google/gemma-2-9b-it - en_US: google/gemma-2-9b-it -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0008' - output: '0.0008' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/hermes-2-pro-llama-3-8b.yaml b/api/core/model_runtime/model_providers/novita/llm/hermes-2-pro-llama-3-8b.yaml deleted file mode 100644 index 8b3228e56a..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/hermes-2-pro-llama-3-8b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: nousresearch/hermes-2-pro-llama-3-8b -label: - zh_Hans: nousresearch/hermes-2-pro-llama-3-8b - en_US: nousresearch/hermes-2-pro-llama-3-8b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0014' - output: '0.0014' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/l3-70b-euryale-v2.1.yaml b/api/core/model_runtime/model_providers/novita/llm/l3-70b-euryale-v2.1.yaml deleted file mode 100644 index 5e27941c52..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/l3-70b-euryale-v2.1.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: sao10k/l3-70b-euryale-v2.1 -label: - zh_Hans: sao10k/l3-70b-euryale-v2.1 - en_US: sao10k/l3-70b-euryale-v2.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0148' - output: '0.0148' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llama-3-70b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/llama-3-70b-instruct.yaml deleted file mode 100644 index 39709e1063..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llama-3-70b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: meta-llama/llama-3-70b-instruct -label: - zh_Hans: meta-llama/llama-3-70b-instruct - en_US: meta-llama/llama-3-70b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0051' - output: '0.0074' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llama-3-8b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/llama-3-8b-instruct.yaml deleted file mode 100644 index 9b5e5df4d0..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llama-3-8b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: meta-llama/llama-3-8b-instruct -label: - zh_Hans: meta-llama/llama-3-8b-instruct - en_US: meta-llama/llama-3-8b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.00063' - output: '0.00063' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-405b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/llama-3.1-405b-instruct.yaml deleted file mode 100644 index c5a45271ae..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-405b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: meta-llama/llama-3.1-405b-instruct -label: - zh_Hans: meta-llama/llama-3.1-405b-instruct - en_US: meta-llama/llama-3.1-405b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.03' - output: '0.05' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-70b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/llama-3.1-70b-instruct.yaml deleted file mode 100644 index 3a5c29c40f..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-70b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: meta-llama/llama-3.1-70b-instruct -label: - zh_Hans: meta-llama/llama-3.1-70b-instruct - en_US: meta-llama/llama-3.1-70b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0055' - output: '0.0076' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-8b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/llama-3.1-8b-instruct.yaml deleted file mode 100644 index e6ef772a3f..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llama-3.1-8b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: meta-llama/llama-3.1-8b-instruct -label: - zh_Hans: meta-llama/llama-3.1-8b-instruct - en_US: meta-llama/llama-3.1-8b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.001' - output: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/llm.py b/api/core/model_runtime/model_providers/novita/llm/llm.py deleted file mode 100644 index 23367ed1b4..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/llm.py +++ /dev/null @@ -1,69 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.entities.model_entities import AIModelEntity -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class NovitaLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _update_endpoint_url(self, credentials: dict): - credentials["endpoint_url"] = "https://api.novita.ai/v3/openai" - credentials["extra_headers"] = {"X-Novita-Source": "dify.ai"} - return credentials - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - return super()._invoke(model, cred_with_endpoint, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - self._add_custom_parameters(credentials, model) - return super().validate_credentials(model, cred_with_endpoint) - - @classmethod - def _add_custom_parameters(cls, credentials: dict, model: str) -> None: - credentials["mode"] = "chat" - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - return super()._generate( - model, cred_with_endpoint, prompt_messages, model_parameters, tools, stop, stream, user - ) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super().get_customizable_model_schema(model, cred_with_endpoint) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super().get_num_tokens(model, cred_with_endpoint, prompt_messages, tools) diff --git a/api/core/model_runtime/model_providers/novita/llm/lzlv_70b.yaml b/api/core/model_runtime/model_providers/novita/llm/lzlv_70b.yaml deleted file mode 100644 index 0cc68a8c45..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/lzlv_70b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: lzlv_70b -label: - zh_Hans: lzlv_70b - en_US: lzlv_70b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0058' - output: '0.0078' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/midnight-rose-70b.yaml b/api/core/model_runtime/model_providers/novita/llm/midnight-rose-70b.yaml deleted file mode 100644 index 19876bee17..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/midnight-rose-70b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: sophosympatheia/midnight-rose-70b -label: - zh_Hans: sophosympatheia/midnight-rose-70b - en_US: sophosympatheia/midnight-rose-70b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.008' - output: '0.008' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/mistral-7b-instruct.yaml b/api/core/model_runtime/model_providers/novita/llm/mistral-7b-instruct.yaml deleted file mode 100644 index 6fba47bcf0..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/mistral-7b-instruct.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: mistralai/mistral-7b-instruct -label: - zh_Hans: mistralai/mistral-7b-instruct - en_US: mistralai/mistral-7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.00059' - output: '0.00059' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/mythomax-l2-13b.yaml b/api/core/model_runtime/model_providers/novita/llm/mythomax-l2-13b.yaml deleted file mode 100644 index 7e4ac3ffe0..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/mythomax-l2-13b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: gryphe/mythomax-l2-13b -label: - zh_Hans: gryphe/mythomax-l2-13b - en_US: gryphe/mythomax-l2-13b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.00119' - output: '0.00119' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/nous-hermes-llama2-13b.yaml b/api/core/model_runtime/model_providers/novita/llm/nous-hermes-llama2-13b.yaml deleted file mode 100644 index 75671c414c..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/nous-hermes-llama2-13b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: nousresearch/nous-hermes-llama2-13b -label: - zh_Hans: nousresearch/nous-hermes-llama2-13b - en_US: nousresearch/nous-hermes-llama2-13b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0017' - output: '0.0017' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/openhermes-2.5-mistral-7b.yaml b/api/core/model_runtime/model_providers/novita/llm/openhermes-2.5-mistral-7b.yaml deleted file mode 100644 index 8b0deba4f7..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/openhermes-2.5-mistral-7b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: teknium/openhermes-2.5-mistral-7b -label: - zh_Hans: teknium/openhermes-2.5-mistral-7b - en_US: teknium/openhermes-2.5-mistral-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0017' - output: '0.0017' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/llm/wizardlm-2-8x22b.yaml b/api/core/model_runtime/model_providers/novita/llm/wizardlm-2-8x22b.yaml deleted file mode 100644 index ef42568e8f..0000000000 --- a/api/core/model_runtime/model_providers/novita/llm/wizardlm-2-8x22b.yaml +++ /dev/null @@ -1,41 +0,0 @@ -model: microsoft/wizardlm-2-8x22b -label: - zh_Hans: microsoft/wizardlm-2-8x22b - en_US: microsoft/wizardlm-2-8x22b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 65535 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 2 - default: 1 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 512 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 -pricing: - input: '0.0064' - output: '0.0064' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/novita/novita.py b/api/core/model_runtime/model_providers/novita/novita.py deleted file mode 100644 index 76a75b01e2..0000000000 --- a/api/core/model_runtime/model_providers/novita/novita.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class NovitaProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `meta-llama/llama-3-8b-instruct` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="meta-llama/llama-3-8b-instruct", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/novita/novita.yaml b/api/core/model_runtime/model_providers/novita/novita.yaml deleted file mode 100644 index f634197989..0000000000 --- a/api/core/model_runtime/model_providers/novita/novita.yaml +++ /dev/null @@ -1,31 +0,0 @@ -provider: novita -label: - en_US: novita.ai -description: - en_US: An LLM API that matches various application scenarios with high cost-effectiveness. - zh_Hans: 适配多种海外应用场景的高性价比 LLM API -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#eadeff" -help: - title: - en_US: Get your API key from novita.ai - zh_Hans: 从 novita.ai 获取 API Key - url: - en_US: https://novita.ai/settings#key-management?utm_source=dify&utm_medium=ch&utm_campaign=api -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - required: true - label: - en_US: API Key - type: secret-input - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/nvidia/__init__.py b/api/core/model_runtime/model_providers/nvidia/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nvidia/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/nvidia/_assets/icon_l_en.png deleted file mode 100644 index 5a7f42e61792b7b8be0529f6ae0ad4f3ba5fa7f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112528 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 diff --git a/api/core/model_runtime/model_providers/nvidia/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/nvidia/_assets/icon_s_en.svg deleted file mode 100644 index 9fc02f9164..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/_assets/icon_s_en.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/api/core/model_runtime/model_providers/nvidia/llm/_position.yaml b/api/core/model_runtime/model_providers/nvidia/llm/_position.yaml deleted file mode 100644 index ad01d430d6..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/_position.yaml +++ /dev/null @@ -1,17 +0,0 @@ -- google/gemma-7b -- google/codegemma-7b -- google/recurrentgemma-2b -- meta/llama2-70b -- meta/llama-3.1-8b-instruct -- meta/llama-3.1-70b-instruct -- meta/llama-3.1-405b-instruct -- meta/llama3-8b-instruct -- meta/llama3-70b-instruct -- mistralai/mistral-large -- mistralai/mixtral-8x7b-instruct-v0.1 -- mistralai/mixtral-8x22b-instruct-v0.1 -- nvidia/nemotron-4-340b-instruct -- microsoft/phi-3-medium-128k-instruct -- microsoft/phi-3-mini-128k-instruct -- fuyu-8b -- snowflake/arctic diff --git a/api/core/model_runtime/model_providers/nvidia/llm/arctic.yaml b/api/core/model_runtime/model_providers/nvidia/llm/arctic.yaml deleted file mode 100644 index 7f53ae58e6..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/arctic.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: snowflake/arctic -label: - zh_Hans: snowflake/arctic - en_US: snowflake/arctic -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/codegemma-7b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/codegemma-7b.yaml deleted file mode 100644 index 57446224a8..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/codegemma-7b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: google/codegemma-7b -label: - zh_Hans: google/codegemma-7b - en_US: google/codegemma-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/fuyu-8b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/fuyu-8b.yaml deleted file mode 100644 index 6ae524c6d8..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/fuyu-8b.yaml +++ /dev/null @@ -1,27 +0,0 @@ -model: fuyu-8b -label: - zh_Hans: fuyu-8b - en_US: fuyu-8b -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 16000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.2 - min: 0.1 - max: 1 - - name: top_p - use_template: top_p - default: 0.7 - min: 0.1 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/gemma-7b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/gemma-7b.yaml deleted file mode 100644 index 794b820bf4..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/gemma-7b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: google/gemma-7b -label: - zh_Hans: google/gemma-7b - en_US: google/gemma-7b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-405b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-405b.yaml deleted file mode 100644 index 5472de9902..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-405b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama-3.1-405b-instruct -label: - zh_Hans: meta/llama-3.1-405b-instruct - en_US: meta/llama-3.1-405b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalt - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-70b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-70b.yaml deleted file mode 100644 index 16af0554a1..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-70b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama-3.1-70b-instruct -label: - zh_Hans: meta/llama-3.1-70b-instruct - en_US: meta/llama-3.1-70b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-8b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-8b.yaml deleted file mode 100644 index f2d43dc30e..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama-3.1-8b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama-3.1-8b-instruct -label: - zh_Hans: meta/llama-3.1-8b-instruct - en_US: meta/llama-3.1-8b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama2-70b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama2-70b.yaml deleted file mode 100644 index 9fba816b7f..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama2-70b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama2-70b -label: - zh_Hans: meta/llama2-70b - en_US: meta/llama2-70b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama3-70b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama3-70b.yaml deleted file mode 100644 index 4d591d4226..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama3-70b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama3-70b-instruct -label: - zh_Hans: meta/llama3-70b-instruct - en_US: meta/llama3-70b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llama3-8b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/llama3-8b.yaml deleted file mode 100644 index 0139566674..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llama3-8b.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: meta/llama3-8b-instruct -label: - zh_Hans: meta/llama3-8b-instruct - en_US: meta/llama3-8b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/llm.py b/api/core/model_runtime/model_providers/nvidia/llm/llm.py deleted file mode 100644 index 1c98c6be6c..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/llm.py +++ /dev/null @@ -1,247 +0,0 @@ -import json -from collections.abc import Generator -from typing import Optional, Union - -import requests -from yarl import URL - -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult -from core.model_runtime.entities.message_entities import ( - PromptMessage, - PromptMessageContentType, - PromptMessageFunction, - PromptMessageTool, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import InvokeError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel -from core.model_runtime.utils import helper - - -class NVIDIALargeLanguageModel(OAIAPICompatLargeLanguageModel): - MODEL_SUFFIX_MAP = { - "fuyu-8b": "vlm/adept/fuyu-8b", - "mistralai/mistral-large": "", - "mistralai/mixtral-8x7b-instruct-v0.1": "", - "mistralai/mixtral-8x22b-instruct-v0.1": "", - "google/gemma-7b": "", - "google/codegemma-7b": "", - "snowflake/arctic": "", - "meta/llama2-70b": "", - "meta/llama3-8b-instruct": "", - "meta/llama3-70b-instruct": "", - "meta/llama-3.1-8b-instruct": "", - "meta/llama-3.1-70b-instruct": "", - "meta/llama-3.1-405b-instruct": "", - "google/recurrentgemma-2b": "", - "nvidia/nemotron-4-340b-instruct": "", - "microsoft/phi-3-medium-128k-instruct": "", - "microsoft/phi-3-mini-128k-instruct": "", - } - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials, model) - prompt_messages = self._transform_prompt_messages(prompt_messages) - stop = [] - user = None - - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _transform_prompt_messages(self, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: - """ - Handle Image transform - """ - for i, p in enumerate(prompt_messages): - if isinstance(p, UserPromptMessage) and isinstance(p.content, list): - content = p.content - content_text = "" - for prompt_content in content: - if prompt_content.type == PromptMessageContentType.TEXT: - content_text += prompt_content.data - else: - content_text += f' ' - - prompt_message = UserPromptMessage(content=content_text) - prompt_messages[i] = prompt_message - return prompt_messages - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials, model) - self._validate_credentials(model, credentials) - - def _add_custom_parameters(self, credentials: dict, model: str) -> None: - credentials["mode"] = "chat" - - if self.MODEL_SUFFIX_MAP[model]: - credentials["server_url"] = f"https://ai.api.nvidia.com/v1/{self.MODEL_SUFFIX_MAP[model]}" - credentials.pop("endpoint_url") - else: - credentials["endpoint_url"] = "https://integrate.api.nvidia.com/v1" - - credentials["stream_mode_delimiter"] = "\n" - - def _validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials using requests to ensure compatibility with all providers following - OpenAI's API standard. - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - headers = {"Content-Type": "application/json"} - - api_key = credentials.get("api_key") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - endpoint_url = credentials.get("endpoint_url") - if endpoint_url and not endpoint_url.endswith("/"): - endpoint_url += "/" - server_url = credentials.get("server_url") - - # prepare the payload for a simple ping to the model - data = {"model": model, "max_tokens": 5} - - completion_type = LLMMode.value_of(credentials["mode"]) - - if completion_type is LLMMode.CHAT: - data["messages"] = [ - {"role": "user", "content": "ping"}, - ] - if "endpoint_url" in credentials: - endpoint_url = str(URL(endpoint_url) / "chat" / "completions") - elif "server_url" in credentials: - endpoint_url = server_url - elif completion_type is LLMMode.COMPLETION: - data["prompt"] = "ping" - if "endpoint_url" in credentials: - endpoint_url = str(URL(endpoint_url) / "completions") - elif "server_url" in credentials: - endpoint_url = server_url - else: - raise ValueError("Unsupported completion type for model configuration.") - - # send a post request to validate the credentials - response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300)) - - if response.status_code != 200: - raise CredentialsValidateFailedError( - f"Credentials validation failed with status code {response.status_code}" - ) - - try: - json_result = response.json() - except json.JSONDecodeError as e: - raise CredentialsValidateFailedError("Credentials validation failed: JSON decode error") - except CredentialsValidateFailedError: - raise - except Exception as ex: - raise CredentialsValidateFailedError(f"An error occurred during credentials validation: {str(ex)}") - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm completion model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - headers = { - "Content-Type": "application/json", - "Accept-Charset": "utf-8", - } - - api_key = credentials.get("api_key") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - if stream: - headers["Accept"] = "text/event-stream" - - endpoint_url = credentials.get("endpoint_url") - if endpoint_url and not endpoint_url.endswith("/"): - endpoint_url += "/" - server_url = credentials.get("server_url") - - data = {"model": model, "stream": stream, **model_parameters} - - completion_type = LLMMode.value_of(credentials["mode"]) - - if completion_type is LLMMode.CHAT: - if "endpoint_url" in credentials: - endpoint_url = str(URL(endpoint_url) / "chat" / "completions") - elif "server_url" in credentials: - endpoint_url = server_url - data["messages"] = [self._convert_prompt_message_to_dict(m, credentials) for m in prompt_messages] - elif completion_type is LLMMode.COMPLETION: - data["prompt"] = "ping" - if "endpoint_url" in credentials: - endpoint_url = str(URL(endpoint_url) / "completions") - elif "server_url" in credentials: - endpoint_url = server_url - else: - raise ValueError("Unsupported completion type for model configuration.") - - # annotate tools with names, descriptions, etc. - function_calling_type = credentials.get("function_calling_type", "no_call") - formatted_tools = [] - if tools: - if function_calling_type == "function_call": - data["functions"] = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} - for tool in tools - ] - elif function_calling_type == "tool_call": - data["tool_choice"] = "auto" - - for tool in tools: - formatted_tools.append(helper.dump_model(PromptMessageFunction(function=tool))) - - data["tools"] = formatted_tools - - if stop: - data["stop"] = stop - - if user: - data["user"] = user - - response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300), stream=stream) - - if response.encoding is None or response.encoding == "ISO-8859-1": - response.encoding = "utf-8" - - if not response.ok: - raise InvokeError(f"API request failed with status code {response.status_code}: {response.text}") - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) diff --git a/api/core/model_runtime/model_providers/nvidia/llm/mistral-large.yaml b/api/core/model_runtime/model_providers/nvidia/llm/mistral-large.yaml deleted file mode 100644 index 3e14d22141..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/mistral-large.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: mistralai/mistral-large -label: - zh_Hans: mistralai/mistral-large - en_US: mistralai/mistral-large -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/mistralai_mixtral-8x7b-instruct-v0.1.yaml b/api/core/model_runtime/model_providers/nvidia/llm/mistralai_mixtral-8x7b-instruct-v0.1.yaml deleted file mode 100644 index d2c4dc5d93..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/mistralai_mixtral-8x7b-instruct-v0.1.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: mistralai/mixtral-8x7b-instruct-v0.1 -label: - zh_Hans: mistralai/mixtral-8x7b-instruct-v0.1 - en_US: mistralai/mixtral-8x7b-instruct-v0.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/mixtral-8x22b-instruct-v0.1.yaml b/api/core/model_runtime/model_providers/nvidia/llm/mixtral-8x22b-instruct-v0.1.yaml deleted file mode 100644 index 05500c0336..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/mixtral-8x22b-instruct-v0.1.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: mistralai/mixtral-8x22b-instruct-v0.1 -label: - zh_Hans: mistralai/mixtral-8x22b-instruct-v0.1 - en_US: mistralai/mixtral-8x22b-instruct-v0.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 64000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/nemotron-4-340b-instruct.yaml b/api/core/model_runtime/model_providers/nvidia/llm/nemotron-4-340b-instruct.yaml deleted file mode 100644 index e5537cd2fd..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/nemotron-4-340b-instruct.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: nvidia/nemotron-4-340b-instruct -label: - zh_Hans: nvidia/nemotron-4-340b-instruct - en_US: nvidia/nemotron-4-340b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/phi-3-medium-128k-instruct.yaml b/api/core/model_runtime/model_providers/nvidia/llm/phi-3-medium-128k-instruct.yaml deleted file mode 100644 index 0c5538d135..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/phi-3-medium-128k-instruct.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: microsoft/phi-3-medium-128k-instruct -label: - zh_Hans: microsoft/phi-3-medium-128k-instruct - en_US: microsoft/phi-3-medium-128k-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/phi-3-mini-128k-instruct.yaml b/api/core/model_runtime/model_providers/nvidia/llm/phi-3-mini-128k-instruct.yaml deleted file mode 100644 index 1eb1c51d01..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/phi-3-mini-128k-instruct.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: microsoft/phi-3-mini-128k-instruct -label: - zh_Hans: microsoft/phi-3-mini-128k-instruct - en_US: microsoft/phi-3-mini-128k-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/nvidia/llm/recurrentgemma-2b.yaml b/api/core/model_runtime/model_providers/nvidia/llm/recurrentgemma-2b.yaml deleted file mode 100644 index 73fcce3930..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/llm/recurrentgemma-2b.yaml +++ /dev/null @@ -1,37 +0,0 @@ -model: google/recurrentgemma-2b -label: - zh_Hans: google/recurrentgemma-2b - en_US: google/recurrentgemma-2b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.2 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 0.7 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 1024 - default: 1024 - - name: random_seed - type: int - help: - en_US: The seed to use for random sampling. If set, different calls will generate deterministic results. - zh_Hans: 当开启随机数种子以后,你可以通过指定一个固定的种子来使得回答结果更加稳定 - label: - en_US: Seed - zh_Hans: 种子 - default: 0 - min: 0 - max: 2147483647 diff --git a/api/core/model_runtime/model_providers/nvidia/nvidia.py b/api/core/model_runtime/model_providers/nvidia/nvidia.py deleted file mode 100644 index 058fa00346..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/nvidia.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class MistralAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="mistralai/mixtral-8x7b-instruct-v0.1", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/nvidia/nvidia.yaml b/api/core/model_runtime/model_providers/nvidia/nvidia.yaml deleted file mode 100644 index ce894a3372..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/nvidia.yaml +++ /dev/null @@ -1,33 +0,0 @@ -provider: nvidia -label: - en_US: API Catalog -description: - en_US: API Catalog - zh_Hans: API Catalog -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.png -background: "#FFFFFF" -help: - title: - en_US: Get your API Key from NVIDIA - zh_Hans: 从 NVIDIA 获取 API Key - url: - en_US: https://build.nvidia.com/explore/discover -supported_model_types: - - llm - - text-embedding - - rerank -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/nvidia/rerank/__init__.py b/api/core/model_runtime/model_providers/nvidia/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nvidia/rerank/rerank-qa-mistral-4b.yaml b/api/core/model_runtime/model_providers/nvidia/rerank/rerank-qa-mistral-4b.yaml deleted file mode 100644 index 461f4e1cbe..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/rerank/rerank-qa-mistral-4b.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: nv-rerank-qa-mistral-4b:1 -model_type: rerank -model_properties: - context_size: 512 diff --git a/api/core/model_runtime/model_providers/nvidia/rerank/rerank.py b/api/core/model_runtime/model_providers/nvidia/rerank/rerank.py deleted file mode 100644 index fabebc67ab..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/rerank/rerank.py +++ /dev/null @@ -1,121 +0,0 @@ -from math import exp -from typing import Optional - -import requests - -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class NvidiaRerankModel(RerankModel): - """ - Model class for NVIDIA rerank model. - """ - - def _sigmoid(self, logit: float) -> float: - return 1 / (1 + exp(-logit)) - - 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 documents to return - :param user: unique user id - :return: rerank result - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - try: - invoke_url = "https://ai.api.nvidia.com/v1/retrieval/nvidia/reranking" - - headers = { - "Authorization": f"Bearer {credentials.get('api_key')}", - "Accept": "application/json", - } - payload = { - "model": model, - "query": {"text": query}, - "passages": [{"text": doc} for doc in docs], - } - session = requests.Session() - response = session.post(invoke_url, headers=headers, json=payload) - response.raise_for_status() - results = response.json() - - rerank_documents = [] - for result in results["rankings"]: - index = result["index"] - logit = result["logit"] - rerank_document = RerankDocument( - index=index, - text=docs[index], - score=self._sigmoid(logit), - ) - - rerank_documents.append(rerank_document) - if rerank_documents: - rerank_documents = sorted(rerank_documents, key=lambda x: x.score, reverse=True) - if top_n: - rerank_documents = rerank_documents[:top_n] - return RerankResult(model=model, docs=rerank_documents) - except requests.HTTPError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - query="What is the GPU memory bandwidth of H100 SXM?", - docs=[ - "Example doc 1", - "Example doc 2", - "Example doc 3", - ], - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - """ - return { - InvokeConnectionError: [requests.ConnectionError], - InvokeServerUnavailableError: [requests.HTTPError], - InvokeRateLimitError: [], - InvokeAuthorizationError: [requests.HTTPError], - InvokeBadRequestError: [requests.RequestException], - } diff --git a/api/core/model_runtime/model_providers/nvidia/text_embedding/__init__.py b/api/core/model_runtime/model_providers/nvidia/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nvidia/text_embedding/embed-qa-4.yaml b/api/core/model_runtime/model_providers/nvidia/text_embedding/embed-qa-4.yaml deleted file mode 100644 index a9b5e25c3c..0000000000 --- a/api/core/model_runtime/model_providers/nvidia/text_embedding/embed-qa-4.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: NV-Embed-QA -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/nvidia_nim/__init__.py b/api/core/model_runtime/model_providers/nvidia_nim/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_l_en.png deleted file mode 100644 index 5a7f42e61792b7b8be0529f6ae0ad4f3ba5fa7f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112528 zcmeEt2U`?bw=S_!Nh$+KR%k>7LCK*3BhY{_B2ls=k<{cIT2w~Kg@8!<96%%^NKTCi zZA%gua%zI)&;-fh?!uY#UC%l9{({e^9<0J$d#}CLyWSOc`Qmp?75Y=`r^v|2=+UaT zwaLhs{vacxtT=%HzfnYSz9%Cu*GJ#Jp>vLHu|cbKZ0kn<>ekHO{@@!aW=6-?X)noE zL$J*!Kk4N)H=-{woG!PMyeOhNSvG{sxHkRQ6;_^0ci&vQ;4)}8^8Oo+87dAcVabO2E* zOb-%hR+iFWvHtzXUtRs*5C3(S_v!3^-T(V0w}|TB5C7cZE(??YbN}zZi%$J#TC)Er z0RZAZO8Acwe)7V9knkTQ{09j?HSixK{09mDLBfA&;Q!+e7vU_n{k!K(SMJiyAI3-D z?nk69f0r1__uLEG%%KFiC+SIN)BimaS#a(k6-hT>-IQuQX-K+YhmJ;wW$*XLTSm; z-297{p;W)wIuZ8>Kca#{XW!nnpgX{aFF)D zFX27XPen#LJ94Bdtt(yHZCaAlJZqkwl#`USq${o9m(9W6GfSP`p}4(%piv{NxI3D< zNUi10Y|FDWz2s1sviW97a)?9zfb@E)Z7_E;r}n?kAKW(#lq}SqV(&AZHJCKCDW&e` zR#u+xC7fO>J7oW{&%rr-b9(Xl;pi-NiL+xFN8t;a(Qc+5rU2TlBMWDf)McW9oVSlV z#z9_Y-aI{|gh5K)U`dxIw;YrUB<(g&%3oG2c+5nG8IjrYrb(^)Ydn)7%GWlXr_jmC zv%92J+D1D3-q3)4#N&3;t9v=Jd+p&O&5r{%+7AnMtLMW^nhv7efyx~J8n>RqCvl~E z7>>#D;ZN0SLk5A$3O{*VPW5ioCet6@-`Wnaj&cpWy_d?m1ItCcQ28_pSs(&qT^nt~_O?GivyKh(h z_l7#(TF-s+EK{2Q{iyn2>5=o?LK`veM^e^a?tEd+QD@(FoQiMvR?XW3m&ya>y|zsb zuAI%>gZV4JP71^~1M?c>yUq9wiK_!~-*~(q2=sVA=sfhc@}qc;to_EHXY#(T)Rc?r z2ALlC0EyFs+YvazR=fRo1QGN2Z+Ef{L=}Ym*S0>IQYvw*Zs(B06bHcWk+xoNc>f49g=s zW0n?sugP^SE`hx-NDa~dVzY{zyJR?_Y5k6^GD`JwhjK1Ecl8p!j=)Nz=GoBQmuE<4 zQSN4}IkY{tzMYAoLqh}ZR=xR_?pE^E9^sJ^ety491|CRqCBROG{m2HIebF{rs=HVj}bND+*O6NJ`G}GmfA4zk|F!F1=ESKv0F=(R3dqZq)%t}m zpYs^X8=B6$_Sm!O{h6_@*@q-{U%(qWtT`PUf4W6^5feqdjD|FY)soGE;4)|ArEKucU7a@gH(ZZ5bj9oVN=D4B+YdB4bc2i!Ttwyk>KY@Q(mZOa zY6n)8oq;*Fms|fAZ#AA#+@1?`FkSBVba!ZtR5!&&HG#? z$|}_l+u6x$BzP*zMRm;HD~>Yr$c*LpEJZ}>mrKi!&PM!qx5IgvGm#nJYDW!^7SHv+ zPcMno#g}<_yEnbJArsA|(b}@@Nz*<0Af!mj4t!|3y#0QNgR9V~Q0jloQ*OaTRuh@x zS3}tmkS#wmo2u+3C2z|0`h#40Sc3<#A#Fh`S1JE6KtI{1qBbS7+bt{2X>)mg8pIG^ ziK=tIY!RcIL`L+OUff}DUf2+;S{YCu8JK-u_YhGe$2ijWY~Hd5Up29qK4M(!x4hBa zv-f7?*Y{x+Jz^ru8y?UqS4vyCozB$5RWd4>!&WF%G7`+~t)->IldHIi)%sDZo-*7^ zxF#>VUFa^Dzb6 zM9Yy6Tq${)dICvYbiYghGtk$IWCmD9g~Os-K7jl78khwpZ8|9DZb zjk7%XD=*g4vsi9mw)qz!z(d7F2Z~_*FO_(CQjo}x68Ut$nMHSxiZgw=olU_BO(9#G zmGf>rw~p3UJ;yeTSM0O=l4=VFfmiiDa=-S=WW*c*-ZB3Lg*KpI^Cf_{w0y-qp{R{<=@s|K?aOQ)Ih`MVc^NzyMBF7K-A#u5 z$9TTro>x?-iJPZaN#}{+zU(m{XR{pcd~St3v%Na0l*jNSb{mtw?g3E#8<6;!O%6{k zDXxEC<}RsDt^I2xeYA(!4S|!K#%}$P?pe{VKUzqerK#J0t{IVe-_5h*JW1K7ed^apDOyG=G;K&xZf z{|lkxJSO3&*tVFOlA;On7eD2LN8;WdfT~Ts0v8SxbPqg#3?1!u9YbgS>K<*DC62 zOlrpa#yDvtH7Cn94VgYsTYz4hd9ia{Fo+VG<<4kn3iOj&exHipkcq^C!4 z^P!}F?~%4mK#D2 zlx%L{7C$izKcQa4UD>86vm~eOkL2p-C%A4X*4z^dU})rIK@w6|ZxXC3={dkFR)Vh^ zlM6kgYi+^EXI#s!Or8$~oIDe_`kzq?gH4;0)50@bRV6AevcbfTEw+8Jz?}{H)Os1qz;kVrc0b?0Vy+2Uoo$`C&N-V@ z%@y>lW4w^0zs-4g>iD+kK4iCk50=nomAA$SLSeLU4M3-U{epyyA$S6L-#wZ_F= z;_yY`IexU5OM7zp$)w(ZZ$tRdB%asOucdu6V9g~ex3cw^Gea!XLFSao3zeRSNo1r0%NXh^=KE=?EakqXf__56i-+O|I(Zw^#mkHY9 zVSPk8G-Pe32U(%Fv+23lG1K&VmfT~CYVW>0(uDRYZS%^QoevY~4-Z79L)q3F9|cZz zg4e}Nl-;23)|wE~Y>bISg~In$(GL~Tr+xD1f0+*nQwIf0tG?^l8W*g10HLl5pf9%c zRG9wM*t?csN=&d{8ST&q${`m@py)D78c#LYf_}Cvg)vrwzE|3T1>=DQxwRLh=93MJ}F)HA`q2sPVl_V7EGm)uFEmorfzR=f1Te`+zCE+Y8v3!a$V z)>wWZS*UvPD+SgLTo$lI9N2H|{02S41FJxrB45rnOV4N|dO1vd+OZ*C+Jrbfz^utB z<1&ST%~R~&29*1Dl?p|UuN=BkL6`Qqr-FhO8s_1_IrgiFnba|Ne9q!01e7a9#%aWd zArmi_13>tFnk32Ot@U_X2$fCTub|Mkya8*>u84i?B0+nTO9qh!Ar18aPBtt0OqVRi zHhpqgRo25=8AAjjfDS2ttVFKcZkMEu&JGVWya-EYW#ANr{)0g&GS6U7cle%pP4HdD~3MjoC>`<#H>@fB$n1XLr8I%O7(+-hS~yVyeG5{B%y z0{%Sp7IT%MS#a4^99Xh~p>N{B@CA4?+CRiNFA(Bx6U(l_1#&tZ6Znm-Y_?p=>0 z6oYu=TA%ERrKAg7jAT=KLdn0=3~0^@)W@SN4nC9ofy-yScCYOt831UYOpBuPhNja1 zWCN*@S(B0=2q``fTfH!hG^|q$$#n^~+U1N4<0sT^NuchD8IB4NK33|21#?oL3XCJY zhOj~yK`9GB#t@S6*#YJyGIlC%n{AjET`UYNGi+a2JJ`s*E*;6knqrPYaSIV@frB6! zpO~S(QndTDP#><#Og@_=-cCLb&ye4lY~^DJo97+=P=-xlQp%q*Vbax~(+eu!HM7UellV;(Zrir+M(1jM3ez+s#p zkK9Q$FK2q*As=Wp3RV+#vE`hp{|S+$PD&Dn_=f zCO~uLY(Wvu2^tBFBn7a@&a@3|6A_pLt+Hi*3!M-s3KoJzh}*7#MerNsyCzCNJ_zwL z&q5OZ0_jAhN|p(+D3LkRY_mx$KBLb&GC@E#$4**$O^ID6@Gg99KD8Y; z^rk#Kd%XW3WTK{#i=ZiQqDWMO#qs))IHeekNckc|czmV_8t=ZH^End@d->;aZ3Yk>lmZt*<{!M1#T*7!Uh<-;A>8z0< zOfFAh2?o{yDvD770-DW)Wmm~Sp87~GXwLNlkU>#pLP;HRUOqvv+WD;jvSJ?rys42Z zFpl1~@EUl2xBrj3700B$H^~oa6eo=PqoHhRA@rITOz5n2v#$~xw8GPG(Lsx7z}m&P zlRpgVIhX{!2=cXz6?_^P6*%^|nHCXc97Z#lMEO1ey~QMK7nY zssb1s6M{R<`1u;{Y7Dj11j->6ygW7fx)F#}99zT#^3cS`;*eFM{1QcIN?m7t?Lk;U zGTXiYCodsY8h}x~bvJ!c&c1ZLIVDv-Qj@FlI;{T4V<-X_Iecq7$x6!d<~fv2y@{Tm z77)Hwc)-l)*UH4DBqG;9+>XIBz+|OSyY%c(VD#WVkJo6UQ;DZd=b#JbPuK|H@@upt zJ*+t=J(0eBNu(htRwKSm<~W;df~Hcru+DKBk!{-*@W}4R{JrM%PEGvqKo9ukedm(b z30CZ_lS>u%A*;u2YLKSZc}s4e)TwVf?aLCo0+KGK4~0Giy`+!$EUWv1_a>TIKJ(MUMtlb zujY@n6#q#9mGWeTpK>t@iZ9hk_lAYR1`-3uB7D&A2<-l%hq*|C&YKwI{pq_2{VN-^ z2mR%Woixpzdl9A~cmNz9veB~Uy^aS<%#%d^$ysTHQv$4lEeDX}4KUR)6*jI+My>iT z@)0gapixspCmAzHeT++HTvup{ATx(&$b9yTd5f&jQ8!8A&nQ( zW{c=7v)Q{PW@}iNjkIu6V0$Y@kUJj|9oJ1I=}aGzoYId3!OQ5n-Y0Mb1JdpL zc3|C#w95Vx&H8Ug@+CuH$Fj;DAWJGvNO8%dZI|oKZVWHo*4jSC+msWi0tln7rw{q1 zh>R|0ZTK1U)j^rZ=KTh_^O(jW&dSxH@uJUp1fk0djyE(;ECSq`>`WQw=3-36^nIRh%N|y1~l?s{t55=;itqoc-zp|8qQ_;AjUzoownK08#_& zC~6~q^t-R&$i&lGj_zGmVgCM{wLFEN()klPVM3)#4oFr9crs&1pJCsZgR72{$LTR) zbW6+S;%HP_(*=kI?__*AQ89Fq08NR|jGrJ(ZBreC8R^$dOA_%v?cAgUJCVk7AO9$1 z_u`A!3CKh_pxKi~K`-LUgF16V89|;DPZdnHb17FA~OE#Z3Fr=Hv>p$3T zN*7jzoQbVIL!>v|(%Y3T7%6~gcx%v9gDad)c`GRfN2zZXv~9By+;^lnBkQA)d7sMF zFXC;6Mc{#(dABepOhZAUy?uHaR=M!qbx4@Tp2V;ik4$8TrSa{iSt@_nTVFA$S;&Fd zQy23X-MF!*;LxjxC{d=_E_P`?Z2U7WEF>U&bKP28Xy1&lMA;4D_F7U7XR_I-=r#O; zEy(#JD?MsLv)6Hhxw@!PpU&x1yM_CZOhr>dMD++!2vOb|?!;lc$jLG?C4`V?VO{bc za`99Z>j4!ly{H@rEc7MqSLL+foqY$zHq!7^9j#J?^n~=PTmQuYd8vmq4AB}1GsJq; zvyrbf;@7RU&LP#uVB+NVk7}|fu^We&eR6&X1KDa$=A3|9F(+r3?ZjgcH$grZAKfi2 z^N4ew$}}ntfNTg8+rY4Rwd$m_k$X2TvWaake`vOT{Q6PLY!KUit;_R<1p32vnW~{j z*@;cqx`*!n66FXOm|dY0KOB|WmY)o-m(DXEQ8xQ9LcEyH1*%g+Fb#u)S!GuwC~Ei# zxBZ4(3UK$FN6iCmFo@$|Z1o22A%Hy7X7zD$@L01SDHKvzsr8Npn0*-s8U?Opq6{Ds z*<!r_wMd6y5q0p=SH$l~Y-#3msP*VtP!1F`Gwkkw(!c>1a@H4QJkcD%cA*|!5 z8rI{o1SP?Mt3kF9*imdj7tmtNHvEKJvmYZg*%3;WG5`gkpW3mGd_zT!oB>Yfh9!in zP7di8xOamz<0DO8zoS!f%-+ly8O+r6^m=LYb8IR$HiqS$pkcI7G23lpT>hmufZJ&Z zjDBTB@W#6wveZ`{o}C#zyi>@7I6AclGK)l;pWfW%a(?v513hZE^2cfNOETYOhpO^9p zu=5WjA=1RFEI3E5iuWrrIGtk|DXM6pH}ef&s>eK)N1gC2EV?TGbyPC+7Us*<$(1}D zm)Gu)2ZymzYrj-v0S}^2RHQ1Bo^Po_DM1WL?%;&~|+OJ7#gk$&Gcu^h@y=TxLy=>R01UK_eMjyZTW7S^>VSjqZ* zY!JtHiU$nHh|w9i&s0Se@KgkHnmA()CjMNo<*^wOKWhFV8jw8#p)R#6_L>p%o$J2Q z*s|36bJqA1<>BSQ^2elQh z1$5L{XDmRH2U)H7icIqxMoxb~@PL%my95-4m_cO$`h5;gF*R4E5gQCw8CwARP;*0> zJP<{c+q&Uw`NQ%`7v=c)&`Cd)x<2AcYim(OlGd`l#mKWV2ICUHPJyR@5uJiSH)t_>Q28CViV|<+L?Dw=m?ON8T1$Fv(8cuNRpdpBtpc!{t~IN-=I=wLeD)D~ z>aNa9&3X=819HNHaw3COG7I&oZh4}wf3Mazc+F<*+RDZy<=WpG9Mdhet8Q%Xp5IU! z7FM8O8)1Jyg2T=0VcW*e6MN9qDLqT8ciRlk^a}gU zRiPMHVy$H5)Brv>(;x-AzhiTva2C;^hIOUsJ1nxojElh)f~%(l!zU>;^g>Dqy;<~w z12rAlsqPPwYmz^Dd0;ju+<^;mrMIb}wtJgW-_)8=SRd`298-oG{t8?Lnw5CohcENMIs$ z@Eba9HoSlcF3e7^6zrmf1K_?&B5EvBnwU1OG%oB>m$O6zf*$7h}nl(Atw{BxAqYQ zO{F%*VfGzz&H2!?M}+nX*Dq`f2y z@Uh75NixnCQK0jl{6`!B?~Tom_lh@`Kv&z!HUBfmYKXUVdlM^|0Ce%f_)iKDmDC`O zkA~^`D)C#=}e%#Jz>!Y)(uN;1(=N*mTYN zR4Kz6Bv@%HU&N*b0Y_WC27(A5GImnD|2Rv!*e$-Q0Y+8)4&HDbG*)A=2SX*rxF@2#wE)V1r_DGiniXlw-QS@3e zw>SAfer0ThF+fVBp=KR5;9yJf!{e+#qb$J!45X^d2b+s1@ckU2 z_~B5}Q*DhtWz|3Ed3o~YnIs#V8rP|~*Kqv|uBiqLogTa_k9u9N%_5E*nsbqXJt96t zSLU{u>~Tj4n!}JC@}M|oCd%Qr#)d7gg5p{#YUPtHz++b)vNEC9Wa^}{eQ4)kOrr`x z?CLhunbq7W*%)qbQ!%Zc4l9usT4W2MbRm9GR~bI^4ssnt=+g@sm&)vZ9>7;ALw*}& zk|NDbAocG2l8ecM*HLeP!MIdMeMBKJnMF#13OCt|Bfz=N8yXrMt zo1(r4SG|Qm4+IkC51}Tk6$b-TTgmn_)?;nnY1osE$DjcTP zEIOHm^s62o-1Af`eubjeN*s^y8n)}JqBXg;?7512SJGRd0o9iuUaBVXjC#KqGv491 z4Nr$#UB~7IHAUZK_O^A6zH5T|YTvdeVMB&43dhQyXw!kaOqiEU zR*eV=w}wMBk9%ha8pCZT$MS||?>Cya$n+OKEWe_pw4#jOpSZ_Dn#^)0?M)Knwk?2o zjHR7F>q7-rt)}~{n){`~UPd3-?D@MV4P`iaVy)ife@aKjj$br}d+_M^&{BhEZmUkl zaMRd=J1P!oW=}os@)k+>oEV2ZYS~rK8{Xj;;HY)rY*T;Nm_S$XarUy1vFxI2Polj; zg4F|*C8t8;tl1*BgOS(UE0g9etxiTDTfbY}nj-7mxUwG^RuK5{ZX+>H2C(w6`f44O z_LaOZkG_Z^xR%3$eZT6j0oi#zwQ@CnsRUCfvIRf6T1aw84m;w7p9YjZ)gUWv?Ma>v zpftKv0tx#~K9X?^iCwAaZXw+yWN;iLvgw1B-WZO56^~lQ1EkQfaY=-miZeJF1F?Y8 zDu&`qrUTSK4`?IPb8-Z<2>%3yqU_pb)Sds5>q_cNuZaX}*`aW78ubo84r!FEzy|$E zfbm&8pWR3rHI!+vV|;(a4Y~~qey4hgfNGem^&sn|k5SuiyG7TE6WK)LGzvH(3fK|) zA|h4uMtW7Q2lt#nIQ6UPRL6I!hc=8ARDX-dUYUH<0?s*&i|J~YWaKf;b37mHl2_Gg z&VpcftgFSlT21n+I{RXW8u95`7UG((G@=?wNMlig#Diy){ITQfKfxw!ChpCb8t!GD z4&D=%UWcZVlJM1lSH9*UTe^%y+=rL%2rz~uuqhae8P@$F4YQ+ol7K|bK%%d69Gnsi z?FO9rTnGZPvW>WJQ6ATTJl*)K7CG{Y`gTnkdZX?NA^oYG@m5DZ{}Vrg(*1;b?Mp7&*j&-IO$S_n47SQRy3!R;&PcWsRr04)dj z)CjpVR!8HEzu`@z2u{b5-A>$N7tnQ;p%&c#NcO2J4CQyO*1_0aY%^BlxqXqf)%xNo zqd-s?#lI-^d-g2xUh!X?!P0M5n>{!Jh97nFCV|4|PO(OE>7Y-RvuoUlCYLO86hLvY zbT~|ENFUDj=0h18X@cwONr&dgfGao|@hOl-EKIDi<9a{+a4qOf1IMaW4J8N2cB~%c ze+Gcx>Lo61gLARluj8z8QV!236;*s{5%vYeKY{>BMIK^onx|blXsRNde^pX?>^y`{Q8b!NKma)dJDa&<;Gbc5qxdFleP#-4zahYtoSYW zCpRRhiQR>RfsHY&8_?yeA)^`R@&+eikVd)>ryzQ{HtQjM;2p?{w^dCn4g{;cMb9-E z?}4knpqQ@eeguf=;l~{#Zi^OBQvoJQiQ~g_a7NXGj}P9te4NviI+&>8;J?3^*Q4k< zXf49w|fZ>)5fWw5kq5CL3%yp}%X70glM#!+xH6k?v(zhvs`A zpjRNfr?L(B6Y0t)kLx|i*#@EXB80@?0E66)q|>Sn?KyFGG^&7;kQzhuyH8rq8%rfM z4_(Uj&-vE_*bG1V*c9F-0X$y*JD1l^|} zr+pUabHvDMO3FO=TRXMmb}}4$lbnl?T^XASU?+=h5r5E#D>~%{5sKK-qG=j00kse$ zyvZ3p$&)l;6}hi(920 zj|vpo?;71gJKb#cWgJJ8ljx)(svUiY*wf-Q5=*8JHmkynrG(`?V(!z_-TzxG1xKZ1 z?12k|=a-LFI$|c=p^d;g?;3#7T(V)H5dwZFc+01Tmrsg=H^g4(nVUkxLF?Y~AYHRU zC&gJkO!0hsi>94h1$3d=MO(>l}`Gz$zy^JpcoBq8r0gz)5+~@KVCD zP47?TS^Bdqj1_nKgoSvqaeGq+yp0f1-@@C{b3(W}hVE zOMMtK^|riwb|U6(qZ4b|4~jmuL+sBOZC7?yfKe#9#wjEM!NaY$O@hA>A-s~YEX`N? z;euD#m&#&Kd1(s-t1Jg!6#&f)b96NGJftuCDj~;s^gv7j94sO_niU}ehQ16811-(d zp+v@2UgL}4DRxQ)9QAI=B@3X7GuqU|uJt;SK3b4o#^{Hq3#f8GPH$;$cxTbsWSj4j zh$NUGazFSEFX8(fH^wbGLpzKoEg?Y?tx5&uivN13N!`OrgG82jmSy=d*BGgU$o9Ed3AC@WWyp% z5hcFRXp0(~;b&T2WUT=09H)>Lmz)OBhXd;XcFJg6)k9J6MBBLvDq?w}LC^ynfGz{Y z=+9hwQBdm>o43SaEf_BBu`Z(uzNU%#jDo*tuxe8)wgM&7*P)e-mlhkRIQe~M{aKR2 zMMf3+-P<1rT|v8oL($nxgs6$Jds9_}BB~Xb3%_kicq(r{x{~bqw&i(ewhXft?AGA2 z((+*$eW^l3C9-&#iw9m8Q(%a-g7QBBh+_nQ_HhSdHyhddS#8a;j?xq=uq9O@rxo@O zQe=td_rpzi#lH?jT+xQ$^-{_wBqta*Kcy3gn))ne;?QC&M{4Yl73qIny$E2lf+g?JJ(nLyj%FY_HwgkAkN6P(V1f926ws z`v9AZP2)qWWe7uOv(uxzmWSKe$TjceL~`M5h^gA<>u;#{&!M{x!z3u%K720Tq~Q2c zWN|Z|KIIA>U#3n6#UXXYpZN*SR${_0VoKn{YH&$BV4xA9VIWhCdfap#b4`2cHU0ic z5*5_-h(X(z4$jKZmqS-^K{TcacXBOoLZ6XgNH!Id(dmYara45QwRx>$UR~m<)ri3M zTytgD_CV1MbA}BMmP9+Mre_qWTDG2_+hQOe)q1hDGTheaSZnM>Bp>b{_#~3?YM#)| zxKYm9uS$H(8w=#wO~IXpW!mh~A-WR5ch5Ss2h3o+cceohlAr4sR^KFwU%Of&k58(0 zGwy*!gDzVupD?a=ldgXxT2z*vkgY1%n=;uuZtKHW#C@z7Bs%@OBqJH&4Skb5kpbP_FR0zsh#W{ zpRTr*+X)BZ2usVYll!N$!(V#PS@S}s27E;*8<~Dx z5Fp*k5X%+n`Xi&3DUqs%r@#N6`F;GwNENm00&0IJZ${da!{8K~eTd=~+=^qTXTr8% zqyF`ggR1O&Ph>jQ2ASDroUw5ZKI(;Z(Cb(s?0q1UcIo$+j5RxI(`QIQmtxhNicJau zN?XdT3%aT&=w7Kgobqs3OzmKdzMN-(|NSDb-BaEvLbS6GMjzp1%UJGpWg>_#^9!sM zjGhYi@B}Lv3|mXBj5Br)c0fJZ{_|L&VB27iOZkV)-X~r^*O9j&-?|`pr5GouI#)zL zd#5M*hFTpLtPf)wax=2glMXOmdalxY(?B|k;=z?TUnKekbVs|%{WqB$DqBiha+(Wq zWA~|~;$J$vWRo(Dn_)wuvwt(c_waEc&EyBe8^~rS>Nt!E)6UP*D`IWa^4yE&`7V$&etB$3}OJ>*-wK&zMU31L){$Hu*N{8?|u+ zxWBw)jLhC2rO#7=UR^Ro#*Jjw*5te%&pRh8j~iFDX5ky7!$x=*N}2lbj@^KCY8oA z*#;vr2EUz>Ak*R#q<<6BHOZiJg)L)h2n!EfI!(NPovVleysE3FP(+2kT;Ft^sfZb^ z{m?2}M{g-0*^tgZRaF`zqrI~)_Nnc5y9|kp#dD@&;w#e*1C;T=Ln0tv^UlEx33fdY!xi8!8vS>3HAcmEi$g< zEiMx$j^n!Ih`_qRE=6QP@{eekivWirJBozI2nvc&s^PxF0u8UJYJZ=?YQY?+_LL4% z?pX_DAEM2R#Nv5{Z(dHja;@0mlZ1Wd6Hc7`D^cC;+Yu*GJUs(HURAQcCk4>toYMg=Ym- z6xqR4iI73qZ77^Hrb+Hfv2@v@aGLj_slN$j%xq~BYfhLs2w_Z&cm#MeuY-Te384)x z#fihd11|P#7NRy$_0`1?)lzFI2+BM88XzMCR}X&~hF1Gi7{i7UgvU9?Vo{KePd<(l zcyIKbE=402Ef0`pB#()Y%=})zNQZ3{L(F{vaEwunLIEY|Q07QTTZy-Uj3(orb z$=RFWKAoSjh8mqylR%!2qf~`*L_#voEG%Z#)m~RQDW$S~{#&p_6?aq&GgEW-aO`AP zk8ItuSFRp+*kg3=9?=`Hh!2m9;^{%$yrWjJj0*B1db2#k%>Z_rV?B41-H071Jf~ob zOrPBF6(Wj!OeKda|2icE&3k9Kd$Vt!$|SymdN@Vq@yrHX^2GY zCfw*UHS*{1aI6&`s7#8$J%;sio0xmeb$R(qUMgxq&d8fRTlu$zb2qimTD?eSqNH4+ z?kyN>b;zJva2TH}psrD(8r7PrR)sP)UP|yrTsijLF+tq-OIPXu&(34-sH**$Kqem3 zHNFyTq2u@uu|fZ0+NOokUH=Aa8Iv(DdrpQ6t=(fC{ZU_!`SoTN(;Pjvp87$wa>vyo zMV3V1FewT?P6kOc396Fs3RL+ZXzH}NPxhBS;9Mq^*pQgBdG_RKVf>>EFm-3%H$MSe zqNj+$;CYw%>3%Grd_KV!(-h7()k1F^ThdPKOvP_9G+@|L6zba_NL_vIV*xgv$n9l2 zixyQ;DUnTIh)6ZTRsBBEQRODQZ1^Qa6~O5SZcQUzVDO)Hb8IgRRk^hS8{$NMLIk-K zK^goVck`9|7$qY5v5NTbp&1#8A+H#es1e5zIJ4~nAeD#k_C*;e0A>6rr6`F(>gpoU z9UyoJ%6qM!`$ldRql_0ZH?e!IG}r#&&)m3tuz0|~7a!H))4us*%25AQD}wv)@8`Nk zy%mZmUPnSsr?WjdDO;7byISu6qA=$G%78K?cEyQ&I5co0yNorQFfx0W9XLov&e!vE zvU-~?#(q4~R25v0ZnD#3dCpXb{RY%-+4LsX`>Y+EVDGi384~Yl;-kje0+y)d26q#m zyt;1BLL!l_W<9+wO%eiOd=20htfO;x9c*9y9z;(e9|N`zK~{*XfFOR-L*_tIusU`U z4D3?<$crX)p3=Ap=|~zP4}y3^NN*g7$z(I9ckX9&exNomOBh$zZ=c*#gP+E$Yol;jpV5u+70GI)x&pVe4j?-{&Jz` z`GBe#REfi?61K$6f98dQZR8@S8ejkSJxGM>aNGslPSW7 zORqR1fQtFM@xdsy;=P#1_?!PCN29MX3UVEidsFb=K?v@60;0&sc*E$={s{I?Q5!|l zDWss`Xb3X7Hkxw z<5-9o4S&VJ@jLV?8gk|Z0;mct74>XqG zfpxp|f$H`1k4M~*MX6^O{2uIF0Qp0zFR|wIBS#^O+aJTjDKmbmCtVIY7s(H+9GRk? z(zOKNhcN-J4Kp96B8qKWqQE7)I&wOSE2^b$*(#XPJyNVPzX|RHlK;$?b0k^Gw8)R( zQ}YA;HQWbP?X?=tFO-0!bzBTHuD#F4+2PLe(#C9#dGY3T+^+j8WyS@tU_dC90UKpL zSHQ3iJHAcfio-3nKT`FUE8WGTehc+^g%cw({~bPdEd?l#sPf33-|IRS!a^#VTun9S zoviBI)f4;2yrF&N!O49g3_TEg*D_yK+qINJpR)coq;n5QU}Y`pOrUBfk0+y7-Qy!U zng@I-s`KMkx&vigyNKI3H}`e%ZE0;nlEC8lbbIWMq&9`IY*bcoi`=d^GJgB~G4!F! zxgeruOjV!u{%xC1?M)%bV|t!j2KP}KMeE$n;>~Qdc_r~r%GLERZUagb6$(xW18vV@ zd6HuRA4v|p)_@h{Yse-2B#G~tjUbk>LCnvp)wkUk7rdRJHYu{J#MyTeX zueow!7reM}3QI{cEP^+xSV);CX)t2gBtS?#%y2Au4(`-K!>~S=?6}G60AXt0)RrtI zyAKc|4YmeUT*QYjX&y5Ml|QARmAzu)omY!fk>AT!QTt<=v;6s&By1>*&aZ{A zH8y0JWqA18lrsih&i)>i@tDAuc}rC7&woOwUC9Xp_=^G?fL!ghyA+xLk@wg#*#GKn zflR9BB4TKu_q@A6w1eIrrZ3;ehBQ}OtQ z>Ob6t|nZkuz_Sj&_skvQyIO+MZAzP_&Z|_CAtSAgP{TuoUASadjIs5KNApE zoBaxf2>2|`&Ox3DtrkUnoe#=*N#N~?E;Ee;undb?=d+65)FldCb5++${Q)9#0ITdZ zsz_&13@4itsG|;4bv^Gy3IsDdQU}r*Q)$4TxPhxH7n_M|#C80BcAxzmRittbeH)H7 zM#s(#`$ClBlQP}J?$)q+zwy+Y6O@RHEx=iv3grH=Ii zc%!O|(&4ZnZ&S_5AmEHMruHPS9;4DO?+v*y%^MhR@?vm|NYf);LZX>t?x)aM$;*hm!LXI^mGr%m+3G2SbsClA5Jz>p`Afu@7VRc65*L~tlr7%19$g{+K1C>SV0d#Qzz zHacU&hx*&8eM%^!xaEB2??cLO7*9A*i2ym3omeqF1*HL!TQ5kxHX^H60O|)cGpH_3 zpnVGv=0Dm;92{1@yMtA~u6s+h2wot)I03gJ(gnI;<7P-0p4yjHdqQqbtL^xjz6ODg zI~`o9N+_rK*(b;td%#e`1jY?YF@ySMPLlP}9Z1KM4b)18=J&Eyv8qPF349U5jc;!k z$m4hHS*`|WyvzYj3Zz2)4waLWC`Nrq`Q3TlPQ2DsS``Zh9bAIV0*;ir_3Vr0(rou*?$qh!8tc@i8KZOf@8a-4 z?i`aa00BQC5;*3vj=Q1u9?bYQKY^CdUxX^2VlrBJn)SBtAIT%Jwj=XC`ylfZq>m(1 zl&p(c(BE|7)Mox6Oym~xKT&bYo8_ky*e%L(Ji5~(bz=XwY$*v!a`bml&h7C2^gMJ& zDV@Pf7FBX#P|f$>?%sake}$?5VRJs>_u$dfK1N=to6D#u-t75# zPz|8@&d7>0RptWOVQG0s&|6w$o~)T`}iM?D{X+?qI#zMMp-{ltAU)=?CLZY7<&%4j!L z_KG*(MD_t((Sul4{rq%=?+o`cDEip7nLTDIBq_xyQJ}U5=7S{4YFK4BbcfD^#!Ezo z>l<1P&{OzXQ2Pd|P_zUm09@#BFi-=ETasac5hul#@pN{Uzut|}jH;LHhbrHh?Hj>m zU01KzuWj%Ox`1}w1@zwKF5{Y7Og5U(?ubBV^Wi#7;uNniZ&6(>?oE5(d`yn{5~rue89 z1{qOBr~NE{(Ng&jhZEmUaz2ruxIjv@#f#U=<4g5Lrwoyq$}p+geWcb5$Jndz=zDE; z2kTJh&bW*{T}{>`0(S3Or=`0PaRn{|dfqWow7q}8O<5PJ!vVTjvdQ7E{D*=m)WYQc z?x;0C@%$G!!pzvnrfI(eJ?Wd9v4rQDBPn%3=4a(qbCQ}nm zE3!2nPJK$L9w>5UTDg_;q@Bgnqix1m&6soC765Jrc0tgS(#yNkv+;j;x(;|K`2T;y zIa~JLXBx4T_>G*`vE;&pWazDc3j&$!gFwP#NhWGOwi+rKq(3pVRaE zzdWxVJ$dr^e!rjb{=7f$@0S~#x=eWv^YIX$mxpI3zye+n917xIb%ZRyJZyz2Kjs^9 z8r`bt_Xzv%+hdv|Uu-*QJAzchgk(2=OLgXIo%?ihe5H~TAAO9hu(qEJA%F4s@O%?f zNSHP{`B2wP!QXY6pZ8bu5=I37fYb0HInw-nt#I?z(0CiKOp-aB6=J~Cs+Ow;R3_UUGscGIDQ+o-l20lwvA*8PM@;r2rtgg3%+mUe39JN7J? zjI>6+m?!uRTAViC^Dav-2Zr+9jOi z`j0mZH?$IuHvk)FP2|dReM7z>|00%?o=Xrq6LF{o(?9*^%2ZNPmfnW6j@G zaffbijipRgrt^Zu{Lp2MyDjq=`^19|#Xw5$Q=J|N3ql@-zkz4OMIM$MTPqx|R$JPj zHJnjQOT^`8@RKD-pHtAwGM>f%fA9>JBd;-^Z4`*dlM=olZD>EKAK?J32n=^W{_v8R zCDWFU!VjcZiDY}u)K6`SUmqnZuIf?e&q-+r`?0u8MA@FW=*V#$Z`ayJ;C6lE3jEkY z+#WdH@H!r(WPLrwubff)T#0SOT)Kpn)d)8~i5k+VE*{%K zR=Q>3O3s=d;}^k>`ssg`K7!+K9d^r5pzTyw(%ihPRbl;S>oNY+2EmKEhfGWa_d1fa zPsy6T8cz%wE`FoP6aq9duZ{U|DQ%p_A~4MTF}z-c;k!=^D~tY%_8V)XA=NWWQm@j! z5XQDg7co9!*@zV<%P)wg{iLb!BG@Z|tH=EO4&B-HaZ>abDqe;Z^NQA>PE?c$V)_g| z+jzI$uC9PCOn0A|mH$3U!%{Zv5xz^{MB6EllSKAoTP2=}{o1N`<#PFsB)uj(sraVO zsz{iK6-!1cRWKfG+PK>j@3QN>>#jbE$$}(mv-2x5+4GnVH1K33|$# z8Oa>>EAS}&W_C4oU7IW5l@&vW3!YHNsXd#{iK48SODFP~zlRaP%yc?&%l<{8^$cn~ z)ek<`-mJ>{^<&wk5OsofwWzj6rd$kaR`{(@`q#~X^x{6u@RJoe1ilZVZl#CYnq-K( z9+)(Wj;g{cY>B&-8nsGB#rCTVG}>+6eG!T5|(i8aIDjAlJEcrWMtjc&N^!wmN3DNX*yh&jXXeVu!)md9gg4G0Wb`ZQxn2 zt)5mA1vPp>9p-PrTmn>S9)ZDNK8`V>I5Z;Rv)xww_rZjo$YyRDy_iLcz{T6xW(8t3M{mBM{y+=5_wPegH zl=CZNywK;7UkJOlz3M~9Mkn{^47}s<&qDBPcWeHB!I%j#W!~ky2p=Bu+|_fK+u^8N z+k61AnlM|2eyS0^eKjPuOzMSAio+V((_-+Y=B1~ECNLAcmf_IJLf_OQJxAu{*eNh4 z8a{|Jkpjw|w~YKvNwoEz6st{z&EKjD=zexq^-d)HI(?JN0#8t0S_}M|PXx_jnM~_H zE{=jDE2dNA{ywNhJ;^ay&GvCLqnHn7nSb@lR`?J#ey>No?oG~sYzAGQxkp31HF9^@ z#h2b>UOA|>a9KgkyLxU=w^G&4dcMQj=ai&oA{+Qq3z+moGZ+?qj&u(9`(J{t`-uQ9 zMj8&{U6Qd0hINyl`f=gcTEu#K?Hey{DvtP5JGZ%PO!($%_fphH7sZOLyI%dMvi(i) zECEmAEPuLLFR+=$s$jqO)IOg9znWMrsA=Akc@{@^FOk0#y&u_%*#3H*zb*UN!M_6% z73?K}C$w4D0(FWB^jH}pWw2_cG2!6JQ!kNBq1mAOt;bVt+PE4CQjic1O@MoN=kZJe z!a~TGn8rr4m6?Z=R+}gV7BcAoCLKLnPa}Zuhp+Or?`O}lW#Dta?Yz*YPTJn;Xa5Fb zYxuE{GXF1V-&0E6#)Kiq>fRY!wbyklX=Bg*+A7--byltQ&j`jU>)f)FD!lu_&qc+5dd6tD~xQ%aE}lpeHKMH z@VGz*^Ru-|yD+a&WWGxpvE!8s19&j9nxl1VMf&7FF^XZFp zcTLLHWABp=O{6h%t}lD*g!V)S3Ml*QAB-1JZjD}-8OkNs;yGEQb63-MaN*;Ri-+;% zTzW+X`!Qbue;7)tD8g+wNvE;%a@jt;gBy6WJ3qokgH!XEDwGj_2#jJ{bpG{@TjFqy z^2?UBOs8rz{&$#GIP*Lr8VcLLh<^AnANKw(Z|^r6EO;_MUssCHl511gixqRNEwEj{ zlf_~88E%QHzz^ip&S{uRg&VuYYf4=!unV5<$oiYlY}(R74-na7WhiBpcmFw`m=;Bz z9?&hIp`Kq`Z(E2iM7J#{&37oPn)c;U%;~TIM}GWdh$L!$cS4E>ZQu1n$to=8>(RkC zYT3p)(0g86aSABw{UJLr|~i4;ujK@iwHGKJ*q^g9dR-H;C!LfdV&R7p$W7W)=Nw2>15&`;6g) zR`u|qn6JgZzo>@YlQes0dK2$nx!%N1iuLBN&@X>n1@O#r=@2{KLAJplnx+}kDv1h+586Wm-uxn(Ytn1F@gyLVZHx;cb0m@tZQNn|~zU?)n(XKULssm%!ae zV0R&h&+up0G9O7=0fm4& z_b-Kx2yu(qM!xtqw3@r;Eh$SVkARyy_O+H%z13RL?Sx*x=qh1!k94(?X>Tn z$iAr%zc?^?GhL+kdp*8SzsXr>hNJ3 zNlU(EIb27%ELjK~NO)m&Nvzv{GJ>T{Bjf-ghZhIkhq-~w&*S&*iy+%jZ`1+uHu44L z;==DW?d{w8tS4{N3t@@#TKSFw!Dc&@4Q#fKrZXkB!hp>zs6d={ZOx(m#}r&60>}dS z;ggHskqc<<7@W}QXZ<&?vu$?b#JxaB@zm?p<;ae@xd)31Orf{?cgRt?9-_qeaAg54 zF*y1rymt5ySX)_H1qyDfiHw4gT@Z&pr$ZnoJM%V$8vsazImp#H)={4=Bo8T6SQ^|UIUk_yPAH1u20@jaKi|1 zazpF`zZh?>77oKfpdDjPDdr2SYD;XW!gNl9!m=qN#0z+#4^A!B|ItG@yOB(qF<>TN zg*{rwr**3D-%pVU#Ckc`7JO%Wah-5PEr#1JPA%%H=YkV=Ka-v{A@7--xWQoBog*Ca z{$-I!Uk-g2HHf{2y*?{%1KpkZ=JC}cEDb9+?f&Mp*Q;X)UHIrx*YvKr{G=?>fWG9cgY41N9yeL= zgEL@!B$DM4HHxYHIDASN;sRnj=r33|D`~TY*A^lZ_c|5$qcBFUwN>H>aZREk-B2-l zZ0*4i!BV7$#(c-=4EBX1*G}wfaH!a>1@l8FrRZ1_gQo{Y=)1D|a!fG9r1|w9&kIb5 zPE7K$(;lle{N4y}0TYmBYU*}rZcs_7d%p#S+4|KsJ?Kk9&1IUma1=2x9R49SokG{4 zGB#<8pyQBxG|!EjR|}5jFyCAifE~xQ0;(=a{)qnIrsX59+^1HF+WozQ=!m`*0KYr1 zflmjynVLpRqq*Z)wnpeq9($FLf?)khr0G73JB{Tn=^IK8xlxNawlBy$?C_GQ1;4#O z@+wP2_{T{F&C?$qKF(!2P-epl48_b0!Yp-u3X5&5w}z#msfr8tPCoT^zCj<_wl*CJ5%c2LK^M~=uy>P9O%%#Sc)!JJ z5R(q@FW7?!1*R4+McD@luiL7AFWQk2mSQaHceF+7-@B?42*Nfw0kyw>G9|9m>@rF) zx_)u))k9KmNlx34A301$YCEB$&fKDZ>2v<6F5Ct4$!(kPDSa1i&bhf@Hn@cD3EJwI zXwl2bxsh4*mYH<1T{*&-qVV;e{rNpvlF0GyJ?mROe%DE(QGQfB ze6Q>W3r4~@*;>Mnv=@iY)1SW;2#cH^K)SBNK-;cQxUqLf)pleXx~=Sod`qZ-uDgP_ z&?e{uM5W1x`P3eWEquN9ZLtdDaaDrVEj#mFhH~&qUjywDOj`xv|DI*=1{E7^(1BU+ zUK??B$mXP`euRBGQ{@4wN2_bAOkiGRmjH8lJP(JTO-29W5A4nDMZTndVyCj-)6i5j z^$LM=`X{212W7^}kvC;ot!t5SjY+M@4)zX0ezg7q5l6IHom;tuRX9-^d2&5Bn0uUH z^HB*IHTN)DiS$q=U?HN_EI%upZO(Q4v)<7~^1g;>8cBF74L-)NCu}V_<5(?Ierzc4 ziCI!3^Kzf=jn9T|wP^#kDqW^0Xlu!8IAYFKM=|_1eMg>rY98B%EUiu)g|U~ni)SmE z7J&FGM|||n8neZO9z^iE2DP*@oJS4#^pkm!j2Bfyi6eAxVqx zyqgK@@`)?kFj3CDMV&S>db7rTK?b!8PN*JE2#&||QVmu6Vu(Lz_q50NQ6I1;35lI} z!lyPxSTAc$s?aZKy~W@Lqz*!gD#+&Jtwuhb877D9LB~N*o8J_2RAqfllvkbRPo%vp zQoMk$rQ*Ub?Dp9lVZ2B~ypfDI<9O$2rmAu`Z~T+@-XVJJco7N5cXoZRm$1YW{vBKR zo=|VO-_DTMj1!nSC>AUg`$NMzLe!_+D}g1{WI>-xc4?4=VlI!U-V{Cv22S_&K$D5fG$J* zCNM&LWi5`A)N0zIUS3~iDHXo=X=J9AaM))(QjT<)S-wb5>o=1VS%SaH1cqH*F)T6b z&$cLARPxTKb?ILB6xjRKbA3HDC?V$`Hh+zpIV>HR>AOqi$?T~lhr&Hy$%3#>57r8Thl!Ck#{f_<2e;yV-vV=CE9sl+C64iy8 z&DLPMk_{P^T?vq;7&y_w6q;61#K*N6BN5y;zymApGN)Km>=;bB<4zlD=8-y)3fwCz zOZkuxfjh9xq}YQ_B1eg1WqN~s3>WznR8dAq9d*{Mq2AzR#hwB3@c?81N{RmNef6qO z3lpYSi{6oMKi)sFPdp4dyp;mLc=1|~`x-n6xGEg?GU3PO%|$N`$nZ+TAv@#*I+(v1 zbpYedXHAu17ZW_2UV?=%rBlUlV+buR5^s0J|27!R9SQ`|OsuD@b_lC*t`s>2YE(nc zz@-fKL_I3Es4_Tdos{#vpVVSOFGo_Xp=D)0`xi;=^yu~D#V#4*kZgxNjL^L~p)+wa z*Cbipo~!p`W~6(AMVrA@c~*Aab>;5mMqgC*N8jTAgC!jbg=~G7kk~EFj875@&jilZJ?J(A3-aNCyRoQOa?gCcBpbQMQ(*~MVQ^;7$L`Xu&*Uf z+5U!RufJ{l`}-r#)2(K3qu<{xPJnG-cBDSFmB6#SZLn zZy`+$Xb=&cofcL(UHX6nvNOGM!#^JZGEbCMV$ zOk;Q|;9~wcwmY-i8mN)nht>Bg$2ZWjl4`Ox7iFj){n`5A_FMrfPQ#+26)Qjev>(%@ zs;bzxAc{Jz{(_)sdhrV6jWwStviHBQygjLs;GN5F#TO{xTNGiKW2x(E7@h7IL0{oHyCY`)#J6MO5X4nrV!f71fgXTy8YLCpBAIh8&#_&?q1MA9x_ z>Nz%^O=CY}tFRmB(g8u-U^tQ3N8yB)Z+Z)&5j8@|f~+eAh_7yprK6^L2R&1E=Z4nz zx*F=5$cDf1qa~;$6a+zK&blGzBLOs} z*1Nvk;A%NmY&U(!!EqrYN66y-Pm7*63;1ggNi}?jco|$$x!~PvAF>Q69Gp45R2Jb= zt780$C}!hN4Irv@O;nZz7SvZ2Nn^379ss+|`?k=2(Y-R^j5_`9d*ZqWvy<5h)tsAGCrL zh@ZZ!=5A&CfoPt{X0h!F5lnH!2(JvqwwRPnnI1OrR!A+fF#^xX^)q zyNlP9x;-&Jp3vLXYx%lz*XS`{ms1IyKJQ+ZtR4^aCMyEHwAmFyBq>K?o@&{RSZ6#6 zB-@&Z$&2StAD6^?-dv+po$yyQr=L3(^;p8SL$19hM zd!YetqhXNThNjLs8pEI19B_d84|2=^-J6*tn9c@y5X$U@X4yWdgJQg|$KZGH5YEnuudB7b`|xc? z+6#C&K9KWq@@%hHn3xRWPNqo|(~^oZMt}lTiSF%luDt>t**ZcP$k&VlS#VMG*(qG-m$@${7l@;sP#+)l1t6pzH0{l^5p?39!k4H6eq)W^Eo;(BK(#B5Vu?8{j9Egn*`tGx)mOXLxhmS%m^Ib~ zo>$>jDbTupcH(-%{`m@9F?U~7?;6qJYH-ci>5i)L78G?8Y@omrN(QIhAHsfqG2{b~ zu5qZEBF^k(wg6<%qm@Db<^IpVl^O*sQI;wIXIj3Ru86*32Yk8xgqFBI2?UdU>sY$eAddAFyRi7SD=f7Ga3I8St4o>w>%k`d|4hc&zKYBpYQbs<# zaEVtql6F1}t7goyJZf zZ;H3e5nG0YZt`ZGa_g|HuOM+FrhGx?(iQhFC9Wb0brCzwEFnM4T4ddh^fo2cC7$cbR5t4hIHQo#UI5yZsUZSC|*L#4m7L#GPx$mL{(q z6s+T~CFJBn68neBb~BeMgz{W++IUkdV(qzkwj@`Y%g2=^n=vw;tl@0MW*;(Nn|*ZL<>`&|KX z%R*?EDU(cniz>$(z#C(^CR{bDIBMFNqRsdf+|;4DCTLG! z{?2)B&;1Pm%^xRPrT`Th>aU?NTnkH@M*DN*H=%JyqWPNn4JNTtPUH6(m{J5*C8BSNHxj=$~o^s@ubiF&Yp#3s7x@JRKoAa zTvh@t%xsfbyf{T@Fpc?q!H8$<+-|ZhX@2$jD-RtVBkG z>&4(~X_x35_~x=8dz7j$%?#QbQqJm#Gn|S&QPS})eBU(YuRoSB41Ygk3!AIg7}e-0 z-1tu_R5@}8fR3z*TwpdcQ3(l9A=H8g6g&%ApsxPZD#SxO`G;*5QpxrUM~a$l!caeX zL;E*E0J#o0isugtL(cX$px_wHzokww77cxM0Axf@=9wQhIGa^0EB)3zhyFq+WSe1h z>Xp76uX~>n80H({S4E_{8us-9+n!bNaRP_6<}3G0A56J&Vj!VoF5lNhxFBp-$#b=A zXweuME-Nh@FYV3tu$`+hu~K$`2WKJvtNX{ zo?HUg9yec`LgUG@mV#f>ilOUKVsBAwz_PBm^CbGe+eB&$9ewAX-%SG%AwOqu(vuw3 zJ;H}=w-Aba$`6Xh-^cVB7)j6E% zen4|u&rChCWGY_&p_lrLVfWn;JcP*kkj1NvVt568!Wcc1CTW7PRDt2wmIo;8 znZ%%slH}r|%ISC@rsHqOopbwFP#9I9F*DnQkVW}HW#3QcQM->49Z2($hz~ldo!Dg9 z=C9cE>ck&uBDJg+LZ5G`am7#IP~^f9WDo@-fpX% z;+sqJO*)OOz1OpikRK-rUvj^?GTb2AC@tCaXiZUu9{VM7CRX-sjwXtfmdw2|_c&yR zxwFEihP@3y=K3O>=!pT_-Xm=l-#iosu$`Ia=Bs_~`9&I7sp~N+L{`N-p1y~iC!SGdpFtH3f4J$wmx9rCiL@~h|Bz`JWR}NPeQ~yENo0H_wmuTSz2J#@lf=VO0D0W@2A|2TOTf zj{i)G@lVn9=n1ht4NbU>pc9oW_glrReaAfkZ>%ix1U=TDywm)?-+Zr?C@Z7hDq}-Z z{_fPQw>-kxSO;La44kYuO>+=6QNVouS)8rp(=m}-vR@0X=v0pAk=K%L#?+WU(CnHH zgRR`o5f2vcc>_E*}z? zkV6JB&*TIKO?E#}!Ca>+Ru6f!wx8VGCbF=J@3c5APE#}!YHr97bPhZAR0-dW6=KKH zW2fG`WIU2Bi`FNJ9^dlzV4hXmiz>&!z8cgkOkJ z>1wM*H>08jf60fx+5LdBE2$#Eaiuw!@qkNrNg(%8SgFbVZ&bOy+{4v!#2dRLYs*@>AZplVD@lu>YKNs$u0b99{IFVb9wKYIfja|Q1ZO!tDK zoFA|izN?iVmrT#?0?%A9Za=y z-S0eRB`4i$X!M2dR(qx%wT{mcbCFkb9ge$rW(YAddX1?d0!J#*&3$)K*VQBj2`y=9 zkMCyATvek8kdh-YA6&zWArdo~uGns8my%_jRtby#MpY=ZQ35-LlxloSouoQ>N@RpL;QEOJ*WKvF5GUKO3qb z%}^5pBI+IO#o)t_0BdIYx@=1*bzeB6_d<@qSSo|Ye3rxm2#k!a>nMf`+BnhS9T!7d zfmYFmpYJ{sEdq40;C{F8&HrVrZ9bR*>>t5p1;(GD6A*YX5{gEch_b$R3MQY3-zt1q z%VH}0pSj@%iyJjwSXX0!0d$jClx=R}V~H7cl$8Nwzoqg4*C+sOI{sjN<%61S3HH)l zG~ZLO^qbfn$N&ij(3!vbF0v_yE4Y%ZIC0cPDrim%n5QfRN2P{)BOs-UDCZ3Yp9ky$ zHHm?q!%%-zdP%^Fhq^GFKlJX#VTvH zHwDgYVk*8Xf6(uU&snyD+yqhE25tU#_DE5)nHWk3YLw_N8`7b9K`-YBRV->V{l)uf z+6t77@c)L8L$+zmDrADNjI&$3n(CYMSFlc9A(6IS%i`$?nm-PI&UEjE7CZK`{=|lR zZmzqFVx9OVlk#8L1-$vb7E%nvl<}M9IYOtx?Z0_k6Q+C4MfLE&ZKw7`?xE!>yb2`S zafAin>Wa$0<5o$qZ%ZkPrh>5L$7Tr`FqbnM#kB<~>3m)rxFmKZ;}iOK=`WC&%UdvY zyio|rEQzmO;4L42(P6y>9bSw}Q?=+34cKMt2McK6c|HjdNS>jC-r*m^5=&nHWVTs;*1(A17Ik)<3{IP!6wSJg#{Rpq@RlGC{nms)tOPUpEnJoCr0{)5kc%x)VA=wUt2h$K3dm3+U{YO1Ditc$N3&RL)GI>h+p z9B}tc#qh1Sfy`R3wJcNJ8 z({P*(X9-G$e#L5;w;rAicY*I=FvH^XO@vrq zOHsdZz5d)-*g?_znnZlDlZ|h%Q>saC3pYA@as$rii~=IqBWtRb%^S*v8=h!J5JUIg!>sxrbc3mcf-1}pacz1)6u&3ve* z7I!V#oKyePJ1ahJkHNm8jK}9DZ=UTqk^3z<2U{ajhOQ}MXcoDUCS#hL?8kCnx^QdQ z#`HhIYmc7D4K;GAJp&x-F_DkLG&Q*5eqm*>XE*J_PUeQ-ezVZD#}`9XF)+md>?27! zv;lO^T_gJ3OkU6IdBcWQi2j>XVh=(%h-iyTC{jutB6XwbZ(W#O#9xM|Iq+_#Z^eOG zEa1Ne&x|1dD29(j-e>>39tKgg!@RApZeHS&%ROgJF?Y{OfIW(5U#i$X^W)|;o_YqP zpu;-;z(FA02^K>ZX*X&hY$3zUuGD6t&1Vx6wRS1uqxjVn_1X)5ycrk%8m}*kD2WB3 z25z=c7OjR&%_%W@N#&*L{#M~J zUd^fX_jbW4YxDS?l@4ASTZr6)0MSC;Zy&qKf|C6`fc$m?%{f(C-8_z78No5$yT zuU5lvD*)=#qQ->=?bTEq^#FbUCE7=*%L*WG_skZ-1AkLdk+qLjLAddzs_1zW2BYe% z$loQS9B6ozDzaT*Aqt2W{!^ILrK{72La5?WpBe~{q}=+ZoAL{R(@FQ$QZf7PDUf`U zUHET6_2?~@H>_G}cG+YXiBSDtdf~m`@nWASPY4T~O}zzsjiU_S?mRdVY~aj(NPwvR z=WChWO4vSk21(P!QzpO!(0GqCcMDe(Y>P#e#q`5{`WxXP#z}2u*Gxn{yTQ-~9+xoB zE4`UJ4@Fo8^(LJiK(8{U%$8nQ9@P&0BgAqCAQ8%^hUO)hp_40smUPk=Dy^~BZ2rZm zQqX*eFke|A?`w9AnBqn=R>sQ2FRhZxhy^~9bsqo9_7!WBmXu@nP!bElWNrbw(_Q-DLtBRx2 z+czRtFd_5SoGkYx+qj6c!xWHYg*4T!2x?p4#^`wEw{8(P{mG_qDO`bczN3uid}82y z>)a?W$@iX}ll?)Db-B`aCeki>t!oKdnL3mQC85ikl^y{NK@A&AYau*}ak`S}wwU-# ziRBJ6V5!?ye5pA&)-?=o2ok=>j{h)f+SRk642i8yOcV%AF9|NDrWG-E9U$wIk}soH z?v3L*8c*;7SdR651JC<%1CW;qUmm+e&w_nx909X}eY+tG?yy-;RUq8J=d|w_BG^w@ z6d&;zb31464yc*Gq=sGLn&%qf4`sj0--C9-loHHWsP&u0&cwDwZ)O!5lJXC~x|Yul zhZ@YBk@Y@}@W^S%Ec&xo{NI2hg82v$N$5YI z*eDL$sNjY9>QUR#!Zxk!RblJE2@69R*@(zj2s1NsCHa~rtT}D?t zpiB{B481Znzt*d|dm562XfUn$CrUzXdLJ&D9_w=u^91|h$c#SWc}&e`10X{3JvbHbPVRYmZj1t703F{L(z;C$p7NQqN5=G)Rs*hb^>)S zp7k#Qy_hQJN^f@naQpkQ>OVeE8e4|!fEHT^>f-EIbOCIe{sN}*i6k=~?nUtIy8f(tKpLY^{=gZFro-e5EJ~ zxaEN4f;DsbI(9Z_9DW0>C|<1AQ6eyV=;l{!2q?cio4#od_W)N=`9zDY$m~ABdt0G0 zFhghfE28+Ugsq5|t0KP!Wk&!!%WyvP77Qhh+kfnY%_B zl=~V+Z_a8L9~H;UthAdzITO8zhWRFAyqn9C;hDII~}(!NelLjrli@<$O66vr@e87BJ*Tuvo)EA z6O?M({_?3hd)8}F4-!6EC|dHi4wXiDH1=OTdolBby@Aj_yB7c{>C`d!0n1d~y;0k- zs!kC%+mM@!m^#PL5T$Q*|4H+c@$l8CLb56}ZT7#y==@BFrj++q%vNV=`DP3%R(TnH1aV%o!Uex@g zm-u)f;a)i5u0|tGL~Z_Iv=+zv2caUMr3{~*b%F+0*ja)xudc1mX~#hU*qFOa5$L{0HQ;RG4Yko@CBc~*T>-+7g0YE3Rr~(S zaXB@*R82ta!S1LP!*s;+oN7CIXjnp~F={Ja@gt6G%NROliUYyN6PJ4o0U^TO8bwW` zxs$i|&|^slt)A2lO2J!l#|b~8Aq`uk7uar3dO+~b;o39*N@9#ukpY^+o8EXC!38R! zXML6s>AG(bLnD)k8$%>Z*UsGj<<0yp5xtKT{v+u zGFSy!W-#hW`{RoydJ^gIyACunWxkM0qvKhL@ciY5!-D?+eds1F`F zWh^$5@Tp~m$5%{UhdRJFz-y1uLDe~sU5=s_0055jEfpDcuzwaQHXz_ej3hBaGf48{ zkVkAULYOLCl(Er*jR*chbN*PXsjfRZSPteAwvmXZ?h@~vxo;k;D`k&1+TA^W@!|RU zno&p5{*KcJHF`(q)OVrt#3go(U$@d_|Bz^m8mg)@ZrS8U+If7-Mbw+Nn8g~s7m4b* zoDWBLR{R;TU8G0s9#T!F^dhXduHZ6YUfXJRKYN@e2uXYUkiaF{5oAbViB~c0C;dgJ z5GmOXRR=u|ObDhC3l6k((DGN_JwbJ$9^m;hbO>s8>~Dm!5bheTBeT1bPny~V$?5^a z1Pb*56|&$aM@9Q##D&AJgL0NMc~j*%g1zX;tf6>6?mON~cU>yWbyF?-ODd;Z$w-$M9t z!B?*ui6s9{nLa5ZSkT{oK9+Ljo?@$0nx@-{qPK^x7g@R77dq{{KK4xa5x=-EQ|+eF zI!Vu8hPP}8<5&6`A*VMb>84ad9z>Y!F`X(Fc}zw+-Ilay8|?78uy%|XHz!hT_NL?d z=p_P`X!@q_qus}E3Gt={?dZL3L2;4a)E(vwC}@&O$!Fj7GEX;p(a}aA2*w9T!Yd3J zO!IN{gvgSXMcYWtH4`CQ#4jC^bqr!eGySzBlQN0T)3O^+W-h2eWl#U`p55YzVI`at zO_m`tq_01r8PFAX={_?Pn!^mZS0?>Mjuzl7=*oDG8JqC{9}BMwgn#vw3Y;FfEyk1^ z+&1%B?}fXhtd>QHOuS)Ee53!%_xC1_yh~O!cwK?(C`GL=J|_GfPvk5;Fl ziTMX47XwAbnp-K=B^Pp-+$A~r3Evq}3wcCowO6iYYpl5QcA8SrJNVNrwUjUSeE11G zeHN*_o4S^{(4v8#j{?rj$R#4g7(Ijb@ToaYy95*QVfH@S0rm9fwp>FdW!}lUwTAF~ zE9lof)9m0G3^bwtc^hpbFqY13NO)&xdKWw3{&^%fg%JI*4WzhOI#Y+PrJaMW^WOOZ zF-8yDXhHjuqB7^0sH!&6^&(h!9jdHY-NN&DRthc|UlF!Elx|CUXhvN7N-|K)uyCi* z8{HYC)MhW)Uga!J)w<#3u#yGt9SrYl3Yb=H*V542zBQUwJ@S_HUso6EuLvLujKCV0>6(s=%)`jbb1cyRaP_B9wR5eH=L<(5!jx5sQ6|y{_mE zMU^5%Di=e4Q&;)lVV>(!0|8qsbLj?LsVU+A=opa)adQ8B5-p8QVqW`l34{%41XA&R zI;smW=aqn;<_`JwxWK?F2H-Dc;8#V{YT{8c+k2Lc_h#L+f(E^ zln2z?A}#Xd&-jD3-T{))qJqLjVUG%_r0rOnqAHh-71Oo1)!Y{{afBENx8!xib_ZI6 zj^H=7?=~O)=Eg;r=$P9TUa>qA0xnyV)mwor-u&xUmuVqRu<+g*@c=2+Yn^GjMMJC3 z*}fbpN$9bCi?pXU8Oq&$TBWh2t!{@BNoA=av^j8Q()E|1AV|g14bFgO5I6iJmaMXH zps4c6rNLkY2~LAi+8hchL17IGYDK6HjHg!h^WQtEy^!aB_9&+k*5_^T}j)v zP2U!${^i)Q9o*KaIS0iiG8V49Kwy(>o@M%CJ5=)a>o3-Cxt9Wt!H_pV7tww^u{qPq|TFyocLrV+d5jmn|Aji~TaZ9%oS1{534n*DWyu@8Oh z-S7C}2v>D?ZYRAoJ9vj%>RsiUU#hjkqS4xOnR2CXuk^vV)n}3;gwx=72(`JL-k#gi zlykJA_zvk^+LEoHCpP$O{DD2wdUWD8?FFJt!VJNCKLIv=Z6x+7+^DuVeQs~PGM_uu zfX)fmrWkW3tkv+rNwgy?7iJYNp?+yE?0|baa*!fxQ4~ee@85PN$5JEY+oi)l#Gu}Q zmX1L((lI&l&@eZ8PIm`}Gk2rDfh|ht88l;KE_i)15e(4iFK}UdoIPfiG_r8M6SXdK z_7ze3=vUnHQxX$v)^s_@W?q!;UD~gIRfKr8{uWy|f}&tj&{TK4d>j0&F15u|-d~3$ z>k+)(t-oU}^RT=N$?Urnv2M-@uQcI!^ z@QuOVYm9+c{iHS2oc@6`$u|EM!e8gasC4vlOyqB`<`+k-FAAF$?NCYPn=LERf0Yxu zR(H(}nl?dq4gmV>IjvEIZFbY#S-lrCq;!9#^=wDh`(nitke#%4ih%!H zJiI_lBlN#fHZQZ!EeTNp!&j5q|2^?EGZjOaZH2o4(u}oS60>{Re3RR)y{jzqq%%Nr zftNGO^prW&r4fa$cdIRYp1!)y_%%!Ow-*HQw~}WvZpk8h-K_14U6ZXn+b_1I1uVr< zCQDIRpC{|dR)eSaJ^$Hd8h7~kHKs2>^Ovnq9$WMcXlT9OfeOoqj^ImR?0cr`y|p~I z>Hg<73Vt*Pj|lr)v>A>I+d<>p6Dyd{D}HQYA7Yx=3EJi@-6_yaKOh11q5ms#yoj?; zZ~+<=lHQbKsFr3YW}I-r8!>zqqa?Du;`iYSlV70(vFpDrvY{r?^YNu53s3QRxhCt{cp+y37N=b<|yS!USgcs$nl& z3ibu?vm|AgW_YT6rvpWl)2HGkId!WsJ>Y9>@0- z64Dycq0KY%k8SNS&051QchEj}l75bu=J5G^Q~$VLGZy(qqrY#J#K-IlR<|11J?j&6 z9@p`*$FoNB>}xSQbBw>t&7}N|Dc|nGS=4{6aN2trtr_JO6uaSI*59MPx^M`WwQtJB z^AR}{guN~gOV=n!-gSm+%H|HYhl0EuD|nt zdw;V2BR4yJ@w*Ed`>WkIl{OUbV3LhvebVfPn!*Wg7q&b)WE)a2GZw-; z9DxBJ8Td|kuDIeqca{>kCZr#R*ivZKW=KeQ++)|AyYVX|SspKPiv7NbDV-Azxvn3e zrlT0kzIN#C+}68kdW+XHEcfip@gTMf&0<_loQR0yw=G&iI2)zItdHS%1cWhZZ6B*P|vfhiu0l2mdrA zCH@wJ4bG50uTRnC^!+aq7b8HCfEylJ$!d7vD31tLV~^;0^+q}TsR86z z(YufRY)PicJX~P!pOF5Cfh!+epc3IRIW1tk>klJtr4PxEQ}rveQ$W)7A!9)AeHk;C7I`-ZF*6>_{*}b*$-h}m< zu9w@=q>Gje&)E(`7RSa`*F_Z0T)E+UMuz2`Xwz<=vuw#r47gAig?Is$)yWKJo}s01 zMfKeQ8B+V#6(F@k34Y!{i9L*A88e>9tfF@fLLz4>tOZ11GpBj-mC&80TJ! zcn3rH60>bPt+2-Hy~6I zFHH_iQ;Y;^fQLSh7W9~qEUoFxlI)7mf1qt6*@|T6w*UP+S4$5_>v}IuRd2Et=gJ0@ zT0(95)gM9=z7IFM3H2U7SkQ9=n;_1pI#Y7tdSm*f?9t37{PUyPH4_l=E1MWBy7K-f zki-olzsyKKPGvZopyoV0FB~U)`4YiRW}4*x_e|#ValvE>{I=5nW9m!bp!i zfB)C(zFzmfaLwa+&Uv5rd7tx~hvy0Kf+Am@;~J(9C9Q(*L;v{>JP%UTmR?r?Iq7A< z)I*!M-xqnmU`P+B6}}OA>Jl@hT4vH;vrW6V907ducM6Weep|8eVev14okdrK&bY->yv+i=Dc4(h)D%yCCn_ zvt~!f?Za<6HvDEpo*cGk|A8f{>fSCm(sRZ=KkR0H_}daLkn;cb;TyK6ZC;PiueGr9 z-9#2`25#m`he!$E`%^>@o}zwypGs|CTu=Fz*cmHK$S44`>e|VdCP>(Z%%O;84D)t7 zM;G2jJ$#mj*UBCX4dJEHrHz}bBQwPSLE--A74N#e->On(97j2SUeFb3Wj7Y>dLQ6ODvRvLSVmm>+$353}MW;k)j535-t_S~)w zD}-^~4_>28x*?(o6!i(U06M9Ba1sN(JX=HJqCM0jCvZ@cI{jb>S}H&8vdQ!BPc_c< z(n*$RGR_->Yj3u_xM!`6V7OKg&Mo!xv;^|Q+hQ+1%fUf}y|5`>A&w{9+c0nNM#Iy! z468wMhy^JP=^O!8c!d;#0jn)`xQZrJ2t!doai^njZv{6~{V^oE8D8vA0HF1jQmoEao#Ftw-R3t%#kN+L3O|- zx^NR}OVV0B=6cQS9Wt|uI0ApDr#%3>|Acsm78#8b+P17RRJ67}l3IJzXIdB7V5DL} z^zBQjV&4raZ1Vbsjz1LonJxg8DIiaZj;iv`0WBt#Jt`|qAf`aZHXP|S6K8O*h> zqwrWQW*Bj@MLO#1B?sYNLGBHH9HHWqv)hSvh@&GQRG<0g!#}os(=NjIS7&-6iz@bM zaF}|4MZ@z1Vv^27eSRDPx+P#U?MUt_|Kde=>*y`ruh@^x7}NgL2SW&`le77W3W&DU zcO50~I(W2k-zfeXSRP^1mj+;T;#~YAFMP*VGODcU%-vNi<9lfuWA_3Vd|vZC-~n8kmIFF4C2-IrI2oK^?U3*Ux_trfS1yssP*zw_4+ zdRb(AIO2)X>>p70hIuQ9WKL+rIk1v3Wf^H8AP*_^f!EI>a8t8cqkr84gB5dI2j;V> zH7;d4CRJmy95*`hY3&JE*+ERItLfY9rs%}fQyWV^{*#EoL<{-xAUSIB_7(lDz)r$l zv$p?A4K+hb-qo#24w8m4TNUDteb}Y7?7aWHwD~6=v@7K%f|q`wqu=KA$sc8!nQX1( z`Q#|vS<78zh|j6;_~K>AU0sgAm8^@mG2rxG6X@9smLvT;ks3<2eW(|;9_bqSE9(ZHGvJw1a^O`?WJ6)p-UuVXfoc`n@ z>jRi|THKZlq-4*THAnbYHDH{DQdY0;+#a%*dCmR)blL z_X%AOy!DXCOyBXZN=%-nU2)U7WYy^*|1)9;Wf*XHfXr+Kgt#%3OAoH+UE`oJSbzv$ z&P3fr#l*fgcS9yIXfS0>)kNkmb91p!moPt#pmgd|`TyXND+7 z-kA0oNLKaJ4|<7@ZMf{~l}7Ztbn6d#oimX)b(jA%xRUUHGLGX1|{w;>Na2YKNzE8y4@9lZU?Kqa_ZTW>W!Gm zcP;AKp9LW=6R|@B+U*d61*$?DF>IV8Q{3crG0%1f;m+4oMrMai?kLZNJOxmU6><4X zX$L8rs-NPIPv-5=eU`u2qJkJ7_yIM^=J<>1CIo5=^$;>M63y8F!2%2NgCw~EG~oW? z1cf;6-A&k*9UO4TXh%5IoKki}X41SNWHvKgzgVykorWqKG?_5=rNQ0#=)&FSg6*=f z#p6tvO7CUd>7^WEYK*pDO&$|nbFv5Hr2}czzMSj)1ETM!A-9juUIq>4I+!5M(8s)=oldQ~w;Pg3 z=krISkD-}65Yr+>i%Y22_G@Cxq&Vrs$!6&^F?OD{8o~V=Ivue+#zYflNrSx94bARd zZJwMWE~+_xF)73rdxj{`u+=BSATJmz+;l%&ZCmG#zD2WR0mpZZ*hw4O?|bz{WNX^} z32|}tsJ51ejm`cOZm=52QoM`4oz;)l4vlPZSk2a7z2(uk=ZJVUGX5ShW;8U!*7m}Z zvY3J(hYB~MCLbX99~{E1(M21UpwfX$=^#@rOipv{{o`(VL{RZV7_z-_C)GXCnw*7; z`|?G_CZ{YItiSch&&+V5QQ*c8(=*iTT-;M#t&%_*E5qs!G`UHd*2Y7Yxq|L!hp3$uy;;?hXEaA zi(do-kLQ?ejxFK&2K)#}U1n(Una`o(yb#b+_XCQasq5c1kr0}4v=Nd_zEr<@i(7Y@ z>a>q~`2&Ia3p*Hv`_5;tM?LhC-8N|ZD4iNX`o^OzZ3c13BoQLwvmD073!s2(^gi+x zom@R5xm{&K-!`|tK|1aW+HAR^mmZ2!`TGY6Tp!@_H zg2*AEm9PI~Z{8&JQ?{*9$n@XPQ@b>;qy00RE(<5^o(y^8+8-}ZDoWT5RD@ADdFMp0 zoO`_5vz(@LirO|gCL+M@3q$Q7r-pOun&gFuc-UdlBZQBzKNx+{-s%81G|q-0-*2$A z1?Db1EDhihlV%KoyIi9We;?-9w|m>uP^%V{W!vK9jEXm_)wI?Gc|(Taae*6vI-z|} zzDU|C-TJyJDRD;_cPSt4oRE2Lqe=|1WHA^Is}@t5Bqnjm!%u{{Gh)D~gC%%Mv}$`a zvFD{=Re4Dn-8{Y;f8%uD&?i`k0&-Gb@Kw`?Cn&I#KWKSCab3VrEcN7jFV;E*=b>Vg zpXa@cz9141tY*i(2iVt;TWQ?OdTbIq0P+ASF$&g`TUr)$_35$a@YK)@{zA^CZA@3H zC%K1bdR^Bc#IPaH{;d6tLc$4z{9hq+RNsc#VIuG@0{nN>Xf4bU5@*RpM(ffraFW=ICT zwteKn(0O5NFEk+Da`vL)r7bsZf}tl#yU7F|J;{r>7h_c~H&u~_i@W=S^Bukz%_cJz zCwXMGx`I>*?h-s<#OC_{EIM(GQ8BiyAJS6Km!X{XAWrCd*FZ9@kv$fTBd8nqjCBhA zqzJS1^EgKz3>_(ZX&QduXI0hcI^0E7HU=;{-hCXx0n7$T;Gvqk}my<+7!;kOWcwYV=>BE z20Co`hYC!r#Z@Xs%ib1P94aICsBe6KuMIysTys&$qwdDpRI!7;dftvd^7i(&obhU* z#qCc$^`x}S9;8^^g#pj@gYajC#ZoqDR4@d8<4D&PN$lx-dH7VQ#)vK%l5greXejYk zI=EFGBd%)}w!jc<4x*>I5xQx|dh$RpUpd@UvGNvy2?t}MQ1B52eacmrhCOmp&Z{C+ z$tW)=;e4O}A7Jn<9#Oz{NDuZi*V1=`I8f^Bjb{~~%QWTvX`K?4w` zC*K#@dR-jsU&2ds2O5D;T=7$9_8elokF1X~AVe~=#_D1E{J z(@z0|n{<+a4hF?BYvvvkzpp4TIz04vGI$=`&Mdh+W)vsKethHdwWIsb5o9p_+p?DJ ztywMj(Yow~8|Of>CAwuP8>2gTeYI-6`qi`dHmdnU-O}q4>qV{27RFJi+T`FT93kR% z6?021w8|77$Rcwb%nt!s;0?}NY)4}9zr^M%|JmK5Bs59y*a~i8! z3b4iNC?d$Zp4uCX93|@F58X<)9)M#6bblbqvm5{9JgNOBLjT~CVSLc)g#m6#hMiRX zCrtUO2-N%ikW)q$t$QYtDsuEB@~B3cEf-I5wlh1x&RpRDrD4Ca7Jmg}%CoVRpLrxS zy6*gCfZ;Dr{I_Sf6(4Dtb{m@Xe?=WE!4@YI$vmME)d1nMIQCq=MMhHowKH{n2*m5z zuVyvP@m>C~dNv*&X=)!;+`La9ARl`j1m_+}a~G+t6lKa4*cLl@U=%#EnwWGfRKW}jygs;4*^&HlvY>+jRahYk z(|h>{F_92yiu-Mn8zV~bIvvFp>%HFa#;M-TC#t643A0&*>lO%SOy8X9nMNYrIyVln;|52j&gy6tX# zH%N{ml_&X*>9ssuxPkVEUVGS@A95ucwHuX$-v~CU5%&8+CxKpw5IM~y^A9*YxMPnu zM~V^muRJt9(UNQs#=`f@RJbFRvV#%#O9vpn>&(e9$rslZCM|GrQNXoLvq6}G)QcYF z{Xkc?rSUR+D9MA6|J@5ov{2wy0-<_PNd9puu>FStzqn>aH933e9riVe+u^a?!lmbn ziQA#E+{gPv9rQ%k_E<5(($|qs=9GT?AI5)G2Uvo?p{&6-1Wg{Bb~^K2ne2brfi~gw zG=6~nxKdEnv8>^6$I(skyly}bE-_u`$*0>cj%-7m%(PW-&#k(r2@K24`puSkH)I}Z zEW4A(N+7!eKnEd@#suR;EN44>(9M<_lO+`SM+^GBhV5%@;>an!PmorTXiZZG7>yOs z%30QM0TSVd^5{U4 z)^zs2l<6J>x`OZ&lr8bD`k{MJy9Ph^k60(91a5x*E?4-V9r%P{rc&88Ic$y;4C0(j zG-j{c#-qPMxTk_?L0)#(SSfrx`7OJoO1|z1Wu&e?xJddPYn11(sK98vtwJi(GVYx0 z^BE8*)add;oj9(iZ{N@dv9}r%9ss>AklFz|3%D7QKZ6Jx0+UG2Ptar55_^*3nzFyZ zqSC2F_RyJ3DtgY1#_XOKFb1TyvjH#m5JBr3f|I9wK~<1!xbs&tq=&EP`7KKbLfk7N z)Dj+}9gA^2^TUMf|5iZXo;G3sG#;uZm^O{B&F!NHE6-+<>)|sH`w1WG14oDP&Zp@1 zJ3{ljh+@xdAMA}U&ZR6+E2AQ4-JAhq@ni`*lrK$`u~YdFG2{ahde&`k@O1YV1);b z$o?QmTS_LPKu=UZw1IKT?RpES@U;$5_c(jx68?!x*rRVi8A(4HPAc8XvA{Hl1CuQZ zK{Cx4waKXv3|mM~ywM7p#2&%#hR=x@eNCJ+R;%qO;o>l5v1Ne*EmSMI_>YEIjqE8C zz#0((Pb6TC1VsaT^9}PG6;Sw>HLl@asOK}2UzU(Gd%7nOgo7bY;wEJ2;TT%f+eQ$b zi0q!$heFmHWdchhNZdlnC1!4EvBsf$*#^I)*g~&1M<^Jl@?2VRrBgr5X2Tdsuq*!n z(e{N@5ZYgC)}vPH2R|CnOnLH2PxJxknpw;MsA*P86U_b;n+Z_!6ayP0P+++MbUHDc zJQE+|Sm%2MTCXydS?8f{{2d{ZINR5>Ji?w0W&A5?O4BXRH5qAmvuPAXfxu z2|ojGg^jUN9p=9Scl8&%%L;jhUEEGIQ)KW{2jEF5f=xQ5lY2zVeZw#xSJ+3i9B^iz z;a=`J;SC^0v4wtF(faI-bu~!Wg`(=D4_w$qP4s>?x;H~?NrQFv4bvKQ!u-t`MBVP< zutHCL)2cXJxk31btldVa-i96F7>Am@rACTQ@+}TMnHp0*Cz@rn=Vj1aSfDE&-OmT zkf^(6m(zodnrZ8VUrmU7Hy{$~vmPLIE3=Aeyj4Ivz7? zP3-io@ci)c-y>_;LijB^JraIZK?r0tIu(L3F|^#3U0fp1IBZllW%W00-N!}UvyU{xD-QDMQFck1x{DYJkCyx9Pe@Cr+ z-6^X&(nIxRVp6ZBh31!_|m4y8v61}eis$RUVuZYC?7S`ys34BTDkGTvdGpChRdox zMBR|zgcyOhMbHi^74~1N3cGp#DuT+y8IxBsOR6(yT{_D8U^8 z>H~w)oCL;00=gH!yAqW{ZExcB&%~M9KaDxEgk_6r3w{4H_9sjtnDb6w#8KkU-B_I+ z6rzArSxrmbThH^#`7)+*n-5Vdw5zM7q6+wB)_h42Z4V~5h?6x2t3!9o#BRYBspEYe z*B))1D&E;jcC#Ibu2ps3s&!VpvGH`*r`z?mueLis3Z_-W4#!UXvDwZs#_yp_dA$~% z9e?(&{GQKu4YEs}u!+}e(kep*`O!(DL|6z$w|Hj!C@+iHOXNJ!7&*iWd{{bhj;t|r zWv)%qLXAWoLTNL)+4ihlY1*RKddzPz!hvo0k~;!$ulUX{)j0e|r(M+1=2ajWrF>QI zh$Vn0-klX=SlB|LY;mgFmDRnCDapF926T~*F(Q=4$O7bvdlShS!q>&3QYzP4K5q8v zD0resO- zuTM~EpOpKpLh$?p#BA|GKJ!Uw(}E1gjP7?(dWMVMg2L4cY)N@OO%E}?Av?ELx)<_ z1fqikgYt0%wf}GTW`FY&4tUW0%r(Y0DI}MP*-H@&k1~hV!*@xLe;tRcdr{<1KDPco z1qF+|`-`_xlwe)8VQ;DdIjB;o{0OeqpJg){_y9uz6-vtD!e^agm*?lpJDgBj=PC+4 z@9jNtWlV>`?{)I*;a7vT|C$y%s$7Z1$e<3E;%sE21iA=s@9b6a)0oMjO}bw)jBNVeB%ECKf;gwZdxtAiL~XxQf~~J*NKppM2HdAZO6^}{PSzg#PHTO_JYy4y8=SCdL>!DC$9gv=Xwo~YHuSS;p(Xm7R^ zIsByd@|HGjLhOdJicR8Qy*9j(_;-uv#tgqPYteKD@{ycLKqswR% z!*k2=w7^LWcO4gt>1$khR(sd`i`noK?4T(I)={^xpOK~l6#vJja2PTd!&ja2szZ_- zRLsvJl0|Bt)~xPl^Vl6Rv3`=CN_M$=OLaU@FsQLMcNdir6El4)@7TGp#g`U_2J^{q zNh|_hwW0N{2Cb>A=C?|@Jxe@9h`SXHFIEmDG8y14BOu$VLy@u6M}g6;5KbmD)Ru(E z<>Ek8RmAC~P0b<$*|3J$5@P)123jCKyi%KTmrtiOLc=4?GJoq)C9ldNwFJq76kK@y zN$gTw_({XQulp{P9~Zd82XhFmYcxg{FHH>=nYf-lf3X@~MgL3S^@oIPGO^d3@jZL^ zKZ04n;PvD~T*CnKMAlIVFEQu$B6hL1QJP?F+UNNRFb_EnB~o-LeQ(>1rz*@Z#O6eo z&nd^x?A$#$62ZyEU%C14Tbw41en}>5RZ#w`z=OvM$V;Ly&gN^d@%7@Z)QO!w$7DR@ zbzpcBj`ZEzft4#jTb%tQa3BYJEzA3@n8x^%&!|lzANy&3ROy0uA1bJ5e)UKaL(pAj zXSL^i6ZnMJ>&XjqAAaj0?+`jO0))K=*C2dZ?(9ZgULGY?d+F}be}H0WD&>94#H*p(|CF#b>dvA z^%L6RZ<5IM``_9oRI80>f|?tVeP@}66n*J4?=9+lxOQ}Ek4t{f7oteye|R!{pb&hQ zNb4I67QmJ^C2B?csX699DTVGohAc6v0+f#Kd5Yc|m*+QcpN{1p$eaK8!Nb(kIWWYB zU-4Y;#k7z$GJUc?&X*@V)%hJZ+HiWcdT zpDXLcG3HUPTR(P(h>XM8wSUWRBgzI2mT_Zn1B+w5>HRQuqWapy zcvAam;s+o&a3%(fH$?K(i_-b=Ql?(tWw;&ZlK;ejDabX|!J8rkPrwfHdp&fI1Itrv zNhsRyAVyT9qZR`@S{6w#AO}22W4{|CHcN6O(yuj(I`8@%d|5!p+fMrzFB`QQ;!(wi zS;@3r;63z>|5{tFrsoOqE{3vM$v8ys)TlP>n>N2H(P&5o!8r^!CwNir$G@q0jvJ#KeV)XYX21 zr!T3pl@|{b>lS;=@}hLrNzv;ma7~fKIt^Q7hPbk@g?#I2N0>V_r@-wzGm%Tx=$I75 zkv_Wr5y5MP*9&CuwgSd~L%jKy0L`R?9Qa}0=R0X<0-CLloBhZ#a>ibYXs`FKyyO^Y zpG|N(m#BUF>SrC_vq9%Xn-61_c4m6mgSF_O7sbBLMlmeUiTb!pj9$rXF2%Lh9|jHb z|5h`93l410OP;$0*b}caS1|6zb&=~51Uux&BK6QE8{o*24_RkwFs|FkeAqyn@OCRu zNs#n|4#0@En3zLi_`qEKi3QwH(R?(T))JRbqDc7VOgeZ^-O8)!)L9~D_ENt(3I-Vs zbQKX3y%g#!%XJ45TUb0dC-!}>aN3-%@_(;EE9G}`<^>1d)zoCLewN|0~>20hRg zhaYC91I>ZfU#s3H)TVXfL0MKEV#K+<#@yYx*mQjE*}MJ3=F(XK>?Pn(tCn!cfu+Ww zU_HKt9&yI`zT1n&j-s|>iXC6A_Y*( z^U1!9#95+fu<#L8j^B^XJ23dyZmFj5z5nvLTFE&wME8m(svq@)`nBh$c<@tgVq%>4 zv6_e9=J$l4VJUKWByqVNg}PPBi0Lbx50{yB>NBhqz6efb{x=RS$IC$!a;7o5ffe|Q zCDW=aw=dW%TE5<3Y>JKOC-N%;7+V9AhmR9zl zn4dYf=(pF0mwY3J&?3BxdorC{@Nk49mx| zA<9v3FSt9qYK(}zB)KHXs%MstrCUgm0*RDW`%)9^0^^SDxcOsOhA3Q4__Kdkf<&b6 z%eBQO8;|YC4_g(3MyhdrcUCN>?pqjW>NQM1>il@Xxozcz*s4uh9V6go8+V@2#V2S(`t5vOJco;FjGV4>R-{#h^|DV&b#y z;}uL7F6fdge<_n#-94FBQi^%46 zd+bkyQM5%SS$y3ap8puc9dvD%^ET8b)bk3Fb0C?b9`Go8W^VLg%xj9DM$!8>s@)ZC ze*BEJ;oEoZ_+XyN#w!_W1wYOQ3Bcsez8ffQSPI=09-V61f`m$Zk1QHIe12O99}*YX z6o5U?Tul=BXSxj%Dl6>8l5c7-mKJ$RoM__qZVw^Wbm1UW4vM7jQqDcQ_`7#+CNqV; ziSp@=;wDFy7dRH%@sgO&ZB*;#@ROrV!?&wKU`{`qSJ0zP^!N?wC~@)a-65#cR9te~ z3J%v)X^{rcqlY)JO13apc?DnMEq2Y{sItTh>!C5w@oQGR{A)kjMhaLS=k>W>G2UV4 z{3pE>Roo;GLqC)8_%(FcDlp-0Jrw#wrw7l?~^{Z<9MM*qZVIq zQBo%Dumr;W%d&uH@tLhK?)i`JiR3n<>2$mkqBq?S;CCH2`IxkaDp0*6T&7+=R@$`1 z@z7@!oJ?;PP9>Ah--z%_3^hX9qT$NJIlNo!`ifiV4D7fM=K)0peD2X^iU9lmYT0V` z-GbVk!D5>&qhbzCWgaaRAIjB1%V~A_swrfLR6lyXW7*r9UY#Dt=WZzTGeN?Q zXHP+TG&<{`v)8iBe0fW^xRKzZx=J-qzYf3Qt+S_ZQ6tVEX*O91dEyv0oBVbe>@!$p z$&P&BBHvA~x60^?8<3MQBnKgsoBVs|?`HgajDjMkr0{-IUv8I-6M^)D2W<6#TY&oM zTi=zWVSJ8&#(2r<`$=c;y9o<}(BE`}&bTd4(1J)!>?Q3@LrYOThdaRefW1eW9NQh``u1*aqE|~9<=dGI;qvPZ zEa3Pj#{I5ZtmyKZh@5f!@m{%cw-&P zFdA{I3GZXK_3U8;0F#-%l0ZO4-m7|6Bb^ZQ(Qs^Ui=dcrAgRWN0C3td9uWfVJmAk6 z-&NsXEHZ&b(`48gg11GKljcE6gh03TA51D?X#PJMBxW zvd`~v<4i)GtX99#;LS8oibmJ8GWL?m>;d{lA?0-lBRBAcG{wZa-raMaY_b{z2J{}D zZl^M}qaM)mv({X_i#_s0!JQKdWgSlkbVDM)R!%mT4EVO{y8QGgPU5PdNKk&UYYlVaq5t%CyOyM zfX}SEr9=vp-_Cy^pRTscTNTG#S2{Z14^OxJZ)X@80}#4r-am&ILQ;~wbQwrpx&-Aw?x`PBI>}l zIVKj|Dw}1rZF!3!20`GUl_c_tODQAU2lL61RdNiU0&!q1Y6$Xa(6H}#Xm->ef#V4m zLr@0q3K?-yZL66HDr4OjVQtsBCew$jwQe_0lpXMZl@W3q$y%oM0jU@$ITtg)@e)(sI ze#JpP8?=(o2JVn1*f~Sb(VB3M6mhVD_aJncI6^LfeFg-a?B-;4;)WLUXKCHFf96z! zOU`Z%c!H0UVO>~fcs>D37C|;HOv zQxyGpluB_?2mfx%Cr6sBuL@zkFRH}#Y9LxU4_kuCltMO!bP4*ssx;w*N?f61Fy;j{ zB35qJ6xkOT?AT0|K4sLA%-q9>0!a>W6?|=+7Ud1Tq%uI%MuL?8Buwyj%lwX;AMBS+ z-=4&Y3RuWS*=YwI^_*+tib7c)jy6!!)riwT3=`L|3bEP?;_m9>_q~~J*b6TZ3cj6d zwSB@ziE{)b5a7|BTH)`-t8+}!zwyGx1c*Umi!9|*{aPCMelICaj}UnlzwK>TKXFN) zRZs6NWjw+O-Ch4%zPlzXMsc)szo}X8KdT!>qYk{G3cPK*k^5Pri*r`b)l~P!VJ1@E zh2>NrC*;2!QKzm7cYKILPH3gB(#pb-Gx8x6*s+L%=Xw9Y-Rhjoe_?f6$TH60Myxo) z)Vk+~*RA=b?0OcjO;MuQ;!_E+R?D5B-1AAG%DROA6|ESHqjj{S_m)*M@rnnm8ndc)4_V7%|)%p)p(yU)P>@2sDfHfoV$eq zDwSIX9;^Y2QZ0s-S=LK4#*LqvAluDZVxXy@OI_Om1l%XgdTL!MvpK)OQUQUw3g!tf zJMHpZlODu?#hP|)Z z?B8N`n7#|GoMqA@y_6t$J7Q{IidI)h$dOK4qk9+_TSYV3zCwZhYKZLi3uwf-@(XW?_*jUkSNK5M+=6}x394kA964hj$dYUmcoB22`Sy*RK*r>k!q4OG*xJXn zLxO!?zj4Z*obN<(jC3(>WQg_N;;>n--?*tz41XoXq{o5tk|$P4e_f$TmdES8%>cEoh5iKlFp)aN{)P6K{&f%-vWXFl9?#&>!kb zrLQ)S-trb|d1x_Ld(+?ED7(sdY)0R-f;~}{*gCvMSBYGa%wFEzj9APuzTeXR#U_7T zqUK*gY_%`j0AhHTbH))SjFmJQUJ>CD=(B)aJ4}1l0$X4TC!wlk&ux$;uQmX#$=wws zKLO`PuRDwdS28K0AK4A8grmopxR-}_+qNwTJ?*l$FZmfCOj``4Hzki5X(wD> z>+W~(*lrSQD>X2Tu49{350e!YHs=JaZ9AdD=ZUCQ3ujhnRsho1 zl-5ZJ-{yC2XgdB_a^TSX$5zO!sf;Ck>z#)R9$$Ssqp$S(i4k1h+*QbCL~fKY*XpVU zjq~BxrUE9+X$8FhYrEaca6)!P;8V`VOS?G)V*Gba@_|CFl}eLDD~4dVk%}V>a|Og9#WCNDepA zZ@Fy!K_}O2i=}}UQ40(c#h&Y!zI%WnFQEIt9ec3gSW-hyk`BSFrCBA5$D<7g9pZ*>*TD z^;tJ;?fP_Jeq|Z5DdIwuN-%a~H!+C>t12d=^nYMeom8$lN&2Tz|%9U;99CaCTBAcYB5(FW*Yzx12pY zg%|DnuxUgK$j!?o5^eU!y*-y*%Qg&qzN~Y$6Nk%H4E>de)$>)I6n|af93=d1E!5*Y za3U+*hMoQz%*fdf_x}gdw zUAElxJ3$=5#iM)b(wvj#OY5giohlDQ1Y-nL^ZHP{ip<*6{qZtvQGpzMz0e61G#DU% zyovQHBH4!P-v$g*c*=Qh>2LQfLByG{o~_KY!IR>wqJ8UJ#)q=Ji#H#>ZIQ^Ob&kq|fS`u7}|p zb{A-p&-}&SHT3*i(v>-}yfCR_Xj1QT+`f<9y&&3i6Ju6NY~!BS#sq zTG+u1D+LQhg7s0RYw5)B2`DX$F35+daM$$DXRRf^I4$;_WSJ0jK0Dkt1AU!$oJ^ZN zQ~>2;oHL$zW4~$T#(xA-66X7`TR|=gjFb?HeZ#(QO^q<}8t-7%@)aY&`qE2oLD$3@ zadu;&3}J%W&CK^_vYUhnhTH8~Rq>1^1Bh&s_k>{p|J)5VK;%ec7%|ay8FS!BpJJ6$ ze(yd`2;_*2caN<7)*Uz5E5|lLmA~FPW`V7pzy z)#e=fZWx^>D@>snOvyWy-D8S-iq78SE8ak3WK&mUb2AFABb*P&&e`nQbY?Uo?HAgM z%P{EK_cI+=V%2va0Z#{z9UGE!GSxIX-aAzn;+FI2<%2`_4m-$Lg!TD#4GiduY8C*9T5&ehqm$|$cT zHWDg*;?50R-EsVR{#<;{s?ic_qSL>Yl=ok1I9vt^rM!p zeDmz-*NzYtqHrF;1!r55ZEum%BtTl6XWvmQaVg_Q?U!5a?C$Z1g!HCVE;3c$iioPW z`tMA59C8XOlSW=+?a-iV4?#yZa3#w{FlG}%LC@%`h=DNCIrBF<`HDN{_hLch+bA*n z8t9fr>sv_}K)j@ku)e!=n$M2>YrJE`pHy7=@QQuFQ1>xlpdLq?p^^k*A_2|&1C!EN zF8XCpWI*$yR#N+SqJb<*P83m!09?#+m@){gV6#>vt7cmw)Ry24i1{7bEVd#C>TAZR zU)+qF%v}xC4#!h&Q3Cc4mVkPUV4m{pUO2fd@ zh&V{Oq0oa^?E7@6TdVoHrJ(sD9y;;yuv`O!-(YaXyW%qQ44|aaem^QJU_(F?f+3w zj5Qy@QwTPvbAj4qDil;iujoOis&`C&C$bL`A73>HzoUS9P5r9DcbqTHIG;V?vxMOU z0^=wzc1p*&@N0%t^QA1e*iH@WdoW3DjU#O%$38XD$0-Chm$ zC4a+8m2#R70P`Sb+J%+t<0bnNGdHC0UcVfv+A(Bd%o zL!MmjiK;AQ6rZ3a<|;q9mHAm?#*sElyQA1ygP#d_`kErZs`+EudZHP`BE*cg?kaE& zZnwDrIq&Ny7~0`$0v4iha-2i=RP~6xpxngBrb$rD;nl>8E1avR47Texzx)PP?iPFA z)>l9P`?!6G;qWGgg$Lv^c932oI73KxggK6L-whxGS6QR|UT>GerZP|^iul?fkGXN& zW#>E;21%^!e_sdW;5w#8L4u5|TxmAQ5h~B3QzWm4G(J6sETj3Km^Js|r4tyfaa5XY zXF_ER64Xg^ig1MW=uJ+SUKU|Xyg34ljH&-5k^yOkMKThs;1^xGMVX>DeUpJgh($#% zYz|w%p|l4_x$E-kgig<=3r)No(MnKjsnI5tnrn8ogQ!LRdeeb*`V?k)%RaVdm3kbC zEbu>bzO!bw@T$;d4JcIMIt;xWKI*T?CC$z7{93;srAlM`e$=^R#k|GN`JMHd@r(>5 z$Nx%E=G4)}RdCG1kC}7C!JBZ=C{Vs1Dv9!(BwTU}-K=pO`zcB%LsZ^%pM_8qYR%=% zMDHYl^re){DKA6q+0oy6pv$FIyoMw=jt8pDr=TQCuw>M&>t(=y@;Tr0w38XS}tI^H~$B~74iz9-^4K=kbdB9O5 zfD(S)eSf*m`fJYN^(xb6 z;Cz2q*sN!U<7{Al6&x5N#-x;Sy$G^jH_p{_TfouBJpW4f4i>@$&u>t%96GQHYN!_k zrX2z;{)j@6xdG!(VH6)@yU-4o@TO|cIF=>6oQfS)$5wRdE^L!-L*!6fuW=lX;7fUL zcqs8ymPfMc3%_v50b%9rzI_ssOTG7!gYsc5+kWv1#iA>Lh$2xS1@>d;BNI>lr7CvO z;nJ)UBrs`xlt0{t$0!YW7gF>;}6&><>6A%ktU0W10Stp)>iW=^= zxNa|z&%Rf%l*kdMuNNv2o)u<9AsLLp#R3GCLGomC z2CdqbQ`pNoBU9W6@uzIc|Ik^VfG7XZ8u`axC@foBibSa)BvjVdubJ8Lv>MtmI=?0C z&Rk2DmHs3?n{#iipQcAmtgf6+97Jjw!)8UxuQV-XEJ4n@Lxy_xdz&IbbvJaNFG(M89wI9_dBWa^m^A12Joh z3)$5z$`Oj&w#?ncpOTy}P>IA_`6VSDWWRF*T%MA<7eJJ#0q~0&ky(}I6Mjt6o&cix;9G!)D`7Bx=~~(j?9O+=>v4)z32m#V$JK zcM7ho6@;qWpIm7S3j;Lp^qXSmG&;frnr25VdfP`~wAxv&E3fUGSot zeVcXWC;|qyul~(o858eDoQJGXeD%Yx?9Me`_8)g$oOyQ%o3`G|iRHlAjxJw6(DjA* zGcNwva7Kv4X8{la$<`~%C~|ZekU8AfGpTy1-Z8f4KM%7MCIr_24X9V;Sb&>nLM`a3 z+yIz)C$BB>qXl7i^hWb4KD&aJH53YHi+H!L!`IM9@g)ahg@cP1cOeQ&sMJ7T>MB>i zoB&$V+;7}S7oWHb>mi=9AaLu3Vw9TRef?AJOFL;u-8ouLNDPjK9_aybi3>VQ1}58K zL1BDLx?B$w8OJlyieTa8ut#O6Xy~UveR707q5|+RTCU<6g-(LD?popzCk0F+dB~;R z6n`c78n^TjypfIita3U;-a?6BU1Ksi$iCl2{Q2xwMR?dM(N$2ChA?S9Isu?_Q|iYUy!k|I}^r`~Lj2COP)jyCuaq`VH%>x;^^k(#}_-9QAkQ zJ|X#XUYBp5FT~_39q|an+Tz*o_*`{w?oh)@V(UJ5QFNkAa7NDWMf9Rycv|&ey(wcE zwCi-+-})C-=ckT=qf?XEWpB z51m8`b*MN=Q^t1tJ4`qFo6!oI{Q@+Y9B%cai~W5 zhWNHV=&l z9PSHBQLp`M4?p<(35=Sa0b^dgE!8k*N1rR+Hs&Q@Yq?JC0l67ZVE?c%O({f5wQzR@~PoE+wXGS{q~3IjS**3B!c z*%1D2XTq-e&0$3wOTT_Pbj(rt5Ur&->q({_i=iB}MK#P0q}nzy%STrX8+oJF_=+Yv z94X{Znr1|JJF{}&7sG&uZ>_P3Z2!amJPVSN|3P8e<<36n*{oggXm6Gv6%(=X=& zUoyA?!FL zQ3tT?ZA#HT@BHKjGF#+U=cFZy|K{~SLg%MUQ4F}M0lc5_O2{2HUwY9=A1&_ac0Xlh z_yjv>pKK!!!2@PBAUgm%|5B?q7E9QsjcYAbHmEL-%zxwz=brGnBB=&vV38>ya9u5Y z%W%H61z1d=2dA=Iyjb3*(tw!$-27>JBIOTer>ooWW`l%J_7d;=>e7TL=_@H1jcrPy zi@o`f=buk6j)$8&2rx~s*Ojtc#ZJ1=7)i>!Sa>da3b`@<5`e)IWLIOLY9HVOc^i^+tQl!+`VFM@#rA<4+#E&nYxLz>q)xFNti*r`eQhcBgxi6JZ7uyC~ zVR~f~?7Q7;;Rk$`O>=s0b5FrU1B$@5|wHu@q;$6F^moiq2vO%X9laE!WEO#lWXQS7~vOP8VIMe#7>_@LL@M_vj2TRKH zbn!3SMnOVy>5LSs!rJl*nEie)Lfo#4@iwI}sO^;IHsYmPE}9^JLoRX|?}}2I;VU^2 z%2+8oUWB~x(aCKXDWPw$j46lWj!u!m0eY;De>#v}T+?8|ijFf~QvQ zf`QN;?LCG%act8+)+LU$kNhzCw~hNOa$&P3`%zmMFUkJ_&TBbp(@4*=I~GDGOXxYb zGK+{^yZ*wBP_V#L0K9_`<`laHU|7f07(DipZic=Fu;)RHj=r)?>P6!o21E70Quf^y_)c z)ps9!(Rk+g4yJ_Q2yCcEfCUwq^ZpVf?0>U!yS}qqCv^6LO<|RD=xw=FxYUviUAhmI zHn1*xGm>N|@6{Wk#&>!z^WPE75N){QC|F)X+uXhGSk^a`557QLkn)Y*>3<~9ZDqFx z9E!{(u;1m$D&xklQuRlcr7rVS7SivCY0Uqij4k1QdjE;fNUVOuf97EVbi@U2MJB@| zEBdBx1TLn{T=y(&4i}<8wt}n`zER8ZVzeNZe!~w{>=1qN3QCJOXew-zNw2$~DNQT$ zEtY*bc<*PX2l0!O=jW}RrFatf5p;?-eH^q-f%#7)R$URqB>Hs>0!#n{qY)ejMvVNM z!Fx1_rKhx)wLg8F=4dl(nILW;OM%FvUK=a*tVp#WoGDVuo-JYiw?p}VcSr^?_H&xy z{p-}!0svKvd;#=Ib` z!dKnyogqf0!(^6;mm-;(NTh^KF^9+{4H9b^xmeb3a-^AekNx@Bx#Egn{BCUUX2H+S z0WVZv?R}r^Xd~8oc+%`VvX(KQ=7|sx&)?16Wo?b;@k99# zqYrQ=!$9S|LWr?wzwn0#W9`Jp~#RRI?` zKvZQmqwc8VqTHZ-2v0-hqYaqp2pKnD188D8bJ6Nb?2$3=-qVP;h4-up|qD7)*D3X0GG$*G>q!g8kku4;$ zL`nUw&(!(;zR&A<{)Fe{yv{kVaXN1Iecjjfe!s8lzAn`6?7r6-iajJ>Ym)3^D#)C0 zlz3BdeCTfKETpSqh@hiq)bmJh2~wP)rsbAQVpLU(uFC(|JQ4F;D8#;O<3a;?>y#Wa zUY|3OJ)j?pzId_Ykdw>Z(0m;g{O*$5UytvKkDVKQ^w}~0x~1b(9nEea++lA?IYH1DlaBx5pMwjhDc>|dOS`-I{Rtq4=A@}%HT ze_ScuW5t$UHwzT7dD)x(_&(RF2x)N@J+Dnaa+S4jznxgFo|;y7>l-C@5?@3YP%Tvm zQ_@ebnSONse(c7epqHU!8ofL%QP*+EbmvDHWE%f^@u$LlFFBl)5(J)m-kENiPv35P z$a&W1o#YcPX_a|56{Cv;DN=ALHo#5_cIxh^F@32O<#}p4j&mMMH%t^;l_Ukf`5Tw- ze%X=T_e~es#zL;2c+%*Hlvjyi5URIQcftGfvFE=lNlZ@kYh1qwQ=oVDBY!S^MPhXq z)7)1yfOM|lwaLHGAf56h=?tTbNRy*Ydmi5CG#*4w16_zsgfq%*$vty)&7UE`@nuf; z`R+T1qtCqu)BwxMsr$VR#%&Yd86=01d-cKYDbifWGLi4$IZ{!&j(={1g#$ASJp5At zRf{svP@U+;wbP#D_T>67>B>h=CL7)Sz*u(-|01l5iY-D_*u51uH62@BY!q1JhYN)h z`O@v+At=6TNm6KSFDMAUDLTSV|MyYn0ID?)-FoNZVv&EhFYWsDmI8^9orV=MRC0k9_NoK|@1A_R!AMC_Pn^S#awgriFWQPw-z_Gad`&0$*MedLp> z`m=BM;OA)+Qwv*}RXupH@ARK}X^#@7)(ptK&VZ?S-)FuuHwPrn4W$oxt^D|;-=Muj z`RVPn4QiQzQNPedF`f-tH@Vv zvFzWSdd1wrG?;1Hb`rUlbL_v2p&q!eF-~^-UGe5jwZj2aE)5PHKBK)H?@!P}Se7q= zmqV^+khT~yDeRliHM#W?zhnmjWi`QxJiv5E<7Tn?z!DNydqHjUS2Zl58qc11j~>hWgSrBQbf675SWVzI`wOxLe18tSSJg zB1^5yO+N`(t$1z=EW2eA#fsEHVONru2~!$uaWH&FzAU(^Qj)VF(O;TQ6XGmvrMCxdYfkY)9DRw1UiBfySfJ*&6_+8#ezEjp>3?h1(M_ITuuseobdwhlzf@hJRoAnxd+ne>0i#v^)<(*D5f_x2f`Xv7bB0#0kt$0A-r^t_DDJZ+| z+r;)aU!k!VUeSzocvZoWeTIA!&e3FMlx!yaP{+Yu$ z_x<`xlV7T^z5r=a)A93lcjW^)sKn%Uny3`>9eccHM>Xe*eIF~R=y1+w&<7VTSLI*W zTwijSzNB#a*pxe5M9xIc(q%0h7G$P-9MvNIfMx~A;KBvwCBVB`{81)UN{v2br4AAfl2m5#~2+qxvLA#-01F z?tHSNHo&~&J{6zq{(y@g0YY8n4HT&2czh>CHvE%r1hjK##c2srcZiuvun-~#7rgtd z{3!P+#yiP!^Jm?Kik+j+8iKwyFWBxk2|_XM zeE~DizI`8c=;l8L4#$J0ayGlqL;@E+J2&z^vJhXl1GuH^r;&5grY9H(gGPSt1AU0g zVcxssprBb`l%dA@u+&uC^F*$|Z=hrui??h*;&9CNe%Xjbo>p#kEWQ>tOYZYX8wx{M zzS?{s4NLS`v-~9pb!;`Smog%Fy67y_dXjw=W{wU{qvf&#J>x8POvz2Oj(PHF0a$6| zE^xyyY*{)*j$idYm}h&gg~pJ|e&tEiVJ(38lzm=;UZ>4gxINgL_XrLMHL_R|@gDUS zNHQm(gv`{zmyP7NE({=S&rJeFQyErusHl4mDj0Eng@uJrzY6A-Yi5q$s<)B3Wcya4 zIb_^C)A904V;30ehfVqOJ-e zZN_}k^&G8H^i4YbF*ky}04!)*PeP%_xa-n=epubG#so688>m|oX1Yk z-%9-TwM*NSiYu=S)WMO8feS!ZU3^3A6IhrtRQ;)x>ZK#Y55n0?>(@0m0UJ3Dk2){s z+O36D{oA7)VL(#QTljKzbMF>)FTROt*(RZYx0<&8N8`>#=gP2ff9=7o<%fyKsduuc zJsR@^DL! zpsYrRzO=6LaG!v;U097iwFLo- zx}f8z5Iu?+KfE)x*iwcX4g&jhz`P@h*t?6CWISCpq5Zk;g)!o}8t ziKBVw4K`6FBqs0_ka&3Bq|w(m?sN#!-a~l16DhlFL_A4L?m(m=@F$K$q0)No|02W! zC;js(9=x3+o2hVVbxE>Z_R`h|sJ7(X1~*!+nb}g4_NNg8_6obQjUB2N`M(rNe#5=b6D7L%X^7dqB+{u?KMh?FWNtKn9wn zRIkGl8#;^7n%s$E&O3bQe5LhdTFrH&!TJZTL?L{r(+l%sLZ)cEK#^VFf4e9V#}tM& z0`%-@8^#a`87mY3497yPs`&7Gp2CRpTs*8Y4^waY=zg+BUm@`&wRUICKw-z1CxSnc zd(>;O@`Y#1@mbZvsw`qf`Ta%|d&`}d|JXFSgckToVgKRVUxgS?2yY{T4|3fSl5Cb! z9fuHYy_4B>boQK`2hv(nU!0ykRIta; zGq;^JCGVh{_YU${Eo0`#*7rb3=Y*wBb#A1dr1dpw+cwvpjOvPOxbA=ViJO9YRl0n~ zqfR6HW0~|MslS){2#6iCUN$Co_XAmeOhu;IfxzG(m+kc5@?Zy(;JrYgtOId3&niYG zz){d@|GLA_4(49^WpuBA+0f}6W6zI)zS>>)OdiE8-SDq>d3jpJN(2D85Oax&79$ws zb{v|MnH5+9Q~5AIzw?cF1kf77DXxPe9>y> zla}K#!wlz62kyIs}X<5vgUg`S^3Sxa3e#D{?L2n)-}t>8lZ^wF0;;gZrOhIIdnv}UsS-3_QK zz>lIOs3#FW=)C-1=p45AZW_J2^ZNRY2dN^=aN0CCHD+b#2^IpaN>$_J)G{gYh~UnmlE9Tw=#`3 zITBrE>G+JRL22s6eW#UL_QPvJ($=RaP< zRw+6s)GFdbRegDK^p#V;U3n%rAl$@9*|DeD_?MvS-3OhHK?PYg6<6hpyqhzj`G42F z%F~q0PrA@d2(h$mKYmkbJRkaX%(KxvfHfRO82~7VwBYFI`9z4BkfA;HpIkfj+8VPB zTYN_#R;K4}T5ifR;YU5kpVZf@&wKendD2FpL2WR;iXXBc@CLaKl8z0)vTT^W*||Js zb;G%f+aAfzDMJZ@47T98MfdKVedcEqv78lKyka7{zfy;e%BSb(eQ=a;jt^7Q!v;$mA3Sbr7-_Z z228zfU;{OFRz3fmkiQI-T zCdClCiEP03v>t{32m`oJZz1I!|4#w|?!7{RBQFa;9{i8`5E)UlnuEyWm_qEl zA)+w%`RVb#KbhNr&B?iFS9`-_bPs!5YTd2Z-vk%ca_gv~EB3KcL7YJKluZ33s*r zYTi?8%Ahbes2Ub+#2_=?1peapbYnmnF06Zd1bWImzQ4gUi{5cSR2p%2pBzbjk%v6^ zC7UfAtt>d`AdPTZfGGL#-J6r=LXX5tcHQZ14Vw>;)Gk6H(*<5(> z2zy03bt@IfU!~;!2#tkrRP3k&KZe%m@7_g5GSx z_8w+S0wk1By_TG7b%lzA35#@wD+1x6yJJU?ILKZVw@?l$s@kc6J((2iZOnMORqF}a z$s2UYNhBmN!;^Jyl<$^C-}#XOUGz!BFwCzqlCL&N;Z91N0PRk)0j*}_A5?gy_@~wy z#&M;d2vA3UWQ``(cTVZeAH(iup*v?%x*GJb>Z@lrJuy*(AbFMxB#~^pN?aIWz}yrZ z=*@a3=t*C*An!9sWB>*zm(Mo>$91saBk5Xt-L1w7`H!Btb(*~I_wo7gZ@W>RU``cU z&a9H|Im5F2-#fT{UcUW@0{MkBa!1$Ef;}g%_>HMUQY=KkXz2MqsI?s4U)5=%Vbx;A z&_oES!{3y_+j#|~-PWv)Nl$*bZ{7I~WU!o8sBGb6B5!_4$7%s7edL6)!)xlw^ye zH#dq%+x2bZ1O0#-G%pz>u@dkSh*7>m2dWo>nMJZ}&bR&yAiaZov*`Zg&&$`o;>t5< z!L-0M+RdPs1!r>_nR~5KJ{kJD4>hsnpIc8pBIL+wuv<$7f#G!;A!^*>t=U;02MA>6 z(x|~v)W($!9eW5_ZpVd+($5!&peMze;K2-HTg6c2R;M7)eaoyMIXC6caTniEXp+X? zKZXR@QA-&AjSjQz8+GLfb0Y2*@jOLWLDuxDblZ-(KnW0r65uSFD^tf<;G{a;EA$g{eaS+|<(7-V8|^oA?tNl@pWe zRz5GyvPP4)QZ-1mJcu6sSCu1y<-@!;Z|>Yff=;;Nkua71S_OO(7XXC(o+8ZI?3$U4 zpwaxkijvQK72Al~Ou$9-%zd;{kAutgxRcj+V^>=z?fm^! z=A)Q-EyEcE^p@7!SDbY#F!l83F3kiTc&PfUoNZ{W93JzG250-$KE*#u@*AfM-+c#W>K?W)*-+6+0z^_d;X zSopa1rX0uyI$_~C@nx@V# zORno<+Mk%B%;4KzEqME1C6-8zwNyt^zsI#~rG)}&3LxeuWS3Xa(Ur_F84M&Oni30Q9 zOnZ9B8niQ*J+2Ci4csLHLmug4JKI4NRA`c=>^M7U(R2gWLG%VbtrM|7S@&k(;RVp3 z?;T3Jb;FAV)OtSCPAU{kDvCl`T%=Vz^OI9a8x{C!&DC59Ct!8`^C{)wE%d#T4&7JhLfr-_p~!mg7vbJbGDxvveH z+&lnD&RMlL5VBUOW_+ut`FPm4h=aGkHvB?wa3B&ON4n0@^^#`5oyZM?^+@SugtyQI z4z#nL7%Irq%V?(1lag@)rHRiuE70OkwUN=u^V9&s+Z7b--@b6r5=-^AvkTgj^dk># z2Z1!~_F)-NriU6M6?z??%;+vm?Ht{6`?62dwT!3FkG)3pru-M7Zueu49;asE`dt-o zwuWEoXRF}an*!BcTA!Flp<0I~dNkmSuFz7%7VQW_>iukz)L(k_%^vbXdwSU}WV;}~ ztC9MLVnxlOn1;TJq=ry-Xvxy|Ycb>+2ooefEsk^o(NVDH@-OU7o6UWNl8=qUbdV&s z3FPvak*~Jtd}d+Atiat!y%F{N&~fZ;+VrQ^^9~l#2GCc!D--Yg2+|wCMU?{>zCtSk zOW9cwqR3-^#LQ>I#R!xcwS>LwT0oJ-VD19)@x&knG5$F&h9UeXn;n>&;6vG4r|AA; z&kKUiCMGiuW(TGpnS1j@RbA|(iUhMA_UDaB_rA)OtGBMLA3(TX{-e#OI-Q{e$zky9 zkn%GgGi#?Y0>}gL$+h6n&*W|aVEc&SYX1YBD2F%ts}pvW@W4y1DR0|vS{#xHphni{ z95q|YfYekwC%j^L0FNd>@#Trd=j3GDMNI?$R}5NW12W0$GidSfKyFLdo$ql6xx(Sa zbWVbR8J&TGH;9S6v`Ma}KT}3jtX#ow<#TB%TNH94=Qj#~ z64|i)t-Yx2c0r1)KzNJ$2z>Y|R+Z^Y$d5A1Jw+AWkOkf86U;+85P|J_O+k-xDOJJ zt>{+X`K_6hY+1BuF}Zk5Do$0lk!H@BoojF5$LzXiUb9C#WQ%$70CuuNd`0+jYOefd z9-4c`R@2SH-NGb^>CT!~Tzjegki>Pa(gG|1blC4)pLMX@ykNyA)xw}ud|$><43Y5B z_X-+6+m{JGKs7+^!C0(WHi&GcmXc^QmApN??&+rb4-ZnU=}CK@54Q8>aw~YXq02c6 z9s6-LHw3MZy^a6H?p&s@f|{EP84)Y;?%qNBHtM&surRRK91T@=ig;zLr8xRxw12bt z26bikX4?x7H2*PEuHi*yd>%PuOH5&d#Tm-}=17z(jl9Z9cTU;tr14-2GfXApu`(fV z4i8scv>G=?6@5Nc z+k+``)Zm@LcN*d)0-F68cAQ3N{HQwW%}B8|?>^PttO* zReFL^flhoe*#7K#nPUUA@#%xpUgBsIR6F4%ep51eH)nRvb)*qshKC7hJfBDF=0Y*m zL^*F5#)`mNE3-9*p%E2br3>Ri==U?oQE@4(1>@JK6r=iOlRfZcSM%L(_E+76D|S@v!v+YYRIMI5=U8DM7D++LN8 zf&ccfPYbPMU#2r`mqUM|Cl{*;|NZz;>1+p*SMknAl=6exRf-f{k)ij4?Od)030lN?TXqK^!J|@kp>T#ql!nN)4 zewAwDcE8j7Td@UfAx=4lZ~te{24KJh?AnfT3yIR7$Ji~;(O>+KAdOqV^Q~N!z-;61&lIOzfUI6=W_>=^Dq5a@i?j!Xpo48bM+b;jpx5>=cqRcpI z>+{zGJ!c4{J`Uqz<{R6T`tN}$G{I}FR~)!zp-Zp=;YrO+@k_(BQDc?an+EMx8O!nl zKAik46)%TA`7(>#Jb?+nR-BL8_w|k)?_W%s=SfjWci#f zwbL4;NmBIV&b91sMh2W;x>sH#i(ZFAp)<)$9&)H4&LJk425k-eWzilc&zN*0nFeJA zo3^YYyJ<#xkJcF>bbqhSEmubGd%rnB$8?z(kUO8d%1&^DfPpwA=)Ck672;I3ZJCj% z@tVf4r7z5|66<80t;vhoWxXEE%>WlZ*07o(l*xOsHmUp>|H>(toxWxscu1(o@l3T^ zdxPmi8HQ4su>t1&+rf6C3f>tl=k~u0YpA_o&;2;=LtK4?Cw(tGcYK+4ixYLe*;jvV zBd>OO8m^nmn5DJm&|BBJo+I1j+AN9Ib=0W*je_6{OVvm{NnK2a8Yt`CP@57th8&xg z8FQJSaaO!6#Gol3DyzPU;^u<27nF0QuWo%Sj+PRML%Ux&lH6M-`JMc3T|WI=Fg=q^ zC2!wY(SflWG(xmXN+^LV`?vmbXseIBTcU!gAl|FN_2bFY{1?Zs$T;?0I;s;OhL?0| zl3v|Nej*S|3LDy8fSIjD~##!{xGTd_zrQk%AU3-Q7lRY)o_QP5e8(+Yh{7zDf)G=>!r$O^}}mvHVC-^qoi z8AVEYuX|wv6S8wJXY&}R!BVG}tbD;uxOAVTvaY~XFZ{D)53lOOe|}$i2O}@)D*d8o zC*0Isf6e0WVUJv&7B~BP?ZZ#I-88oszr&HEb!qIt)EN)d(xdQKvI{#p(to&a@{Si2 zj_C$aKh<<#h$<=IDZG57co&0(X^oHU{OHHn)E8AuefCXp0uDCT;K89w|2VK4-v~0= z$T;{e{mA$l!EJQf@sMrbkvEFir{)HU*O1-YYZEM_Nj=U@HNnB*cBHk`-#q;;+wkta z?ITuZfi2Ll}tI zqN5VL8zTi+$ai9qHQqQf7Ax#|wyyHBUc|w+EY4hM@ThVJvoQ_VgBpTfhuzy8sSBN! zOAXk4UU>GtQ1BYSwI*l1e_H+xyOP3Kw!x74*u#QvA3WxCVV0nAB~$C2h5nfO*D&-B ztn-gOE`%qWK)=a(d`2fnf&zw+ zFRC1wa866xf7QS3vNB!j`Owqmsgs^kBRzyCwN?)wotE?%1FIuBsE@54*pu#t_K`xL zB&>;1Y|FGUgeT9D6Mj5%ob7>4*|rYUaYDdq6n$Y$7CzerkYp$9Sn+pLc+K)-8Dj_X z=P?5yOF1g4?LP1X(&dUcu>cT+hwKE2$wP1Duzv3`rl9W1E7NxMgb)gD@wbC)ZLfxU znqOVJCz79S_%_-xcDK`ol_P;ucJXZAtpgiYB9qR&CyvF^Pw~vZ;VyV_dBj}ByCuUy~If46v#BF7J!#!Go$#c>lZ&t&r6lQyiWwDhS3WhRL}ScMp(8Xq(g zcP4?CHVh@H)9zjPYOh``FYP$^T6@;2d&`*`h#2CA7+BkYu-p5=p{L8=t@{XltNf^4 zh67Rd2UG56J6?yVdOv#c(7AGZ;tfWqz;gG#D^)1T&}`52(?U2Sb|e;V5N@XX@k~ORoTs~Kx)~APrye-Pd(e4G8~(#f`Dqq3 zjn}2Q!&v&l?kxJ5|FJ>#+T>nTRCbUP_cVam^r)#~1c4kCOg2^Na!@9mr9G_%(45jpa2H!pmq`MxP3v|`!<10^} zPayD!F{?#PTeHUvl!a-zrhB$d&34aclE#u4sIpKh8+}keIhP;7?G$V@`U4@4A{e1H z9;F^(Ns1F-6H=yNkDVrehc2utdGC&6DmVgh{h4zp3qS@xpJ8Mn@DK}MQsY%zn#GAN zmpBgSaa}f%Ldi$${mi_NzA*Q^;cxzqzBps?yekGaSuVfb%qyrIc}jMlfSuM zM$u08GzR!eJ@e8zqLrFY_jQPyF~fJxrW-2vUr?j_v;2-& ze-`<&Y_bW-+6hzXpivph_-)%~FMopky%0im-n((e{p_HpIJhx-n#d|k+>%X{C|$)n zKT2GfKg6FXEo~cLaS|&&m~iQ&=&kJWr@65kHl>yAV?Nw;vag5onCiCzCU zJw>L#qjMpKa1*Z54e6%M4}+i@e@up7)kD0d+^6=^@hznOPd*PzY*QAg;{YJ*H~w!B z??T>O#Z{`mE;D}KLF0i1&CvCMZCPR}7;0Al=lm`Pkv_Nx)|@d+VfcVe=gi0>+gZj7 z%nR=TPg8b@QSUD%b=$-7SJOnze1-c@m?Q802XsYyqE+Pms0UE+0>Aa;mI?AwnlElJt}K|Mx#^^B8h(+h%x)K%I}uNkK?U*L$(aQUbDqW-L#)RG-AwbXKX5ym^-67 zP%mkAR>RfARxC%*KM5ZZ>BJ|6puIlz@wG#TH2WNw;XDN=a<|#(MQ*u%I`67kTNXW^ z*6TLi9pNXxpOUOdUjNS{w!#*^wk#P&UsEh?`2$w-HFmM>!uD5yBkEfXpzZ<$o15sR zltIUzw{=$??)FV<5sWGq4S14{6BY{8i$1m(7f2bglI;_%$N&0k&FgO>0`Vy z7mn0daZvcZU+aax4pZdxV589e2lw)J&vi!{&N+?mRRKMro z4{ZPme;}3{Fh8)c1{nx58#(+TIaDTy5RH;;yzW;aXyOvt-<)3ZnG*gKq+P?VN&*Lb zV2s3^ynUgduzJDUpez0aIIKPd+`{QggR6Kl^~f zCMo=N^!rOE+z_&%vhIEGf?Tq}nM4KmTmN&Aq5z#QPq1}tO4`e&WusAI<@X1%Bh%{pSZ!m6?-(WS@|JX@_w=b zR^DN3@E#)x;)*hraI`&xHghOT#LPe+SYmEfU`^9O(@rPKJ#^?jOv zR;o>%zrnwz_l|Xh#BoyM`dYmv25Bl8TLAQBEEm`_ z;XsguI8AqfvQ`Df4HipiO2T^u>xF&`s`vU|{V9AD6jtb}Cc)ig)(U zbk2*T%PPU)lsTj-^S8XC@7Kr_?2CUScND~+GZA26+8^*9(OAAi8 zoaT>F%5YLC|1#1laNkP`r%B5RP15=*b=51(G3POUxSOeataFX}H2Jsv6A1+A{Rc-)GOR1LD6N)qbg7iDH!S?S+~6Kx|E?UGJ#X|R*nJyN)kK*v?s%f`zL z0QY1(*`OjcP>FMhlOH!bQ{I5Rc+-Byr|VhYCb;3`ci4le=D+Iic8-6ymd!T2oI*|b z$i2v?4Q@AL7YI-E!^j1?|82fUxJmKSncR?fTdCh!CtuQ;&;O3}oIeD4>@p%oimsDI6iYWXg^<$;UfUJlEkrOW!9a@(da#IvzZi--|-5#?%piaI{a+d zq%uKI4j)WGG9B|>67fEM7ZG{Z*q;ika^dYS$dqqnQy{DnQ*Cwy(^S=80NyD17GXWV zndWOeU)+F=XEoW#RKw%ezKjP3qZWgG#4iewq!igOuj?aVFnQ5 ztfpYvIIK|Ss)-TAz&QE7FdCXBe4Rb{xcRon-Ml6xrl!dG2z6hiM$FCD+Ch&#UW zVa;oJs0Ul(^?@5MWsQvIkY}&+X?}&PTVv%ZC#->U4#qbdF{G%0^s#q)c>(xU8UOZ< z#yP)4w7yctMXJHh8umZSDUDvjyrXIK7NHg|CGt_OLA`{u&cHS#ky3$cXZbqz#_uR51K*w)Vhbi>$VrK z2e$4@FgcrD*O*{?X2LdQluzbMrwMeK+S%bwL(&nmQduHEvD3Na!nyzN_JZ0&Q_2b+iSvk{e zjb;V|ISGfIlRL7%NqW@WG49p`f(+r&l&lUh74fjjmeWu%%*o4Y*XsXKIANS|f>nbPR5u^&8 z%_+ItXoxHYAy^wiFh%ivFlQBj(jU~{5Juq5!Rc<4H#InUlxFD7I`$gI4E%~^R)F`? z4a+E}RmI;W#Nn$hbZ$_xdhNiqo>PR0%RK~oLULB4R9uZXMF4=9m8K|ni`B#3@6zAP zp!a<_G!jN16vm0$UE3&uuXs`=KoL!uDp6;@uZHv5h$BZNt$IA4 zKjhHWR89x&b>eFj@j6?#L8KyRW+H?vVXpsP8ha44L&<8F!hELm_tXaLO63>jrz&^k zuJe@Y^Ml}8%#brT7~|DW3`B&>^S2>f6RCTdVho5p;nGc?neoqb{69H?34k!?M)`t` zkR`S;5T!mxrT@YPH0kh>6G#rFN8H`Rmf-_{V14B@ApPYxxlHJVC%3e#MO4Hnvf2-9Q%v=I8K z+)Nw=!O{q5&DqoE?6GaiEVP8B%L;3{a$<8_C z1Y^`@j$d-5fXiGzt>>fECX5(x!3Gh%Nre*b!r=Agh%n4iJAgwr^^dAwKf8k}KkucI zv72|w#60pdq5tsFbC#z7HCCRb4h$#`ohq|ceu|rtsdI$E+i~os>77n?3j0Fm#xD%) z9XdgNf`*^Ckrrcgp;J=d$F;Lsgpytt9W}9qD{427HKv8F)I8CIar!j>hA*8pFi=)) zcBM8=d>7a1e>%y?8uXb67)Fd2g#5HQ)G$1YtA2tC zN2)-)R9pzMQ!(XU-FCB9J_`G_H_1&k=i6O7x@py%3@hca7{9XS;$wDUx4SJ_REJMe*e$+fIFGnlc z(w4RKvbRc~vjW?9ifol~!2hW7QvtA!V9gp{Pdk;2vR&|C`v1y3K{2Qz&OSn(Dxc3k ztHm(8q$}7er(|#&y=w&;8BQtKNJW_DsJ(m*F)T!oYbXVN+d_A)0^TJo#ix(3=Gyu7 zQ3JM;V-WE2Km|0*_qbUGSlwy2iG}mwjS?K#?QDSoY1nA4KuIQIx~cPi@@II32JLC#~Xj3cOE$;G1Y);fdrSK=5x z^hqLHu!q#!fxHoGvXOc1(w@GKc7glfNvE{>-mLHE*5bO*n@s(-_e(l1QTbeUCZ1mX z2A!yC|3%&ZZ6=u$&?5jIiFv(iY^C=;^Q#Vhpp63D$*=!5zXe?+_&hnt>HlFfg8Mit z9Lp?w7?UzZ2H9PA>U;PNUquChT1pUmDUO>{ePrU7p2c>D$C4Xx?+#yF zgjbq8LIIWAH(Jz$IBA?5z}}hzbO~>HIO)@ouML*v|EyUS@#ec}7IsWE05WA^{|VP6 z?b~4jz=AxcL)g5x0S*%vnDM@Pq-7cUzZ|FK z_u1!^cm?Zroc{2hr`r*JID%?JQ8k3T0G&-6JUiYXa>OxTWWsL_d3`Te0Z##sdkqwMJlEvrz63e`WC|!K8lq`QFY2E3*5&nx*nD z8i^}d-7+0(Rb-8NxBb!di-PbWUORUZ5Br=O{M?3sA_J(Cl zyNzsVO*dvh&lu8uf7k{2!*{6g_iXeSE3gSMGG)3Jf%N=<}xx$ z_XEYt1t8~g^IKTvaNYq`+R7g_fgX-ohxbEy0oHy|>{TdmH-dk}gno)hgj$3ejQGiG z$3R!i9K(167SNBku4Id3OlBJfQRuEeejfh(WaULeuuwQwUx*S;jTzXI)Rr|4u+P=K zIbLQn+WP%a&%mXR@xE$(p6r~`ry;GOStZeAHtZDtNk;OK4X3HXrIQ*THc@-*75P=u z1q4C>m!BeE_f9a=`8@K28`9t}znMO~Z*j4P$2mU`JZdKO9iZz%i9fk7l~B}n$1<(O zgc6f>on8SL9t z^!f5$qNp8dAzBW`@{z)Fi#_~U_a>0h{}tZ_zP*%qnz7HMo7tCWa1x?a*9r|{!47** z4mt>cu)e;zX{Rzv18N-O=DBD8gI(OS45-MLBM9g^OgY$hV?-}8l=Wiz!(hP-qAcYc zlF&&Il6u6Dg%){9SNlEaGgBfu502q`ua&**e|Vu`&Fs9W6lM~a;YdHjbMFVako^nh z%joNcIMT_D5#)QBuN_S_E|_Yt@8xTeb!niXvYg&XVv)a1$F?ZXGZKuY;oU5=40i zW<6u%P~9)S*aF6A^}og>%e{@Mp$PLN@btG8?h^4*#LcNJ$kH_=(2c1gVczFQm615G zGCxG}Ri(juY{X*d7(ClL;|y{gp0o4M|CB^mf#_cMt`+ zgqm)S3Cst}2ONa)eirjk?GRx+VSQ<-xYrb6hT(#|u;6B$OtmR*C%RE-ildmOc1^QA z>8_OzR7NS!I&ml7t53ZSr4cvF`r*W8%rk{3ZT4gKbl&q2Cf=Y!_Uj@`>)j-G$qHTT zM%^WRNkD$PzwxFkW3t>;VsEzi9z6FmP1J&_z!n`n_J&G+ly6h_`Q+oB42YVLe%KD6Dp9|rEP=ye9*Q)#+ z)rheTVy^+pP}#i=A>3ra7}T9?ji>0s;jfG$@LnQs&cNgHzj$vUUN1r)`PwAq?0f{u zvom^NoP-L+ayv?rO$a*`2S>C;xm_Z(Ret-J4m zEjCG&@*Wv`!}i%%d?Cq@U*NDb)sWWyHtW*Is+~8e!)cr3eK$Uk+;7U*e(WZ$LlFE- zf>6|fYka0x)5x>Yr$jXoH32!8u)q%UFx47v=e|XX?VoZ!vfw@r4?ujX3@xVfw>frS zPSW=fdy-kVeOclTs4`p%MoWSZxP?i@;hz+uegWchtmH98n(byd;i~v!g`ImfGYp-H z2~Ao?n6v_@M>xV;5vr+Qn>`{(?uRfOur9BX6Ahq)*ssc2LVhH$ z6+HU|r`V12qVyoi@2U}7H6oto%fCY+_T0^psShoi9j$G~j0k=#5%~Y2?eTedtO9tK{VpLez|e~zI~9*82ob3oq@iJpwHDNk3+c8NatyB7L*4#u5=WE2c+ zHavGh{E{nw!S*qJU-P~;jHcPRarm|o;Goq)3J9S$Z;8P?IQT_1u5QK%as;9csCLwKnxW(}Dj4|E>tdE%H7GWRu3=oH)wxmV z`(H&GX6fRelTSI@2=vXJiHJykDmU=*U7{9;;e=7|xo=bZO5!7M^2#TST$cN&h4dqtGns`mj$+= z3rFiSp*CIC3Q5yD_jTs;%JX)!2clEa;af31hpWS7L3`>#K9!3>%sNqyQERthTLSMb3 zXpXNpV<~mp?1^oYK+-;yuyJ7ws4Hx>$vlKI`+e}73*{@9)u5sFi>M9r zekvIk?h>AuK;2bz$Y!P$1J4LxxY$U|M=@_Pf|x{KolR^I;ZsdX5=gb+xrMnxq96+Z=M{H#R}k|JPSpHhpB-=o*mS=nvJ5 z{m3uebQKyD(>G^3YEZX$dUJ%MbsPt2odrY3+9IdSty?WSaJLVP1xuqUZU>M4BBw8= z6`XinLf~{Br2ZtTEwyEwM;FNB=A6oul;?HXJ^01FsuQgr{Mlf$r6{&daum9QK1env z$%^qT!wu$oW=FiR!e)YYqs9?#P96gR{U!xVTChr0nQaR)B#R~ETL#EVJ>jh~mpcCs zSziJU^&b5_i!t^k`_9-Rd$NU;FoRTsLPS|=h(va2=86{UOp*|~hN2QmmeB9EA(W;> z5>t^>BviD%=SThD|NFcT&waX{+jZuAzu&Wd&gbmkgsH6c<1G+!bznITpp~7yBcdJv zpJ2a?H8B#7m#17|g6QK7ty@Oj+>g299ykuwiB?iQ{GLAWEG>E%;<44|>b3E;gY&!B zeI+g!Me?!O#`>fG9g*>WH8z5DZOqOgOHDkm=sfX8k)Tm^C;!s8VW5=E#syh;EpVLR zAS(A`4@vf5%%{`&PNz=1x;|6?<*jm)}Nym{o_h|AF8mi_A*(~wcv zzvh{*ZO^V-H(R$X&bxu=kpLIJwJ2u7IEiCiBG~_8V6Es0`Y0 zABxe&E?p7o0|JMfm}N@kKa_&~eJx07ld+M`t?#+9s*WR@+12e?n;U}`MXD_}H6*Jn zq`a`2-b}D{h584({%aR;FlqRTg(a_>fn&CPwX45i?Y}zRZKyT65-hFr;C$yI7Pq7u ziZg31!-3otg)8A2v6j4doXVLCjo?ZsyI9w^WWmTanu|-v~8amx@Xza5&>;bY` z)<3}w*3u*uY|1~^qE}OC5j9~GWRJSpHS-N)oku%A8P^M(x&k*`O7mvnp?9#QxbI#g zZkXVq?pe8mo~G|z;{@iTE^D7X19r81qziHOo(Rg-brGc|bfWf3<1Kc2gL6JV$Z-@9 z>3KX4)ip;oP#OK^++iw$B3~E{=^|A}5vzEdJqq%$D)r)4csp}-*k_AMb)))OX4xC0 zF6(sLe9xS?jr}FRV9R3Pxql_3^c!wsK6~Oi#$OHkI4c}d6kkgG{0>*e2gsdPv;+ws z-ZpygJmr9TB)T^qzIBOyWY#Zt?HLqX0pGXZ8vt2}##Boq4$VM41ioXv(SxB$uGzB~ zn(2!ZQk`<;7VPG_WQDsrN$LCB`@8G^eqV8*N06bgHGzL#mFC(Vmm9G%(&wYawCIc! zNsy`QLEnFu^eo?}rtKSFBAidCZ^!V#!s}v9B*&5TJW1F#jQ(oQ`$-(U|F(ZodKL8b zes@Y*3%#%2+u8W57l^a0n$}%-w(u4C+7?y!wo`=zI7+%k%aueVrs6v-67)I)Gx(lO$ zfNJ6bUn!1$6&ne(+;T}sjEA03>2YZNwT^*|Tt=e#cu*tIhAB|I6ewp1{b3OXy zLdsKUBP!HPvC|)oWZ7s^d%7mF!`JDLnU5cqxS2gV_(m8N1&JD9Vtu#Vi@fj@| zk4OQ*`iRF%$dfqMrTO1t?;Y(t&x|D@AHnrs(V@f0RYPEPDs77f4 zNf|5MAw~atTi%-^yT^FAJUE($FlN;))m4QuGe#_r4X33o$^>j($C7c6|I|mkKG~EB zLPC0KvXt@nkq*EeH8nlCREd%omQyAE5#({Qp3CWLt8H(Mlh=M7**#XBj`Qqoxa9b) zq0*;aH2G%x*#@;J-bgJYWeO*P=G$@2>n?vAQ{vbY)k#mRvREVxuF+xW>G5=rglLW1wazXa&(byf&hdqkiWsXK(0!~)fCZ9n+w4=iwc^pjGsG%wH~C!{tu4_AEX`nc@3YzFr`}lKX!|q zfNO#=A?tO76gr!opscf~1y@sJYVa)LgFe)+I49J+J&Kj5rn#tVdd$^X$I%!SIKnJ-@vsJMQ@_= z-rFVLa$PafVkVp|aLUoScD&)ot67{2aaZz@=v=tErFS|fpOa5Nlhq_$&qkJJ{sgAAr-!D6uS)islt7{JL30w4TPrW($;@(tE*tO%9v7b*HcMPF7I;I?QKT1kGZbr zWLx`X`aUVFfaDI%ogi3ISF0E5RL#=-Yfo$5;moknx$OIW!UzAV=NH}!0ulSufF}n1 zu{*H?*x%V63=TP1)&KU>bTb2u896SVeC0~rIpoNCuj{S({;E?O-*32%kr`-=Ud@!i z7xT3$Wo5!;DzDsSb8;mMA7v*JFUX1=n?N?5J8|mrT)Y5Cly2q*AMP1=A(|fCAvi2Z z&_c!D;@+rIPMwp0|7Pg`U)Bk%ggM{7VAS2Q6h(p0oc|8R~q+o5mDL63c?HuD^$2LzeCmtuM%N`}H0~7fz}r4v#*tJX1^V zMY7b&EDuN9IH7f3QKBw^%ugG$*C!sDSlxLi-7Ft`p&Ca4WDMkX#Dpz|dP-?KT8I~< zX`DLqZPN#Z#4hnJVG)-4EK~<=)*WM>N@bktj&21}$hQ-JndN>qX%~{>%F)9E^DSG`HK3jDz)1E zw-fW%sB~+w{rXzy(Chma8?lMtmYJHI3%F*Tm+}L%(*>lCjIm?T#!^VIMehHH-FDp| zXf^uW$<_O9_H?X&pa!KT^h7jh9f=G#7<8uXd(Wf1G?%79d-#)Q>DDxgqIh2l#Z;~n zJL*tvL}QygIjE!v%VbwJElqmb6GF>xhpkjW4^+Aj=JmI8G3!`9_y*(;C%`DIaCeeK8Hkg zW*6IQyMKT;JmYqC|tGtB_6%8fw4mPS&0tZ65-k`d<<$PZQunO~#iS#K6^{jVJ zze1v@huKgUt(2;6v2*YHMrUu5?85F~jtt(xG@94E_GR1jo@P_zJOUS_^aw; ztbSLB>uC0j(mRq$2o2JWscPVDs!p&~3H9rg)~MR}ZouT?xwAwH(9=mSUvKscFo{`y ze`2bs<||NArf{aaaPCY;C}UtL^43 zp#{RoKQDmt&X+BBQ=x^qbW$2qXWXEE#~GYk1)&~#Qf!+!L&UiZ;FswUhN>BPLJD*P zNZs0CR{YfYMvNs}0J@Iq5Z;BZoH5327qb+a%+iN&A!p~i;Ad-8%7`>kUvQj}hklv0 z&%e@;#x#q0+Xd(j89)s9KJQCmsL?5KbsnR-0Ka%K08H}6zF9)Nq?{tH^ zjjZKr7~^D+6EA~FMMb){HiiEn_M;u^UcMW5*RmGB^f+@NNFK8B=E|~z2EmZrbA;x$ zbujEZ^H=kvn{s41zTNLM3BNXrThVpM`!%+#OHuhn8vXAmBw3EEnQp`GjT6*Dz0yI-%c&EUkD)WOA>a-@rYy zd+ei5RJ2QW2gH=LeG~H*ooCc)jh0=nMpn}vx&c=e4C`B-_GNW)rf=}0hV_P_;15f% zVNeqZQ?KQzvL2_$;YHQ64$#=f)tW9&ehT<11OthNLXi}A?lVDTU)8t|)D8>BiHNBD z)96D7=mbyCvE4z~nGlUESY;6@i&s>_J4swntKYtnW7M2RGH(oOCZZMtXUWY3rgt&E zWlKe=cuXihs*$y{dIpYos$FI2g_d-YG;gnlZeGF;-%-ci@_VsX5Xpu?|xE1DiYB9Fr{52;A+PqLn?7a zDB|^rr)dC1bygi+|LyMSCZqXiOCti9wHjils-{0`T27seK}Z@^tv}US_q+bO18FREd0BLV`<>1%kP{kI zdk!%SuRWO!hIIby!;pZvu14nXIiYABs(nBNU0uhCDMUSUgPqznnG!-QP{Ln5-Xj8e zn7WqZHqVNt;AhECA@7O&GFz8h1a$kdDt~&-SBrGswaE zJ_BC$EBKC*F5GV^9XT`13lLP6#|-DBhI$V^;AAw`&GJ|e1!kv35!>M#S&b{sq+VM^ zIxNRchYD+WO|TC2_=^;QRD?GGZ+J26yp`0VYNWi;*(h=Foc0jvQ%{i;+@EkB9zgDN zc9U3+PXq6EYVWvu4bGl=Gv8Zn(<+BIV!5Wne=ABhpJn^_i!(J&oj3Sjx$jz$HmHCwFRvq~ z%nQR)d|I1r$+o~+h!m{n@FeE0cn2YZ+xaf4L-)wGU;6qN? z1u8rax zSF9!1jcp9k`8lYQ|MXzod?A91fdR3{T2F50(a0)U>mJ!%zXBs%R+uubwd8loCla}dkV+_fbFV-wY6~lm@`TPJj@-TM**xF| z|G`Jgq^Nr~Jq%tP&s z4_}Il5Jhq#PHOD5cge`GG6{C8uJ?v@r8--+uEou>EUE*6c}|!-Oc;) zQBWtnS76HWx=#0hvv$E_)NDiM4`EJ?0Gsrrg46n))nEK;@pXc<&db8kc=>qw1uH{{ zp0E9fw0dx5RRx1&(H@St$ADYRN)v6KY@QJDGtvp(^keKJZ?Vwxv8Z`C0JG>>wP-u# zbDafyfd}4?<92Xb^?y5lx=y2bWh{h}vE)hU zksO@I$g#(|m;Be~O##v@59Ri3cGTxp?6IPy(Rh|WP8bP+t7AK%s_CRK!^6sh;@(%x zT<-nHLR>uJw{r3e?+AaZ1mG{+xPC~HTw&FZrw3!F4?s)R34t9A^z$nvXwdD5)-T<+ zvX5v=67Kb&9(;1;_0{jL0W|oOkUrMakHS~Qq6o)u-|K)AJ(rvh#Ao*KhyaFH6N*DR zYQ4o6CtP&m_bM$1D~vdW-^`AiZ-GcbztNH9=+&M&pJ8}l*Zy^8yW4vNavhwrg~?Qt81!?%-V;I1N0I>|k&=;;GZD@cQ^<41 z$!F7+Tw5;euA9N|=R0y#_SOVb>=}pg@<7Wl)pC%0AlQH1)0Ros{K1v3E!`_McBjXg2$g(Mn0v9n1mOLSA#7nRi zV6^Gmr7t)-r~k%GJFr+Z^*+%|d)>{J+yL&@JRTa?f}4tv0!Up({1ze>*;C4dY2J1r z>rJ=V%xm_&4SH7$XAaU&Asg9h$09>6yeeohgMK0>-89|zko7~%LkuA$`~dL`zoGGJ z-Vp3j00LC~sDYl0h-porkFi4xJr%F0yh|jP|Hn2v>NCE5nRq>#H$)Cp{wFeCPXxPh zJ$BT3$S;-zzI|EHLZ0G$-HnSDSKxc+=xI87u~IDAV}qC<7#I>K(nZRotE_+W=Mt11 z%B$qP`1E)}P9_uaM7#_>45(J*%Z~Dp)oJG;R4m`6wBy1n1JX%mdWf@bZOu8m$0M(s zNdC7m9NCyn!Zrwe{qXteQ%SwsgE@-3dJLMWOYOYt#;K&$?u?tHEt_DcomQ>iMylL% z-%LH)WweOKfI>BjRf!4R&UHCVH?yVvG_|~W1sjoA0g#j2UnY=ll8v9tl$+_65y=lV;*0> ztX3z`aL*keV&4qJ4Fj`lARBros3#Dqxn+s77vfO^ZVHTz>zq+0{CNOF%~^JWGa4ZN zO+6!i9k~_;Jc0Ufo^OCm7@ypW!fQ|6H%6-9Dj9cB!88Y?3=&$JoS>hp+XLfV{wu;!bxSpjg(J zkz}5)u8yCLo?AG)k_mlN{nVrbVh$n#%v3%_$(LnfmMeKH*$WMP;*`L?xZNi_>q@UQ zfV*Lt{$rp&Q`yF4^JzO+h)~9L{(9iX^<@$kE8xNypbH<++cZ?Cg4IV*g2jb4xlS;? zyxdaYW=i6Fi-{tSuY!-3b!`$EN4)I{>u6d=5({BmU9NNheTB0+?KE0UTNHWl_QkzW zQZzGtIu~PZCTS7>@xYUDfB;mhdZq^LK5^DE7hxcZq?^VTe8Pkhkr}Te9rQi)h!Y2i zZzJx1GL_%DD54cHwa4d}tFVzikBESg`mK3V51+$z!B*XwdOExAGI8O@$ZM~XE9XQh z^1A@bL1?Py>nH2(|KGTRs^QDLP?Tg%8fcul@e{@f(bwXUeI2JlG=s3W6F_z8d5Uom z%AHf{R6BJgn(kOe%sSGAIMH)(ht~%e@LuAP#`@!)Qb{z8sd@-A=adwmKYzFmb6#z# z1Ug|ow0*DVp+TcMndO>cB-}Y$N)=f6Lth_`YqQS0kfePI{Zjca-2=7*+g zug^FJ=`v|*GkXxz09^){C)b)+!GIhiS8q9ld^XH_eRV0W%PNBk^oXsS`dv-CHIQkX z{~sKJ$8-u`vW$fg#W}x&;lzZ0P0LF@2}eh5PQZ2Ii{RYGis5tR;GybpA3(=~D683H z9v}^n>5J^fst|1Lf~~NyxCNWcKX{AJMV&^TT_c1!Z!Yej63yegqJv#&k< za$Xg9e64vVwo=MHi0WYCP>^5!s#n|uc_Ma3Q4Q}pF2$U}U6Eq4x6fnEC5d|@&_WlD zXM8RB>*oX8!0(*gy~J)0XBF#U*hf%I*U*Ic3o5^8jzQ`X3H?NWZt?Nq^%fS3cjYYe`zT zi}KGCMvt@s*-g5wMphneW2M7R=>$*mdTub5Bu2U;Ise7pp0WKP{t!=#eNdQ?waeHu zuQ5W(W}$=I9e5X%#CdTCS8q(!(b^_;rsv~IUab{p6saEK1@~hg$2$b+=JIOcB_@v+ zyz;~z)8Vtr{NcEf6-!Zxv_C+9vy@mmZfQ?F=&!Qp^OtB#oytE(l6Ikm?gBCIjk>W%nD^MP3m*pGM5Ept05hkh1Ix+|u!>!2sQ6dYvb zxp9v|yMSt9O*pt*6rA7j0y)npf{-X^iWtOXz5b3?JzDqiln}o-==xg*tQnFZ;v4ag zbuQ~85=bC&5|O^1Bt3XP)}3K$gtg#X1|~_Y%u1!dXV8!p0rXuEHYn2y9|{YH|2Sc% zKI6uIAr`P+=*Pe}+`!bF>cGUDBt#~<=fvW*93yg=&x^tmFckUqrMffPueoelOMVN| zpP~^>)P{HvdsLOu)yMxsX!%l2rHJ?v(6XDlAyJW$r}!1pQEI}XZ6CH(r7Ag?A%_I+ zjVtE4#G_?0`;DH3nA7)TUwmjcHIlhNkj6fiOE`_|M<$W0LgkXIFJ964S1P%Z0fLbN1?_os_-0)+Js_ zS5@^K>4~!B-p=WNIR?`ABXIxxBmJMW2u}B-hT5|iL01irh-n->-*)n#qnvK=av0NSnrX=3w<*q^+(U5kG$*LeyoX^{`4YgK>^>@*Uok+W%&+1>Wl(G4c=-F40jRE zSmmeLtu>OPG_2aP40`b7TmkwS+K12hb<30U{Vk&ye=JM{vK%)n4~ptoCeqhy=$uW# z%#$j1;QNlm4iJ#ht#YD(>n-K^CaX}y2&3Po8{%`^Q%=&0==Lj9g;@8i!v%`2#uQ7# zvddG`cRpBH?YLo~pfjHS4qAQktJP-ofWf?>oGCcrx(CnlP`m7>I6QPW__hBVbU((R zDCGo7Gze-tWk%P?Y6HX){F!=r6$n-X%$AR?YUesRXP;(Nkf|-7)hhgzr?Rg?$f7v@ z*)-BBQqo99cK3&$l1+*hihGIIsXG(6S^@sfc@ETPezvDd8x6P_K?w{y0QHDu&s@$o z(CR>huPUI~0VhI$u_tisfo${Da^^UhN@y^6!*@c_|Hx~6Yw91+Ec?+Kj5Yp4jz(6N z@bVa^Mpgb#fhM6Q6yLmEV&(vQ%u7Jep2fK=v!FjiBv1D9w5lxd51qPAKg@15!<~jI zR;q?O7U&J-XEeFK_xG%b)HV{94LV{%u0Hmqe8JDDL^?p4nviUZ{QJhhZKLzKrPty3 zcpJCy+?4C|-mzQRj``{oDb8^3SiZq@mV>m@!g~z#3t}Zfzvyk#4YvUrfvmGBT*@Ea zecR4mfmR~r4<&%HUeDruJZh&xZJ81sdsLIGxS*R`kt~hV26vRIG0Mxe=IU{s@*axY z5yfQB>9KUH%bBZov^?9umMS>raiX&w_;My+0i9#|vxN4XycPB_k|yF5YFZj1VH~yD zIO=1u^+kBS&5@Q#*-4Do{E8|Wikr(sD2X=AWsnigbRNGN8lI1qW)djXp6dt(c;N3~ zKl<8Xr>tX{f|h>!&xtNbQd`zw{fVT+hOgMwj2x=%Zy{fT_4;o=Bx;qL#P+hSfAJ1t zVCE52*OblME>7CBV(^%Ua;YGr=BY!Eo%8V})#=jkk)NL0EL_cYG&pl|&l;8oHoPrp zhxCD=rqQd#NH}s_ZOc)OHl`gEbwOXIkk5UF(&BSIp=EW3K9WE*5tPKlY!+FY%PJ>& z0Z!)VPS^P<>X4$*_Rp z5e;FdKSo7f_#rH&a(=;?>V)E>>yK~<0BCyoea^nLbW>@do;o6GM ztO4b&ZlJmKskPvlmdhND4pzUpfu$R?&72r~LQ#d+(%=^3Pxrt%70X=>GQC?ibNz)< zfG9@i#$pTUn;{s#iTb9PNdnyCMlEg|;=YoYoHjcsRLrYa-qMP>hK0BfTb~!A3v^t1 z>1rA5tudbJVGnn7IwTEz@;!0}u_af02L#oPIHi+2TAI$G50k~NmL$!cU?dIRw^8bt zO+qbkOjNv9q&4$3U+daGo>Rx+8^a&Je?7~wLn{EYbg~{Uy!3ARU6AhjEKs82tpTiY z+Ln77*}>$z$Eb>0r`7O&DYteb=GR4EM!xF+U2OM{qcN?!@lc{M9gmi&Rf*KExPfUB z_PQN$pG14&m!x5c3DIm<}9}XQ;~_N<|(ckL}jg@_60u^dlzAT4Zp77)6j6 zq<%=(W8HIg!jFbdzG}v-R!>%}vi@a(3|w#T8Oy&r+-IBxWH;<7n84?z@_zrO(KZ*ZSS7C9&F&5$!d8MCAhtKw@!!!xS^T1T>PUS%rc(y`%4nXmv z61jrfGS0B>qfE6F&U#_4FNOpb=9-44TN z`3}X`gK;@Q+#5(25)WWo9DzN=vInRhs=;l*YUTLwYp%!Tkt2D@kZ1rR-kZ0W-x_8z zyCSWq_0~MrM>RapHzV8F2L+M3Cp8bT#H}r9h#n_+9oLlm9W28Ep@*t4qmQZhF31gE z5_GtDa^EqiuwMbn1d*oQ-LY0XtQPe+vhrw*F!|XbMU2bA9E~xBZJAk@99X*C3>S;q ziVVa331`NGnxs%r(KWJ2)pf~Sjv5*(RQEdqqShlJ18h$9HbYg-QJy6mRD*XTn2{({ zlQCGN#tK$rd9<0~zmh6Owdr5CB-JAhc{P|~(My_VzhG?f!-B&+LC+55;3n`zI=wHE z^Mk_B`oHyow*((@J$BYtsD}qI63rVcR}>6!?mm%i3Xa9wz^UIk*2mv-zOyx3-5XR& z4%05pWCZdOEwy+?D1T6q)EET}i~;C|{dq#YX835jB_a*Xe%aPyxvW6VT1l8oZUdU1 z$uG95qRFX4QSwH|IcIU!n_>#EiWo)FeL-I@VcRjo&a`%(WQD=|?>k94TZiU+`-gg? zjZGvhWUE?j6-e$yH<~I$BAOur1Ig;d7ybBp)J+cmK6EDTLC|&ZcYRO0ZFlT^BWt3l zq~3u^O8e`Phm0`$uwOQ^bOYBe(`&%dzXuV`nHoCYBf?8|v6dfXJcL|Do-;X@`4@N_ z#$EDiMBlPo6nU-r6h%P79vF8a8b^J{9_IJGjY@c6NMXV4J*gx|?ia6xd{yg+GWQk9 zHGFa`f5D`&$2NZyQXt3fA-*|#rMaQ=bwZTt+^2*ODjf&r^TQSG0Z_8^j$!`2#EF3| z6Q@*Xru^yVQ`aO)7T|{=;^;a_9LdSk0~7f=^As}Hg3ivM z=0+4eaKkxKou4Ll5)I`H&LNqmqIWT7k!CkO-az73_G;4|cREQ^j>p{9PN=_p`ix=t z>t?Yt6d1=iwPVbEW99XO2V8`PO>Qtw!C^){6X?-4GUR;|=Nc#h0iV7fdW4eDpH2w- zbIn-4U=W<|0+aw&;r^JH&}cy;g#~ed|2+0kMYRNEdmakk=>C}BN;)qJ=~wXU%3H?i z?m90#J#Kb0cF-?>C|SjD*XcbNfV}~n=l?cr+O;~8*1fx=ywerBHsj`43q$0|Q@4mN zME`g0L;@IuV@HsOIwm(|3(RjY?~B!3UOG4X z6o2$;(mTe__`BpNWr`(kD;wo>GT)}^xXL)`3_62L#lbzGDX~XebWkd)5 zOXm8KPl+ws30)KYFVp+1uZ8wzZR(jP!psagjjEx`#P+(M!R?3oAMJz7KlabgsMn1L zwuwIwH4>*dr`lnwu8-w%&Y`a)^goOd0Z8hJObgLZe=7~yri zx8X$9C=J2JGv4g^wMwYyLe8uCdUhWa!o43n6qewi@_QG_NWwy<(th!zmqi4iGwL`-b?n*H&Yp#scm2ITJD86=;tXppOG}=5fYOA(W0#6#aGjr zaOc%${W?{3Jl~9Y`>o5CQ;W2rY7$I%LXIG}@(q5qUp{>(*Le%-YpS&vS$0V)qqZdB zODFdRO!p_GWN3IIm)vGA`}O2FBr*i~TOr9T`Wr584J$}YvQ8TyU47ANeKsw zWk4ka3}@6J+|eyf2@qQfdh)gv(QgtT5S4-B@!&!!#Mj=5^fhE)wsxo%N#LIKIG^L* z;>LNOScc3@bKLs-Y*oN4E3(d9=$gQ`LNM4yEh!#3k=J{79Hk=ir6(D;bPGr z`n8Z@a@0jX!1y5``-b|7)~R_ZDwx6xZbWLK1LEkoIeT_U!2147+u4^zE;QRNw;*O- z=+vfp_{jUTZZ{i8Sy9K8B)KDy0cl5dl)P>(4;M%!?+>_;y#X4ZlPe}qFw#8obkTO;5+sxwQxMM1~>jy@Ag;e^p7dr?#$s*>_A#@v0v$@J!#H-nWTNYam7lErDKT6TBZZlv*}H|01~X9k-$=Wpbo?6 zd-;-2kV-=*-yGR8mi@&VYRgms!rL51nmsdZ` z<|IBlFh~URj&ZrwS(@Tm5hx#*Tvw!hlr(`o%rNyKl^XE6?VTRW-qP{as`3Cpxiv>O z2L>I`vi~YbkUl*37}#-j*$KvnB~Q3&8=WPgi1TVc5gjD~kgY1e2XBV^4*lk<$^|)W z9}}$3A{5VIuErZ%jJpIkcfOFXI5SUA0j1-$5wym&%IN$I!JNkIs1hM=DNM+0R~(NK zn^8{xXr9?~8#$0U%s{>-oYG@eKUim4TN!RU(RPivIUQ++*4b9EvTQ_0a?vBqJWj}A zy5Mmq4h|((Y*0}>!@ewOZ|>db?6LD`9YL>>NqT(TmG}i>d{(GEK*=@zQ4DK;;YZvA z2petA3hmW`#w%*AW3P!ci8qNaKpnl}`2>?SaRY$@^6v=oNO>?Jg2~cNjCV5~d2e<( z`ZH$lXXh{b>hQoo^!DeHW)P8@T|Akr6)ED7jjM-fZ>@mja@mS9vnl{n z0EzoU%#6a4KB+5Shz#=qgp3-~rLOsq$|Yz}t~?bi2AlWUPWi3oi>SO1eC?S>rwJo> zXIce2aT1Iw*0yHKqM`zqxXKNm=A_Lzf^&A=mwr zZ$wCO2Te`rp|`durMW^hKue(C6>nK9?*+3XUhhe^K9|&0ND166@M{fO%auM&hjo!= zpwK_j7CPDi>Wl}wT-_Z6cF!6TgxDuUYiu$`=Vu{3jPaR0u+e$WG%%1fv-;lCpLuU+ zP&C!ZU>BO4XA;8O=EI*oXxJE{nw8Zkw&;&o>c8j_w)Y(xxnt$g|1Q1rE&zNYiui}6 z(icX-jH^+ko<4Q?ayNYJJ-3)|QLKvu;8q6e;sNfi!wc=TpG@|PIf-TFL|7kVjyK13 zGr?|-Ga6MjT~q)}M02_c9ZXTxiqWHV>zQ!Q#S)I>g-vaUP2L-VbF~MRCcHAP;l^wfy!hM2{;+v+m|)UzU>W~3@^Q9 zrBQXuz~@fs(|+b?eO5BBtu)Nn?$9{uu_KB+xl}%F*lSa(2ty`AHaWPP8>#w*^yM_O zT3>3tCG z7iPX$T780GAwavt^M&y1AJXAZoYMNTijxU3-fw>1)?U_|FtjG6@awjOiym4ULJxZ7#)A87Ji6}W;COFHauSZ zQjC|O*6+iDhZ{Pz26>GTnVgS-NCzW@GufM`MNN8x`MRY=%`rj|6|%CyyzfQJG3`Ju z1vi0voqKN!y_`>Rjp-flUcVkexi>McuZB?0( zF^2yk`U_e$%O<9}7gMWTa7?4Eea{~TbdJ|Eksd+>ArX$*4AQneIskLv)a*kK84}-} zb`pJ?t@^hX+J!d@E|9ha?Q6?Y3SxT94HbD2d-hGf=@PRvzeQx;8|lC$XOc?8rgz&i z60R!1khjN_8NLmhc+_P60WCn7Wh&5aTH3f@#mUxAc`vEn{I9nKY@iUPbUkaQ|GHXq zP#BSyu=h#&XQUB&KkM(mc5sYX z4XUF2-i%gUrpLN+U;2_tfifeGxDW<1O@)l~iOmJ3Cu}#HC9!~DA}Vv9_pLB<}LfG$zM=XVU7ZqGw=_6Hh%$NW2OAhlw4ZG$NI2&8a3I zl+@1$nG|nePrNdC6Ml3NW_D`+Uv3oV*8(=U4TzmZ`iA!b#5oJPgbrjbIAou=r{0nC*JNa@O2_kz+K&W`-)Bh^;{e1lu$HJm3uVOWQ zUWhdptYdaOcx%~c#jIU z-mv+AVRTeyBrnr`H(Yw!wf{-S{Inx+tZUJJK&Q$+etpKkR7F*P41vgLcUwiri3v8OvoEqBW|}7v+dn01if#9^!LUtDc5$aBk;^ zXshDketHso!H^s0=6A?3brK@bol^4IYZ-#fYJ2sWH39g^PYTX+{&H)>KMae_NTu&F ziByhUQ$8`3IqdQQ_R_R5u9qk9H&w=nf4yrJL5kv`;s|_5op|<(`mE3Te#w_DI)^PE zKcnCCT{?W~=OHniVET#Swy*KuhS0P_K3FEsxQcJjMvE+x#G*MA)pzrtp&$f(8sB6B zFaSMDyqv<-S+Y7*OB>>_@Gv?F!`i7Y+A5)w03apd?a*VR#w_sV5+k&1O3s_ApwTIW z_czRl3#$UwrBCb!%F3+FY*FK=n@jh>yurL~`d@?W`?sDMM~N`Ls%s8x3~&n(_3>%( zTCuCEPt~QS@WI1J1+vxUxyQ-X(hONr_la6PB=x|mZmaC^ukfV8cHa4JpUxZLBQ8(> z1l>=azkazN18}c-pFd$vR0404LKD#oYBob#l~5C;f^`8;v|XiE04irzrdacn$~Uno zysbsd??y9x<*Pbs!l9i~6Br)_l*xh@S{w0)J3DQLBj0D88A$)t7Vopii-lon zX&31`PMc_9Su|et)IeIoi#mLZ^$abkQ*M9Sa3mP39CR#rY7?}x?%8$k);dDAuOr)Q z-M%K9p#8^YvZ?d<9dsLjM}V1L4jwBKaZSV~1+?ZV=rMc zW00t`IHK^fZR0Q9Cs*4mGKyA5K1ZvpQrpcp8ieHL*%HdG%1-V&MnJ6vMPB{#Sw%7Y zo-IL+M~y;MuSz^_+0aujGLq;w7-;R$oaN+P35H(1p9o|| zBKvWL^rPe)73@)?sKdO&9dGY{`o_yx#b_1+7@HiTr8{BdJcs&6ie|ytiI%4h)^19B zIy#{qYAn7GCBAee0uJ=lvIFIF&Yf7k5zb9~X+oDU%ExEOgAYzX*H4LM>S&Nc|0?x0 zz-J{#3@>bF1L-<6LPK5tq&Ac*Uc9Cx$`t?}?==3C*5oO!g5Lfp;X0%hu^qNBV zIlxlGmd3n?k+=Sr(RK<(WGicX+ONEz7U8`(x8V3}dx-&)roRmQigQd564Rb*(^b+V=;yplhk zgSm+6jzq3_fP?XD=ZN79`T6;0=^9ye`3{QGcwx2MrX)aEEZ=DPP2KdA00CC0-?|yY zWiv8xmH#bI3*8OPxjAG=aA}a>Qa{6{p#F0HrnH0c1st9LliGhCxd(jJXOg}V`Kx^}Z+-}B)vd$mYHtti#%r}&JM+U3t=!5N)lez%BJD%b9q{w@te z(#q)*j{!5PJRT!dk&ufDEoHR!Va(+WFR}Mc$2-Nzld*Q4GjZa-YAT9?>pH{jpEtj+ zE`N4RLvw()?27Cpzk}5tm99h$9krpvs>3veMa=n#XZNJCYjvHPk zlFjQRCySRHb6o@WZ3gpKi{0o_e`ZBxu=vq5s|SPiXJ6ycejGKXTdl$#?y<(?Iz2)O zuLubI4GSLro9JI(J#Nl7j}?WuFbl11UnD-*=v?_cSL0OoSNb;gy8~J?QUSSRJNS!l zceV*zNG@1*9aQM!aSe&LCCp<7sLCqDN|UH{yxZy|fMnoS$V78xb5X~JF-7h}aG8gW zl$$WhF9e3U$aLFt)lTRguIro)%NZlg%P+Ut$Hjr}`JJq{@THM3>x<7TjX?dbVKSx1 zm{mNLxWg;dp=~SG1^b$Cx%STOXuq8k_JSW6@qHRF;0S?vA6z7hJqwgsK;)#%1AM-K zR*{3rxCF2n=%>t&JY^O_?XP$ z&2$-5`=$aR=6#P_=>0+7QmczM1s!I;+bWt&azU{%DEdw>dr`-=_{`KAxY6) ze`E@~jp*fhr2mZ2#pmZg;Wc#5`N({FRIp2zS>;e5k3+SOep7vfFf!^@(&6}Js87{( zYhG05{3T)rd40e#yo2E0?K$*CLE=X|!_Kpp@lSClWt-)Y3WS$6`5ak9G}8C@a|O79 zd0NCDniUCjKe*WC?4K)BhzGA3?DAB6r3hQZLnRl*X4G&KLA;B>OR7I-bZ2%|VZ1CQ zG0;%lTM2hDqt;5Oz9rBqM~%WCdqC#o2~^>Z1p=+dG5;g)eId*$Mi;qy4Z4Px6NdH) z*ze|yW>itSI~1JZ_*@cwkvDlE>NvH-aK?fCj!8tVTyiv{TecbaQjIe1cN-_C+7Tlq zZpV>oG4XU}7;jYOaAJTZG&NX`-ru#oIZs_i^TVxiKWw(-c(jcOD5|J;bO>ELSSRfz zcEWm#|HFD{HB;4>v+mf4Rg5(ow{3hoYL?v|RtX+(Ma|{Y-%ej=ibgt$&+@#3g3$K= z-8kihwejfTwbZMgLQMbLTV%O{Hh*s$?koN)HV1Iwhont3>>NicZ~SHx#_28t_d z&kpv*E8hqeP++npE%vMJA6nb}#4G4semDSdQEoFpOnB@NfIGbixH71nc^gJzS)mk& zP}sjOL6(l@!HJi95#0c(S^+P@4<4AAmfTO2&t*+U0PJX1iMs;5PRU9O`dwdE_6Zl? z>1@MW@W&qu8WI`iRpkG0d!`s{z-YjN8+OEj){>wEIf76xtqis{RT#GQz;Yu(u6?x> zkiUJ*V9M2H|Cb-JO>9#BpYJvM#tu}wE`_qyHjJ!|`Phpag6=;3bg30Zf9M2<#6)k= zidqrrlj*7fya+RYL)mMx7&N_O`#cgwsqWxIP0o{0*)Q>I!>$8tqo${_g_ur43hcjT zdFUKMG|Bz11MMUt72rgQH`3gAr-_N=$^__XTMCWJ*%{98+_)lck;btWI5=koftGAo z(Jj@;pFNAi4IS|{$Cy`hLpSGl?m~9O%zV}NyCldU7|(7wklT*N;%w7F*8pLrl(_P) zV^UXhf^1lM)Ly|evhND8#dc$=OX7iq4gbP?3z1Z{Z8u8LzlhwU zQ1rRQ_q@4jRQC?f*V?^k`llch3!|5?=Nb?eXRjxxN(t2do}eCgwd zT7|~Ot5v^E`wuIY|BmNg{dqp_SJsz?f0TS4bM^_iITDK!%O35itRhxF%zU&*MENb_ zd3*CwoEM?y=KpE$+rOGR)BQJ6OIwc8IaW~-I5RyR$HM^i2!RqJU0UI^7*InJE{14P zt_D;Hm&5?tv7A=HQyoFM7E=PrP5^}vNlbw8D5J{aopi>4?$2)?sWm&>&jqJDyKYTI6GD%76*jMi~CgN}LF9lgQ% zg%#s_3ON|d3BjzQ6G3#V2>r~pLPG5)w`s0Q`jm5bPyzb7vOEWK68J^TS?+s@=8uJ# z7V3%vPUS!zeV$b8AzIaTEMy_x;LMuy!77lzk#n>@{i(gfx`VaeFZ&7y=3e<=j&bOP z-pGNTTb>5S%Bb|Z(@E=u8Ekm2Q-7hWixZC%r!QzTb&8rAh;n&yd!OZD5~Hjzc38cZ z)LphBe>9=_lk(_wr!L&w2SLwX&BtJO(DHmFHH7(jey8MP;fbnMkbjh`&~Md_pOToRs!t^K(eiTytQI zO#xv#B7uj6#)e<7TyJP3;<-ey3V4+YmH#P-O8Z#o<^s3H%4;v0hRF+|%&3rw0l~0H zr@8x5g?PXv<|mvOF<06aWO|88QKqZhOs+D1c*)`Dzh-wLoJPw?VHPIpW`&4t;&^4ZB{zGB7!ipMA@BQk`^2E6iLuoQqoKk8L#dBV4sAVoZ zE}nV+=tq%*N-1Y+rr{gq^|#$l#1pmQSS9<=f-FQzs2EMjZNB@x3kIX}q6hTHm;1Ee9{8Xu>Te?Q*kE1xUGTShiGSQIE*kkh_K z+`U z$%zDCCkn4>RsI@7!lI6`@@i@8g_{y;Ta?J47s9#qtgo>H^eGxao@&%rP_1<22G^`Z zzaxA&4TbHK>_=vj=Kz4rqOQp8{J2LOkXd9l?q$L4(o(mlBcLEONZTw3Kam<#RPa_w><-`|G6t-XpsYJ7Cwc`CUO@*!=c+x39DB)O0iu)IRnyoBD2cJ=>SqXU{ ziCb)bI+PJ2Zi{wl)K2lYgR%kDmJ*Qf5TF<)LySPZTCFS8e$}@@@oD7cObQ;vAv+DT z??7J+A>9?5GO5FKxtuGxYB3{Dlm!JcCKLFPCj(e`cz|_&+^+mdYOB=jWz>H+xB6uZ zT8L=#M-Rr*_J9j7*or9ZLF~C5X--jFRq8YlX&VieyDF{tdX2n8U(K62t5qfT`e1lZ zNrJjE*{l^Cq6Y7GK>k-i2y&i$8Plm316MQDQXbejP$v6f%n*&?7|Aex0Gx#!$iog` zd&Ch2zMC+&ja8?m5hB2~9%}zVBG#@K?hbN+!V73f4|?hV_8@{GG8&&e`wmHg&jhea z$U!s?6*GqAPNQ>zw&APk0Dmig$${W@km8iI)~CkYD@rG#*mvYBZJ+vkzL%+SZy|ri zwa!IiKES0R9HMnJ*aapM8A2Q-W%LkGVV}5GSvj`TY7}PrX}1Nld;H}?XJWMOQ24{g zMH&OX4|b6!51(u)jPCzrQK9lbnHpMF3UMYUpw7j|2gJ3VQ~cEwCo&r@5cHDS0y*&2 zPDGa+&iowwfnng(#Yvjovy9n?KqKaC5o!!u?EsrZsc`IlWlW4U5o00@WZ&l6X@Nx$G)Wh{$Wx?ir#C4c{3n zMTrR66>k#FM};c`^T~P*EQ>o9l4Zj8mBA7+$mL&bWJ6FOsNj5f1|KcU%X2C2Z_aHQ z+(B|F32`KP-qU1^`-tn6-}7)0lY+tut($PhT&Fayb|JBAl{*Q^Xf@gt*GedeCp5>m z$`PYAB}U2#4YhmL+-kHJRaIzO9_le0j&$3Dbi)iCcOG>>m6z~>p^m%P{6@aYsk2NX zJKB@4)&{I^|7mES^-$wA!`X{dn+5a_4jg_!a-BM35V@ge2oZ)=AP6kUw8m?k!1*iR zA>@v`&6}ahFqxo)Jbi%q#e+Lz!{lCJt}t!_4~N2SR8I57Gyp%W1aW;_LKenDJ1VdI zuKho9jRdWaG9)XX!TrbM~IRK7J zNyDxsCxjey0~?<9HPkD{EV6+B7waA-rG3kz@e}||!Y}=XhN4t@W??L%9wQ_Jy8GrD z`D$(sIf=6IEwMD(Ub%#ohW%6t@>vTmvMU=fLoRqgl3V+h$F4E5IgvSYw)#8TfL@E< z&tH>}{CmiM7m+mTnPq;F^s*4~3u{tb<*Q0p|&!MAA0!%@KE zS(p_uR=QCmhN(2=^Q#gI9_hrt9}KFeg!9XUt7S)j7&irWToLw{3d#PV({}A!zI{%Y zWG-Ch#F2(0IinHYj!+mQYw$R8_+Q_65CnNBsg#9AY4zxYlYlM+ZNV+HM9`acLf8-+ zO{2)0SxLU8SFaG%oxGrkI|NkISDhfWk%nH}cD4Tf=^T6X64pj_wCn5z@E|=bbcR#?-9*2udfb{?uaBXM&;IeC#s4YN6xbcsFdt8ej18GonP8YdLuCtWIMCtNq==?6go^qAH~^N` zt(oK^!$P;I^<@rUnzDD{;R?SK*YUTj>lqmg%-0TTAHDu)C(TFG+!|F@yOXvZp}X2G zc@`x6$RwHpq`xHVTOsTv@Jb#l6ATFQD1-;cJ_j}P*d&>A#qz&IcVkgfYYLnKC#;0p zw;0m$er|~Lmv=X51Boa<$UP3K{ox@#mE8Y+VUiz;MjFd|)^(Y=r$BV5Q zSRCS=QQmDo5-Od&Ypgdao>uCH)swX>4g?9K(}r|84z*tH+-?V_%pCFZoss${1OxEG z9#Gi64Me|JXG=JG?cyLcA`Kx&7-45s`(HgP>gu2 zfJS~fKI9M`L?0M0mc9Ootqnx4@i#7W%+dEu^)1wfhI$cBVhP+Qmsqk80%6s--LqeA zOxGCTdF*jfZt}vsf?0~Spu&9bG!V3bMBR1n>1{J+RFlCZHKewlUhQHBZN9fibN;zy z%6G%?{4Q)18Rpl+xR!vyy_`I8Q!VgN<@sBlu0kF2CShpXY9^y8mQLIB>=kd_iu^T) zp4)S7eVUN*@RIb1pzHQYuM|yF*$SxAi{!Xu>y4#T4XF*e0@h(nfRYhum{(iuP!y-m z66w(%2o>EGHR#(S7N&S?SzN#aabdk^z7}}6I#nub;<27alo(uSI9`vb@pu2+@8YXY zrHL9uwni|+uptB#A<+@{hTJQxG;!P7L+`b3#fsT;DXNb8ir{{V_rJV~!>6@M9qseu z4n5~o%(tHQDkIWx!6ige37NhYYTP$;tPA0UE_p=xM;dYo)f9mzqj1(z7+PIy-o;&zijR~Qdq`pp$amI-K9 z{%Q0IdIP-&`iHC92!{TL0lJ}nCV~y{#MXMu1$+%M=(^N!kt4OdTxEWFArAFLTF|w2 z(6j4`EH<63RUa<&8psMSA(y;3hZUCDsOH zk)#0ujRU2HhS1U!a8-H3Q(u+31)W?0MIKJIMD`lfY5&aDNphZ+Li<;!{p+dj0ofrUEfD!k^(k#of12nUaAaBag|atI zLy=okIZOaF;~1XRU+Ue=h)#{+!uF8=hx_xwy~|zE^YOI(w0*R}gP$(71vK(QsYCmZ z(?ye^f6uRBWq~W^bfjGgW$}YIYHQgO_|3g4%7Rslagw#+XCgs z*?7#GYvjK{M(>-erOwb{cYf{@o=kR4$s{pWQA(Bjpg;ql1bK((&V|0Tt>_quc6EP> z3y3cNB9QKHih)9Wobt(|KD3Hr3KpBG!{f6B`L6gXOt-bLozu)G4f=v^v}-z~ou)$fCyh4RUu;9gVB z-`(d7NRxL`hO+JqFUtGvU~RnLZGQlfE@s|lLDls~#+G}*{kyTd=pYa>$}@0o`bZPi zyHqCg`LTe!d&?3A3flqP{Mn#X?1~iKa<$&H&dD;PRat(_q@!2P0xEmy`utADrEb}` zXJfY$85PMS644G49^lseQxWiLQI{=;kA=>`S!9#+Ae&mqI?N`(#h?c6F@nLk;MOAgZ zpnfBFVkNXmyhv*U`p&q}9_arT>>S37U$P7ErT|FjeBJ|o1x((+8pg~o|HiA|$VArD zz95Pm0b9^rlc)2Pr&a1RWzuIO9bQK?bmKW-=Iv2idz8S}nau+lek34Oa*@ z;Ze(qg4(OnRRyxfF|Tr7?4bT{W6IM+0ym^^sRS^%5#~V8{{4ovG`H0+Dnqx!yM%0e@##)y!*l61ydcL01aOph~1^DvJr^+9U;<()w5m9`4^v71jTb0 zOEm+;uP+et57W(qx(`ZZ(C`T=J*bR%tIiWCMx*T7RZqS@XTJB`0qo@UR*Qw-)6i4J z(`3E{o+D4Ug5hqy`wbo)&J|fE{01N1j+dw2V=+i2o&1BqM5-r+_-KE_G%bMVRnaXy zWZ7SWOR2U2h5zs00Xa}uV+{RaB|3x7xEPPbHO2bH%N6`k`+RQmbuM#mCaVKv`B@y* zIpgQ1jR*k1r7QnpiW?MIn94cK21IxuO{??!v$rJYEY1(~BpYB`U)TAdOkzf+1JVTP zwE=1BoVnE*sZEst0uSuE{0>Vu?if75tP*S|Tc^v6$*LE^ZZ0mIRMx4Vv>*9v=4b(% z(p7;+Z}5BI9#Kyj3fug@bRB(B<^T>A!0C={l=yO$$#l6Zp*aVq-Os4UQELG0Z8C*oBrjF+ZSjo~AX&tv~|9X5p#c~m)3I?3BnvFl%GhDe;U$I#Gv4<*g zOa4t;s;)+IxQc`79F_h)PZMqjH45HUw95b2HlJJ!>X)H}ID-z54X;Y{F}PZ<{wDyv z@_jnXykgw3S6_a0@XT#tdMQg8;T>Jy;7W)`8-f4ARxTsiw#oxhe#lZZa}e){w!VY? zd3CGeG`9-9D!W;CBAopI&~^?I-KSTI%(R%`+ z$Qs>q0T>f1EVHZGUGfU~jlo+)KurRXTijqM@ZcGIsR{q!On~}KDKm`ZE$F$7?oz2Y z6wdc8cLNlD3*^qO8^0!?Zs?%j5W?HZ^+@2R*;?geuK_}b{fA8+q3?^7V%g786IAD^ zxgE|vV#Qv7ckOEcF<9c0#f<(@I$N4a4T`m97tsCz{Uuatgri;gA5x_-u^_&}51tlG zhg3*RUrq@5IZgBGx3*^ML`R(DqiYSqOp3%i_veOJuQ$-H2V#$dopIP>^mR508lK8b z+gPQ5*W<=*3aJ>j*o-D*MQjI#yO5vy?gW;&_5z0DE@@!6vk)vbz*ZcnjnkcigiFoab2c ze$F+^p}Z%e0lcYC4Qh)&s4QLb>&k8R{wGskAMM@ffM@4u9>$?l!3k9Fz=#At>|StQ z;zmOJ@+@or249S8KMqRt!1m(qebh&g8J6lf_UX(5O>A_z*!u{5?-9{*+5Dl zPf#Wz62#!^qCzqECMb)I$KMs>M|Tf`Gd#2iEm|E@UTTm@(qoK+D7b> z-YH-JZ{{eReOS5NiF5pkaFTc8%hwzS)_fhv!2AoyJL)Y|bmo@wI5vqim_z1gHscLZ zPFcoMemb1)Qh_H{#K`|`IqWWQY9~!p5Pv>df3Ez^g=(im6=IO@YD9)NdyPC zhjpwpdei=1+IIEyZGEe!c`&$FR5_rY-YBXPELPc(Pu>|~YZJghHylo_GWK47G)hRH zY3c3eB-gC>TMres&me&#?SS)6-KA)p=;>oI7&Ub``o+qK~c)-sHN)))#~jZtQpJMIZTM z&)olPe&!^<*4w!^XLv4pdLfXTP?Z_&NF)G|``abf%zDp1hZERm*9j8NMPEW6B0I3D zjfo0yG=iTR5i+O8G+7T`2(hLy z4fBCd%FX6M?P`}4RxIGD1BpA&l>)z5BN*jWQbFNHS6s5-wmy6}Fwv&dpuc=`*?J1d zzm=iiOG*H{3fkIjA#uHrkIpQ#5&VNiD?ZAHuIF9&LrujNQue~OsBB2|-VfV%?r8p> zh6jKD!?p|5b6;-wTLk*8GA%dB d|DUzq)X+0`B)<7$*tXrTcZGdbzvIBE{{viFZSnvB diff --git a/api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_s_en.svg deleted file mode 100644 index 9fc02f9164..0000000000 --- a/api/core/model_runtime/model_providers/nvidia_nim/_assets/icon_s_en.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/api/core/model_runtime/model_providers/nvidia_nim/llm/__init__.py b/api/core/model_runtime/model_providers/nvidia_nim/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/nvidia_nim/llm/llm.py b/api/core/model_runtime/model_providers/nvidia_nim/llm/llm.py deleted file mode 100644 index 6ff380bdd9..0000000000 --- a/api/core/model_runtime/model_providers/nvidia_nim/llm/llm.py +++ /dev/null @@ -1,13 +0,0 @@ -import logging - -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - -logger = logging.getLogger(__name__) - - -class NVIDIANIMProvider(OAIAPICompatLargeLanguageModel): - """ - Model class for NVIDIA NIM large language model. - """ - - pass diff --git a/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.py b/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.py deleted file mode 100644 index ad890ada22..0000000000 --- a/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class NVIDIANIMProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.yaml b/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.yaml deleted file mode 100644 index 0e892665d7..0000000000 --- a/api/core/model_runtime/model_providers/nvidia_nim/nvidia_nim.yaml +++ /dev/null @@ -1,79 +0,0 @@ -provider: nvidia_nim -label: - en_US: NVIDIA NIM -description: - en_US: NVIDIA NIM, a set of easy-to-use inference microservices. - zh_Hans: NVIDIA NIM,一组易于使用的模型推理微服务。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.png -background: "#EFFDFD" -help: - title: - en_US: Learn more about NVIDIA NIM - zh_Hans: 了解 NVIDIA NIM 更多信息 - url: - en_US: https://www.nvidia.com/en-us/ai/ -supported_model_types: - - llm -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter full model name - zh_Hans: 输入模型全称 - credential_form_schemas: - - variable: endpoint_url - label: - zh_Hans: API endpoint URL - en_US: API endpoint URL - type: text-input - required: true - placeholder: - zh_Hans: Base URL, e.g. http://192.168.1.100:8000/v1 - en_US: Base URL, e.g. http://192.168.1.100:8000/v1 - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens_to_sample - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - show_on: - - variable: __model_type - value: llm - default: '4096' - type: text-input diff --git a/api/core/model_runtime/model_providers/oci/__init__.py b/api/core/model_runtime/model_providers/oci/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/oci/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/oci/_assets/icon_l_en.svg deleted file mode 100644 index 0981dfcff2..0000000000 --- a/api/core/model_runtime/model_providers/oci/_assets/icon_l_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/oci/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/oci/_assets/icon_s_en.svg deleted file mode 100644 index 0981dfcff2..0000000000 --- a/api/core/model_runtime/model_providers/oci/_assets/icon_s_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-16k.yaml b/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-16k.yaml deleted file mode 100644 index eb60cbcd90..0000000000 --- a/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-16k.yaml +++ /dev/null @@ -1,52 +0,0 @@ -model: cohere.command-r-16k -label: - en_US: cohere.command-r-16k v1.2 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 1 - max: 1.0 - - name: topP - use_template: top_p - default: 0.75 - min: 0 - max: 1 - - name: topK - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presencePenalty - use_template: presence_penalty - min: 0 - max: 1 - default: 0 - - name: frequencyPenalty - use_template: frequency_penalty - min: 0 - max: 1 - default: 0 - - name: maxTokens - use_template: max_tokens - default: 600 - max: 4000 -pricing: - input: '0.004' - output: '0.004' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-plus.yaml b/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-plus.yaml deleted file mode 100644 index df31b0d0df..0000000000 --- a/api/core/model_runtime/model_providers/oci/llm/cohere.command-r-plus.yaml +++ /dev/null @@ -1,52 +0,0 @@ -model: cohere.command-r-plus -label: - en_US: cohere.command-r-plus v1.2 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - default: 1 - max: 1.0 - - name: topP - use_template: top_p - default: 0.75 - min: 0 - max: 1 - - name: topK - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presencePenalty - use_template: presence_penalty - min: 0 - max: 1 - default: 0 - - name: frequencyPenalty - use_template: frequency_penalty - min: 0 - max: 1 - default: 0 - - name: maxTokens - use_template: max_tokens - default: 600 - max: 4000 -pricing: - input: '0.0219' - output: '0.0219' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/llm/llm.py b/api/core/model_runtime/model_providers/oci/llm/llm.py deleted file mode 100644 index 1e1fc5b3ea..0000000000 --- a/api/core/model_runtime/model_providers/oci/llm/llm.py +++ /dev/null @@ -1,469 +0,0 @@ -import base64 -import copy -import json -import logging -from collections.abc import Generator -from typing import Optional, Union - -import oci -from oci.generative_ai_inference.models.base_chat_response import BaseChatResponse - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -logger = logging.getLogger(__name__) - -request_template = { - "compartmentId": "", - "servingMode": {"modelId": "cohere.command-r-plus", "servingType": "ON_DEMAND"}, - "chatRequest": { - "apiFormat": "COHERE", - # "preambleOverride": "You are a helpful assistant.", - # "message": "Hello!", - # "chatHistory": [], - "maxTokens": 600, - "isStream": False, - "frequencyPenalty": 0, - "presencePenalty": 0, - "temperature": 1, - "topP": 0.75, - }, -} -oci_config_template = { - "user": "", - "fingerprint": "", - "tenancy": "", - "region": "", - "compartment_id": "", - "key_content": "", -} - - -class OCILargeLanguageModel(LargeLanguageModel): - # https://docs.oracle.com/en-us/iaas/Content/generative-ai/pretrained-models.htm - _supported_models = { - "meta.llama-3-70b-instruct": { - "system": True, - "multimodal": False, - "tool_call": False, - "stream_tool_call": False, - }, - "cohere.command-r-16k": { - "system": True, - "multimodal": False, - "tool_call": True, - "stream_tool_call": False, - }, - "cohere.command-r-plus": { - "system": True, - "multimodal": False, - "tool_call": True, - "stream_tool_call": False, - }, - } - - def _is_tool_call_supported(self, model_id: str, stream: bool = False) -> bool: - feature = self._supported_models.get(model_id) - if not feature: - return False - return feature["stream_tool_call"] if stream else feature["tool_call"] - - def _is_multimodal_supported(self, model_id: str) -> bool: - feature = self._supported_models.get(model_id) - if not feature: - return False - return feature["multimodal"] - - def _is_system_prompt_supported(self, model_id: str) -> bool: - feature = self._supported_models.get(model_id) - if not feature: - return False - return feature["system"] - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # print("model"+"*"*20) - # print(model) - # print("credentials"+"*"*20) - # print(credentials) - # print("model_parameters"+"*"*20) - # print(model_parameters) - # print("prompt_messages"+"*"*200) - # print(prompt_messages) - # print("tools"+"*"*20) - # print(tools) - - # invoke model - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return:md = genai.GenerativeModel(model) - """ - prompt = self._convert_messages_to_prompt(prompt_messages) - - return self._get_num_tokens_by_gpt2(prompt) - - def get_num_characters( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return:md = genai.GenerativeModel(model) - """ - prompt = self._convert_messages_to_prompt(prompt_messages) - - return len(prompt) - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - """ - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - return text.rstrip() - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - # Setup basic variables - # Auth Config - try: - ping_message = SystemPromptMessage(content="ping") - self._generate(model, credentials, [ping_message], {"maxTokens": 5}) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model - - :param model: model name - :param credentials: credentials kwargs - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - # config_kwargs = model_parameters.copy() - # config_kwargs['max_output_tokens'] = config_kwargs.pop('max_tokens_to_sample', None) - # if stop: - # config_kwargs["stop_sequences"] = stop - - # initialize client - # ref: https://docs.oracle.com/en-us/iaas/api/#/en/generative-ai-inference/20231130/ChatResult/Chat - oci_config = copy.deepcopy(oci_config_template) - if "oci_config_content" in credentials: - oci_config_content = base64.b64decode(credentials.get("oci_config_content")).decode("utf-8") - config_items = oci_config_content.split("/") - if len(config_items) != 5: - raise CredentialsValidateFailedError( - "oci_config_content should be base64.b64encode(" - "'user_ocid/fingerprint/tenancy_ocid/region/compartment_ocid'.encode('utf-8'))" - ) - oci_config["user"] = config_items[0] - oci_config["fingerprint"] = config_items[1] - oci_config["tenancy"] = config_items[2] - oci_config["region"] = config_items[3] - oci_config["compartment_id"] = config_items[4] - else: - raise CredentialsValidateFailedError("need to set oci_config_content in credentials ") - if "oci_key_content" in credentials: - oci_key_content = base64.b64decode(credentials.get("oci_key_content")).decode("utf-8") - oci_config["key_content"] = oci_key_content.encode(encoding="utf-8") - else: - raise CredentialsValidateFailedError("need to set oci_config_content in credentials ") - - # oci_config = oci.config.from_file('~/.oci/config', credentials.get('oci_api_profile')) - compartment_id = oci_config["compartment_id"] - client = oci.generative_ai_inference.GenerativeAiInferenceClient(config=oci_config) - # call embedding model - request_args = copy.deepcopy(request_template) - request_args["compartmentId"] = compartment_id - request_args["servingMode"]["modelId"] = model - - chat_history = [] - system_prompts = [] - # if "meta.llama" in model: - # request_args["chatRequest"]["apiFormat"] = "GENERIC" - request_args["chatRequest"]["maxTokens"] = model_parameters.pop("maxTokens", 600) - request_args["chatRequest"].update(model_parameters) - frequency_penalty = model_parameters.get("frequencyPenalty", 0) - presence_penalty = model_parameters.get("presencePenalty", 0) - if frequency_penalty > 0 and presence_penalty > 0: - raise InvokeBadRequestError("Cannot set both frequency penalty and presence penalty") - - # for msg in prompt_messages: # makes message roles strictly alternating - # content = self._format_message_to_glm_content(msg) - # if history and history[-1]["role"] == content["role"]: - # history[-1]["parts"].extend(content["parts"]) - # else: - # history.append(content) - - # temporary not implement the tool call function - valid_value = self._is_tool_call_supported(model, stream) - if tools is not None and len(tools) > 0: - if not valid_value: - raise InvokeBadRequestError("Does not support function calling") - if model.startswith("cohere"): - # print("run cohere " * 10) - for message in prompt_messages[:-1]: - text = "" - if isinstance(message.content, str): - text = message.content - if isinstance(message, UserPromptMessage): - chat_history.append({"role": "USER", "message": text}) - else: - chat_history.append({"role": "CHATBOT", "message": text}) - if isinstance(message, SystemPromptMessage): - if isinstance(message.content, str): - system_prompts.append(message.content) - args = { - "apiFormat": "COHERE", - "preambleOverride": " ".join(system_prompts), - "message": prompt_messages[-1].content, - "chatHistory": chat_history, - } - request_args["chatRequest"].update(args) - elif model.startswith("meta"): - # print("run meta " * 10) - meta_messages = [] - for message in prompt_messages: - text = message.content - meta_messages.append({"role": message.role.name, "content": [{"type": "TEXT", "text": text}]}) - args = {"apiFormat": "GENERIC", "messages": meta_messages, "numGenerations": 1, "topK": -1} - request_args["chatRequest"].update(args) - - if stream: - request_args["chatRequest"]["isStream"] = True - # print("final request" + "|" * 20) - # print(request_args) - response = client.chat(request_args) - # print(vars(response)) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: BaseChatResponse, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=response.data.chat_response.text) - - # calculate num tokens - prompt_tokens = self.get_num_characters(model, credentials, prompt_messages) - completion_tokens = self.get_num_characters(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: BaseChatResponse, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - index = -1 - events = response.data.events() - for stream in events: - chunk = json.loads(stream.data) - # print(chunk) - # chunk: {'apiFormat': 'COHERE', 'text': 'Hello'} - - # for chunk in response: - # for part in chunk.parts: - # if part.function_call: - # assistant_prompt_message.tool_calls = [ - # AssistantPromptMessage.ToolCall( - # id=part.function_call.name, - # type='function', - # function=AssistantPromptMessage.ToolCall.ToolCallFunction( - # name=part.function_call.name, - # arguments=json.dumps(dict(part.function_call.args.items())) - # ) - # ) - # ] - - if "finishReason" not in chunk: - assistant_prompt_message = AssistantPromptMessage(content="") - if model.startswith("cohere"): - if chunk["text"]: - assistant_prompt_message.content += chunk["text"] - elif model.startswith("meta"): - assistant_prompt_message.content += chunk["message"]["content"][0]["text"] - index += 1 - # transform assistant message to prompt message - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message), - ) - else: - # calculate num tokens - prompt_tokens = self.get_num_characters(model, credentials, prompt_messages) - completion_tokens = self.get_num_characters(model, credentials, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=assistant_prompt_message, - finish_reason=str(chunk["finishReason"]), - usage=usage, - ), - ) - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nuser:" - ai_prompt = "\n\nmodel:" - - content = message.content - if isinstance(content, list): - content = "".join(c.data for c in content if c.type != PromptMessageContentType.IMAGE) - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage | ToolPromptMessage): - message_text = f"{human_prompt} {content}" - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [], - InvokeRateLimitError: [], - InvokeAuthorizationError: [], - InvokeBadRequestError: [], - } diff --git a/api/core/model_runtime/model_providers/oci/llm/meta.llama-3-70b-instruct.yaml b/api/core/model_runtime/model_providers/oci/llm/meta.llama-3-70b-instruct.yaml deleted file mode 100644 index dd5be107c0..0000000000 --- a/api/core/model_runtime/model_providers/oci/llm/meta.llama-3-70b-instruct.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: meta.llama-3-70b-instruct -label: - zh_Hans: meta.llama-3-70b-instruct - en_US: meta.llama-3-70b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - default: 1 - max: 2.0 - - name: topP - use_template: top_p - default: 0.75 - min: 0 - max: 1 - - name: topK - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presencePenalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 - - name: frequencyPenalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: maxTokens - use_template: max_tokens - default: 600 - max: 8000 -pricing: - input: '0.015' - output: '0.015' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/oci.py b/api/core/model_runtime/model_providers/oci/oci.py deleted file mode 100644 index e182d2d043..0000000000 --- a/api/core/model_runtime/model_providers/oci/oci.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OCIGENAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `cohere.command-r-plus` model for validate, - model_instance.validate_credentials(model="cohere.command-r-plus", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/oci/oci.yaml b/api/core/model_runtime/model_providers/oci/oci.yaml deleted file mode 100644 index f2f23e18f1..0000000000 --- a/api/core/model_runtime/model_providers/oci/oci.yaml +++ /dev/null @@ -1,42 +0,0 @@ -provider: oci -label: - en_US: OCIGenerativeAI -description: - en_US: Models provided by OCI, such as Cohere Command R and Cohere Command R+. - zh_Hans: OCI 提供的模型,例如 Cohere Command R 和 Cohere Command R+。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FFFFFF" -help: - title: - en_US: Get your API Key from OCI - zh_Hans: 从 OCI 获取 API Key - url: - en_US: https://docs.cloud.oracle.com/Content/API/Concepts/sdkconfig.htm -supported_model_types: - - llm - - text-embedding - #- rerank -configurate_methods: - - predefined-model - #- customizable-model -provider_credential_schema: - credential_form_schemas: - - variable: oci_config_content - label: - en_US: oci api key config file's content - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的 oci api key config 文件的内容(base64.b64encode("user_ocid/fingerprint/tenancy_ocid/region/compartment_ocid".encode('utf-8')) ) - en_US: Enter your oci api key config file's content(base64.b64encode("user_ocid/fingerprint/tenancy_ocid/region/compartment_ocid".encode('utf-8')) ) - - variable: oci_key_content - label: - en_US: oci api key file's content - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的 oci api key 文件的内容(base64.b64encode("pem file content".encode('utf-8'))) - en_US: Enter your oci api key file's content(base64.b64encode("pem file content".encode('utf-8'))) diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/__init__.py b/api/core/model_runtime/model_providers/oci/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/_position.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/_position.yaml deleted file mode 100644 index 149f1e3797..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/_position.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- cohere.embed-english-light-v2.0 -- cohere.embed-english-light-v3.0 -- cohere.embed-english-v3.0 -- cohere.embed-multilingual-light-v3.0 -- cohere.embed-multilingual-v3.0 diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v2.0.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v2.0.yaml deleted file mode 100644 index 259d5b45b7..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v2.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: cohere.embed-english-light-v2.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v3.0.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v3.0.yaml deleted file mode 100644 index 065e7474c0..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-light-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: cohere.embed-english-light-v3.0 -model_type: text-embedding -model_properties: - context_size: 384 - max_chunks: 48 -pricing: - input: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-v3.0.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-v3.0.yaml deleted file mode 100644 index 3e2deea16a..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-english-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: cohere.embed-english-v3.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-light-v3.0.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-light-v3.0.yaml deleted file mode 100644 index 0d2b892c64..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-light-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: cohere.embed-multilingual-light-v3.0 -model_type: text-embedding -model_properties: - context_size: 384 - max_chunks: 48 -pricing: - input: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-v3.0.yaml b/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-v3.0.yaml deleted file mode 100644 index 9ebe260b32..0000000000 --- a/api/core/model_runtime/model_providers/oci/text_embedding/cohere.embed-multilingual-v3.0.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: cohere.embed-multilingual-v3.0 -model_type: text-embedding -model_properties: - context_size: 1024 - max_chunks: 48 -pricing: - input: '0.001' - unit: '0.0001' - currency: USD diff --git a/api/core/model_runtime/model_providers/ollama/__init__.py b/api/core/model_runtime/model_providers/ollama/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/ollama/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/ollama/_assets/icon_l_en.svg deleted file mode 100644 index 39d8a1ece6..0000000000 --- a/api/core/model_runtime/model_providers/ollama/_assets/icon_l_en.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/ollama/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/ollama/_assets/icon_s_en.svg deleted file mode 100644 index f8482a96b9..0000000000 --- a/api/core/model_runtime/model_providers/ollama/_assets/icon_s_en.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/ollama/llm/__init__.py b/api/core/model_runtime/model_providers/ollama/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/ollama/llm/llm.py b/api/core/model_runtime/model_providers/ollama/llm/llm.py deleted file mode 100644 index a7ea53e0e9..0000000000 --- a/api/core/model_runtime/model_providers/ollama/llm/llm.py +++ /dev/null @@ -1,726 +0,0 @@ -import json -import logging -import re -from collections.abc import Generator -from decimal import Decimal -from typing import Optional, Union, cast -from urllib.parse import urljoin - -import requests - -from core.model_runtime.entities.llm_entities import ( - LLMMode, - LLMResult, - LLMResultChunk, - LLMResultChunkDelta, -) -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - I18nObject, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, - PriceConfig, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import ( - LargeLanguageModel, -) - -logger = logging.getLogger(__name__) - - -class OllamaLargeLanguageModel(LargeLanguageModel): - """ - Model class for Ollama large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - # get model mode - model_mode = self.get_model_mode(model, credentials) - - if model_mode == LLMMode.CHAT: - # chat model - return self._num_tokens_from_messages(prompt_messages) - else: - first_prompt_message = prompt_messages[0] - if isinstance(first_prompt_message.content, str): - text = first_prompt_message.content - else: - text = "" - for message_content in first_prompt_message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - text = message_content.data - break - return self._get_num_tokens_by_gpt2(text) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._generate( - model=model, - credentials=credentials, - prompt_messages=[UserPromptMessage(content="ping")], - model_parameters={"num_predict": 5}, - stream=False, - ) - except InvokeError as ex: - raise CredentialsValidateFailedError(f"An error occurred during credentials validation: {ex.description}") - except Exception as ex: - raise CredentialsValidateFailedError(f"An error occurred during credentials validation: {str(ex)}") - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm completion model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - headers = {"Content-Type": "application/json"} - - endpoint_url = credentials["base_url"] - if not endpoint_url.endswith("/"): - endpoint_url += "/" - - # prepare the payload for a simple ping to the model - data = {"model": model, "stream": stream} - - if "format" in model_parameters: - data["format"] = model_parameters["format"] - del model_parameters["format"] - - if "keep_alive" in model_parameters: - data["keep_alive"] = model_parameters["keep_alive"] - del model_parameters["keep_alive"] - - data["options"] = model_parameters or {} - - if stop: - data["options"]["stop"] = stop - - completion_type = LLMMode.value_of(credentials["mode"]) - - if completion_type is LLMMode.CHAT: - endpoint_url = urljoin(endpoint_url, "api/chat") - data["messages"] = [self._convert_prompt_message_to_dict(m) for m in prompt_messages] - else: - endpoint_url = urljoin(endpoint_url, "api/generate") - first_prompt_message = prompt_messages[0] - if isinstance(first_prompt_message, UserPromptMessage): - first_prompt_message = cast(UserPromptMessage, first_prompt_message) - if isinstance(first_prompt_message.content, str): - data["prompt"] = first_prompt_message.content - else: - text = "" - images = [] - for message_content in first_prompt_message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - text = message_content.data - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - image_data = re.sub( - r"^data:image\/[a-zA-Z]+;base64,", - "", - message_content.data, - ) - images.append(image_data) - - data["prompt"] = text - data["images"] = images - - # send a post request to validate the credentials - response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300), stream=stream) - - response.encoding = "utf-8" - if response.status_code != 200: - raise InvokeError(f"API request failed with status code {response.status_code}: {response.text}") - - if stream: - return self._handle_generate_stream_response(model, credentials, completion_type, response, prompt_messages) - - return self._handle_generate_response(model, credentials, completion_type, response, prompt_messages) - - def _handle_generate_response( - self, - model: str, - credentials: dict, - completion_type: LLMMode, - response: requests.Response, - prompt_messages: list[PromptMessage], - ) -> LLMResult: - """ - Handle llm completion response - - :param model: model name - :param credentials: model credentials - :param completion_type: completion type - :param response: response - :param prompt_messages: prompt messages - :return: llm result - """ - response_json = response.json() - - if completion_type is LLMMode.CHAT: - message = response_json.get("message", {}) - response_content = message.get("content", "") - else: - response_content = response_json["response"] - - assistant_message = AssistantPromptMessage(content=response_content) - - if "prompt_eval_count" in response_json and "eval_count" in response_json: - # transform usage - prompt_tokens = response_json["prompt_eval_count"] - completion_tokens = response_json["eval_count"] - else: - # calculate num tokens - prompt_tokens = self._get_num_tokens_by_gpt2(prompt_messages[0].content) - completion_tokens = self._get_num_tokens_by_gpt2(assistant_message.content) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=response_json["model"], - prompt_messages=prompt_messages, - message=assistant_message, - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, - model: str, - credentials: dict, - completion_type: LLMMode, - response: requests.Response, - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm completion stream response - - :param model: model name - :param credentials: model credentials - :param completion_type: completion type - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - full_text = "" - chunk_index = 0 - - def create_final_llm_result_chunk( - index: int, message: AssistantPromptMessage, finish_reason: str - ) -> LLMResultChunk: - # calculate num tokens - prompt_tokens = self._get_num_tokens_by_gpt2(prompt_messages[0].content) - completion_tokens = self._get_num_tokens_by_gpt2(full_text) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - return LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=message, - finish_reason=finish_reason, - usage=usage, - ), - ) - - for chunk in response.iter_lines(decode_unicode=True, delimiter="\n"): - if not chunk: - continue - - try: - chunk_json = json.loads(chunk) - # stream ended - except json.JSONDecodeError as e: - yield create_final_llm_result_chunk( - index=chunk_index, - message=AssistantPromptMessage(content=""), - finish_reason="Non-JSON encountered.", - ) - - chunk_index += 1 - break - - if completion_type is LLMMode.CHAT: - if not chunk_json: - continue - - if "message" not in chunk_json: - text = "" - else: - text = chunk_json.get("message").get("content", "") - else: - if not chunk_json: - continue - - # transform assistant message to prompt message - text = chunk_json["response"] - - assistant_prompt_message = AssistantPromptMessage(content=text) - - full_text += text - - if chunk_json["done"]: - # calculate num tokens - if "prompt_eval_count" in chunk_json: - prompt_tokens = chunk_json["prompt_eval_count"] - else: - prompt_message_content = prompt_messages[0].content - if isinstance(prompt_message_content, str): - prompt_tokens = self._get_num_tokens_by_gpt2(prompt_message_content) - else: - content_text = "" - for message_content in prompt_message_content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - content_text += message_content.data - prompt_tokens = self._get_num_tokens_by_gpt2(content_text) - - completion_tokens = chunk_json.get("eval_count", self._get_num_tokens_by_gpt2(full_text)) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=chunk_json["model"], - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=assistant_prompt_message, - finish_reason="stop", - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=chunk_json["model"], - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=assistant_prompt_message, - ), - ) - - chunk_index += 1 - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for Ollama API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - text = "" - images = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - text = message_content.data - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - image_data = re.sub(r"^data:image\/[a-zA-Z]+;base64,", "", message_content.data) - images.append(image_data) - - message_dict = {"role": "user", "content": text, "images": images} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - else: - raise ValueError(f"Got unknown type {message}") - - return message_dict - - def _num_tokens_from_messages(self, messages: list[PromptMessage]) -> int: - """ - Calculate num tokens. - - :param messages: messages - """ - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - for key, value in message.items(): - num_tokens += self._get_num_tokens_by_gpt2(str(key)) - num_tokens += self._get_num_tokens_by_gpt2(str(value)) - - return num_tokens - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - Get customizable model schema. - - :param model: model name - :param credentials: credentials - - :return: model schema - """ - extras = {} - - if "vision_support" in credentials and credentials["vision_support"] == "true": - extras["features"] = [ModelFeature.VISION] - - entity = AIModelEntity( - model=model, - label=I18nObject(zh_Hans=model, en_US=model), - model_type=ModelType.LLM, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.MODE: credentials.get("mode"), - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", 4096)), - }, - parameter_rules=[ - ParameterRule( - name=DefaultParameterName.TEMPERATURE.value, - use_template=DefaultParameterName.TEMPERATURE.value, - label=I18nObject(en_US="Temperature", zh_Hans="温度"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="The temperature of the model. " - "Increasing the temperature will make the model answer " - "more creatively. (Default: 0.8)", - zh_Hans="模型的温度。增加温度将使模型的回答更具创造性。(默认值:0.8)", - ), - default=0.1, - min=0, - max=1, - ), - ParameterRule( - name=DefaultParameterName.TOP_P.value, - use_template=DefaultParameterName.TOP_P.value, - label=I18nObject(en_US="Top P", zh_Hans="Top P"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="Works together with top-k. A higher value (e.g., 0.95) will lead to " - "more diverse text, while a lower value (e.g., 0.5) will generate more " - "focused and conservative text. (Default: 0.9)", - zh_Hans="与top-k一起工作。较高的值(例如,0.95)会导致生成更多样化的文本,而较低的值(例如,0.5)会生成更专注和保守的文本。(默认值:0.9)", - ), - default=0.9, - min=0, - max=1, - ), - ParameterRule( - name="top_k", - label=I18nObject(en_US="Top K", zh_Hans="Top K"), - type=ParameterType.INT, - help=I18nObject( - en_US="Reduces the probability of generating nonsense. " - "A higher value (e.g. 100) will give more diverse answers, " - "while a lower value (e.g. 10) will be more conservative. (Default: 40)", - zh_Hans="减少生成无意义内容的可能性。较高的值(例如100)将提供更多样化的答案,而较低的值(例如10)将更为保守。(默认值:40)", - ), - min=1, - max=100, - ), - ParameterRule( - name="repeat_penalty", - label=I18nObject(en_US="Repeat Penalty"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="Sets how strongly to penalize repetitions. " - "A higher value (e.g., 1.5) will penalize repetitions more strongly, " - "while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)", - zh_Hans="设置对重复内容的惩罚强度。一个较高的值(例如,1.5)会更强地惩罚重复内容,而一个较低的值(例如,0.9)则会相对宽容。(默认值:1.1)", - ), - min=-2, - max=2, - ), - ParameterRule( - name="num_predict", - use_template="max_tokens", - label=I18nObject(en_US="Num Predict", zh_Hans="最大令牌数预测"), - type=ParameterType.INT, - help=I18nObject( - en_US="Maximum number of tokens to predict when generating text. " - "(Default: 128, -1 = infinite generation, -2 = fill context)", - zh_Hans="生成文本时预测的最大令牌数。(默认值:128,-1 = 无限生成,-2 = 填充上下文)", - ), - default=(512 if int(credentials.get("max_tokens", 4096)) >= 768 else 128), - min=-2, - max=int(credentials.get("max_tokens", 4096)), - ), - ParameterRule( - name="mirostat", - label=I18nObject(en_US="Mirostat sampling", zh_Hans="Mirostat 采样"), - type=ParameterType.INT, - help=I18nObject( - en_US="Enable Mirostat sampling for controlling perplexity. " - "(default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0)", - zh_Hans="启用 Mirostat 采样以控制困惑度。" - "(默认值:0,0 = 禁用,1 = Mirostat,2 = Mirostat 2.0)", - ), - min=0, - max=2, - ), - ParameterRule( - name="mirostat_eta", - label=I18nObject(en_US="Mirostat Eta", zh_Hans="学习率"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="Influences how quickly the algorithm responds to feedback from " - "the generated text. A lower learning rate will result in slower adjustments, " - "while a higher learning rate will make the algorithm more responsive. " - "(Default: 0.1)", - zh_Hans="影响算法对生成文本反馈响应的速度。较低的学习率会导致调整速度变慢,而较高的学习率会使得算法更加灵敏。(默认值:0.1)", - ), - precision=1, - ), - ParameterRule( - name="mirostat_tau", - label=I18nObject(en_US="Mirostat Tau", zh_Hans="文本连贯度"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="Controls the balance between coherence and diversity of the output. " - "A lower value will result in more focused and coherent text. (Default: 5.0)", - zh_Hans="控制输出的连贯性和多样性之间的平衡。较低的值会导致更专注和连贯的文本。(默认值:5.0)", - ), - precision=1, - ), - ParameterRule( - name="num_ctx", - label=I18nObject(en_US="Size of context window", zh_Hans="上下文窗口大小"), - type=ParameterType.INT, - help=I18nObject( - en_US="Sets the size of the context window used to generate the next token. (Default: 2048)", - zh_Hans="设置用于生成下一个标记的上下文窗口大小。(默认值:2048)", - ), - default=2048, - min=1, - ), - ParameterRule( - name="num_gpu", - label=I18nObject(en_US="GPU Layers", zh_Hans="GPU 层数"), - type=ParameterType.INT, - help=I18nObject( - en_US="The number of layers to offload to the GPU(s). " - "On macOS it defaults to 1 to enable metal support, 0 to disable." - "As long as a model fits into one gpu it stays in one. " - "It does not set the number of GPU(s). ", - zh_Hans="加载到 GPU 的层数。在 macOS 上,默认为 1 以启用 Metal 支持,设置为 0 则禁用。" - "只要模型适合一个 GPU,它就保留在其中。它不设置 GPU 的数量。", - ), - min=-1, - default=1, - ), - ParameterRule( - name="num_thread", - label=I18nObject(en_US="Num Thread", zh_Hans="线程数"), - type=ParameterType.INT, - help=I18nObject( - en_US="Sets the number of threads to use during computation. " - "By default, Ollama will detect this for optimal performance. " - "It is recommended to set this value to the number of physical CPU cores " - "your system has (as opposed to the logical number of cores).", - zh_Hans="设置计算过程中使用的线程数。默认情况下,Ollama会检测以获得最佳性能。建议将此值设置为系统拥有的物理CPU核心数(而不是逻辑核心数)。", - ), - min=1, - ), - ParameterRule( - name="repeat_last_n", - label=I18nObject(en_US="Repeat last N", zh_Hans="回溯内容"), - type=ParameterType.INT, - help=I18nObject( - en_US="Sets how far back for the model to look back to prevent repetition. " - "(Default: 64, 0 = disabled, -1 = num_ctx)", - zh_Hans="设置模型回溯多远的内容以防止重复。(默认值:64,0 = 禁用,-1 = num_ctx)", - ), - min=-1, - ), - ParameterRule( - name="tfs_z", - label=I18nObject(en_US="TFS Z", zh_Hans="减少标记影响"), - type=ParameterType.FLOAT, - help=I18nObject( - en_US="Tail free sampling is used to reduce the impact of less probable tokens " - "from the output. A higher value (e.g., 2.0) will reduce the impact more, " - "while a value of 1.0 disables this setting. (default: 1)", - zh_Hans="用于减少输出中不太可能的标记的影响。较高的值(例如,2.0)会更多地减少这种影响,而1.0的值则会禁用此设置。(默认值:1)", - ), - precision=1, - ), - ParameterRule( - name="seed", - label=I18nObject(en_US="Seed", zh_Hans="随机数种子"), - type=ParameterType.INT, - help=I18nObject( - en_US="Sets the random number seed to use for generation. Setting this to " - "a specific number will make the model generate the same text for " - "the same prompt. (Default: 0)", - zh_Hans="设置用于生成的随机数种子。将此设置为特定数字将使模型对相同的提示生成相同的文本。(默认值:0)", - ), - ), - ParameterRule( - name="keep_alive", - label=I18nObject(en_US="Keep Alive", zh_Hans="模型存活时间"), - type=ParameterType.STRING, - help=I18nObject( - en_US="Sets how long the model is kept in memory after generating a response. " - "This must be a duration string with a unit (e.g., '10m' for 10 minutes or '24h' for 24 hours)." - " A negative number keeps the model loaded indefinitely, and '0' unloads the model" - " immediately after generating a response." - " Valid time units are 's','m','h'. (Default: 5m)", - zh_Hans="设置模型在生成响应后在内存中保留的时间。" - "这必须是一个带有单位的持续时间字符串(例如,'10m' 表示10分钟,'24h' 表示24小时)。" - "负数表示无限期地保留模型,'0'表示在生成响应后立即卸载模型。" - "有效的时间单位有 's'(秒)、'm'(分钟)、'h'(小时)。(默认值:5m)", - ), - ), - ParameterRule( - name="format", - label=I18nObject(en_US="Format", zh_Hans="返回格式"), - type=ParameterType.STRING, - help=I18nObject( - en_US="the format to return a response in. Currently the only accepted value is json.", - zh_Hans="返回响应的格式。目前唯一接受的值是json。", - ), - options=["json"], - ), - ], - pricing=PriceConfig( - input=Decimal(credentials.get("input_price", 0)), - output=Decimal(credentials.get("output_price", 0)), - unit=Decimal(credentials.get("unit", 0)), - currency=credentials.get("currency", "USD"), - ), - **extras, - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeAuthorizationError: [ - requests.exceptions.InvalidHeader, # Missing or Invalid API Key - ], - InvokeBadRequestError: [ - requests.exceptions.HTTPError, # Invalid Endpoint URL or model name - requests.exceptions.InvalidURL, # Misconfigured request or other API error - ], - InvokeRateLimitError: [ - requests.exceptions.RetryError # Too many requests sent in a short period of time - ], - InvokeServerUnavailableError: [ - requests.exceptions.ConnectionError, # Engine Overloaded - requests.exceptions.HTTPError, # Server Error - ], - InvokeConnectionError: [ - requests.exceptions.ConnectTimeout, # Timeout - requests.exceptions.ReadTimeout, # Timeout - ], - } diff --git a/api/core/model_runtime/model_providers/ollama/ollama.py b/api/core/model_runtime/model_providers/ollama/ollama.py deleted file mode 100644 index 115280193a..0000000000 --- a/api/core/model_runtime/model_providers/ollama/ollama.py +++ /dev/null @@ -1,16 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OpenAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - pass diff --git a/api/core/model_runtime/model_providers/ollama/ollama.yaml b/api/core/model_runtime/model_providers/ollama/ollama.yaml deleted file mode 100644 index 33747753bd..0000000000 --- a/api/core/model_runtime/model_providers/ollama/ollama.yaml +++ /dev/null @@ -1,98 +0,0 @@ -provider: ollama -label: - en_US: Ollama -icon_large: - en_US: icon_l_en.svg -icon_small: - en_US: icon_s_en.svg -background: "#F9FAFB" -help: - title: - en_US: How to integrate with Ollama - zh_Hans: 如何集成 Ollama - url: - en_US: https://docs.dify.ai/tutorials/model-configuration/ollama -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: base_url - label: - zh_Hans: 基础 URL - en_US: Base URL - type: text-input - required: true - placeholder: - zh_Hans: Ollama server 的基础 URL,例如 http://192.168.1.100:11434 - en_US: Base url of Ollama server, e.g. http://192.168.1.100:11434 - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - zh_Hans: 模型类型 - en_US: Completion mode - type: select - required: true - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - show_on: - - variable: __model_type - value: llm - default: '4096' - type: text-input - required: true - - variable: vision_support - label: - zh_Hans: 是否支持 Vision - en_US: Vision support - show_on: - - variable: __model_type - value: llm - default: 'false' - type: radio - required: false - options: - - value: 'true' - label: - en_US: 'Yes' - zh_Hans: 是 - - value: 'false' - label: - en_US: 'No' - zh_Hans: 否 diff --git a/api/core/model_runtime/model_providers/ollama/text_embedding/__init__.py b/api/core/model_runtime/model_providers/ollama/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/__init__.py b/api/core/model_runtime/model_providers/openai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/openai/_assets/icon_l_en.svg deleted file mode 100644 index dae73f58d7..0000000000 --- a/api/core/model_runtime/model_providers/openai/_assets/icon_l_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/openai/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/openai/_assets/icon_s_en.svg deleted file mode 100644 index 70686f9b3b..0000000000 --- a/api/core/model_runtime/model_providers/openai/_assets/icon_s_en.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/api/core/model_runtime/model_providers/openai/_common.py b/api/core/model_runtime/model_providers/openai/_common.py deleted file mode 100644 index 2181bb4f08..0000000000 --- a/api/core/model_runtime/model_providers/openai/_common.py +++ /dev/null @@ -1,60 +0,0 @@ -from collections.abc import Mapping - -import openai -from httpx import Timeout - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonOpenAI: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["openai_api_key"], - "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), - "max_retries": 1, - } - - if credentials.get("openai_api_base"): - openai_api_base = credentials["openai_api_base"].rstrip("/") - credentials_kwargs["base_url"] = openai_api_base + "/v1" - - if "openai_organization" in credentials: - credentials_kwargs["organization"] = credentials["openai_organization"] - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [openai.APIConnectionError, openai.APITimeoutError], - InvokeServerUnavailableError: [openai.InternalServerError], - InvokeRateLimitError: [openai.RateLimitError], - InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError], - InvokeBadRequestError: [ - openai.BadRequestError, - openai.NotFoundError, - openai.UnprocessableEntityError, - openai.APIError, - ], - } diff --git a/api/core/model_runtime/model_providers/openai/llm/__init__.py b/api/core/model_runtime/model_providers/openai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/llm/_position.yaml b/api/core/model_runtime/model_providers/openai/llm/_position.yaml deleted file mode 100644 index 7501bc1164..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/_position.yaml +++ /dev/null @@ -1,26 +0,0 @@ -- gpt-4 -- gpt-4o -- gpt-4o-2024-05-13 -- gpt-4o-2024-08-06 -- chatgpt-4o-latest -- gpt-4o-mini -- gpt-4o-mini-2024-07-18 -- o1-preview -- o1-preview-2024-09-12 -- o1-mini -- o1-mini-2024-09-12 -- gpt-4-turbo -- gpt-4-turbo-2024-04-09 -- gpt-4-turbo-preview -- gpt-4-32k -- gpt-4-1106-preview -- gpt-4-0125-preview -- gpt-4-vision-preview -- gpt-3.5-turbo -- gpt-3.5-turbo-16k -- gpt-3.5-turbo-16k-0613 -- gpt-3.5-turbo-0125 -- gpt-3.5-turbo-1106 -- gpt-3.5-turbo-0613 -- gpt-3.5-turbo-instruct -- text-davinci-003 diff --git a/api/core/model_runtime/model_providers/openai/llm/chatgpt-4o-latest.yaml b/api/core/model_runtime/model_providers/openai/llm/chatgpt-4o-latest.yaml deleted file mode 100644 index b47449a49a..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/chatgpt-4o-latest.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: chatgpt-4o-latest -label: - zh_Hans: chatgpt-4o-latest - en_US: chatgpt-4o-latest -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '2.50' - output: '10.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0125.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0125.yaml deleted file mode 100644 index ffa725ec40..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0125.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: gpt-3.5-turbo-0125 -label: - zh_Hans: gpt-3.5-turbo-0125 - en_US: gpt-3.5-turbo-0125 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.0005' - output: '0.0015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0613.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0613.yaml deleted file mode 100644 index a1ad07d712..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-0613.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: gpt-3.5-turbo-0613 -label: - zh_Hans: gpt-3.5-turbo-0613 - en_US: gpt-3.5-turbo-0613 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '0.0015' - output: '0.002' - unit: '0.001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-1106.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-1106.yaml deleted file mode 100644 index 21150fc3a6..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-1106.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: gpt-3.5-turbo-1106 -label: - zh_Hans: gpt-3.5-turbo-1106 - en_US: gpt-3.5-turbo-1106 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.001' - output: '0.002' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k-0613.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k-0613.yaml deleted file mode 100644 index 4e30279284..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k-0613.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: gpt-3.5-turbo-16k-0613 -label: - zh_Hans: gpt-3.5-turbo-16k-0613 - en_US: gpt-3.5-turbo-16k-0613 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16385 - - name: response_format - use_template: response_format -pricing: - input: '0.003' - output: '0.004' - unit: '0.001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k.yaml deleted file mode 100644 index 3684c1945c..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-16k.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: gpt-3.5-turbo-16k -label: - zh_Hans: gpt-3.5-turbo-16k - en_US: gpt-3.5-turbo-16k -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16385 - - name: response_format - use_template: response_format -pricing: - input: '0.003' - output: '0.004' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-instruct.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-instruct.yaml deleted file mode 100644 index ad831539e0..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: gpt-3.5-turbo-instruct -label: - zh_Hans: gpt-3.5-turbo-instruct - en_US: gpt-3.5-turbo-instruct -model_type: llm -features: [ ] -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: '0.0015' - output: '0.002' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo.yaml deleted file mode 100644 index d3a8ee535a..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-3.5-turbo.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: gpt-3.5-turbo -label: - zh_Hans: gpt-3.5-turbo - en_US: gpt-3.5-turbo -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.0005' - output: '0.0015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-0125-preview.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-0125-preview.yaml deleted file mode 100644 index ac4ec5840b..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-0125-preview.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: gpt-4-0125-preview -label: - zh_Hans: gpt-4-0125-preview - en_US: gpt-4-0125-preview -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-1106-preview.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-1106-preview.yaml deleted file mode 100644 index d775239770..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-1106-preview.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: gpt-4-1106-preview -label: - zh_Hans: gpt-4-1106-preview - en_US: gpt-4-1106-preview -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-32k.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-32k.yaml deleted file mode 100644 index 8358425e6d..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-32k.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: gpt-4-32k -label: - zh_Hans: gpt-4-32k - en_US: gpt-4-32k -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 32768 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.06' - output: '0.12' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-2024-04-09.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-2024-04-09.yaml deleted file mode 100644 index 0234499164..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-2024-04-09.yaml +++ /dev/null @@ -1,57 +0,0 @@ -model: gpt-4-turbo-2024-04-09 -label: - zh_Hans: gpt-4-turbo-2024-04-09 - en_US: gpt-4-turbo-2024-04-09 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-preview.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-preview.yaml deleted file mode 100644 index 8d29cf0c04..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo-preview.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: gpt-4-turbo-preview -label: - zh_Hans: gpt-4-turbo-preview - en_US: gpt-4-turbo-preview -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo.yaml deleted file mode 100644 index b25ff6a812..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-turbo.yaml +++ /dev/null @@ -1,57 +0,0 @@ -model: gpt-4-turbo -label: - zh_Hans: gpt-4-turbo - en_US: gpt-4-turbo -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4-vision-preview.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4-vision-preview.yaml deleted file mode 100644 index 07037c6643..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4-vision-preview.yaml +++ /dev/null @@ -1,54 +0,0 @@ -model: gpt-4-vision-preview -label: - zh_Hans: gpt-4-vision-preview - en_US: gpt-4-vision-preview -model_type: llm -features: - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4.yaml deleted file mode 100644 index f7b5138b7d..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: gpt-4 -label: - zh_Hans: gpt-4 - en_US: gpt-4 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '0.03' - output: '0.06' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-05-13.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-05-13.yaml deleted file mode 100644 index b630d6f630..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-05-13.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: gpt-4o-2024-05-13 -label: - zh_Hans: gpt-4o-2024-05-13 - en_US: gpt-4o-2024-05-13 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '5.00' - output: '15.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-08-06.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-08-06.yaml deleted file mode 100644 index 73b7f69700..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-2024-08-06.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: gpt-4o-2024-08-06 -label: - zh_Hans: gpt-4o-2024-08-06 - en_US: gpt-4o-2024-08-06 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - json_schema - - name: json_schema - use_template: json_schema -pricing: - input: '2.50' - output: '10.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini-2024-07-18.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini-2024-07-18.yaml deleted file mode 100644 index df38270f79..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini-2024-07-18.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: gpt-4o-mini-2024-07-18 -label: - zh_Hans: gpt-4o-mini-2024-07-18 - en_US: gpt-4o-mini-2024-07-18 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - json_schema - - name: json_schema - use_template: json_schema -pricing: - input: '0.15' - output: '0.60' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini.yaml deleted file mode 100644 index 5e3c94fbe2..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4o-mini.yaml +++ /dev/null @@ -1,47 +0,0 @@ -model: gpt-4o-mini -label: - zh_Hans: gpt-4o-mini - en_US: gpt-4o-mini -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object - - json_schema - - name: json_schema - use_template: json_schema -pricing: - input: '0.15' - output: '0.60' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/gpt-4o.yaml b/api/core/model_runtime/model_providers/openai/llm/gpt-4o.yaml deleted file mode 100644 index 3090a9e090..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/gpt-4o.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: gpt-4o -label: - zh_Hans: gpt-4o - en_US: gpt-4o -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '5.00' - output: '15.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/llm.py b/api/core/model_runtime/model_providers/openai/llm/llm.py deleted file mode 100644 index d42fce528a..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/llm.py +++ /dev/null @@ -1,1182 +0,0 @@ -import json -import logging -from collections.abc import Generator -from typing import Optional, Union, cast - -import tiktoken -from openai import OpenAI, Stream -from openai.types import Completion -from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall -from openai.types.chat.chat_completion_message import FunctionCall - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, I18nObject, ModelType, PriceConfig -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.openai._common import _CommonOpenAI - -logger = logging.getLogger(__name__) - -OPENAI_BLOCK_MODE_PROMPT = """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 - - -class OpenAILargeLanguageModel(_CommonOpenAI, LargeLanguageModel): - """ - Model class for OpenAI large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # handle fine tune remote models - base_model = model - if model.startswith("ft:"): - base_model = model.split(":")[1] - - # get model mode - model_mode = self.get_model_mode(base_model, credentials) - - if model_mode == LLMMode.CHAT: - # chat model - return self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - else: - # text completion model - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - stop=stop, - stream=stream, - user=user, - ) - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: list[Callback] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - # handle fine tune remote models - base_model = model - if model.startswith("ft:"): - base_model = model.split(":")[1] - - # get model mode - model_mode = self.get_model_mode(base_model, credentials) - - # transform response format - if "response_format" in model_parameters and model_parameters["response_format"] in {"JSON", "XML"}: - stop = stop or [] - if model_mode == LLMMode.CHAT: - # chat model - self._transform_chat_json_prompts( - model=base_model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - response_format=model_parameters["response_format"], - ) - else: - self._transform_completion_json_prompts( - model=base_model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - response_format=model_parameters["response_format"], - ) - model_parameters.pop("response_format") - - return self._invoke( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _transform_chat_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts - """ - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - - # 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=OPENAI_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}\n")) - else: - # insert the system message - prompt_messages.insert( - 0, - SystemPromptMessage( - content=OPENAI_BLOCK_MODE_PROMPT.replace( - "{{instructions}}", f"Please output a valid {response_format} object." - ).replace("{{block}}", response_format) - ), - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - - def _transform_completion_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts - """ - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - - # override the last user message - user_message = None - for i in range(len(prompt_messages) - 1, -1, -1): - if isinstance(prompt_messages[i], UserPromptMessage): - user_message = prompt_messages[i] - break - - if user_message: - if prompt_messages[i].content[-11:] == "Assistant: ": - # now we are in the chat app, remove the last assistant message - prompt_messages[i].content = prompt_messages[i].content[:-11] - prompt_messages[i] = UserPromptMessage( - content=OPENAI_BLOCK_MODE_PROMPT.replace("{{instructions}}", user_message.content).replace( - "{{block}}", response_format - ) - ) - prompt_messages[i].content += f"Assistant:\n```{response_format}\n" - else: - prompt_messages[i] = UserPromptMessage( - content=OPENAI_BLOCK_MODE_PROMPT.replace("{{instructions}}", user_message.content).replace( - "{{block}}", response_format - ) - ) - prompt_messages[i].content += f"\n```{response_format}\n" - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - # handle fine tune remote models - if model.startswith("ft:"): - base_model = model.split(":")[1] - else: - base_model = model - - # get model mode - model_mode = self.get_model_mode(model) - - if model_mode == LLMMode.CHAT: - # chat model - return self._num_tokens_from_messages(base_model, prompt_messages, tools) - else: - # text completion model, do not support tool calling - return self._num_tokens_from_string(base_model, prompt_messages[0].content) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - # handle fine tune remote models - base_model = model - # fine-tuned model name likes ft:gpt-3.5-turbo-0613:personal::xxxxx - if model.startswith("ft:"): - base_model = model.split(":")[1] - - # check if model exists - remote_models = self.remote_models(credentials) - remote_model_map = {model.model: model for model in remote_models} - if model not in remote_model_map: - raise CredentialsValidateFailedError(f"Fine-tuned model {model} not found") - - # get model mode - model_mode = self.get_model_mode(base_model, credentials) - - if model_mode == LLMMode.CHAT: - # chat model - client.chat.completions.create( - messages=[{"role": "user", "content": "ping"}], - model=model, - temperature=0, - max_tokens=20, - stream=False, - ) - else: - # text completion model - client.completions.create( - prompt="ping", - model=model, - temperature=0, - max_tokens=20, - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def remote_models(self, credentials: dict) -> list[AIModelEntity]: - """ - Return remote models if credentials are provided. - - :param credentials: provider credentials - :return: - """ - # get predefined models - predefined_models = self.predefined_models() - predefined_models_map = {model.model: model for model in predefined_models} - - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - # get all remote models - remote_models = client.models.list() - - fine_tune_models = [model for model in remote_models if model.id.startswith("ft:")] - - ai_model_entities = [] - for model in fine_tune_models: - base_model = model.id.split(":")[1] - - base_model_schema = None - for predefined_model_name, predefined_model in predefined_models_map.items(): - if predefined_model_name in base_model: - base_model_schema = predefined_model - - if not base_model_schema: - continue - - ai_model_entity = AIModelEntity( - model=model.id, - label=I18nObject(zh_Hans=model.id, en_US=model.id), - model_type=ModelType.LLM, - features=base_model_schema.features, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties=base_model_schema.model_properties, - parameter_rules=base_model_schema.parameter_rules, - pricing=PriceConfig(input=0.003, output=0.006, unit=0.001, currency="USD"), - ) - - ai_model_entities.append(ai_model_entity) - - return ai_model_entities - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm completion model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # init model client - client = OpenAI(**credentials_kwargs) - - extra_model_kwargs = {} - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - if stream: - extra_model_kwargs["stream_options"] = {"include_usage": True} - - # text completion model - response = client.completions.create( - prompt=prompt_messages[0].content, model=model, stream=stream, **model_parameters, **extra_model_kwargs - ) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: Completion, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm completion response - - :param model: model name - :param credentials: model credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm result - """ - assistant_text = response.choices[0].text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_text) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - completion_tokens = self._num_tokens_from_string(model, assistant_text) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=response.model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return result - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: Stream[Completion], prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm completion stream response - - :param model: model name - :param credentials: model credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - full_text = "" - prompt_tokens = 0 - completion_tokens = 0 - - final_chunk = LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=""), - ), - ) - - for chunk in response: - if len(chunk.choices) == 0: - if chunk.usage: - # calculate num tokens - prompt_tokens = chunk.usage.prompt_tokens - completion_tokens = chunk.usage.completion_tokens - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.text is None or delta.text == ""): - continue - - # transform assistant message to prompt message - text = delta.text or "" - assistant_prompt_message = AssistantPromptMessage(content=text) - - full_text += text - - if delta.finish_reason is not None: - final_chunk = LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - if not prompt_tokens: - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - - if not completion_tokens: - completion_tokens = self._num_tokens_from_string(model, full_text) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - final_chunk.delta.usage = usage - - yield final_chunk - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm chat model - - :param model: model name - :param credentials: 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 - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # init model client - client = OpenAI(**credentials_kwargs) - - response_format = model_parameters.get("response_format") - if response_format: - if response_format == "json_schema": - json_schema = model_parameters.get("json_schema") - if not json_schema: - raise ValueError("Must define JSON Schema when the response format is json_schema") - try: - schema = json.loads(json_schema) - except: - raise ValueError(f"not correct json_schema format: {json_schema}") - model_parameters.pop("json_schema") - model_parameters["response_format"] = {"type": "json_schema", "json_schema": schema} - else: - model_parameters["response_format"] = {"type": response_format} - - extra_model_kwargs = {} - - if tools: - # extra_model_kwargs['tools'] = [helper.dump_model(PromptMessageFunction(function=tool)) for tool in tools] - extra_model_kwargs["functions"] = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} for tool in tools - ] - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - if stream: - extra_model_kwargs["stream_options"] = {"include_usage": True} - - # clear illegal prompt messages - prompt_messages = self._clear_illegal_prompt_messages(model, prompt_messages) - - block_as_stream = False - if model.startswith("o1"): - if stream: - block_as_stream = True - stream = False - - if "stream_options" in extra_model_kwargs: - del extra_model_kwargs["stream_options"] - - if "stop" in extra_model_kwargs: - del extra_model_kwargs["stop"] - - # chat model - response = client.chat.completions.create( - messages=[self._convert_prompt_message_to_dict(m) for m in prompt_messages], - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages, tools) - - block_result = self._handle_chat_generate_response(model, credentials, response, prompt_messages, tools) - - if block_as_stream: - return self._handle_chat_block_as_stream_response(block_result, prompt_messages, stop) - - return block_result - - def _handle_chat_block_as_stream_response( - self, - block_result: LLMResult, - prompt_messages: list[PromptMessage], - stop: Optional[list[str]] = None, - ) -> Generator[LLMResultChunk, None, None]: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :param stop: stop words - :return: llm response chunk generator - """ - text = block_result.message.content - text = cast(str, text) - - if stop: - text = self.enforce_stop_tokens(text, stop) - - yield LLMResultChunk( - model=block_result.model, - prompt_messages=prompt_messages, - system_fingerprint=block_result.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=text), - finish_reason="stop", - usage=block_result.usage, - ), - ) - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: ChatCompletion, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - assistant_message = response.choices[0].message - # assistant_message_tool_calls = assistant_message.tool_calls - assistant_message_function_call = assistant_message.function_call - - # extract tool calls from response - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - completion_tokens = self._num_tokens_from_messages(model, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=response.model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return response - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Stream[ChatCompletionChunk], - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response chunk generator - """ - full_assistant_content = "" - delta_assistant_message_function_call_storage: ChoiceDeltaFunctionCall = None - prompt_tokens = 0 - completion_tokens = 0 - final_tool_calls = [] - final_chunk = LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=""), - ), - ) - - for chunk in response: - if len(chunk.choices) == 0: - if chunk.usage: - # calculate num tokens - prompt_tokens = chunk.usage.prompt_tokens - completion_tokens = chunk.usage.completion_tokens - continue - - delta = chunk.choices[0] - has_finish_reason = delta.finish_reason is not None - - if ( - not has_finish_reason - and (delta.delta.content is None or delta.delta.content == "") - and delta.delta.function_call is None - ): - continue - - # assistant_message_tool_calls = delta.delta.tool_calls - assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if delta_assistant_message_function_call_storage is not None: - # handle process of stream function call - if assistant_message_function_call: - # message has not ended ever - delta_assistant_message_function_call_storage.arguments += assistant_message_function_call.arguments - continue - else: - # message has ended - assistant_message_function_call = delta_assistant_message_function_call_storage - delta_assistant_message_function_call_storage = None - else: - if assistant_message_function_call: - # start of stream function call - delta_assistant_message_function_call_storage = assistant_message_function_call - if delta_assistant_message_function_call_storage.arguments is None: - delta_assistant_message_function_call_storage.arguments = "" - if not has_finish_reason: - continue - - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - if tool_calls: - final_tool_calls.extend(tool_calls) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.delta.content or "", tool_calls=tool_calls) - - full_assistant_content += delta.delta.content or "" - - if has_finish_reason: - final_chunk = LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - if not prompt_tokens: - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - - if not completion_tokens: - full_assistant_prompt_message = AssistantPromptMessage( - content=full_assistant_content, tool_calls=final_tool_calls - ) - completion_tokens = self._num_tokens_from_messages(model, [full_assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - final_chunk.delta.usage = usage - - yield final_chunk - - def _extract_response_tool_calls( - self, response_tool_calls: list[ChatCompletionMessageToolCall | ChoiceDeltaToolCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - - return tool_calls - - def _extract_response_function_call( - self, response_function_call: FunctionCall | ChoiceDeltaFunctionCall - ) -> AssistantPromptMessage.ToolCall: - """ - Extract function call from response - - :param response_function_call: response function call - :return: tool call - """ - tool_call = None - if response_function_call: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function_call.name, arguments=response_function_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_function_call.name, type="function", function=function - ) - - return tool_call - - def _clear_illegal_prompt_messages(self, model: str, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: - """ - Clear illegal prompt messages for OpenAI API - - :param model: model name - :param prompt_messages: prompt messages - :return: cleaned prompt messages - """ - checklist = ["gpt-4-turbo", "gpt-4-turbo-2024-04-09"] - - if model in checklist: - # count how many user messages are there - user_message_count = len([m for m in prompt_messages if isinstance(m, UserPromptMessage)]) - if user_message_count > 1: - for prompt_message in prompt_messages: - if isinstance(prompt_message, UserPromptMessage): - if isinstance(prompt_message.content, list): - prompt_message.content = "\n".join( - [ - item.data - if item.type == PromptMessageContentType.TEXT - else "[IMAGE]" - if item.type == PromptMessageContentType.IMAGE - else "" - for item in prompt_message.content - ] - ) - - if model.startswith("o1"): - system_message_count = len([m for m in prompt_messages if isinstance(m, SystemPromptMessage)]) - if system_message_count > 0: - new_prompt_messages = [] - for prompt_message in prompt_messages: - if isinstance(prompt_message, SystemPromptMessage): - prompt_message = UserPromptMessage( - content=prompt_message.content, - name=prompt_message.name, - ) - - new_prompt_messages.append(prompt_message) - prompt_messages = new_prompt_messages - - return prompt_messages - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for OpenAI API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - # message_dict["tool_calls"] = [tool_call.dict() for tool_call in - # message.tool_calls] - function_call = message.tool_calls[0] - message_dict["function_call"] = { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - # message_dict = { - # "role": "tool", - # "content": message.content, - # "tool_call_id": message.tool_call_id - # } - message_dict = {"role": "function", "content": message.content, "name": message.tool_call_id} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _num_tokens_from_string(self, model: str, text: str, tools: Optional[list[PromptMessageTool]] = None) -> int: - """ - Calculate num tokens for text completion model with tiktoken package. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - try: - encoding = tiktoken.encoding_for_model(model) - except KeyError: - encoding = tiktoken.get_encoding("cl100k_base") - - num_tokens = len(encoding.encode(text)) - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - def _num_tokens_from_messages( - self, model: str, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. - - Official documentation: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - if model.startswith("ft:"): - model = model.split(":")[1] - - # Currently, we can use gpt4o to calculate chatgpt-4o-latest's token. - if model == "chatgpt-4o-latest" or model.startswith("o1"): - model = "gpt-4o" - - try: - encoding = tiktoken.encoding_for_model(model) - except KeyError: - logger.warning("Warning: model not found. Using cl100k_base encoding.") - model = "cl100k_base" - encoding = tiktoken.get_encoding(model) - - if model.startswith("gpt-3.5-turbo-0301"): - # every message follows {role/name}\n{content}\n - tokens_per_message = 4 - # if there's a name, the role is omitted - tokens_per_name = -1 - elif model.startswith("gpt-3.5-turbo") or model.startswith("gpt-4") or model.startswith("o1"): - tokens_per_message = 3 - tokens_per_name = 1 - else: - raise NotImplementedError( - f"get_num_tokens_from_messages() is not presently implemented " - f"for model {model}." - "See https://platform.openai.com/docs/advanced-usage/managing-tokens for " - "information on how messages are converted to tokens." - ) - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += len(encoding.encode(t_key)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(encoding.encode(f_key)) - num_tokens += len(encoding.encode(f_value)) - else: - num_tokens += len(encoding.encode(t_key)) - num_tokens += len(encoding.encode(t_value)) - else: - num_tokens += len(encoding.encode(str(value))) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - def _num_tokens_for_tools(self, encoding: tiktoken.Encoding, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling with tiktoken package. - - :param encoding: encoding - :param tools: tools for tool calling - :return: number of tokens - """ - num_tokens = 0 - for tool in tools: - num_tokens += len(encoding.encode("type")) - num_tokens += len(encoding.encode("function")) - - # calculate num tokens for function object - num_tokens += len(encoding.encode("name")) - num_tokens += len(encoding.encode(tool.name)) - num_tokens += len(encoding.encode("description")) - num_tokens += len(encoding.encode(tool.description)) - parameters = tool.parameters - num_tokens += len(encoding.encode("parameters")) - if "title" in parameters: - num_tokens += len(encoding.encode("title")) - num_tokens += len(encoding.encode(parameters.get("title"))) - num_tokens += len(encoding.encode("type")) - num_tokens += len(encoding.encode(parameters.get("type"))) - if "properties" in parameters: - num_tokens += len(encoding.encode("properties")) - for key, value in parameters.get("properties").items(): - num_tokens += len(encoding.encode(key)) - for field_key, field_value in value.items(): - num_tokens += len(encoding.encode(field_key)) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += len(encoding.encode(enum_field)) - else: - num_tokens += len(encoding.encode(field_key)) - num_tokens += len(encoding.encode(str(field_value))) - if "required" in parameters: - num_tokens += len(encoding.encode("required")) - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += len(encoding.encode(required_field)) - - return num_tokens - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - OpenAI supports fine-tuning of their models. This method returns the schema of the base model - but renamed to the fine-tuned model name. - - :param model: model name - :param credentials: credentials - - :return: model schema - """ - if not model.startswith("ft:"): - base_model = model - else: - # get base_model - base_model = model.split(":")[1] - - # get model schema - models = self.predefined_models() - model_map = {model.model: model for model in models} - if base_model not in model_map: - raise ValueError(f"Base model {base_model} not found") - - base_model_schema = model_map[base_model] - - base_model_schema_features = base_model_schema.features or [] - base_model_schema_model_properties = base_model_schema.model_properties or {} - base_model_schema_parameters_rules = base_model_schema.parameter_rules or [] - - entity = AIModelEntity( - model=model, - label=I18nObject(zh_Hans=model, en_US=model), - model_type=ModelType.LLM, - features=list(base_model_schema_features), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties=dict(base_model_schema_model_properties.items()), - parameter_rules=list(base_model_schema_parameters_rules), - pricing=base_model_schema.pricing, - ) - - return entity diff --git a/api/core/model_runtime/model_providers/openai/llm/o1-mini-2024-09-12.yaml b/api/core/model_runtime/model_providers/openai/llm/o1-mini-2024-09-12.yaml deleted file mode 100644 index 0ade7f8ded..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/o1-mini-2024-09-12.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: o1-mini-2024-09-12 -label: - zh_Hans: o1-mini-2024-09-12 - en_US: o1-mini-2024-09-12 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - default: 65536 - min: 1 - max: 65536 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '3.00' - output: '12.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/o1-mini.yaml b/api/core/model_runtime/model_providers/openai/llm/o1-mini.yaml deleted file mode 100644 index 60816c5d1e..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/o1-mini.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: o1-mini -label: - zh_Hans: o1-mini - en_US: o1-mini -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - default: 65536 - min: 1 - max: 65536 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '3.00' - output: '12.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/o1-preview-2024-09-12.yaml b/api/core/model_runtime/model_providers/openai/llm/o1-preview-2024-09-12.yaml deleted file mode 100644 index c9da96f611..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/o1-preview-2024-09-12.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: o1-preview-2024-09-12 -label: - zh_Hans: o1-preview-2024-09-12 - en_US: o1-preview-2024-09-12 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - default: 32768 - min: 1 - max: 32768 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '15.00' - output: '60.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/o1-preview.yaml b/api/core/model_runtime/model_providers/openai/llm/o1-preview.yaml deleted file mode 100644 index c83874b765..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/o1-preview.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: o1-preview -label: - zh_Hans: o1-preview - en_US: o1-preview -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - default: 32768 - min: 1 - max: 32768 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '15.00' - output: '60.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/llm/text-davinci-003.yaml b/api/core/model_runtime/model_providers/openai/llm/text-davinci-003.yaml deleted file mode 100644 index 76b5d84875..0000000000 --- a/api/core/model_runtime/model_providers/openai/llm/text-davinci-003.yaml +++ /dev/null @@ -1,29 +0,0 @@ -model: text-davinci-003 -label: - zh_Hans: text-davinci-003 - en_US: text-davinci-003 -model_type: llm -features: [ ] -model_properties: - mode: completion - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 -pricing: - input: '0.001' - output: '0.002' - unit: '0.001' - currency: USD -deprecated: true diff --git a/api/core/model_runtime/model_providers/openai/moderation/moderation.py b/api/core/model_runtime/model_providers/openai/moderation/moderation.py index 619044d808..a83248c0c2 100644 --- a/api/core/model_runtime/model_providers/openai/moderation/moderation.py +++ b/api/core/model_runtime/model_providers/openai/moderation/moderation.py @@ -1,15 +1,25 @@ +from collections.abc import Mapping from typing import Optional +import openai +from httpx import Timeout from openai import OpenAI from openai.types import ModerationCreateResponse from core.model_runtime.entities.model_entities import ModelPropertyKey +from core.model_runtime.errors.invoke import ( + InvokeAuthorizationError, + InvokeBadRequestError, + InvokeConnectionError, + InvokeError, + InvokeRateLimitError, + InvokeServerUnavailableError, +) from core.model_runtime.errors.validate import CredentialsValidateFailedError from core.model_runtime.model_providers.__base.moderation_model import ModerationModel -from core.model_runtime.model_providers.openai._common import _CommonOpenAI -class OpenAIModerationModel(_CommonOpenAI, ModerationModel): +class OpenAIModerationModel(ModerationModel): """ Model class for OpenAI text moderation model. """ @@ -111,3 +121,48 @@ class OpenAIModerationModel(_CommonOpenAI, ModerationModel): return model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] return 1 + + def _to_credential_kwargs(self, credentials: Mapping) -> dict: + """ + Transform credentials to kwargs for model instance + + :param credentials: + :return: + """ + credentials_kwargs = { + "api_key": credentials["openai_api_key"], + "timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0), + "max_retries": 1, + } + + if credentials.get("openai_api_base"): + openai_api_base = credentials["openai_api_base"].rstrip("/") + credentials_kwargs["base_url"] = openai_api_base + "/v1" + + if "openai_organization" in credentials: + credentials_kwargs["organization"] = credentials["openai_organization"] + + return credentials_kwargs + + @property + def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: + """ + Map model invoke error to unified error + The key is the error type thrown to the caller + The value is the error type thrown by the model, + which needs to be converted into a unified error type for the caller. + + :return: Invoke error mapping + """ + return { + InvokeConnectionError: [openai.APIConnectionError, openai.APITimeoutError], + InvokeServerUnavailableError: [openai.InternalServerError], + InvokeRateLimitError: [openai.RateLimitError], + InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError], + InvokeBadRequestError: [ + openai.BadRequestError, + openai.NotFoundError, + openai.UnprocessableEntityError, + openai.APIError, + ], + } diff --git a/api/core/model_runtime/model_providers/openai/moderation/text-moderation-stable.yaml b/api/core/model_runtime/model_providers/openai/moderation/text-moderation-stable.yaml deleted file mode 100644 index 5ca1809167..0000000000 --- a/api/core/model_runtime/model_providers/openai/moderation/text-moderation-stable.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: text-moderation-stable -model_type: moderation -model_properties: - max_chunks: 32 - max_characters_per_chunk: 2000 diff --git a/api/core/model_runtime/model_providers/openai/openai.py b/api/core/model_runtime/model_providers/openai/openai.py deleted file mode 100644 index 175d7db73c..0000000000 --- a/api/core/model_runtime/model_providers/openai/openai.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -from collections.abc import Mapping - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OpenAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: Mapping) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `gpt-3.5-turbo` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="gpt-3.5-turbo", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/openai/openai.yaml b/api/core/model_runtime/model_providers/openai/openai.yaml deleted file mode 100644 index b4dc8fd4f2..0000000000 --- a/api/core/model_runtime/model_providers/openai/openai.yaml +++ /dev/null @@ -1,89 +0,0 @@ -provider: openai -label: - en_US: OpenAI -description: - en_US: Models provided by OpenAI, such as GPT-3.5-Turbo and GPT-4. - zh_Hans: OpenAI 提供的模型,例如 GPT-3.5-Turbo 和 GPT-4。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#E5E7EB" -help: - title: - en_US: Get your API Key from OpenAI - zh_Hans: 从 OpenAI 获取 API Key - url: - en_US: https://platform.openai.com/account/api-keys -supported_model_types: - - llm - - text-embedding - - speech2text - - moderation - - tts -configurate_methods: - - predefined-model - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: openai_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: openai_organization - label: - zh_Hans: 组织 ID - en_US: Organization - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的组织 ID - en_US: Enter your Organization ID - - variable: openai_api_base - label: - zh_Hans: API Base - en_US: API Base - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的 API Base - en_US: Enter your API Base -provider_credential_schema: - credential_form_schemas: - - variable: openai_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: openai_organization - label: - zh_Hans: 组织 ID - en_US: Organization - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的组织 ID - en_US: Enter your Organization ID - - variable: openai_api_base - label: - zh_Hans: API Base - en_US: API Base - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的 API Base, 如:https://api.openai.com - en_US: Enter your API Base, e.g. https://api.openai.com diff --git a/api/core/model_runtime/model_providers/openai/speech2text/__init__.py b/api/core/model_runtime/model_providers/openai/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/speech2text/speech2text.py b/api/core/model_runtime/model_providers/openai/speech2text/speech2text.py deleted file mode 100644 index 18f97e45f3..0000000000 --- a/api/core/model_runtime/model_providers/openai/speech2text/speech2text.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import IO, Optional - -from openai import OpenAI - -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from core.model_runtime.model_providers.openai._common import _CommonOpenAI - - -class OpenAISpeech2TextModel(_CommonOpenAI, Speech2TextModel): - """ - Model class for OpenAI Speech to text model. - """ - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - return self._speech2text_invoke(model, credentials, file) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self._speech2text_invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _speech2text_invoke(self, model: str, credentials: dict, file: IO[bytes]) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :return: text for given audio file - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # init model client - client = OpenAI(**credentials_kwargs) - - response = client.audio.transcriptions.create(model=model, file=file) - - return response.text diff --git a/api/core/model_runtime/model_providers/openai/speech2text/whisper-1.yaml b/api/core/model_runtime/model_providers/openai/speech2text/whisper-1.yaml deleted file mode 100644 index 6c14c76619..0000000000 --- a/api/core/model_runtime/model_providers/openai/speech2text/whisper-1.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: whisper-1 -model_type: speech2text -model_properties: - file_upload_limit: 25 - supported_file_extensions: flac,mp3,mp4,mpeg,mpga,m4a,ogg,wav,webm diff --git a/api/core/model_runtime/model_providers/openai/text_embedding/__init__.py b/api/core/model_runtime/model_providers/openai/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-large.yaml b/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-large.yaml deleted file mode 100644 index 9489170fde..0000000000 --- a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-large.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: text-embedding-3-large -model_type: text-embedding -model_properties: - context_size: 8191 - max_chunks: 32 -pricing: - input: '0.00013' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-small.yaml b/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-small.yaml deleted file mode 100644 index 586ba2b28f..0000000000 --- a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-3-small.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: text-embedding-3-small -model_type: text-embedding -model_properties: - context_size: 8191 - max_chunks: 32 -pricing: - input: '0.00002' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-ada-002.yaml b/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-ada-002.yaml deleted file mode 100644 index ef1c49b017..0000000000 --- a/api/core/model_runtime/model_providers/openai/text_embedding/text-embedding-ada-002.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: text-embedding-ada-002 -model_type: text-embedding -model_properties: - context_size: 8097 - max_chunks: 32 -pricing: - input: '0.0001' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/tts/__init__.py b/api/core/model_runtime/model_providers/openai/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai/tts/tts-1-hd.yaml b/api/core/model_runtime/model_providers/openai/tts/tts-1-hd.yaml deleted file mode 100644 index 449c131f9d..0000000000 --- a/api/core/model_runtime/model_providers/openai/tts/tts-1-hd.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: tts-1-hd -model_type: tts -model_properties: - default_voice: 'alloy' - voices: - - mode: 'alloy' - name: 'Alloy' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - - mode: 'echo' - name: 'Echo' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - - mode: 'fable' - name: 'Fable' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - - mode: 'onyx' - name: 'Onyx' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - - mode: 'nova' - name: 'Nova' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - - mode: 'shimmer' - name: 'Shimmer' - language: [ 'zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID' ] - word_limit: 3500 - audio_type: 'mp3' - max_workers: 5 -pricing: - input: '0.03' - output: '0' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/tts/tts-1.yaml b/api/core/model_runtime/model_providers/openai/tts/tts-1.yaml deleted file mode 100644 index 83969fb2f7..0000000000 --- a/api/core/model_runtime/model_providers/openai/tts/tts-1.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: tts-1 -model_type: tts -model_properties: - default_voice: 'alloy' - voices: - - mode: 'alloy' - name: 'Alloy' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - - mode: 'echo' - name: 'Echo' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - - mode: 'fable' - name: 'Fable' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - - mode: 'onyx' - name: 'Onyx' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - - mode: 'nova' - name: 'Nova' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - - mode: 'shimmer' - name: 'Shimmer' - language: ['zh-Hans', 'en-US', 'de-DE', 'fr-FR', 'es-ES', 'it-IT', 'th-TH', 'id-ID'] - word_limit: 3500 - audio_type: 'mp3' - max_workers: 5 -pricing: - input: '0.015' - output: '0' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openai/tts/tts.py b/api/core/model_runtime/model_providers/openai/tts/tts.py deleted file mode 100644 index a14c91639b..0000000000 --- a/api/core/model_runtime/model_providers/openai/tts/tts.py +++ /dev/null @@ -1,118 +0,0 @@ -import concurrent.futures -from typing import Optional - -from openai import OpenAI - -from core.model_runtime.errors.invoke import InvokeBadRequestError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.tts_model import TTSModel -from core.model_runtime.model_providers.openai._common import _CommonOpenAI - - -class OpenAIText2SpeechModel(_CommonOpenAI, TTSModel): - """ - Model class for OpenAI Speech to text model. - """ - - def _invoke( - self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ) -> any: - """ - _invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :param user: unique user id - :return: text translated to audio file - """ - - if not voice or voice not in [ - d["value"] for d in self.get_tts_model_voices(model=model, credentials=credentials) - ]: - voice = self._get_model_default_voice(model, credentials) - # if streaming: - return self._tts_invoke_streaming(model=model, credentials=credentials, content_text=content_text, voice=voice) - - def validate_credentials(self, model: str, credentials: dict, user: Optional[str] = None) -> None: - """ - validate credentials text2speech model - - :param model: model name - :param credentials: model credentials - :param user: unique user id - :return: text translated to audio file - """ - try: - self._tts_invoke_streaming( - model=model, - credentials=credentials, - content_text="Hello Dify!", - voice=self._get_model_default_voice(model, credentials), - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: - """ - _tts_invoke_streaming text2speech model - - :param model: model name - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :return: text translated to audio file - """ - try: - # doc: https://platform.openai.com/docs/guides/text-to-speech - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - model_support_voice = [ - x.get("value") for x in self.get_tts_model_voices(model=model, credentials=credentials) - ] - if not voice or voice not in model_support_voice: - voice = self._get_model_default_voice(model, credentials) - word_limit = self._get_model_word_limit(model, credentials) - if len(content_text) > word_limit: - sentences = self._split_text_into_sentences(content_text, max_length=word_limit) - executor = concurrent.futures.ThreadPoolExecutor(max_workers=min(3, len(sentences))) - futures = [ - executor.submit( - client.audio.speech.with_streaming_response.create, - model=model, - response_format="mp3", - input=sentences[i], - voice=voice, - ) - for i in range(len(sentences)) - ] - for future in futures: - yield from future.result().__enter__().iter_bytes(1024) # noqa:PLC2801 - - else: - response = client.audio.speech.with_streaming_response.create( - model=model, voice=voice, response_format="mp3", input=content_text.strip() - ) - - yield from response.__enter__().iter_bytes(1024) # noqa:PLC2801 - except Exception as ex: - raise InvokeBadRequestError(str(ex)) - - def _process_sentence(self, sentence: str, model: str, voice, credentials: dict): - """ - _tts_invoke openai text2speech model api - - :param model: model name - :param credentials: model credentials - :param voice: model timbre - :param sentence: text content to be translated - :return: text translated to audio file - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - response = client.audio.speech.create(model=model, voice=voice, input=sentence.strip()) - if isinstance(response.read(), bytes): - return response.read() diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/__init__.py b/api/core/model_runtime/model_providers/openai_api_compatible/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/_common.py b/api/core/model_runtime/model_providers/openai_api_compatible/_common.py deleted file mode 100644 index 1234e44f80..0000000000 --- a/api/core/model_runtime/model_providers/openai_api_compatible/_common.py +++ /dev/null @@ -1,43 +0,0 @@ -import requests - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonOaiApiCompat: - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeAuthorizationError: [ - requests.exceptions.InvalidHeader, # Missing or Invalid API Key - ], - InvokeBadRequestError: [ - requests.exceptions.HTTPError, # Invalid Endpoint URL or model name - requests.exceptions.InvalidURL, # Misconfigured request or other API error - ], - InvokeRateLimitError: [ - requests.exceptions.RetryError # Too many requests sent in a short period of time - ], - InvokeServerUnavailableError: [ - requests.exceptions.ConnectionError, # Engine Overloaded - requests.exceptions.HTTPError, # Server Error - ], - InvokeConnectionError: [ - requests.exceptions.ConnectTimeout, # Timeout - requests.exceptions.ReadTimeout, # Timeout - ], - } diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/llm/__init__.py b/api/core/model_runtime/model_providers/openai_api_compatible/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/llm/llm.py b/api/core/model_runtime/model_providers/openai_api_compatible/llm/llm.py deleted file mode 100644 index c2ffe653c8..0000000000 --- a/api/core/model_runtime/model_providers/openai_api_compatible/llm/llm.py +++ /dev/null @@ -1,828 +0,0 @@ -import json -import logging -from collections.abc import Generator -from decimal import Decimal -from typing import Optional, Union, cast -from urllib.parse import urljoin - -import requests - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContent, - PromptMessageContentType, - PromptMessageFunction, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, - PriceConfig, -) -from core.model_runtime.errors.invoke import InvokeError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.openai_api_compatible._common import _CommonOaiApiCompat -from core.model_runtime.utils import helper - -logger = logging.getLogger(__name__) - - -class OAIAPICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel): - """ - Model class for OpenAI large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - - # text completion model - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: - :param credentials: - :param prompt_messages: - :param tools: tools for tool calling - :return: - """ - return self._num_tokens_from_messages(model, prompt_messages, tools, credentials) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials using requests to ensure compatibility with all providers following - OpenAI's API standard. - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - headers = {"Content-Type": "application/json"} - - api_key = credentials.get("api_key") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - endpoint_url = credentials["endpoint_url"] - if not endpoint_url.endswith("/"): - endpoint_url += "/" - - # prepare the payload for a simple ping to the model - data = {"model": model, "max_tokens": 5} - - completion_type = LLMMode.value_of(credentials["mode"]) - - if completion_type is LLMMode.CHAT: - data["messages"] = [ - {"role": "user", "content": "ping"}, - ] - endpoint_url = urljoin(endpoint_url, "chat/completions") - elif completion_type is LLMMode.COMPLETION: - data["prompt"] = "ping" - endpoint_url = urljoin(endpoint_url, "completions") - else: - raise ValueError("Unsupported completion type for model configuration.") - - # send a post request to validate the credentials - response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300)) - - if response.status_code != 200: - raise CredentialsValidateFailedError( - f"Credentials validation failed with status code {response.status_code}" - ) - - try: - json_result = response.json() - except json.JSONDecodeError as e: - raise CredentialsValidateFailedError("Credentials validation failed: JSON decode error") - - if completion_type is LLMMode.CHAT and json_result.get("object", "") == "": - json_result["object"] = "chat.completion" - elif completion_type is LLMMode.COMPLETION and json_result.get("object", "") == "": - json_result["object"] = "text_completion" - - if completion_type is LLMMode.CHAT and ( - "object" not in json_result or json_result["object"] != "chat.completion" - ): - raise CredentialsValidateFailedError( - "Credentials validation failed: invalid response object, must be 'chat.completion'" - ) - elif completion_type is LLMMode.COMPLETION and ( - "object" not in json_result or json_result["object"] != "text_completion" - ): - raise CredentialsValidateFailedError( - "Credentials validation failed: invalid response object, must be 'text_completion'" - ) - except CredentialsValidateFailedError: - raise - except Exception as ex: - raise CredentialsValidateFailedError(f"An error occurred during credentials validation: {str(ex)}") - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - """ - generate custom model entities from credentials - """ - features = [] - - function_calling_type = credentials.get("function_calling_type", "no_call") - if function_calling_type == "function_call": - features.append(ModelFeature.TOOL_CALL) - elif function_calling_type == "tool_call": - features.append(ModelFeature.MULTI_TOOL_CALL) - - stream_function_calling = credentials.get("stream_function_calling", "supported") - if stream_function_calling == "supported": - features.append(ModelFeature.STREAM_TOOL_CALL) - - vision_support = credentials.get("vision_support", "not_support") - if vision_support == "support": - features.append(ModelFeature.VISION) - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - model_type=ModelType.LLM, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - features=features, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", "4096")), - ModelPropertyKey.MODE: credentials.get("mode"), - }, - parameter_rules=[ - ParameterRule( - name=DefaultParameterName.TEMPERATURE.value, - label=I18nObject(en_US="Temperature", zh_Hans="温度"), - help=I18nObject( - en_US="Kernel sampling threshold. Used to determine the randomness of the results." - "The higher the value, the stronger the randomness." - "The higher the possibility of getting different answers to the same question.", - zh_Hans="核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。", - ), - type=ParameterType.FLOAT, - default=float(credentials.get("temperature", 0.7)), - min=0, - max=2, - precision=2, - ), - ParameterRule( - name=DefaultParameterName.TOP_P.value, - label=I18nObject(en_US="Top P", zh_Hans="Top P"), - help=I18nObject( - en_US="The probability threshold of the nucleus sampling method during the generation process." - "The larger the value is, the higher the randomness of generation will be." - "The smaller the value is, the higher the certainty of generation will be.", - zh_Hans="生成过程中核采样方法概率阈值。取值越大,生成的随机性越高;取值越小,生成的确定性越高。", - ), - type=ParameterType.FLOAT, - default=float(credentials.get("top_p", 1)), - min=0, - max=1, - precision=2, - ), - ParameterRule( - name=DefaultParameterName.FREQUENCY_PENALTY.value, - label=I18nObject(en_US="Frequency Penalty", zh_Hans="频率惩罚"), - help=I18nObject( - en_US="For controlling the repetition rate of words used by the model." - "Increasing this can reduce the repetition of the same words in the model's output.", - zh_Hans="用于控制模型已使用字词的重复率。 提高此项可以降低模型在输出中重复相同字词的重复度。", - ), - type=ParameterType.FLOAT, - default=float(credentials.get("frequency_penalty", 0)), - min=-2, - max=2, - ), - ParameterRule( - name=DefaultParameterName.PRESENCE_PENALTY.value, - label=I18nObject(en_US="Presence Penalty", zh_Hans="存在惩罚"), - help=I18nObject( - en_US="Used to control the repetition rate when generating models." - "Increasing this can reduce the repetition rate of model generation.", - zh_Hans="用于控制模型生成时的重复度。提高此项可以降低模型生成的重复度。", - ), - type=ParameterType.FLOAT, - default=float(credentials.get("presence_penalty", 0)), - min=-2, - max=2, - ), - ParameterRule( - name=DefaultParameterName.MAX_TOKENS.value, - label=I18nObject(en_US="Max Tokens", zh_Hans="最大标记"), - help=I18nObject( - en_US="Maximum length of tokens for the model response.", zh_Hans="模型回答的tokens的最大长度。" - ), - type=ParameterType.INT, - default=512, - min=1, - max=int(credentials.get("max_tokens_to_sample", 4096)), - ), - ], - pricing=PriceConfig( - input=Decimal(credentials.get("input_price", 0)), - output=Decimal(credentials.get("output_price", 0)), - unit=Decimal(credentials.get("unit", 0)), - currency=credentials.get("currency", "USD"), - ), - ) - - if credentials["mode"] == "chat": - entity.model_properties[ModelPropertyKey.MODE] = LLMMode.CHAT.value - elif credentials["mode"] == "completion": - entity.model_properties[ModelPropertyKey.MODE] = LLMMode.COMPLETION.value - else: - raise ValueError(f"Unknown completion type {credentials['completion_type']}") - - return entity - - # validate_credentials method has been rewritten to use the requests library for compatibility with all providers - # following OpenAI's API standard. - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke llm completion model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - headers = { - "Content-Type": "application/json", - "Accept-Charset": "utf-8", - } - extra_headers = credentials.get("extra_headers") - if extra_headers is not None: - headers = { - **headers, - **extra_headers, - } - - api_key = credentials.get("api_key") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - endpoint_url = credentials["endpoint_url"] - if not endpoint_url.endswith("/"): - endpoint_url += "/" - - data = {"model": model, "stream": stream, **model_parameters} - - completion_type = LLMMode.value_of(credentials["mode"]) - - if completion_type is LLMMode.CHAT: - endpoint_url = urljoin(endpoint_url, "chat/completions") - data["messages"] = [self._convert_prompt_message_to_dict(m, credentials) for m in prompt_messages] - elif completion_type is LLMMode.COMPLETION: - endpoint_url = urljoin(endpoint_url, "completions") - data["prompt"] = prompt_messages[0].content - else: - raise ValueError("Unsupported completion type for model configuration.") - - # annotate tools with names, descriptions, etc. - function_calling_type = credentials.get("function_calling_type", "no_call") - formatted_tools = [] - if tools: - if function_calling_type == "function_call": - data["functions"] = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} - for tool in tools - ] - elif function_calling_type == "tool_call": - data["tool_choice"] = "auto" - - for tool in tools: - formatted_tools.append(helper.dump_model(PromptMessageFunction(function=tool))) - - data["tools"] = formatted_tools - - if stop: - data["stop"] = stop - - if user: - data["user"] = user - - response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300), stream=stream) - - if response.encoding is None or response.encoding == "ISO-8859-1": - response.encoding = "utf-8" - - if response.status_code != 200: - raise InvokeError(f"API request failed with status code {response.status_code}: {response.text}") - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: requests.Response, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: model credentials - :param response: streamed response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - full_assistant_content = "" - chunk_index = 0 - - def create_final_llm_result_chunk( - index: int, message: AssistantPromptMessage, finish_reason: str - ) -> LLMResultChunk: - # calculate num tokens - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - completion_tokens = self._num_tokens_from_string(model, full_assistant_content) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - return LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=message, finish_reason=finish_reason, usage=usage), - ) - - # delimiter for stream response, need unicode_escape - import codecs - - delimiter = credentials.get("stream_mode_delimiter", "\n\n") - delimiter = codecs.decode(delimiter, "unicode_escape") - - tools_calls: list[AssistantPromptMessage.ToolCall] = [] - - def increase_tool_call(new_tool_calls: list[AssistantPromptMessage.ToolCall]): - def get_tool_call(tool_call_id: str): - if not tool_call_id: - return tools_calls[-1] - - tool_call = next((tool_call for tool_call in tools_calls if tool_call.id == tool_call_id), None) - if tool_call is None: - tool_call = AssistantPromptMessage.ToolCall( - id=tool_call_id, - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction(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 - - finish_reason = None # The default value of finish_reason is None - - for chunk in response.iter_lines(decode_unicode=True, delimiter=delimiter): - chunk = chunk.strip() - if chunk: - # ignore sse comments - if chunk.startswith(":"): - continue - decoded_chunk = chunk.strip().lstrip("data: ").lstrip() - if decoded_chunk == "[DONE]": # Some provider returns "data: [DONE]" - continue - - try: - chunk_json = json.loads(decoded_chunk) - # stream ended - except json.JSONDecodeError as e: - yield create_final_llm_result_chunk( - index=chunk_index + 1, - message=AssistantPromptMessage(content=""), - finish_reason="Non-JSON encountered.", - ) - break - if not chunk_json or len(chunk_json["choices"]) == 0: - continue - - choice = chunk_json["choices"][0] - finish_reason = chunk_json["choices"][0].get("finish_reason") - chunk_index += 1 - - if "delta" in choice: - delta = choice["delta"] - delta_content = delta.get("content") - - assistant_message_tool_calls = None - - if "tool_calls" in delta and credentials.get("function_calling_type", "no_call") == "tool_call": - assistant_message_tool_calls = delta.get("tool_calls", None) - elif ( - "function_call" in delta - and credentials.get("function_calling_type", "no_call") == "function_call" - ): - assistant_message_tool_calls = [ - {"id": "tool_call_id", "type": "function", "function": delta.get("function_call", {})} - ] - - # assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if assistant_message_tool_calls: - tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - increase_tool_call(tool_calls) - - if delta_content is None or delta_content == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta_content, - ) - - # reset tool calls - tool_calls = [] - full_assistant_content += delta_content - elif "text" in choice: - choice_text = choice.get("text", "") - if choice_text == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=choice_text) - full_assistant_content += choice_text - else: - continue - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=assistant_prompt_message, - ), - ) - - chunk_index += 1 - - if tools_calls: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=AssistantPromptMessage(tool_calls=tools_calls, content=""), - ), - ) - - yield create_final_llm_result_chunk( - index=chunk_index, message=AssistantPromptMessage(content=""), finish_reason=finish_reason - ) - - def _handle_generate_response( - self, model: str, credentials: dict, response: requests.Response, prompt_messages: list[PromptMessage] - ) -> LLMResult: - response_json = response.json() - - completion_type = LLMMode.value_of(credentials["mode"]) - - output = response_json["choices"][0] - - response_content = "" - tool_calls = None - function_calling_type = credentials.get("function_calling_type", "no_call") - if completion_type is LLMMode.CHAT: - response_content = output.get("message", {})["content"] - if function_calling_type == "tool_call": - tool_calls = output.get("message", {}).get("tool_calls") - elif function_calling_type == "function_call": - tool_calls = output.get("message", {}).get("function_call") - - elif completion_type is LLMMode.COMPLETION: - response_content = output["text"] - - assistant_message = AssistantPromptMessage(content=response_content, tool_calls=[]) - - if tool_calls: - if function_calling_type == "tool_call": - assistant_message.tool_calls = self._extract_response_tool_calls(tool_calls) - elif function_calling_type == "function_call": - assistant_message.tool_calls = [self._extract_response_function_call(tool_calls)] - - usage = response_json.get("usage") - if usage: - # transform usage - prompt_tokens = usage["prompt_tokens"] - completion_tokens = usage["completion_tokens"] - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - completion_tokens = self._num_tokens_from_string(model, assistant_message.content) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - result = LLMResult( - model=response_json["model"], - prompt_messages=prompt_messages, - message=assistant_message, - usage=usage, - ) - - return result - - def _convert_prompt_message_to_dict(self, message: PromptMessage, credentials: Optional[dict] = None) -> dict: - """ - Convert PromptMessage to dict for OpenAI API format - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(PromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - function_calling_type = credentials.get("function_calling_type", "no_call") - if function_calling_type == "tool_call": - message_dict["tool_calls"] = [tool_call.dict() for tool_call in message.tool_calls] - elif function_calling_type == "function_call": - function_call = message.tool_calls[0] - message_dict["function_call"] = { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - function_calling_type = credentials.get("function_calling_type", "no_call") - if function_calling_type == "tool_call": - message_dict = {"role": "tool", "content": message.content, "tool_call_id": message.tool_call_id} - elif function_calling_type == "function_call": - message_dict = {"role": "function", "content": message.content, "name": message.tool_call_id} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name and message_dict.get("role", "") != "tool": - message_dict["name"] = message.name - - return message_dict - - def _num_tokens_from_string( - self, model: str, text: Union[str, list[PromptMessageContent]], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """ - Approximate num tokens for model with gpt2 tokenizer. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - if isinstance(text, str): - full_text = text - else: - full_text = "" - for message_content in text: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(PromptMessageContent, message_content) - full_text += message_content.data - - num_tokens = self._get_num_tokens_by_gpt2(full_text) - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_from_messages( - self, - model: str, - messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - credentials: dict = None, - ) -> int: - """ - Approximate num tokens with GPT2 tokenizer. - """ - - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m, credentials) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += self._get_num_tokens_by_gpt2(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += self._get_num_tokens_by_gpt2(f_key) - num_tokens += self._get_num_tokens_by_gpt2(f_value) - else: - num_tokens += self._get_num_tokens_by_gpt2(t_key) - num_tokens += self._get_num_tokens_by_gpt2(t_value) - else: - num_tokens += self._get_num_tokens_by_gpt2(str(value)) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_for_tools(self, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling with tiktoken package. - - :param tools: tools for tool calling - :return: number of tokens - """ - num_tokens = 0 - for tool in tools: - num_tokens += self._get_num_tokens_by_gpt2("type") - num_tokens += self._get_num_tokens_by_gpt2("function") - num_tokens += self._get_num_tokens_by_gpt2("function") - - # calculate num tokens for function object - num_tokens += self._get_num_tokens_by_gpt2("name") - num_tokens += self._get_num_tokens_by_gpt2(tool.name) - num_tokens += self._get_num_tokens_by_gpt2("description") - num_tokens += self._get_num_tokens_by_gpt2(tool.description) - parameters = tool.parameters - num_tokens += self._get_num_tokens_by_gpt2("parameters") - if "title" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("title") - num_tokens += self._get_num_tokens_by_gpt2(parameters.get("title")) - num_tokens += self._get_num_tokens_by_gpt2("type") - num_tokens += self._get_num_tokens_by_gpt2(parameters.get("type")) - if "properties" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("properties") - for key, value in parameters.get("properties").items(): - num_tokens += self._get_num_tokens_by_gpt2(key) - for field_key, field_value in value.items(): - num_tokens += self._get_num_tokens_by_gpt2(field_key) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += self._get_num_tokens_by_gpt2(enum_field) - else: - num_tokens += self._get_num_tokens_by_gpt2(field_key) - num_tokens += self._get_num_tokens_by_gpt2(str(field_value)) - if "required" in parameters: - num_tokens += self._get_num_tokens_by_gpt2("required") - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += self._get_num_tokens_by_gpt2(required_field) - - return num_tokens - - def _extract_response_tool_calls(self, response_tool_calls: list[dict]) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.get("function", {}).get("name", ""), - arguments=response_tool_call.get("function", {}).get("arguments", ""), - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.get("id", ""), type=response_tool_call.get("type", ""), function=function - ) - tool_calls.append(tool_call) - - return tool_calls - - def _extract_response_function_call(self, response_function_call) -> AssistantPromptMessage.ToolCall: - """ - Extract function call from response - - :param response_function_call: response function call - :return: tool call - """ - tool_call = None - if response_function_call: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function_call.get("name", ""), arguments=response_function_call.get("arguments", "") - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_function_call.get("id", ""), type="function", function=function - ) - - return tool_call diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.py b/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.py deleted file mode 100644 index ca6f185287..0000000000 --- a/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OAICompatProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.yaml b/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.yaml deleted file mode 100644 index 88c76fe16e..0000000000 --- a/api/core/model_runtime/model_providers/openai_api_compatible/openai_api_compatible.yaml +++ /dev/null @@ -1,162 +0,0 @@ -provider: openai_api_compatible -label: - en_US: OpenAI-API-compatible -description: - en_US: Model providers compatible with OpenAI's API standard, such as LM Studio. - zh_Hans: 兼容 OpenAI API 的模型供应商,例如 LM Studio 。 -supported_model_types: - - llm - - text-embedding - - speech2text -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter full model name - zh_Hans: 输入模型全称 - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: false - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: endpoint_url - label: - zh_Hans: API endpoint URL - en_US: API endpoint URL - type: text-input - required: true - placeholder: - zh_Hans: Base URL, e.g. https://api.openai.com/v1 - en_US: Base URL, e.g. https://api.openai.com/v1 - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - show_on: - - variable: __model_type - value: llm - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - show_on: - - variable: __model_type - value: text-embedding - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens_to_sample - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - show_on: - - variable: __model_type - value: llm - default: '4096' - type: text-input - - variable: function_calling_type - show_on: - - variable: __model_type - value: llm - label: - en_US: Function calling - type: select - required: false - default: no_call - options: - - value: function_call - label: - en_US: Function Call - zh_Hans: Function Call - - value: tool_call - label: - en_US: Tool Call - zh_Hans: Tool Call - - value: no_call - label: - en_US: Not Support - zh_Hans: 不支持 - - variable: stream_function_calling - show_on: - - variable: __model_type - value: llm - label: - en_US: Stream function calling - type: select - required: false - default: not_supported - options: - - value: supported - label: - en_US: Support - zh_Hans: 支持 - - value: not_supported - label: - en_US: Not Support - zh_Hans: 不支持 - - variable: vision_support - show_on: - - variable: __model_type - value: llm - label: - zh_Hans: Vision 支持 - en_US: Vision Support - type: select - required: false - default: no_support - options: - - value: support - label: - en_US: Support - zh_Hans: 支持 - - value: no_support - label: - en_US: Not Support - zh_Hans: 不支持 - - variable: stream_mode_delimiter - label: - zh_Hans: 流模式返回结果的分隔符 - en_US: Delimiter for streaming results - show_on: - - variable: __model_type - value: llm - default: '\n\n' - type: text-input diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/__init__.py b/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py b/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py deleted file mode 100644 index 405096578c..0000000000 --- a/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py +++ /dev/null @@ -1,61 +0,0 @@ -from typing import IO, Optional -from urllib.parse import urljoin - -import requests - -from core.model_runtime.errors.invoke import InvokeBadRequestError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from core.model_runtime.model_providers.openai_api_compatible._common import _CommonOaiApiCompat - - -class OAICompatSpeech2TextModel(_CommonOaiApiCompat, Speech2TextModel): - """ - Model class for OpenAI Compatible Speech to text model. - """ - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - headers = {} - - api_key = credentials.get("api_key") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - endpoint_url = credentials.get("endpoint_url") - if not endpoint_url.endswith("/"): - endpoint_url += "/" - endpoint_url = urljoin(endpoint_url, "audio/transcriptions") - - payload = {"model": model} - files = [("file", file)] - response = requests.post(endpoint_url, headers=headers, data=payload, files=files) - - if response.status_code != 200: - raise InvokeBadRequestError(response.text) - response_data = response.json() - return response_data["text"] - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self._invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/__init__.py b/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openllm/__init__.py b/api/core/model_runtime/model_providers/openllm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openllm/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/openllm/_assets/icon_l_en.svg deleted file mode 100644 index 59bb57992c..0000000000 --- a/api/core/model_runtime/model_providers/openllm/_assets/icon_l_en.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/openllm/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/openllm/_assets/icon_s_en.svg deleted file mode 100644 index d25d627020..0000000000 --- a/api/core/model_runtime/model_providers/openllm/_assets/icon_s_en.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/openllm/llm/__init__.py b/api/core/model_runtime/model_providers/openllm/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openllm/llm/llm.py b/api/core/model_runtime/model_providers/openllm/llm/llm.py deleted file mode 100644 index 34b4de7962..0000000000 --- a/api/core/model_runtime/model_providers/openllm/llm/llm.py +++ /dev/null @@ -1,264 +0,0 @@ -from collections.abc import Generator - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.openllm.llm.openllm_generate import OpenLLMGenerate, OpenLLMGenerateMessage -from core.model_runtime.model_providers.openllm.llm.openllm_generate_errors import ( - BadRequestError, - InsufficientAccountBalanceError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) - - -class OpenLLMLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate credentials for Baichuan model - """ - if not credentials.get("server_url"): - raise CredentialsValidateFailedError("Invalid server URL") - - # ping - instance = OpenLLMGenerate() - try: - instance.generate( - server_url=credentials["server_url"], - model_name=model, - prompt_messages=[OpenLLMGenerateMessage(content="ping\nAnswer: ", role="user")], - model_parameters={ - "max_tokens": 64, - "temperature": 0.8, - "top_p": 0.9, - "top_k": 15, - }, - stream=False, - user="", - stop=[], - ) - except InvalidAuthenticationError as e: - raise CredentialsValidateFailedError(f"Invalid API key: {e}") - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - return self._num_tokens_from_messages(prompt_messages, tools) - - def _num_tokens_from_messages(self, messages: list[PromptMessage], tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for OpenLLM model - it's a generate model, so we just join them by spe - """ - messages = ",".join([message.content for message in messages]) - return self._get_num_tokens_by_gpt2(messages) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - client = OpenLLMGenerate() - response = client.generate( - model_name=model, - server_url=credentials["server_url"], - prompt_messages=[self._convert_prompt_message_to_openllm_message(message) for message in prompt_messages], - model_parameters=model_parameters, - stop=stop, - stream=stream, - user=user, - ) - - if stream: - return self._handle_chat_generate_stream_response( - model=model, prompt_messages=prompt_messages, credentials=credentials, response=response - ) - return self._handle_chat_generate_response( - model=model, prompt_messages=prompt_messages, credentials=credentials, response=response - ) - - def _convert_prompt_message_to_openllm_message(self, prompt_message: PromptMessage) -> OpenLLMGenerateMessage: - """ - convert PromptMessage to OpenLLMGenerateMessage so that we can use OpenLLMGenerateMessage interface - """ - if isinstance(prompt_message, UserPromptMessage): - return OpenLLMGenerateMessage(role=OpenLLMGenerateMessage.Role.USER.value, content=prompt_message.content) - elif isinstance(prompt_message, AssistantPromptMessage): - return OpenLLMGenerateMessage( - role=OpenLLMGenerateMessage.Role.ASSISTANT.value, content=prompt_message.content - ) - else: - raise NotImplementedError(f"Prompt message type {type(prompt_message)} is not supported") - - def _handle_chat_generate_response( - self, model: str, prompt_messages: list[PromptMessage], credentials: dict, response: OpenLLMGenerateMessage - ) -> LLMResult: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=response.usage["prompt_tokens"], - completion_tokens=response.usage["completion_tokens"], - ) - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage( - content=response.content, - tool_calls=[], - ), - usage=usage, - ) - - def _handle_chat_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Generator[OpenLLMGenerateMessage, None, None], - ) -> Generator[LLMResultChunk, None, None]: - for message in response: - if message.usage: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=message.usage["prompt_tokens"], - completion_tokens=message.usage["completion_tokens"], - ) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - usage=usage, - finish_reason=message.stop_reason or None, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - finish_reason=message.stop_reason or None, - ), - ) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="top_k", - type=ParameterType.INT, - use_template="top_k", - min=1, - default=1, - label=I18nObject(zh_Hans="Top K", en_US="Top K"), - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - default=512, - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ] - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties={ - ModelPropertyKey.MODE: LLMMode.COMPLETION.value, - }, - parameter_rules=rules, - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [InternalServerError], - InvokeRateLimitError: [RateLimitReachedError], - InvokeAuthorizationError: [ - InvalidAuthenticationError, - InsufficientAccountBalanceError, - InvalidAPIKeyError, - ], - InvokeBadRequestError: [BadRequestError, KeyError], - } diff --git a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py b/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py deleted file mode 100644 index 351dcced15..0000000000 --- a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py +++ /dev/null @@ -1,198 +0,0 @@ -from collections.abc import Generator -from enum import Enum -from json import dumps, loads -from typing import Any, Union - -from requests import Response, post -from requests.exceptions import ConnectionError, InvalidSchema, MissingSchema - -from core.model_runtime.model_providers.openllm.llm.openllm_generate_errors import ( - BadRequestError, - InternalServerError, - InvalidAuthenticationError, -) - - -class OpenLLMGenerateMessage: - class Role(Enum): - USER = "user" - ASSISTANT = "assistant" - - role: str = Role.USER.value - content: str - usage: dict[str, int] = None - stop_reason: str = "" - - def to_dict(self) -> dict[str, Any]: - return { - "role": self.role, - "content": self.content, - } - - def __init__(self, content: str, role: str = "user") -> None: - self.content = content - self.role = role - - -class OpenLLMGenerate: - def generate( - self, - server_url: str, - model_name: str, - stream: bool, - model_parameters: dict[str, Any], - stop: list[str], - prompt_messages: list[OpenLLMGenerateMessage], - user: str, - ) -> Union[Generator[OpenLLMGenerateMessage, None, None], OpenLLMGenerateMessage]: - if not server_url: - raise InvalidAuthenticationError("Invalid server URL") - - default_llm_config = { - "max_new_tokens": 128, - "min_length": 0, - "early_stopping": False, - "num_beams": 1, - "num_beam_groups": 1, - "use_cache": True, - "temperature": 0.75, - "top_k": 15, - "top_p": 0.9, - "typical_p": 1, - "epsilon_cutoff": 0, - "eta_cutoff": 0, - "diversity_penalty": 0, - "repetition_penalty": 1, - "encoder_repetition_penalty": 1, - "length_penalty": 1, - "no_repeat_ngram_size": 0, - "renormalize_logits": False, - "remove_invalid_values": False, - "num_return_sequences": 1, - "output_attentions": False, - "output_hidden_states": False, - "output_scores": False, - "encoder_no_repeat_ngram_size": 0, - "n": 1, - "presence_penalty": 0, - "frequency_penalty": 0, - "use_beam_search": False, - "ignore_eos": False, - "skip_special_tokens": True, - } - - if "max_tokens" in model_parameters and type(model_parameters["max_tokens"]) == int: - default_llm_config["max_new_tokens"] = model_parameters["max_tokens"] - - if "temperature" in model_parameters and type(model_parameters["temperature"]) == float: - default_llm_config["temperature"] = model_parameters["temperature"] - - if "top_p" in model_parameters and type(model_parameters["top_p"]) == float: - default_llm_config["top_p"] = model_parameters["top_p"] - - if "top_k" in model_parameters and type(model_parameters["top_k"]) == int: - default_llm_config["top_k"] = model_parameters["top_k"] - - if "use_cache" in model_parameters and type(model_parameters["use_cache"]) == bool: - default_llm_config["use_cache"] = model_parameters["use_cache"] - - headers = {"Content-Type": "application/json", "accept": "application/json"} - - if stream: - url = f"{server_url}/v1/generate_stream" - timeout = 10 - else: - url = f"{server_url}/v1/generate" - timeout = 120 - - data = { - "stop": stop or [], - "prompt": "\n".join([message.content for message in prompt_messages]), - "llm_config": default_llm_config, - } - - try: - response = post(url=url, data=dumps(data), timeout=timeout, stream=stream, headers=headers) - except (ConnectionError, InvalidSchema, MissingSchema) as e: - # cloud not connect to the server - raise InvalidAuthenticationError(f"Invalid server URL: {e}") - - if not response.ok: - resp = response.json() - msg = resp["msg"] - if response.status_code == 400: - raise BadRequestError(msg) - elif response.status_code == 404: - raise InvalidAuthenticationError(msg) - elif response.status_code == 500: - raise InternalServerError(msg) - else: - raise InternalServerError(msg) - - if stream: - return self._handle_chat_stream_generate_response(response) - return self._handle_chat_generate_response(response) - - def _handle_chat_generate_response(self, response: Response) -> OpenLLMGenerateMessage: - try: - data = response.json() - except Exception as e: - raise InternalServerError(f"Failed to convert response to json: {e} with text: {response.text}") - - message = data["outputs"][0] - text = message["text"] - token_ids = message["token_ids"] - prompt_token_ids = data["prompt_token_ids"] - stop_reason = message["finish_reason"] - - message = OpenLLMGenerateMessage(content=text, role=OpenLLMGenerateMessage.Role.ASSISTANT.value) - message.stop_reason = stop_reason - message.usage = { - "prompt_tokens": len(prompt_token_ids), - "completion_tokens": len(token_ids), - "total_tokens": len(prompt_token_ids) + len(token_ids), - } - - return message - - def _handle_chat_stream_generate_response( - self, response: Response - ) -> Generator[OpenLLMGenerateMessage, None, None]: - completion_usage = 0 - - for line in response.iter_lines(): - if not line: - continue - - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() - - if line == "[DONE]": - return - - try: - data = loads(line) - except Exception as e: - raise InternalServerError(f"Failed to convert response to json: {e} with text: {line}") - - output = data["outputs"] - - for choice in output: - text = choice["text"] - token_ids = choice["token_ids"] - - completion_usage += len(token_ids) - message = OpenLLMGenerateMessage(content=text, role=OpenLLMGenerateMessage.Role.ASSISTANT.value) - - if choice.get("finish_reason"): - finish_reason = choice["finish_reason"] - prompt_token_ids = data["prompt_token_ids"] - message.stop_reason = finish_reason - message.usage = { - "prompt_tokens": len(prompt_token_ids), - "completion_tokens": completion_usage, - "total_tokens": completion_usage + len(prompt_token_ids), - } - - yield message diff --git a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate_errors.py b/api/core/model_runtime/model_providers/openllm/llm/openllm_generate_errors.py deleted file mode 100644 index 309b5cf413..0000000000 --- a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate_errors.py +++ /dev/null @@ -1,22 +0,0 @@ -class InvalidAuthenticationError(Exception): - pass - - -class InvalidAPIKeyError(Exception): - pass - - -class RateLimitReachedError(Exception): - pass - - -class InsufficientAccountBalanceError(Exception): - pass - - -class InternalServerError(Exception): - pass - - -class BadRequestError(Exception): - pass diff --git a/api/core/model_runtime/model_providers/openllm/openllm.py b/api/core/model_runtime/model_providers/openllm/openllm.py deleted file mode 100644 index 8014802144..0000000000 --- a/api/core/model_runtime/model_providers/openllm/openllm.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OpenLLMProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/openllm/openllm.yaml b/api/core/model_runtime/model_providers/openllm/openllm.yaml deleted file mode 100644 index fef52695e3..0000000000 --- a/api/core/model_runtime/model_providers/openllm/openllm.yaml +++ /dev/null @@ -1,37 +0,0 @@ -provider: openllm -label: - en_US: OpenLLM -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#F9FAFB" -help: - title: - en_US: How to deploy OpenLLM - zh_Hans: 如何部署 OpenLLM - url: - en_US: https://github.com/bentoml/OpenLLM -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: server_url - label: - zh_Hans: 服务器URL - en_US: Server url - type: text-input - required: true - placeholder: - zh_Hans: 在此输入OpenLLM的服务器地址,如 http://192.168.1.100:3000 - en_US: Enter the url of your OpenLLM, e.g. http://192.168.1.100:3000 diff --git a/api/core/model_runtime/model_providers/openllm/text_embedding/__init__.py b/api/core/model_runtime/model_providers/openllm/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openrouter/__init__.py b/api/core/model_runtime/model_providers/openrouter/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openrouter/_assets/openrouter.svg b/api/core/model_runtime/model_providers/openrouter/_assets/openrouter.svg deleted file mode 100644 index 2e9590d923..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/_assets/openrouter.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/openrouter/_assets/openrouter_square.svg b/api/core/model_runtime/model_providers/openrouter/_assets/openrouter_square.svg deleted file mode 100644 index ed81fc041f..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/_assets/openrouter_square.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/openrouter/llm/__init__.py b/api/core/model_runtime/model_providers/openrouter/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/openrouter/llm/_position.yaml b/api/core/model_runtime/model_providers/openrouter/llm/_position.yaml deleted file mode 100644 index d9497b76b8..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/_position.yaml +++ /dev/null @@ -1,27 +0,0 @@ -- openai/o1-preview -- openai/o1-mini -- openai/gpt-4o -- openai/gpt-4o-mini -- openai/gpt-4 -- openai/gpt-4-32k -- openai/gpt-3.5-turbo -- anthropic/claude-3.5-sonnet -- anthropic/claude-3-haiku -- anthropic/claude-3-opus -- anthropic/claude-3-sonnet -- google/gemini-pro-1.5 -- google/gemini-flash-1.5 -- google/gemini-pro -- cohere/command-r-plus -- cohere/command-r -- meta-llama/llama-3.1-405b-instruct -- meta-llama/llama-3.1-70b-instruct -- meta-llama/llama-3.1-8b-instruct -- meta-llama/llama-3-70b-instruct -- meta-llama/llama-3-8b-instruct -- mistralai/mixtral-8x22b-instruct -- mistralai/mixtral-8x7b-instruct -- mistralai/mistral-7b-instruct -- qwen/qwen-2-72b-instruct -- deepseek/deepseek-chat -- deepseek/deepseek-coder diff --git a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-5-sonnet.yaml b/api/core/model_runtime/model_providers/openrouter/llm/claude-3-5-sonnet.yaml deleted file mode 100644 index 40558854e2..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-5-sonnet.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: anthropic/claude-3.5-sonnet -label: - en_US: claude-3.5-sonnet -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: "3.00" - output: "15.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-haiku.yaml b/api/core/model_runtime/model_providers/openrouter/llm/claude-3-haiku.yaml deleted file mode 100644 index ce17d4123e..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-haiku.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: anthropic/claude-3-haiku -label: - en_US: claude-3-haiku -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: "0.25" - output: "1.25" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-opus.yaml b/api/core/model_runtime/model_providers/openrouter/llm/claude-3-opus.yaml deleted file mode 100644 index 68a92219eb..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-opus.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: anthropic/claude-3-opus -label: - en_US: claude-3-opus -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: "15.00" - output: "75.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-sonnet.yaml b/api/core/model_runtime/model_providers/openrouter/llm/claude-3-sonnet.yaml deleted file mode 100644 index ede88459ca..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/claude-3-sonnet.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: anthropic/claude-3-sonnet -label: - en_US: claude-3-sonnet -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 4096 - min: 1 - max: 4096 - - name: response_format - use_template: response_format -pricing: - input: "3.00" - output: "15.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/command-r-plus.yaml b/api/core/model_runtime/model_providers/openrouter/llm/command-r-plus.yaml deleted file mode 100644 index a23eb269d1..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/command-r-plus.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: cohere/command-r-plus -label: - en_US: command-r-plus -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: top_p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: "3" - output: "15" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/command-r.yaml b/api/core/model_runtime/model_providers/openrouter/llm/command-r.yaml deleted file mode 100644 index 7165bf29b0..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/command-r.yaml +++ /dev/null @@ -1,45 +0,0 @@ -model: cohere/command-r -label: - en_US: command-r -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - max: 5.0 - - name: top_p - use_template: top_p - default: 0.75 - min: 0.01 - max: 0.99 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - default: 0 - min: 0 - max: 500 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 1024 - max: 4096 -pricing: - input: "0.5" - output: "1.5" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/deepseek-chat.yaml b/api/core/model_runtime/model_providers/openrouter/llm/deepseek-chat.yaml deleted file mode 100644 index 7a1dea6950..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/deepseek-chat.yaml +++ /dev/null @@ -1,50 +0,0 @@ -model: deepseek/deepseek-chat -label: - en_US: deepseek-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 1 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 1 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. - - name: frequency_penalty - use_template: frequency_penalty - default: 0 - min: -2.0 - max: 2.0 - help: - zh_Hans: 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 - en_US: A number between -2.0 and 2.0. If the value is positive, new tokens are penalized based on their frequency of occurrence in existing text, reducing the likelihood that the model will repeat the same content. -pricing: - input: "0.14" - output: "0.28" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/deepseek-coder.yaml b/api/core/model_runtime/model_providers/openrouter/llm/deepseek-coder.yaml deleted file mode 100644 index c05f4769b8..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/deepseek-coder.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: deepseek/deepseek-coder -label: - en_US: deepseek-coder -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 4096 - default: 1024 -pricing: - input: "0.14" - output: "0.28" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-flash.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-flash.yaml deleted file mode 100644 index 0b2f329b28..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-flash.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: google/gemini-flash-1.5 -label: - en_US: gemini-flash-1.5 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 1048576 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format -pricing: - input: "0.25" - output: "0.75" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-pro.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-pro.yaml deleted file mode 100644 index 679ce9bdcd..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gemini-1.5-pro.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: google/gemini-pro-1.5 -label: - en_US: gemini-pro-1.5 -model_type: llm -features: - - agent-thought - - vision - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 1048576 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 - - name: response_format - use_template: response_format -pricing: - input: "2.5" - output: "7.5" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gemini-pro.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gemini-pro.yaml deleted file mode 100644 index 9f5d96c5b8..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gemini-pro.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: google/gemini-pro -label: - en_US: gemini-pro -model_type: llm -features: - - agent-thought - - tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 30720 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - help: - zh_Hans: 仅从每个后续标记的前 K 个选项中采样。 - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: max_tokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 - - name: response_format - use_template: response_format -pricing: - input: "0.125" - output: "0.375" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-3.5-turbo.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-3.5-turbo.yaml deleted file mode 100644 index 186c1cc663..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-3.5-turbo.yaml +++ /dev/null @@ -1,42 +0,0 @@ -model: openai/gpt-3.5-turbo -label: - en_US: gpt-3.5-turbo -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 16385 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "0.5" - output: "1.5" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4-32k.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-4-32k.yaml deleted file mode 100644 index 8c2989b300..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4-32k.yaml +++ /dev/null @@ -1,57 +0,0 @@ -model: openai/gpt-4-32k -label: - en_US: gpt-4-32k -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 32768 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: - 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: - If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "60" - output: "120" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-4.yaml deleted file mode 100644 index ef19d4f6f0..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4.yaml +++ /dev/null @@ -1,57 +0,0 @@ -model: openai/gpt-4 -label: - en_US: gpt-4 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: - 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: - If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "30" - output: "60" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-2024-08-06.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-2024-08-06.yaml deleted file mode 100644 index 0be325f55b..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-2024-08-06.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: gpt-4o-2024-08-06 -label: - zh_Hans: gpt-4o-2024-08-06 - en_US: gpt-4o-2024-08-06 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: '2.50' - output: '10.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-mini.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-mini.yaml deleted file mode 100644 index 3b1d95643d..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o-mini.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: openai/gpt-4o-mini -label: - en_US: gpt-4o-mini -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 16384 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "0.15" - output: "0.60" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o.yaml b/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o.yaml deleted file mode 100644 index a8c97efdd6..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/gpt-4o.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: openai/gpt-4o -label: - en_US: gpt-4o -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call - - vision -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: Response Format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "5.00" - output: "15.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llama-3-70b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/llama-3-70b-instruct.yaml deleted file mode 100644 index b91c39e729..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llama-3-70b-instruct.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta-llama/llama-3-70b-instruct -label: - en_US: llama-3-70b-instruct -model_type: llm -model_properties: - mode: completion - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: "0.59" - output: "0.79" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llama-3-8b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/llama-3-8b-instruct.yaml deleted file mode 100644 index 84b2c7fac2..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llama-3-8b-instruct.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta-llama/llama-3-8b-instruct -label: - en_US: llama-3-8b-instruct -model_type: llm -model_properties: - mode: completion - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 2048 -pricing: - input: "0.07" - output: "0.07" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-405b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-405b-instruct.yaml deleted file mode 100644 index a489ce1b5a..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-405b-instruct.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta-llama/llama-3.1-405b-instruct -label: - en_US: llama-3.1-405b-instruct -model_type: llm -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 131072 -pricing: - input: "2.7" - output: "2.7" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-70b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-70b-instruct.yaml deleted file mode 100644 index 12037411b1..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-70b-instruct.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta-llama/llama-3.1-70b-instruct -label: - en_US: llama-3.1-70b-instruct -model_type: llm -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 131072 -pricing: - input: "0.52" - output: "0.75" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-8b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-8b-instruct.yaml deleted file mode 100644 index 6f06493f29..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llama-3.1-8b-instruct.yaml +++ /dev/null @@ -1,23 +0,0 @@ -model: meta-llama/llama-3.1-8b-instruct -label: - en_US: llama-3.1-8b-instruct -model_type: llm -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 512 - min: 1 - max: 131072 -pricing: - input: "0.06" - output: "0.06" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/llm.py b/api/core/model_runtime/model_providers/openrouter/llm/llm.py deleted file mode 100644 index 736ab8e7a8..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/llm.py +++ /dev/null @@ -1,106 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.entities.model_entities import AIModelEntity -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class OpenRouterLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _update_credential(self, model: str, credentials: dict): - credentials["endpoint_url"] = "https://openrouter.ai/api/v1" - credentials["mode"] = self.get_model_mode(model).value - credentials["function_calling_type"] = "tool_call" - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._update_credential(model, credentials) - - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._update_credential(model, credentials) - - return super().validate_credentials(model, credentials) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._update_credential(model, credentials) - - block_as_stream = False - if model.startswith("openai/o1"): - block_as_stream = True - stop = None - - # invoke block as stream - if stream and block_as_stream: - return self._generate_block_as_stream( - model, credentials, prompt_messages, model_parameters, tools, stop, user - ) - else: - return super()._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _generate_block_as_stream( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - user: Optional[str] = None, - ) -> Generator: - resp: LLMResult = super()._generate( - model, credentials, prompt_messages, model_parameters, tools, stop, False, user - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=resp.message, - usage=self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=resp.usage.prompt_tokens, - completion_tokens=resp.usage.completion_tokens, - ), - finish_reason="stop", - ), - ) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - self._update_credential(model, credentials) - - return super().get_customizable_model_schema(model, credentials) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - self._update_credential(model, credentials) - - return super().get_num_tokens(model, credentials, prompt_messages, tools) diff --git a/api/core/model_runtime/model_providers/openrouter/llm/mistral-7b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/mistral-7b-instruct.yaml deleted file mode 100644 index 012dfc55ce..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/mistral-7b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: mistralai/mistral-7b-instruct -label: - en_US: mistral-7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 2048 -pricing: - input: "0.07" - output: "0.07" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x22b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x22b-instruct.yaml deleted file mode 100644 index f4eb4e45d9..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x22b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: mistralai/mixtral-8x22b-instruct -label: - en_US: mixtral-8x22b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 64000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 -pricing: - input: "0.65" - output: "0.65" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x7b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x7b-instruct.yaml deleted file mode 100644 index 7871e1f7a0..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/mixtral-8x7b-instruct.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: mistralai/mixtral-8x7b-instruct -label: - zh_Hans: mixtral-8x7b-instruct - en_US: mixtral-8x7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.7 - min: 0 - max: 1 - - name: top_p - use_template: top_p - default: 1 - min: 0 - max: 1 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8000 -pricing: - input: "0.24" - output: "0.24" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/o1-mini.yaml b/api/core/model_runtime/model_providers/openrouter/llm/o1-mini.yaml deleted file mode 100644 index 85a918ff5e..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/o1-mini.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: openai/o1-mini -label: - en_US: o1-mini -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 65536 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "3.00" - output: "12.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/o1-preview.yaml b/api/core/model_runtime/model_providers/openrouter/llm/o1-preview.yaml deleted file mode 100644 index 74b0a511be..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/o1-preview.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: openai/o1-preview -label: - en_US: o1-preview -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 32768 - - name: response_format - label: - zh_Hans: 回复格式 - en_US: response_format - type: string - help: - zh_Hans: 指定模型必须输出的格式 - en_US: specifying the format that the model must output - required: false - options: - - text - - json_object -pricing: - input: "15.00" - output: "60.00" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/llm/qwen2-72b-instruct.yaml b/api/core/model_runtime/model_providers/openrouter/llm/qwen2-72b-instruct.yaml deleted file mode 100644 index 7b75fcb0c9..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/llm/qwen2-72b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: qwen/qwen-2-72b-instruct -label: - en_US: qwen-2-72b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: "0.59" - output: "0.79" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/openrouter/openrouter.py b/api/core/model_runtime/model_providers/openrouter/openrouter.py deleted file mode 100644 index 2e59ab5059..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/openrouter.py +++ /dev/null @@ -1,20 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class OpenRouterProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="openai/gpt-3.5-turbo", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/openrouter/openrouter.yaml b/api/core/model_runtime/model_providers/openrouter/openrouter.yaml deleted file mode 100644 index f7536609ec..0000000000 --- a/api/core/model_runtime/model_providers/openrouter/openrouter.yaml +++ /dev/null @@ -1,105 +0,0 @@ -provider: openrouter -label: - en_US: OpenRouter -icon_small: - en_US: openrouter_square.svg -icon_large: - en_US: openrouter.svg -background: "#F1EFED" -help: - title: - en_US: Get your API key from openrouter.ai - zh_Hans: 从 openrouter.ai 获取 API Key - url: - en_US: https://openrouter.ai/keys -supported_model_types: - - llm -configurate_methods: - - predefined-model - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter full model name - zh_Hans: 输入模型全称 - credential_form_schemas: - - variable: api_key - required: true - label: - en_US: API Key - type: secret-input - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: "4096" - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens_to_sample - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - show_on: - - variable: __model_type - value: llm - default: "4096" - type: text-input - - variable: vision_support - show_on: - - variable: __model_type - value: llm - label: - zh_Hans: 是否支持 Vision - en_US: Vision Support - type: radio - required: false - default: "no_support" - options: - - value: "support" - label: - en_US: "Yes" - zh_Hans: 是 - - value: "no_support" - label: - en_US: "No" - zh_Hans: 否 -provider_credential_schema: - credential_form_schemas: - - variable: api_key - required: true - label: - en_US: API Key - type: secret-input - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/perfxcloud/__init__.py b/api/core/model_runtime/model_providers/perfxcloud/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_l_en.svg deleted file mode 100644 index 060d9de3a9..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_l_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_s_en.svg deleted file mode 100644 index be0c2eeb1c..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Llama3-Chinese_v2.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Llama3-Chinese_v2.yaml deleted file mode 100644 index bf91468fcf..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Llama3-Chinese_v2.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Llama3-Chinese_v2 -label: - en_US: Llama3-Chinese_v2 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-70B-Instruct-GPTQ-Int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-70B-Instruct-GPTQ-Int4.yaml deleted file mode 100644 index 781b837e8e..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-70B-Instruct-GPTQ-Int4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Meta-Llama-3-70B-Instruct-GPTQ-Int4 -label: - en_US: Meta-Llama-3-70B-Instruct-GPTQ-Int4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 1024 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-8B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-8B-Instruct.yaml deleted file mode 100644 index 67210e9020..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3-8B-Instruct.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Meta-Llama-3-8B-Instruct -label: - en_US: Meta-Llama-3-8B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-405B-Instruct-AWQ-INT4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-405B-Instruct-AWQ-INT4.yaml deleted file mode 100644 index 482632ff06..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-405B-Instruct-AWQ-INT4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Meta-Llama-3.1-405B-Instruct-AWQ-INT4 -label: - en_US: Meta-Llama-3.1-405B-Instruct-AWQ-INT4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 410960 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-8B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-8B-Instruct.yaml deleted file mode 100644 index bbab46344c..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Meta-Llama-3.1-8B-Instruct.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Meta-Llama-3.1-8B-Instruct -label: - en_US: Meta-Llama-3.1-8B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.1 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen-14B-Chat-Int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen-14B-Chat-Int4.yaml deleted file mode 100644 index ec6d9bcc14..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen-14B-Chat-Int4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Qwen-14B-Chat-Int4 -label: - en_US: Qwen-14B-Chat-Int4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-110B-Chat-GPTQ-Int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-110B-Chat-GPTQ-Int4.yaml deleted file mode 100644 index b561a53039..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-110B-Chat-GPTQ-Int4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Qwen1.5-110B-Chat-GPTQ-Int4 -label: - en_US: Qwen1.5-110B-Chat-GPTQ-Int4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 128 - min: 1 - max: 256 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-72B-Chat-GPTQ-Int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-72B-Chat-GPTQ-Int4.yaml deleted file mode 100644 index ddb6fd977c..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-72B-Chat-GPTQ-Int4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Qwen1.5-72B-Chat-GPTQ-Int4 -label: - en_US: Qwen1.5-72B-Chat-GPTQ-Int4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-7B.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-7B.yaml deleted file mode 100644 index 024c79dbcf..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen1.5-7B.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: Qwen1.5-7B -label: - en_US: Qwen1.5-7B -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-AWQ-int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-AWQ-int4.yaml deleted file mode 100644 index 94f661f40d..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-AWQ-int4.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Qwen2-72B-Instruct-AWQ-int4 -label: - en_US: Qwen2-72B-Instruct-AWQ-int4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-GPTQ-Int4.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-GPTQ-Int4.yaml deleted file mode 100644 index a06f8d5ab1..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct-GPTQ-Int4.yaml +++ /dev/null @@ -1,64 +0,0 @@ -model: Qwen2-72B-Instruct-GPTQ-Int4 -label: - en_US: Qwen2-72B-Instruct-GPTQ-Int4 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.7 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct.yaml deleted file mode 100644 index cea6560295..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-72B-Instruct.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Qwen2-72B-Instruct -label: - en_US: Qwen2-72B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B-Instruct.yaml deleted file mode 100644 index 4369411399..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B-Instruct.yaml +++ /dev/null @@ -1,63 +0,0 @@ -model: Qwen2-7B-Instruct -label: - en_US: Qwen2-7B-Instruct -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: completion - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B.yaml deleted file mode 100644 index d549ecd227..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2-7B.yaml +++ /dev/null @@ -1,64 +0,0 @@ -model: Qwen2-7B -label: - en_US: Qwen2-7B -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: completion - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-72B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-72B-Instruct.yaml deleted file mode 100644 index 15cbf01f1f..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-72B-Instruct.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Qwen2.5-72B-Instruct -label: - en_US: Qwen2.5-72B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 30720 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-7B-Instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-7B-Instruct.yaml deleted file mode 100644 index dadc8f8f32..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Qwen2.5-7B-Instruct.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Qwen2.5-7B-Instruct -label: - en_US: Qwen2.5-7B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Reflection-Llama-3.1-70B.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Reflection-Llama-3.1-70B.yaml deleted file mode 100644 index 649be20b48..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Reflection-Llama-3.1-70B.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Reflection-Llama-3.1-70B -label: - en_US: Reflection-Llama-3.1-70B -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 10240 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-1_5-9B-Chat-16K.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-1_5-9B-Chat-16K.yaml deleted file mode 100644 index 92eae6804f..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-1_5-9B-Chat-16K.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Yi-1_5-9B-Chat-16K -label: - en_US: Yi-1_5-9B-Chat-16K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-1.5B-Chat.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-1.5B-Chat.yaml deleted file mode 100644 index 0e21ce148c..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-1.5B-Chat.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Yi-Coder-1.5B-Chat -label: - en_US: Yi-Coder-1.5B-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 20480 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-9B-Chat.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-9B-Chat.yaml deleted file mode 100644 index 23b0841ce4..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/Yi-Coder-9B-Chat.yaml +++ /dev/null @@ -1,61 +0,0 @@ -model: Yi-Coder-9B-Chat -label: - en_US: Yi-Coder-9B-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 20480 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/__init__.py b/api/core/model_runtime/model_providers/perfxcloud/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/_position.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/_position.yaml deleted file mode 100644 index 37bf400f1e..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/_position.yaml +++ /dev/null @@ -1,24 +0,0 @@ -- Qwen2.5-72B-Instruct -- Qwen2.5-7B-Instruct -- Yi-Coder-1.5B-Chat -- Yi-Coder-9B-Chat -- Qwen2-72B-Instruct-AWQ-int4 -- Yi-1_5-9B-Chat-16K -- Qwen2-7B-Instruct -- Reflection-Llama-3.1-70B -- Qwen2-72B-Instruct -- Meta-Llama-3.1-8B-Instruct - -- Meta-Llama-3.1-405B-Instruct-AWQ-INT4 -- Meta-Llama-3-70B-Instruct-GPTQ-Int4 -- chatglm3-6b -- Meta-Llama-3-8B-Instruct -- Llama3-Chinese_v2 -- deepseek-v2-lite-chat -- Qwen2-72B-Instruct-GPTQ-Int4 -- Qwen2-7B -- Qwen-14B-Chat-Int4 -- Qwen1.5-72B-Chat-GPTQ-Int4 -- Qwen1.5-7B -- Qwen1.5-110B-Chat-GPTQ-Int4 -- deepseek-v2-chat diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/chatglm3-6b.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/chatglm3-6b.yaml deleted file mode 100644 index 75d80f784a..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/chatglm3-6b.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: chatglm3-6b -label: - en_US: chatglm3-6b -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-chat.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-chat.yaml deleted file mode 100644 index fa9a7b7175..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: deepseek-v2-chat -label: - en_US: deepseek-v2-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-lite-chat.yaml b/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-lite-chat.yaml deleted file mode 100644 index 75a26d2505..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/deepseek-v2-lite-chat.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: deepseek-v2-lite-chat -label: - en_US: deepseek-v2-lite-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.5 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 600 - min: 1 - max: 1248 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. -pricing: - input: "0.000" - output: "0.000" - unit: "0.000" - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/perfxcloud/llm/llm.py b/api/core/model_runtime/model_providers/perfxcloud/llm/llm.py deleted file mode 100644 index 89cac665aa..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/llm/llm.py +++ /dev/null @@ -1,116 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union -from urllib.parse import urlparse - -import tiktoken - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import ( - PromptMessage, - PromptMessageTool, -) -from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel - - -class PerfXCloudLargeLanguageModel(OpenAILargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_string(self, model: str, text: str, tools: Optional[list[PromptMessageTool]] = None) -> int: - """ - Calculate num tokens for text completion model with tiktoken package. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - encoding = tiktoken.get_encoding("cl100k_base") - num_tokens = len(encoding.encode(text)) - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_messages( - self, model: str, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. - - Official documentation: https://github.com/openai/openai-cookbook/blob/ - main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - encoding = tiktoken.get_encoding("cl100k_base") - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += len(encoding.encode(t_key)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(encoding.encode(f_key)) - num_tokens += len(encoding.encode(f_value)) - else: - num_tokens += len(encoding.encode(t_key)) - num_tokens += len(encoding.encode(t_value)) - else: - num_tokens += len(encoding.encode(str(value))) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - @staticmethod - def _add_custom_parameters(credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["openai_api_key"] = credentials["api_key"] - if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": - credentials["openai_api_base"] = "https://cloud.perfxlab.cn" - else: - parsed_url = urlparse(credentials["endpoint_url"]) - credentials["openai_api_base"] = f"{parsed_url.scheme}://{parsed_url.netloc}" diff --git a/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.py b/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.py deleted file mode 100644 index 9a4ead031d..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class PerfXCloudProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.yaml b/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.yaml deleted file mode 100644 index 10ee691ebd..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/perfxcloud.yaml +++ /dev/null @@ -1,42 +0,0 @@ -provider: perfxcloud -label: - en_US: PerfXCloud - zh_Hans: PerfXCloud -description: - en_US: PerfXCloud (Pengfeng Technology) is an AI development and deployment platform tailored for developers and enterprises, providing reasoning capabilities for multiple models. - zh_Hans: PerfXCloud(澎峰科技)为开发者和企业量身打造的AI开发和部署平台,提供多种模型的的推理能力。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#e3f0ff" -help: - title: - en_US: Get your API Key from PerfXCloud - zh_Hans: 从 PerfXCloud 获取 API Key - url: - en_US: https://cloud.perfxlab.cn/panel/token -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: endpoint_url - label: - zh_Hans: 自定义 API endpoint 地址 - en_US: Custom API endpoint URL - type: text-input - required: false - placeholder: - zh_Hans: Base URL, e.g. https://cloud.perfxlab.cn/v1 - en_US: Base URL, e.g. https://cloud.perfxlab.cn/v1 diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-en-v1.5.yaml b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-en-v1.5.yaml deleted file mode 100644 index 5756fb3d14..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-en-v1.5.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: BAAI/bge-large-en-v1.5 -model_type: text-embedding -model_properties: - context_size: 32768 diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-zh-v1.5.yaml b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-zh-v1.5.yaml deleted file mode 100644 index 4204ab2860..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-large-zh-v1.5.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: BAAI/bge-large-zh-v1.5 -model_type: text-embedding -model_properties: - context_size: 32768 diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-m3.yaml b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-m3.yaml deleted file mode 100644 index 55488e5688..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/BAAI-bge-m3.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: BAAI/bge-m3 -model_type: text-embedding -model_properties: - context_size: 32768 diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/__init__.py b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/gte-Qwen2-7B-instruct.yaml b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/gte-Qwen2-7B-instruct.yaml deleted file mode 100644 index 03db0d8bce..0000000000 --- a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/gte-Qwen2-7B-instruct.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: gte-Qwen2-7B-instruct -model_type: text-embedding -model_properties: - context_size: 2048 diff --git a/api/core/model_runtime/model_providers/replicate/__init__.py b/api/core/model_runtime/model_providers/replicate/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/replicate/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/replicate/_assets/icon_l_en.svg deleted file mode 100644 index 63c09470d5..0000000000 --- a/api/core/model_runtime/model_providers/replicate/_assets/icon_l_en.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/replicate/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/replicate/_assets/icon_s_en.svg deleted file mode 100644 index 527316edb6..0000000000 --- a/api/core/model_runtime/model_providers/replicate/_assets/icon_s_en.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/api/core/model_runtime/model_providers/replicate/_common.py b/api/core/model_runtime/model_providers/replicate/_common.py deleted file mode 100644 index 915f6e0eef..0000000000 --- a/api/core/model_runtime/model_providers/replicate/_common.py +++ /dev/null @@ -1,9 +0,0 @@ -from replicate.exceptions import ModelError, ReplicateError - -from core.model_runtime.errors.invoke import InvokeBadRequestError, InvokeError - - -class _CommonReplicate: - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - return {InvokeBadRequestError: [ReplicateError, ModelError]} diff --git a/api/core/model_runtime/model_providers/replicate/llm/__init__.py b/api/core/model_runtime/model_providers/replicate/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/replicate/llm/llm.py b/api/core/model_runtime/model_providers/replicate/llm/llm.py deleted file mode 100644 index 3641b35dc0..0000000000 --- a/api/core/model_runtime/model_providers/replicate/llm/llm.py +++ /dev/null @@ -1,305 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from replicate import Client as ReplicateClient -from replicate.exceptions import ReplicateError -from replicate.prediction import Prediction - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageRole, - PromptMessageTool, - SystemPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.replicate._common import _CommonReplicate - - -class ReplicateLargeLanguageModel(_CommonReplicate, LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - model_version = "" - if "model_version" in credentials: - model_version = credentials["model_version"] - - client = ReplicateClient(api_token=credentials["replicate_api_token"], timeout=30) - model_info = client.models.get(model) - - if model_version: - model_info_version = model_info.versions.get(model_version) - else: - model_info_version = model_info.latest_version - - inputs = {**model_parameters} - - if prompt_messages[0].role == PromptMessageRole.SYSTEM: - if "system_prompt" in model_info_version.openapi_schema["components"]["schemas"]["Input"]["properties"]: - inputs["system_prompt"] = prompt_messages[0].content - inputs["prompt"] = prompt_messages[1].content - else: - inputs["prompt"] = prompt_messages[0].content - - prediction = client.predictions.create(version=model_info_version, input=inputs) - - if stream: - return self._handle_generate_stream_response(model, credentials, prediction, stop, prompt_messages) - return self._handle_generate_response(model, credentials, prediction, stop, prompt_messages) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - prompt = self._convert_messages_to_prompt(prompt_messages) - return self._get_num_tokens_by_gpt2(prompt) - - def validate_credentials(self, model: str, credentials: dict) -> None: - if "replicate_api_token" not in credentials: - raise CredentialsValidateFailedError("Replicate Access Token must be provided.") - - model_version = "" - if "model_version" in credentials: - model_version = credentials["model_version"] - - if model.count("/") != 1: - raise CredentialsValidateFailedError( - "Replicate Model Name must be provided, format: {user_name}/{model_name}" - ) - - try: - client = ReplicateClient(api_token=credentials["replicate_api_token"], timeout=30) - model_info = client.models.get(model) - - if model_version: - model_info_version = model_info.versions.get(model_version) - else: - model_info_version = model_info.latest_version - - self._check_text_generation_model(model_info_version, model, model_version, model_info.description) - except ReplicateError as e: - raise CredentialsValidateFailedError( - f"Model {model}:{model_version} not exists, cause: {e.__class__.__name__}:{str(e)}" - ) - except Exception as e: - raise CredentialsValidateFailedError(str(e)) - - @staticmethod - def _check_text_generation_model(model_info_version, model_name, version, description): - if "language model" in description.lower(): - return - - if ( - "temperature" not in model_info_version.openapi_schema["components"]["schemas"]["Input"]["properties"] - or "top_p" not in model_info_version.openapi_schema["components"]["schemas"]["Input"]["properties"] - or "top_k" not in model_info_version.openapi_schema["components"]["schemas"]["Input"]["properties"] - ): - raise CredentialsValidateFailedError(f"Model {model_name}:{version} is not a Text Generation model.") - - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: - model_type = LLMMode.CHAT if model.endswith("-chat") else LLMMode.COMPLETION - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties={ModelPropertyKey.MODE: model_type.value}, - parameter_rules=self._get_customizable_model_parameter_rules(model, credentials), - ) - - return entity - - @classmethod - def _get_customizable_model_parameter_rules(cls, model: str, credentials: dict) -> list[ParameterRule]: - model_version = "" - if "model_version" in credentials: - model_version = credentials["model_version"] - - client = ReplicateClient(api_token=credentials["replicate_api_token"], timeout=30) - model_info = client.models.get(model) - - if model_version: - model_info_version = model_info.versions.get(model_version) - else: - model_info_version = model_info.latest_version - - parameter_rules = [] - - input_properties = sorted( - model_info_version.openapi_schema["components"]["schemas"]["Input"]["properties"].items(), - key=lambda item: item[1].get("x-order", 0), - ) - - for key, value in input_properties: - if key not in {"system_prompt", "prompt"} and "stop" not in key: - value_type = value.get("type") - - if not value_type: - continue - - param_type = cls._get_parameter_type(value_type) - - rule = ParameterRule( - name=key, - label={"en_US": value["title"]}, - type=param_type, - help={ - "en_US": value.get("description"), - }, - required=False, - default=value.get("default"), - min=value.get("minimum"), - max=value.get("maximum"), - ) - parameter_rules.append(rule) - - return parameter_rules - - def _handle_generate_stream_response( - self, - model: str, - credentials: dict, - prediction: Prediction, - stop: list[str], - prompt_messages: list[PromptMessage], - ) -> Generator: - index = -1 - current_completion: str = "" - stop_condition_reached = False - - prediction_output_length = 10000 - is_prediction_output_finished = False - - for output in prediction.output_iterator(): - current_completion += output - - if not is_prediction_output_finished and prediction.status == "succeeded": - prediction_output_length = len(prediction.output) - 1 - is_prediction_output_finished = True - - if stop: - for s in stop: - if s in current_completion: - prediction.cancel() - stop_index = current_completion.find(s) - current_completion = current_completion[:stop_index] - stop_condition_reached = True - break - - if stop_condition_reached: - break - - index += 1 - - assistant_prompt_message = AssistantPromptMessage(content=output or "") - - if index < prediction_output_length: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message), - ) - else: - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message, usage=usage), - ) - - def _handle_generate_response( - self, - model: str, - credentials: dict, - prediction: Prediction, - stop: list[str], - prompt_messages: list[PromptMessage], - ) -> LLMResult: - current_completion: str = "" - stop_condition_reached = False - for output in prediction.output_iterator(): - current_completion += output - - if stop: - for s in stop: - if s in current_completion: - prediction.cancel() - stop_index = current_completion.find(s) - current_completion = current_completion[:stop_index] - stop_condition_reached = True - break - - if stop_condition_reached: - break - - assistant_prompt_message = AssistantPromptMessage(content=current_completion) - - prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - completion_tokens = self.get_num_tokens(model, credentials, [assistant_prompt_message]) - - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - ) - - return result - - @classmethod - def _get_parameter_type(cls, param_type: str) -> str: - type_mapping = {"integer": "int", "number": "float", "boolean": "boolean", "string": "string"} - return type_mapping.get(param_type) - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - return text.rstrip() - - @staticmethod - def _convert_one_message_to_text(message: PromptMessage) -> str: - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - content = message.content - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage): - message_text = content - else: - raise ValueError(f"Got unknown type {message}") - - return message_text diff --git a/api/core/model_runtime/model_providers/replicate/replicate.py b/api/core/model_runtime/model_providers/replicate/replicate.py deleted file mode 100644 index ca137579c9..0000000000 --- a/api/core/model_runtime/model_providers/replicate/replicate.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class ReplicateProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/replicate/replicate.yaml b/api/core/model_runtime/model_providers/replicate/replicate.yaml deleted file mode 100644 index 9cad6d4f0d..0000000000 --- a/api/core/model_runtime/model_providers/replicate/replicate.yaml +++ /dev/null @@ -1,41 +0,0 @@ -provider: replicate -label: - en_US: Replicate -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#E5E7EB" -help: - title: - en_US: Get your API Key from Replicate - zh_Hans: 从 Replicate 获取 API Key - url: - en_US: https://replicate.com/account/api-tokens -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - credential_form_schemas: - - variable: replicate_api_token - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 Replicate API Key - en_US: Enter your Replicate API Key - - variable: model_version - label: - en_US: Model Version - type: text-input - required: false - placeholder: - zh_Hans: 在此输入您的模型版本,默认为最新版本 - en_US: Enter your model version, default to the latest version diff --git a/api/core/model_runtime/model_providers/replicate/text_embedding/__init__.py b/api/core/model_runtime/model_providers/replicate/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/__init__.py b/api/core/model_runtime/model_providers/sagemaker/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/sagemaker/_assets/icon_l_en.png deleted file mode 100644 index 0abe07a78ff776018db04d84b7cc0ceb1c9ae81f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9395 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 diff --git a/api/core/model_runtime/model_providers/sagemaker/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/sagemaker/_assets/icon_s_en.png deleted file mode 100644 index 6b88942a5ce27cb57e27ce876cecedf3c5dfce3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9720 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 OOmb4nk~QL{VgCa)i~r65 diff --git a/api/core/model_runtime/model_providers/sagemaker/llm/__init__.py b/api/core/model_runtime/model_providers/sagemaker/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/rerank/__init__.py b/api/core/model_runtime/model_providers/sagemaker/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py b/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py deleted file mode 100644 index 959dff6a21..0000000000 --- a/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py +++ /dev/null @@ -1,173 +0,0 @@ -import json -import logging -import operator -from typing import Any, Optional - -import boto3 - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - -logger = logging.getLogger(__name__) - - -class SageMakerRerankModel(RerankModel): - """ - Model class for SageMaker rerank model. - """ - - sagemaker_client: Any = 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, - 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 - """ - line = 0 - try: - if len(docs) == 0: - return RerankResult(model=model, docs=docs) - - line = 1 - if not self.sagemaker_client: - access_key = credentials.get("aws_access_key_id") - secret_key = credentials.get("aws_secret_access_key") - aws_region = credentials.get("aws_region") - if aws_region: - if access_key and secret_key: - self.sagemaker_client = boto3.client( - "sagemaker-runtime", - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=aws_region, - ) - else: - self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region) - else: - self.sagemaker_client = boto3.client("sagemaker-runtime") - - line = 2 - - sagemaker_endpoint = credentials.get("sagemaker_endpoint") - candidate_docs = [] - - scores = self._sagemaker_rerank(query, docs, sagemaker_endpoint) - for idx in range(len(scores)): - candidate_docs.append({"content": docs[idx], "score": scores[idx]}) - - sorted(candidate_docs, key=operator.itemgetter("score"), reverse=True) - - line = 3 - rerank_documents = [] - for idx, result in enumerate(candidate_docs): - rerank_document = RerankDocument( - index=idx, text=result.get("content"), score=result.get("score", -100.0) - ) - - if score_threshold is not None: - if rerank_document.score >= score_threshold: - rerank_documents.append(rerank_document) - else: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - - except Exception as e: - logger.exception(f"Exception {e}, line : {line}") - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.RERANK, - model_properties={}, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/sagemaker/sagemaker.py b/api/core/model_runtime/model_providers/sagemaker/sagemaker.py deleted file mode 100644 index 042155b152..0000000000 --- a/api/core/model_runtime/model_providers/sagemaker/sagemaker.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging -import uuid -from typing import IO, Any - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class SageMakerProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - pass - - -def buffer_to_s3(s3_client: Any, file: IO[bytes], bucket: str, s3_prefix: str) -> str: - """ - return s3_uri of this file - """ - s3_key = f"{s3_prefix}{uuid.uuid4()}.mp3" - s3_client.put_object(Body=file.read(), Bucket=bucket, Key=s3_key, ContentType="audio/mp3") - return s3_key - - -def generate_presigned_url(s3_client: Any, file: IO[bytes], bucket_name: str, s3_prefix: str, expiration=600) -> str: - object_key = buffer_to_s3(s3_client, file, bucket_name, s3_prefix) - try: - response = s3_client.generate_presigned_url( - "get_object", Params={"Bucket": bucket_name, "Key": object_key}, ExpiresIn=expiration - ) - except Exception as e: - print(f"Error generating presigned URL: {e}") - return None - - return response diff --git a/api/core/model_runtime/model_providers/sagemaker/sagemaker.yaml b/api/core/model_runtime/model_providers/sagemaker/sagemaker.yaml deleted file mode 100644 index 87cd50f50c..0000000000 --- a/api/core/model_runtime/model_providers/sagemaker/sagemaker.yaml +++ /dev/null @@ -1,193 +0,0 @@ -provider: sagemaker -label: - zh_Hans: Sagemaker - en_US: Sagemaker -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -description: - en_US: Customized model on Sagemaker - zh_Hans: Sagemaker上的私有化部署的模型 -background: "#ECE9E3" -help: - title: - en_US: How to deploy customized model on Sagemaker - zh_Hans: 如何在Sagemaker上的私有化部署的模型 - url: - en_US: https://github.com/aws-samples/dify-aws-tool/blob/main/README.md#how-to-deploy-sagemaker-endpoint - zh_Hans: https://github.com/aws-samples/dify-aws-tool/blob/main/README_ZH.md#%E5%A6%82%E4%BD%95%E9%83%A8%E7%BD%B2sagemaker%E6%8E%A8%E7%90%86%E7%AB%AF%E7%82%B9 -supported_model_types: - - llm - - text-embedding - - rerank - - speech2text - - tts -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: chat - label: - en_US: Chat - zh_Hans: Chat - - variable: sagemaker_endpoint - label: - en_US: sagemaker endpoint - type: text-input - required: true - placeholder: - zh_Hans: 请输出你的Sagemaker推理端点 - en_US: Enter your Sagemaker Inference endpoint - - variable: audio_s3_cache_bucket - show_on: - - variable: __model_type - value: speech2text - label: - zh_Hans: 音频缓存桶(s3 bucket) - en_US: audio cache bucket(s3 bucket) - type: text-input - required: true - placeholder: - zh_Hans: sagemaker-us-east-1-******207838 - en_US: sagemaker-us-east-1-*******7838 - - variable: audio_model_type - show_on: - - variable: __model_type - value: tts - label: - en_US: Audio model type - type: select - required: true - placeholder: - zh_Hans: 语音模型类型 - en_US: Audio model 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: crosslingual clone voice - zh_Hans: 跨语种克隆音色 - - value: InstructVoice - label: - en_US: Instruct voice - zh_Hans: 文字指令音色 - - variable: prompt_audio - show_on: - - variable: __model_type - value: tts - label: - en_US: Mock Audio Source - type: text-input - required: false - placeholder: - zh_Hans: 被模仿的音色音频 - en_US: source audio to be mocked - - variable: prompt_text - show_on: - - variable: __model_type - value: tts - label: - en_US: Prompt Audio Text - type: text-input - required: false - placeholder: - zh_Hans: 模仿音色的对应文本 - en_US: text for the mocked source audio - - variable: instruct_text - show_on: - - variable: __model_type - value: tts - label: - en_US: instruct text for speaker - type: text-input - required: false - - variable: aws_access_key_id - required: false - label: - en_US: Access Key (If not provided, credentials are obtained from the running environment.) - zh_Hans: Access Key (如果未提供,凭证将从运行环境中获取。) - type: secret-input - placeholder: - en_US: Enter your Access Key - zh_Hans: 在此输入您的 Access Key - - variable: aws_secret_access_key - required: false - label: - en_US: Secret Access Key - zh_Hans: Secret Access Key - type: secret-input - placeholder: - en_US: Enter your Secret Access Key - zh_Hans: 在此输入您的 Secret Access Key - - variable: aws_region - required: false - label: - en_US: AWS Region - zh_Hans: AWS 地区 - type: select - default: us-east-1 - options: - - value: us-east-1 - label: - en_US: US East (N. Virginia) - zh_Hans: 美国东部 (弗吉尼亚北部) - - value: us-west-2 - label: - en_US: US West (Oregon) - zh_Hans: 美国西部 (俄勒冈州) - - value: ap-southeast-1 - label: - en_US: Asia Pacific (Singapore) - zh_Hans: 亚太地区 (新加坡) - - value: ap-northeast-1 - label: - en_US: Asia Pacific (Tokyo) - zh_Hans: 亚太地区 (东京) - - value: eu-central-1 - label: - en_US: Europe (Frankfurt) - zh_Hans: 欧洲 (法兰克福) - - value: us-gov-west-1 - label: - en_US: AWS GovCloud (US-West) - zh_Hans: AWS GovCloud (US-West) - - value: ap-southeast-2 - label: - en_US: Asia Pacific (Sydney) - zh_Hans: 亚太地区 (悉尼) - - value: cn-north-1 - label: - en_US: AWS Beijing (cn-north-1) - zh_Hans: 中国北京 (cn-north-1) - - value: cn-northwest-1 - label: - en_US: AWS Ningxia (cn-northwest-1) - zh_Hans: 中国宁夏 (cn-northwest-1) diff --git a/api/core/model_runtime/model_providers/sagemaker/speech2text/__init__.py b/api/core/model_runtime/model_providers/sagemaker/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py b/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py deleted file mode 100644 index 6aa8c9995f..0000000000 --- a/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py +++ /dev/null @@ -1,125 +0,0 @@ -import json -import logging -from typing import IO, Any, Optional - -import boto3 - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from core.model_runtime.model_providers.sagemaker.sagemaker import generate_presigned_url - -logger = logging.getLogger(__name__) - - -class SageMakerSpeech2TextModel(Speech2TextModel): - """ - Model class for Xinference speech to text model. - """ - - sagemaker_client: Any = None - s3_client: Any = None - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - asr_text = None - - try: - if not self.sagemaker_client: - access_key = credentials.get("aws_access_key_id") - secret_key = credentials.get("aws_secret_access_key") - aws_region = credentials.get("aws_region") - if aws_region: - if access_key and secret_key: - self.sagemaker_client = boto3.client( - "sagemaker-runtime", - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=aws_region, - ) - self.s3_client = boto3.client( - "s3", aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=aws_region - ) - else: - self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region) - self.s3_client = boto3.client("s3", region_name=aws_region) - else: - self.sagemaker_client = boto3.client("sagemaker-runtime") - self.s3_client = boto3.client("s3") - - s3_prefix = "dify/speech2text/" - sagemaker_endpoint = credentials.get("sagemaker_endpoint") - bucket = credentials.get("audio_s3_cache_bucket") - - s3_presign_url = generate_presigned_url(self.s3_client, file, bucket, s3_prefix) - payload = {"audio_s3_presign_uri": s3_presign_url} - - response_model = self.sagemaker_client.invoke_endpoint( - EndpointName=sagemaker_endpoint, Body=json.dumps(payload), ContentType="application/json" - ) - json_str = response_model["Body"].read().decode("utf8") - json_obj = json.loads(json_str) - asr_text = json_obj["text"] - except Exception as e: - logger.exception(f"Exception {e}, line : {line}") - - return asr_text - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - pass - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.SPEECH2TEXT, - model_properties={}, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/sagemaker/text_embedding/__init__.py b/api/core/model_runtime/model_providers/sagemaker/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/tts/__init__.py b/api/core/model_runtime/model_providers/sagemaker/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/sagemaker/tts/tts.py b/api/core/model_runtime/model_providers/sagemaker/tts/tts.py deleted file mode 100644 index a22bd6dd6e..0000000000 --- a/api/core/model_runtime/model_providers/sagemaker/tts/tts.py +++ /dev/null @@ -1,275 +0,0 @@ -import concurrent.futures -import copy -import json -import logging -from enum import Enum -from typing import Any, Optional - -import boto3 -import requests - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.model_providers.__base.tts_model import TTSModel - -logger = logging.getLogger(__name__) - - -class TTSModelType(Enum): - PresetVoice = "PresetVoice" - CloneVoice = "CloneVoice" - CloneVoice_CrossLingual = "CloneVoice_CrossLingual" - InstructVoice = "InstructVoice" - - -class SageMakerText2SpeechModel(TTSModel): - sagemaker_client: Any = None - s3_client: Any = None - comprehend_client: Any = None - - def __init__(self): - # preset voices, need support custom voice - self.model_voices = { - "__default": { - "all": [ - {"name": "Default", "value": "default"}, - ] - }, - "CosyVoice": { - "zh-Hans": [ - {"name": "中文男", "value": "中文男"}, - {"name": "中文女", "value": "中文女"}, - {"name": "粤语女", "value": "粤语女"}, - ], - "zh-Hant": [ - {"name": "中文男", "value": "中文男"}, - {"name": "中文女", "value": "中文女"}, - {"name": "粤语女", "value": "粤语女"}, - ], - "en-US": [ - {"name": "英文男", "value": "英文男"}, - {"name": "英文女", "value": "英文女"}, - ], - "ja-JP": [ - {"name": "日语男", "value": "日语男"}, - ], - "ko-KR": [ - {"name": "韩语女", "value": "韩语女"}, - ], - }, - } - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - pass - - def _detect_lang_code(self, content: str, map_dict: 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( - self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ): - """ - _invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param voice: model timbre - :param content_text: text content to be translated - :param user: unique user id - :return: text translated to audio file - """ - if not self.sagemaker_client: - access_key = credentials.get("aws_access_key_id") - secret_key = credentials.get("aws_secret_access_key") - aws_region = credentials.get("aws_region") - if aws_region: - if access_key and secret_key: - self.sagemaker_client = boto3.client( - "sagemaker-runtime", - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=aws_region, - ) - self.s3_client = boto3.client( - "s3", aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=aws_region - ) - self.comprehend_client = boto3.client( - "comprehend", - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=aws_region, - ) - else: - 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") - - model_type = credentials.get("audio_model_type", "PresetVoice") - prompt_text = credentials.get("prompt_text") - prompt_audio = credentials.get("prompt_audio") - instruct_text = credentials.get("instruct_text") - sagemaker_endpoint = credentials.get("sagemaker_endpoint") - payload = self._build_tts_payload(model_type, content_text, voice, prompt_text, prompt_audio, instruct_text) - - return self._tts_invoke_streaming(model_type, payload, sagemaker_endpoint) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TTS, - model_properties={}, - parameter_rules=[], - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def _get_model_default_voice(self, model: str, credentials: dict) -> any: - return "" - - def _get_model_word_limit(self, model: str, credentials: dict) -> int: - return 15 - - def _get_model_audio_type(self, model: str, credentials: dict) -> str: - return "mp3" - - def _get_model_workers_limit(self, model: str, credentials: dict) -> int: - return 5 - - def get_tts_model_voices(self, model: str, credentials: dict, language: Optional[str] = None) -> list: - audio_model_name = "CosyVoice" - for key, voices in self.model_voices.items(): - if key in audio_model_name: - if language and language in voices: - return voices[language] - elif "all" in voices: - return voices["all"] - - return self.model_voices["__default"]["all"] - - 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 _tts_invoke_streaming(self, model_type: str, payload: dict, sagemaker_endpoint: str) -> any: - """ - _tts_invoke_streaming text2speech model - - :param model: model name - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :return: text translated to audio file - """ - try: - lang_tag = "" - if model_type == TTSModelType.CloneVoice_CrossLingual.value: - lang_tag = payload.pop("lang_tag") - - word_limit = self._get_model_word_limit(model="", credentials={}) - content_text = payload.get("tts_text") - if len(content_text) > word_limit: - split_sentences = self._split_text_into_sentences(content_text, max_length=word_limit) - sentences = [f"{lang_tag}{s}" for s in split_sentences if len(s)] - len_sent = len(sentences) - executor = concurrent.futures.ThreadPoolExecutor(max_workers=min(4, len_sent)) - payloads = [copy.deepcopy(payload) for i in range(len_sent)] - for idx in range(len_sent): - payloads[idx]["tts_text"] = sentences[idx] - - futures = [ - executor.submit( - self._invoke_sagemaker, - payload=payload, - endpoint=sagemaker_endpoint, - ) - for payload in payloads - ] - - for future in futures: - resp = future.result() - audio_bytes = requests.get(resp.get("s3_presign_url")).content - for i in range(0, len(audio_bytes), 1024): - yield audio_bytes[i : i + 1024] - else: - resp = self._invoke_sagemaker(payload, sagemaker_endpoint) - audio_bytes = requests.get(resp.get("s3_presign_url")).content - - for i in range(0, len(audio_bytes), 1024): - yield audio_bytes[i : i + 1024] - except Exception as ex: - raise InvokeBadRequestError(str(ex)) diff --git a/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow.svg b/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow.svg deleted file mode 100644 index 16e406f030..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow_square.svg b/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow_square.svg deleted file mode 100644 index ad6b384f7a..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/_assets/siliconflow_square.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml deleted file mode 100644 index d4431179e5..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: deepseek-ai/DeepSeek-Coder-V2-Instruct -label: - en_US: deepseek-ai/DeepSeek-Coder-V2-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.33' - output: '1.33' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml deleted file mode 100644 index caa6508b5e..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: deepseek-ai/DeepSeek-V2-Chat -label: - en_US: deepseek-ai/DeepSeek-V2-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.33' - output: '1.33' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml deleted file mode 100644 index 1c8e15ae52..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: deepseek-ai/DeepSeek-V2.5 -label: - en_US: deepseek-ai/DeepSeek-V2.5 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.33' - output: '1.33' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml deleted file mode 100644 index 2840e3dcf4..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: google/gemma-2-27b-it -label: - en_US: google/gemma-2-27b-it -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8196 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.26' - output: '1.26' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml deleted file mode 100644 index d7e19b46f6..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: google/gemma-2-9b-it -label: - en_US: google/gemma-2-9b-it -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8196 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml deleted file mode 100644 index 9b32a02477..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: THUDM/glm-4-9b-chat -label: - en_US: THUDM/glm-4-9b-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml deleted file mode 100644 index 73ad4480aa..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: internlm/internlm2_5-7b-chat -label: - en_US: internlm/internlm2_5-7b-chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py b/api/core/model_runtime/model_providers/siliconflow/llm/llm.py deleted file mode 100644 index c1868b6ad0..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py +++ /dev/null @@ -1,31 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class SiliconflowLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - @classmethod - def _add_custom_parameters(cls, credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = "https://api.siliconflow.cn/v1" diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml deleted file mode 100644 index 9993d781ac..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: meta-llama/Meta-Llama-3-70B-Instruct -label: - en_US: meta-llama/Meta-Llama-3-70B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '4.13' - output: '4.13' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml deleted file mode 100644 index 60e3764789..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: meta-llama/Meta-Llama-3-8B-Instruct -label: - en_US: meta-llama/Meta-Llama-3-8B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml deleted file mode 100644 index f992660aa2..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: meta-llama/Meta-Llama-3.1-405B-Instruct -label: - en_US: meta-llama/Meta-Llama-3.1-405B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '21' - output: '21' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml deleted file mode 100644 index 1c69d63a40..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: meta-llama/Meta-Llama-3.1-70B-Instruct -label: - en_US: meta-llama/Meta-Llama-3.1-70B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '4.13' - output: '4.13' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml deleted file mode 100644 index a97002a5ca..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: meta-llama/Meta-Llama-3.1-8B-Instruct -label: - en_US: meta-llama/Meta-Llama-3.1-8B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/mistral-7b-instruct-v0.2.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/mistral-7b-instruct-v0.2.yaml deleted file mode 100644 index 89fb153ba0..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/mistral-7b-instruct-v0.2.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: mistralai/Mistral-7B-Instruct-v0.2 -label: - en_US: mistralai/Mistral-7B-Instruct-v0.2 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/mistral-8x7b-instruct-v0.1.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/mistral-8x7b-instruct-v0.1.yaml deleted file mode 100644 index 2785e7496f..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/mistral-8x7b-instruct-v0.1.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: mistralai/Mixtral-8x7B-Instruct-v0.1 -label: - en_US: mistralai/Mixtral-8x7B-Instruct-v0.1 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.26' - output: '1.26' - unit: '0.000001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-1.5b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-1.5b-instruct.yaml deleted file mode 100644 index f6c976af8e..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-1.5b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2-1.5B-Instruct -label: - en_US: Qwen/Qwen2-1.5B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml deleted file mode 100644 index a996e919ea..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2-57B-A14B-Instruct -label: - en_US: Qwen/Qwen2-57B-A14B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.26' - output: '1.26' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml deleted file mode 100644 index a6e2c22dac..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2-72B-Instruct -label: - en_US: Qwen/Qwen2-72B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '4.13' - output: '4.13' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml deleted file mode 100644 index d8bea5e129..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2-7B-Instruct -label: - en_US: Qwen/Qwen2-7B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml deleted file mode 100644 index 02a401464b..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2.5-14B-Instruct -label: - en_US: Qwen/Qwen2.5-14B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 8192 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0.7' - output: '0.7' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml deleted file mode 100644 index d084617e7d..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2.5-32B-Instruct -label: - en_US: Qwen/Qwen2.5-32B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 8192 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.26' - output: '1.26' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml deleted file mode 100644 index dfbad2494c..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2.5-72B-Instruct -label: - en_US: Qwen/Qwen2.5-72B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 8192 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '4.13' - output: '4.13' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml deleted file mode 100644 index cdc8ffc4d2..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: Qwen/Qwen2.5-7B-Instruct -label: - en_US: Qwen/Qwen2.5-7B-Instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 8192 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-34b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-34b-chat.yaml deleted file mode 100644 index 864ba46f1a..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-34b-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: 01-ai/Yi-1.5-34B-Chat -label: - en_US: 01-ai/Yi-1.5-34B-Chat-16K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '1.26' - output: '1.26' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-6b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-6b-chat.yaml deleted file mode 100644 index fe4c8b4b3e..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-6b-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: 01-ai/Yi-1.5-6B-Chat -label: - en_US: 01-ai/Yi-1.5-6B-Chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-9b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-9b-chat.yaml deleted file mode 100644 index c61f0dc53f..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/llm/yi-1.5-9b-chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: 01-ai/Yi-1.5-9B-Chat-16K -label: - en_US: 01-ai/Yi-1.5-9B-Chat-16K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - - name: frequency_penalty - use_template: frequency_penalty -pricing: - input: '0' - output: '0' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/rerank/__init__.py b/api/core/model_runtime/model_providers/siliconflow/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/siliconflow/rerank/bce-reranker-base_v1.yaml b/api/core/model_runtime/model_providers/siliconflow/rerank/bce-reranker-base_v1.yaml deleted file mode 100644 index ff3635bfeb..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/rerank/bce-reranker-base_v1.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: netease-youdao/bce-reranker-base_v1 -model_type: rerank -model_properties: - context_size: 512 diff --git a/api/core/model_runtime/model_providers/siliconflow/rerank/bge-reranker-v2-m3.yaml b/api/core/model_runtime/model_providers/siliconflow/rerank/bge-reranker-v2-m3.yaml deleted file mode 100644 index 807f531b08..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/rerank/bge-reranker-v2-m3.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: BAAI/bge-reranker-v2-m3 -model_type: rerank -model_properties: - context_size: 8192 diff --git a/api/core/model_runtime/model_providers/siliconflow/rerank/rerank.py b/api/core/model_runtime/model_providers/siliconflow/rerank/rerank.py deleted file mode 100644 index 58b033d28a..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/rerank/rerank.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Optional - -import httpx - -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class SiliconflowRerankModel(RerankModel): - 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: - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - base_url = credentials.get("base_url", "https://api.siliconflow.cn/v1") - base_url = base_url.removesuffix("/") - try: - response = httpx.post( - base_url + "/rerank", - json={"model": model, "query": query, "documents": docs, "top_n": top_n, "return_documents": True}, - headers={"Authorization": f"Bearer {credentials.get('api_key')}"}, - ) - response.raise_for_status() - results = response.json() - - rerank_documents = [] - for result in results["results"]: - rerank_document = RerankDocument( - index=result["index"], - text=result["document"]["text"], - score=result["relevance_score"], - ) - if score_threshold is None or result["relevance_score"] >= score_threshold: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - except httpx.HTTPStatusError as e: - raise InvokeServerUnavailableError(str(e)) - - def validate_credentials(self, model: str, credentials: dict) -> None: - try: - self._invoke( - model=model, - credentials=credentials, - 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - """ - return { - InvokeConnectionError: [httpx.ConnectError], - InvokeServerUnavailableError: [httpx.RemoteProtocolError], - InvokeRateLimitError: [], - InvokeAuthorizationError: [httpx.HTTPStatusError], - InvokeBadRequestError: [httpx.RequestError], - } diff --git a/api/core/model_runtime/model_providers/siliconflow/siliconflow.py b/api/core/model_runtime/model_providers/siliconflow/siliconflow.py deleted file mode 100644 index e121ab8c7e..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/siliconflow.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class SiliconflowProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="deepseek-ai/DeepSeek-V2-Chat", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/siliconflow/siliconflow.yaml b/api/core/model_runtime/model_providers/siliconflow/siliconflow.yaml deleted file mode 100644 index c46a891604..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/siliconflow.yaml +++ /dev/null @@ -1,32 +0,0 @@ -provider: siliconflow -label: - zh_Hans: 硅基流动 - en_US: SiliconFlow -icon_small: - en_US: siliconflow_square.svg -icon_large: - en_US: siliconflow.svg -background: "#ffecff" -help: - title: - en_US: Get your API Key from SiliconFlow - zh_Hans: 从 SiliconFlow 获取 API Key - url: - en_US: https://cloud.siliconflow.cn/account/ak -supported_model_types: - - llm - - text-embedding - - rerank - - speech2text -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/siliconflow/speech2text/__init__.py b/api/core/model_runtime/model_providers/siliconflow/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml b/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml deleted file mode 100644 index deceaf60f4..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: iic/SenseVoiceSmall -model_type: speech2text -model_properties: - file_upload_limit: 1 - supported_file_extensions: mp3,wav diff --git a/api/core/model_runtime/model_providers/siliconflow/speech2text/speech2text.py b/api/core/model_runtime/model_providers/siliconflow/speech2text/speech2text.py deleted file mode 100644 index 8d1932863e..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/speech2text/speech2text.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import IO, Optional - -from core.model_runtime.model_providers.openai_api_compatible.speech2text.speech2text import OAICompatSpeech2TextModel - - -class SiliconflowSpeech2TextModel(OAICompatSpeech2TextModel): - """ - Model class for Siliconflow Speech to text model. - """ - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - self._add_custom_parameters(credentials) - return super()._invoke(model, credentials, file) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - return super().validate_credentials(model, credentials) - - @classmethod - def _add_custom_parameters(cls, credentials: dict) -> None: - credentials["endpoint_url"] = "https://api.siliconflow.cn/v1" diff --git a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bce-embedding-base-v1.yaml b/api/core/model_runtime/model_providers/siliconflow/text_embedding/bce-embedding-base-v1.yaml deleted file mode 100644 index 710fbc04f6..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bce-embedding-base-v1.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: netease-youdao/bce-embedding-base_v1 -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-en-v1.5.yaml b/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-en-v1.5.yaml deleted file mode 100644 index 84f69b41a0..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-en-v1.5.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: BAAI/bge-large-en-v1.5 -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-zh-v1.5.yaml b/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-zh-v1.5.yaml deleted file mode 100644 index 5248375d0b..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-large-zh-v1.5.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: BAAI/bge-large-zh-v1.5 -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-m3.yaml b/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-m3.yaml deleted file mode 100644 index f0b12dd420..0000000000 --- a/api/core/model_runtime/model_providers/siliconflow/text_embedding/bge-m3.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: BAAI/bge-m3 -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 1 diff --git a/api/core/model_runtime/model_providers/spark/__init__.py b/api/core/model_runtime/model_providers/spark/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/spark/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/spark/_assets/icon_l_en.svg deleted file mode 100644 index 521c68cae5..0000000000 --- a/api/core/model_runtime/model_providers/spark/_assets/icon_l_en.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/spark/_assets/icon_l_zh.svg b/api/core/model_runtime/model_providers/spark/_assets/icon_l_zh.svg deleted file mode 100644 index 71d85216aa..0000000000 --- a/api/core/model_runtime/model_providers/spark/_assets/icon_l_zh.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/spark/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/spark/_assets/icon_s_en.svg deleted file mode 100644 index ef0a9131a4..0000000000 --- a/api/core/model_runtime/model_providers/spark/_assets/icon_s_en.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/api/core/model_runtime/model_providers/spark/llm/__init__.py b/api/core/model_runtime/model_providers/spark/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/spark/llm/_client.py b/api/core/model_runtime/model_providers/spark/llm/_client.py deleted file mode 100644 index 48911f657a..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/_client.py +++ /dev/null @@ -1,163 +0,0 @@ -import base64 -import hashlib -import hmac -import json -import queue -import ssl -from datetime import datetime -from time import mktime -from typing import Optional -from urllib.parse import urlencode, urlparse -from wsgiref.handlers import format_date_time - -import websocket - - -class SparkLLMClient: - def __init__(self, model: str, app_id: str, api_key: str, api_secret: str, api_domain: Optional[str] = None): - domain = "spark-api.xf-yun.com" - endpoint = "chat" - if api_domain: - domain = api_domain - - model_api_configs = { - "spark-lite": {"version": "v1.1", "chat_domain": "general"}, - "spark-pro": {"version": "v3.1", "chat_domain": "generalv3"}, - "spark-pro-128k": {"version": "pro-128k", "chat_domain": "pro-128k"}, - "spark-max": {"version": "v3.5", "chat_domain": "generalv3.5"}, - "spark-max-32k": {"version": "max-32k", "chat_domain": "max-32k"}, - "spark-4.0-ultra": {"version": "v4.0", "chat_domain": "4.0Ultra"}, - } - - api_version = model_api_configs[model]["version"] - - self.chat_domain = model_api_configs[model]["chat_domain"] - - if model in ["spark-pro-128k", "spark-max-32k"]: - self.api_base = f"wss://{domain}/{endpoint}/{api_version}" - else: - self.api_base = f"wss://{domain}/{api_version}/{endpoint}" - - self.app_id = app_id - self.ws_url = self.create_url( - urlparse(self.api_base).netloc, urlparse(self.api_base).path, self.api_base, api_key, api_secret - ) - - self.queue = queue.Queue() - self.blocking_message = "" - - def create_url(self, host: str, path: str, api_base: str, api_key: str, api_secret: str) -> str: - # generate timestamp by RFC1123 - now = datetime.now() - date = format_date_time(mktime(now.timetuple())) - - signature_origin = "host: " + host + "\n" - signature_origin += "date: " + date + "\n" - signature_origin += "GET " + path + " HTTP/1.1" - - # encrypt using hmac-sha256 - signature_sha = hmac.new( - api_secret.encode("utf-8"), signature_origin.encode("utf-8"), digestmod=hashlib.sha256 - ).digest() - - signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding="utf-8") - - authorization_origin = ( - f'api_key="{api_key}", algorithm="hmac-sha256", headers="host date request-line",' - f' signature="{signature_sha_base64}"' - ) - - authorization = base64.b64encode(authorization_origin.encode("utf-8")).decode(encoding="utf-8") - - v = {"authorization": authorization, "date": date, "host": host} - # generate url - url = api_base + "?" + urlencode(v) - return url - - def run(self, messages: list, user_id: str, model_kwargs: Optional[dict] = None, streaming: bool = False): - websocket.enableTrace(False) - ws = websocket.WebSocketApp( - self.ws_url, - on_message=self.on_message, - on_error=self.on_error, - on_close=self.on_close, - on_open=self.on_open, - ) - ws.messages = messages - ws.user_id = user_id - ws.model_kwargs = model_kwargs - ws.streaming = streaming - ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) - - def on_error(self, ws, error): - self.queue.put({"status_code": error.status_code, "error": error.resp_body.decode("utf-8")}) - ws.close() - - def on_close(self, ws, close_status_code, close_reason): - self.queue.put({"done": True}) - - def on_open(self, ws): - self.blocking_message = "" - data = json.dumps(self.gen_params(messages=ws.messages, user_id=ws.user_id, model_kwargs=ws.model_kwargs)) - ws.send(data) - - def on_message(self, ws, message): - data = json.loads(message) - code = data["header"]["code"] - if code != 0: - self.queue.put({"status_code": 400, "error": f"Code: {code}, Error: {data['header']['message']}"}) - ws.close() - else: - choices = data["payload"]["choices"] - status = choices["status"] - content = choices["text"][0]["content"] - if ws.streaming: - self.queue.put({"data": content}) - else: - self.blocking_message += content - - if status == 2: - if not ws.streaming: - self.queue.put({"data": self.blocking_message}) - ws.close() - - def gen_params(self, messages: list, user_id: str, model_kwargs: Optional[dict] = None) -> dict: - data = { - "header": { - "app_id": self.app_id, - # resolve this error message => $.header.uid' length must be less or equal than 32 - "uid": user_id[:32] if user_id else None, - }, - "parameter": {"chat": {"domain": self.chat_domain}}, - "payload": {"message": {"text": messages}}, - } - - if model_kwargs: - data["parameter"]["chat"].update(model_kwargs) - - return data - - def subscribe(self): - while True: - content = self.queue.get() - if "error" in content: - if content["status_code"] == 401: - raise SparkError( - "[Spark] The credentials you provided are incorrect. " - "Please double-check and fill them in again." - ) - elif content["status_code"] == 403: - raise SparkError( - "[Spark] Sorry, the credentials you provided are access denied. " - "Please try again after obtaining the necessary permissions." - ) - else: - raise SparkError(f"[Spark] code: {content['status_code']}, error: {content['error']}") - - if "data" not in content: - break - yield content - - -class SparkError(Exception): - pass diff --git a/api/core/model_runtime/model_providers/spark/llm/_position.yaml b/api/core/model_runtime/model_providers/spark/llm/_position.yaml deleted file mode 100644 index 73f39cb119..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/_position.yaml +++ /dev/null @@ -1,11 +0,0 @@ -- spark-max-32k -- spark-4.0-ultra -- spark-max -- spark-pro-128k -- spark-pro -- spark-lite -- spark-4 -- spark-3.5 -- spark-3 -- spark-1.5 -- spark-2 diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-1.5.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-1.5.yaml deleted file mode 100644 index fcd65c24e0..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-1.5.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: spark-1.5 -deprecated: true -label: - en_US: Spark V1.5 -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 4096 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: 模型回答的tokens的最大长度。 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-2.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-2.yaml deleted file mode 100644 index 2db6805a2e..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-2.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: spark-2 -deprecated: true -label: - en_US: Spark V2.0 -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 2048 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: 模型回答的tokens的最大长度。 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-3.5.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-3.5.yaml deleted file mode 100644 index 86617a53d0..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-3.5.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: spark-3.5 -deprecated: true -label: - en_US: Spark V3.5 -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 2048 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: 模型回答的tokens的最大长度。 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-3.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-3.yaml deleted file mode 100644 index 9f296c684d..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-3.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: spark-3 -deprecated: true -label: - en_US: Spark V3.0 -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 2048 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: 模型回答的tokens的最大长度。 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-4.0-ultra.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-4.0-ultra.yaml deleted file mode 100644 index bbf85764f1..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-4.0-ultra.yaml +++ /dev/null @@ -1,42 +0,0 @@ -model: spark-4.0-ultra -label: - en_US: Spark 4.0 Ultra -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false - - name: show_ref_label - label: - zh_Hans: 联网检索 - en_US: web search - type: boolean - default: false - help: - zh_Hans: 该参数仅4.0 Ultra版本支持,当设置为true时,如果输入内容触发联网检索插件,会先返回检索信源列表,然后再返回星火回复结果,否则仅返回星火回复结果 - en_US: The parameter is only supported in the 4.0 Ultra version. When set to true, if the input triggers the online search plugin, it will first return a list of search sources and then return the Spark response. Otherwise, it will only return the Spark response. diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-4.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-4.yaml deleted file mode 100644 index 4b5529e81c..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-4.yaml +++ /dev/null @@ -1,34 +0,0 @@ -model: spark-4 -deprecated: true -label: - en_US: Spark V4.0 -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: 模型回答的tokens的最大长度。 - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-lite.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-lite.yaml deleted file mode 100644 index 1f6141a816..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-lite.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: spark-lite -label: - en_US: Spark Lite -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-max-32k.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-max-32k.yaml deleted file mode 100644 index 1a1ab6844c..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-max-32k.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: spark-max-32k -label: - en_US: Spark Max-32K -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-max.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-max.yaml deleted file mode 100644 index 71eb2b86d3..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-max.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: spark-max -label: - en_US: Spark Max -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-pro-128k.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-pro-128k.yaml deleted file mode 100644 index da1fead6da..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-pro-128k.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: spark-pro-128k -label: - en_US: Spark Pro-128K -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/llm/spark-pro.yaml b/api/core/model_runtime/model_providers/spark/llm/spark-pro.yaml deleted file mode 100644 index 9ee479f15b..0000000000 --- a/api/core/model_runtime/model_providers/spark/llm/spark-pro.yaml +++ /dev/null @@ -1,33 +0,0 @@ -model: spark-pro -label: - en_US: Spark Pro -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.5 - help: - zh_Hans: 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。 - en_US: Kernel sampling threshold. Used to determine the randomness of the results. The higher the value, the stronger the randomness, that is, the higher the possibility of getting different answers to the same question. - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 1 - max: 8192 - help: - zh_Hans: 模型回答的tokens的最大长度。 - en_US: Maximum length of tokens for the model response. - - name: top_k - label: - zh_Hans: 取样数量 - en_US: Top k - type: int - default: 4 - min: 1 - max: 6 - help: - zh_Hans: 从 k 个候选中随机选择一个(非等概率)。 - en_US: Randomly select one from k candidates (non-equal probability). - required: false diff --git a/api/core/model_runtime/model_providers/spark/spark.py b/api/core/model_runtime/model_providers/spark/spark.py deleted file mode 100644 index b3695e0501..0000000000 --- a/api/core/model_runtime/model_providers/spark/spark.py +++ /dev/null @@ -1,18 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class SparkProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - # ignore credentials validation because every model has their own spark quota pool - pass diff --git a/api/core/model_runtime/model_providers/spark/spark.yaml b/api/core/model_runtime/model_providers/spark/spark.yaml deleted file mode 100644 index 3b07b30f24..0000000000 --- a/api/core/model_runtime/model_providers/spark/spark.yaml +++ /dev/null @@ -1,46 +0,0 @@ -provider: spark -label: - zh_Hans: 讯飞星火 - en_US: iFLYTEK SPARK -icon_small: - en_US: icon_s_en.svg -icon_large: - zh_Hans: icon_l_zh.svg - en_US: icon_l_en.svg -background: "#EBF8FF" -help: - title: - en_US: Get your API key from iFLYTEK SPARK - zh_Hans: 从讯飞星火获取 API Keys - url: - en_US: https://www.xfyun.cn/solutions/xinghuoAPI -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: app_id - label: - en_US: APPID - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的 APPID - en_US: Enter your APPID - - variable: api_secret - label: - en_US: APISecret - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 APISecret - en_US: Enter your APISecret - - variable: api_key - label: - en_US: APIKey - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 APIKey - en_US: Enter your APIKey diff --git a/api/core/model_runtime/model_providers/stepfun/__init__.py b/api/core/model_runtime/model_providers/stepfun/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/stepfun/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/stepfun/_assets/icon_l_en.png deleted file mode 100644 index c118ea09bd8a84d7d43f03f6f5bc15c9a0c67e95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9176 zcmd^l`9IX}`|k+Zq9T-uWG%8YBC;=oLAGd$EW=E^?7Ol?A<7y}H1_P&7=)CvFWHwF z`@T)G@8*p6`}6*M&i8T7=llcb^~20-xvuAZEzkR!`+i*$X{fKoNY6nJ0)ZHz+UiCi z5G6o9Z=t0mf9kRB-zI-u@zgN)GUY!KW~z}f*}gs`^rb8AH?khN~0 zOw2vaVS4hmE`X@@sf?&E;7Zm8ffST{U9D{$5uV&O2nUq2BF|b)Jr6g^PLb!n3``v6 zs)j(KwEf)?#{T*yw*HQ`a&|mQcexdO<;eg5!qb}D7jSa+koQ&O`3qN`e13XcjEDO# z5l=@&p1+MUhZ%CKxws>^);^|=x%2(Z=|mIui?m7iabb9Pgi*{F&`fvQ6DK$7k39S2{}1AF>y&T zNl6j1gouZqv!}JMh_eUpKM3jw4_kMXt0&6Enfnyc+Q!ApQ;~;k?B8AhuK$L0_V`z% z$T1W1wRRPg5EVc5>K{QE?Efta0RNWu@H9gF$KL-du!o7CD?-c&;o;)tZcEOcJ@4sI zuJUT`2y0IlcM}&Er+)%zh;;FE@j$w`a!X2!igVw9S=*wVPw(9PO9cj#hdO(BT07e! zpz4Y|WDrpl%1&NFT|!MlT24wzO&lU2p`ocFqb?&SBP)4ZUFP;}Nfp(9bk$vKy#R!> z=Rdl3|IwBHuezs%09?r?>>#9K=jah6si&m3MST`8M*ggC=!7kT0nz||I=g!O=;%nmo5bZx zO8PXPUEUgyx!Bn^hHIK_>z}P@nXpdyy|l8r_Gd%ZX9-)NJsYNluB7he@je`&(Moh30lkcz(apS2>*F8;BJB|IM?F`-C0 z0Rw8G8$Y_H3%|{)gdYieZe@P%JvcZVn_9AqpQ&r?9~@t8>Y0T`Y~#o0TUK{hR)164 zkfvvsjG|Tp(EUC56%psTn@)?NS)+qvi@SUKJG*-d&(_b{k!Y<+VL8*wtLuJfLTq+B zr|lf*0ZH0#Yh!a;KXSt&cC~kCg2Q2p--86QBbi3d#pI8=CH4hk#(WYxKnA4IiMi={ z;*+GQ#`dA{+21xvekA$CGYU*8-3p|*Z znrE~j4Nnq-KMW*&UJ1?r9g#mbIkOa-*H8aw8Jec43vy>WK0&DL8l8DP>;MZy>K~@RHs3^uijYC8_k=Sl95PWzrrkiuAFfYh-ll z%tI2N`$2W{RC?v?2kgkm#=&{SA;^*hf|EdxNT7!#kQGV)S)aJeOk{e~4dmFb;RQzZ6x@ul=Ag()$4A-h!UWUBW%W(J=v)y{F7bfh*Lsmk;|_D52U?xyk_s3 za9K?0rot3@b-{e*JmUqDz5`p@I?FrwyLj$}+d@V=)B&1|p8rmCg$vr4Lz3cXRSD(Nh@uSrJ?;P%%|wmbha&UPTJfn$UqL zXbvPga~^+u^o!~|kS^@d_&Ddm)vU?DJiurK_pDU$EwN!>1;-WVWH5Snh~&ti(%a$G zh|2t=Z55>`rHs2bu6AJTLgMTm%q=ljFuxK<*EdFU<(#rscTT{2@8$@f%BwInYY}eXP78>6R`WFCH{#y0D@Q{s1v+Oo)v8J)Qn4p3Yc3 zwOMBj3!2__s;csxJ<-s8jAqn-3tl``ql_alzvFwg7a-_JhXuW8h1&kf)K5 zZ~Q1+oFAr`SY351>FWzxQp7%;@uE?=aPG(CW!L54kKpls&c$4vQ`APm^>O2LYj5a`J*?}vgot@1f-_K)n zs?6znww><981fHCWqxC9og1gU4l2C z{()uVWtLZu_te;EM1e$rj!cUEMBKwv_&~-)-Pgcl;gw%mRTm#oCPM_}=*e^7DHMGC zZZ?gHQT=-XbfGsYe%GdOEP0LcE$;i@{trjT8!r*>@w5!UKD@qu@8JT?EkK{}@YK)i z05qQeP{^Nm{-krB;cd4->FPjmPVp`3Q(P^u=?$+B1PgP4N}Qj5W>u;)6&G-+@l!NU z?yp`|aO<+D(H+3DC8p?`fzdmNW|*9*rPAI_a_I-`9b}#99@wMjREY zRng1M^iwWtFhibDdbb5@^Dkr$kaMkmJlekICP5-{S0p@YH^z@@UT?q4 z3!I3}Km{-(rNn_W+H7LNAJb2UnVWGUI?eWmkS2dsG}fa&4?Cci{PL za?FW|TqihC;EvL7>}Jv9w$`FgUN6&*)-i0Fl?05w#a8oG$}>khuYXRGvul2pa>Qc* zqbFi!RftE6|;@zK2^jhfp z-#UX2eS?$!*_XoVF{u>Ah;Qp{ytb7~y}3Td%7HaSZ@>LPho!y_yr#b(*jm@HrB5fR!WqhvrdEYowPLs>f)XB_)-?PBnGdAQ z4r`TVYJppjP!X$xdpoKfVUFRfYfh4IgnVq8pD4K3X3`D2X4*I(tMmt>GuhNDD~`M4PuX;k7etigjLN5 zw5xhKs4AB_ix63}yKF))TW?cf_ah+Z;kdjIve~)_Vl4gXrU0?<1Bzv z@`qAUao%lXSAongysK+dws$M5s5fHKR?Ak%FRLz;_;VTrr$82UZ|CEq`=Mc3!=IPA z{9_Cw(Sj0{M=%12oCXhF_$_AbXyPzE8Ut?Czkk^8;MhO46X2P8+&DEYZ^cLs0Rhx|ABL-VCT^@oUPXr{y*);1dO*qB z+B*#sQe>oqmL%6x_+KjBOQTZynKaGcD_$IZfa&zXZp)8O(8wQ%DWlKeHF-H>LS+~~YU z7f-%XY%q>$AU5(fQC$OAbmOBv&Bl3OFk$x}Dx;-=ma-W6UU4V1i4bx`Zwh~{&L9|k zoHvwo7{?`CPr9N4brjH3-Eh-J*r!;ewu&b1J?*y1Qo40#P=J&1Xvh8<>iBnAueM{ms&bvo%@?be|M(f* zvZFFv5oaB3KNPjF9Gu|6jyx%DrJwD}=4T>j)>w^jhZg9kw8XR&jA0Yf#+MNT=?)AB zYjf8IqmjJNNBVm&?2P`Dk1YH|r-coAs7@Av1RffIvyeRel(n@C=&eN*tbynP-L z+lJ#KBlhWozra0>JVz`-;x6?zff>Gb>--tcDKUPq(G`U4yS}Bi)a;+(o={Igu7uA} z49#67k(Ld*EaORiNf6PUiRp^jXCLq2Yp3S)Pfk~kTR9^8x~skujXE5ci?OIn-Sc#S zY3tW+A2UqnF}*VNAcNE#CGcGXKB0K=ZuhrqXMo=*LVO6sdCL_8y9o%)>(z;hM}@D2 z8x}Ay{>neBfZ!j?=DQ(h>{H8|fIHo}uW++>_!)!vmF^b+Ez#Y_Yd2u#y<6D|7?Ez& zOjmCdhb*SkFVZ*tB(M#Cd%Ml>pE0Pe5 zdy^fbcK#qI7f&uBTxC-S6@Xx>vdMRjDe2YdF}xHFnPSCV5ZU8+v{}wnS*r;Z;6gd-*$YA ze^Q@%vrv}w3P2zJsdxA?AUn~HKwUBl@4R=qP)UJ!K8U}1#T;85b&u3yy31zUs z{2ch$sMmO)!f+_6#9^?ONO;4iqxmu(+xTq5uCB>@_8LTeEqyop>z9PwM4Zya824~- z;Gw8d){D6-$&ibiL96p6?9|V0dcSfQT-83y1ay9BkUg_T_276e0wC*6W!FCkBZ{B41UlW{V6lBe-Z z!(r9!CUKH4ku}#PTWrqz+H}ZS=>JXoq10rtm~S~ic?)+m2dsfheZiiJwRngh@H#Sc^rfOq7+ znt#Q-&|senCp4=k**PncJw}D_y4LJQ5j#@t7p~Tj`4)EjjXMkW{osS{AR(1VEx-wYKJXNs$yi_k{hioV@d`_bUdX zp7M>0i~-$3;q-@5R=kV9qgZgu(Yu@HR0&J0z?y0-C0^kjKlA~@1kc>m%-CS}HubJv zl7uZbm&Kw{A0{c;T@JXZHu3mbNqC5u=0t zGM3Hi&cacIYa$A^D89%E=K>fElCF<27L0P;z>b_OY|5HG@u)q)J}RDa5NZ(80uyAF zkt^<0`K(C-in;s_ygeExP2pDo+2rZX+Sz4ga<&u}D64Y-Nu^4??8$={wep&^EZkUA z3=OA?d?c4dLrMSRRy12@k49)1PTxZH)=Rn&RX96uH`|MIuG-(@`QNq&O_Au&;itQE zV-&hrdHHoxner=V^`AfXe)_V<41NnQM|vGZg-`5@h{)j)n;zQSbMPpur#*-7&h1Ra z27_Y)-K~rLb-z9zZIHZ)Me2&NBBmRz1w-brrWvst7KL_N%0FKPV*sO@%Y_vgC!d|L z+MSA~aCgceSjf_kaAp9-%NF^?m&Io$Look6F1F=7#Q27`(xQA$Z4>x8?jn-n!W3hD zCvrwpt3bwXLn6>Qk)-jf?K~AevqL)o zw&r{!UBfJLr(!*LTvgz~s3teP0RF(#?pQ5MIB0PYd7u>Gh~Z3l{f#|??0TCzt4hzF^l}AHv`f;UrdEo&FxeIN35nF zar~a!qw5!TJINFgQp1W$;SzYP&f>FC1a!=O=JH&kxn=R+v8!RWvy!?v0m4aUngoKBGv=n=ygqI1FbVMHf8tTTCs{*6l0%(SFs{5?WiO4m`eIH?p=!0$4P?7%neKj+Ui=+)xkvT0Jc0o0WnP6Ls{9V7Isot#;$ z_7`HH`tK8}9SpnKlCCc+s={-%^H5ntFPD_-7f=NDRPF%@gJ{F6-o0Vj2ifg@t)U+Ba=!OMAJTtnK^+ z&gKDU=C_ocFFefwn?bf_+9Z9uN42x?pv%^u%29-9*k_|;zXq{68+1{fTEA621STJyZ?oUh)UNSMq_S#L({>UPVwaZAFE z1VCUw5SHIXpVGWY_vTZIx!8X7tIQMw?tS&xikbPQhbxBZ&xDB)+<2u?mZ z`{R5#YL!X$&u~lp`ggX;O<>jT{3_V^-LhE^a$GXY*tFG*YmFD*0I7O(WMhAy0uVx~ zsh;%a#>PW?#NE7>PI5Y-JoR79sRr?imsLH>x3(abcYoRtcgEa#x{9j;wTouY7fkm< zA-B=7*lm0k=Dyz1Wu)?GU>A!4Ot#Mc~hIgE(mF!&r4;&aVALh$C_Dk$X>Lk2a0Z zdd6|8usiG4b<400xLP{S3K8v6A{R=Uzt`ge0@_fHLf0ZH7$Xy4kJ0F_Z`@Zb+7BR-Mx86-@R{SOyut0f=Fl^R(0o~Nt zT3W*m11rXFvI1Rgn^Y<_oGP;$Y$+^%uFY!8GOQ2%!IkxzFt=eFuZgg%Xh_C-ZHK44 zcIo07q9B8`yYbukc*zpeRJ9ML{8J%$@qNWNbyp6&W3Q~OB+l0aOuE&|6k#>OMS;3Z zjlx@g_4$n-LcuHUVi=eNfU0q>`QWJb4x(dWU(uU<6nelv{Uy1CxJD&x$+X*-H#kzI z%b2w1{Ib`qqW6}(qN<+D&8;c(`;&vm;-`yeo#$tF?S0(FGl^S+VKuUr-H(svIqCLu zd`+(>nk&sHXwNdtBK~|}hGkDJyPW305%$(-5Wv91@S1`cDdx>u zEqE+>$NkRBoTtX%V|~!O3lM^RPVN%ljXWJ=Ut~|RlxZz{wbtVo1H;|L^5qQOx#xj( z-MDe;n%GS)pdwZ1Anp=0z5MBMPen@Zg{7U5P#HN7u{oTau&a+li4Ws-d;ycZI$)ru zblq34$LQ%IgcI5K0(VhcCi~JgiQ;gGV}vSR8`pJ7qI&HeKLDi`H5YnTmd>1=_i#)# z0;e{TIYy7?`+{e&b$57zfw2JhyDyJFpQitaqQ->X@wj&hR(8mHc8x=kts0R;>qIkP zq2)^>=r+p1f%TG!q}ltGyco6HI3hg;!Ykwft(sfvxp)06bBaqxzr|Q&^={$txo>Yu);F(oA?x* zF7mvhN_BII0A7c$LqkG(>hkTa#>8D>rfvIv=sk8hbFa^dY&S;>^z2_A)n-3~kHQO? zT+|!f6%~s1^&3eGSAZ_7`q84zOh#~>f)?J)!_C2YgBR0jj4yNjF?+A82HP058fcHi zJ$^Rz^0fm#$B32g(csc6-|d}?)vuAi_Mb=gh(4{X@N<;H8lTcEXLoKg4!V0Uptt>YX&)p4xSU;X*ZZSu#y`e1 zaXErJSuAVpg;J{0n5xCkI(~zcg0OdxwjRZix_q0g^%T(`ArK;#Som zsJB_x;CEGbZ-^%oxGeIwm5q(s0D+GLNK|dMXGcw}GW;hy`x$_%BYt$|_+??taju_i ze)7)CfxfgV7h^bdXTGZ2k&vP{$8~#ftTu;or-e0zf8B}tvDL~dczcd+rZ<@#x;*en zX3-|hfqJuyf!!Yfqb0P$GxRGP?RmV-WO#g`H=f0l3lB*aPfh$-MbUXfltYHeBe|* z)W8=>Rj>{L)~G-L)vxy0J*%8nCeG^8?<`;2zdlohpV%udgjF75oQBVI$zn%nd9{BU zNcLFkKRM8qw8A|Nv8s2kK8aT|K~!_Z49Te8N)}s73`k)2^n0<6T5&5fK*!!(yIS(@ z2X*j9gwyUoLa*Sfjd}svAxv?_2ZJYl3lmdAmHk$Mjv-a695MI!@kCS(u}00sS7m|_ zV*sX|F!@hEi`*&utGg8Pci)V>`1yV^iZZ?vD3M!(g z9aA4|@c~t-Xr+LIFs*jVIHfk!_j77|j*lXWU{tVH@N|PfTL=5McV_qAd*=JT^WA&S z?3Se@O%0-l(h&p+QY9!f)a>#;0lw6Gb7^J`HH{^e>0~ONP3CI}43Qad9fqn*+8j)S zX$_7At=MD)@nsp)(#dpnqC}6II9e};V>ekSHiAr!v0JqI`51}nupFaV%IrGW%tVa_ zDRX9|ny0qNv0P(90fD6!B&F#K=Ig}%(P9oiny(i`XKQsIq5_^kz~za!{3y0SAc=^S z2>Iy1U{VPQL$*Yth#yEt?WD|HlC(&;-2D7}PJRRjCvv!au~^LI3Ah3Qn?kUy4l}8> zv(479K?Mb7)e}YwX~fN_S5d3OZKRY*nSFi=lVwQOY#q2M>dv@!t%b|y@VuuQ1gh2l z4KaUo`iCvX$me`<+w7%$P0 zaS`!6xrove4)5hT*YHv=BIQfHa49ofZ*Bik>%kD!K;Gq0^HQ6k_Q%Xr&l6O0?|oDr zgdjc(QWDdY$ZUAp3-9ECa1r1;K)n{)J@DNrKye712e&%GF9SZX!sT{2aSfzP;l@4C zZw8+^u>UyJAA|4(;GYFC%VFATV3DwTKa|$PdjyD}-QD4CPdyVmrr7hS zo|WL&xasrLGj8v(`_Ng}*(-_N*OvNETt!=5%)FC@w_hg@A4+sz5IWAZJ!u**XvGEP zmks*fo`hEzXlya!LsNtrgqqGKyQ^^LC8fu?flkl^7{}Nw*L08b2>rnf)Q~$lrG0Lk zEHnA?#80!QhRbTl0-hLBb!J3}FRiMl^UK(f^=NV;8ZkY#!q#DXyj1V(Y<+#xt|FE! z%OUi9Pj~&;w{}|R35QKw`AsDwcyHL2mb%7b#K>~h&P4ZSF5TKguNvu!ac;}|ssdZH zP?kBS``)sQ6RzzNxf?3}s0gk8S$?27xaEfD`e+ZM!6q4SJ|f`Xi&1{FNCpw+-oe(l z%3KdCkGTuFf0Mbo3vjO6FOcR#dyy~@sgaeQ!@Ae3%u^oe3kjZ375BAftjD?Bm)4TO z$U0oz(D3$aGWuug=HsWM$1S@_0)yYh2qzU0;Q=F7NuA=l*z=c0&T?$P8|QZUKaM-E zJlgRlx@7!wZ%qFOCoek_I>vf9$z6DUea Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - self._add_function_call(model, credentials) - user = user[:32] if user else None - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - return AIModelEntity( - model=model, - label=I18nObject(en_US=model, zh_Hans=model), - model_type=ModelType.LLM, - features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL, ModelFeature.STREAM_TOOL_CALL] - if credentials.get("function_calling_type") == "tool_call" - else [], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", 8000)), - ModelPropertyKey.MODE: LLMMode.CHAT.value, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - use_template="temperature", - label=I18nObject(en_US="Temperature", zh_Hans="温度"), - type=ParameterType.FLOAT, - ), - ParameterRule( - name="max_tokens", - use_template="max_tokens", - default=512, - min=1, - max=int(credentials.get("max_tokens", 1024)), - label=I18nObject(en_US="Max Tokens", zh_Hans="最大标记"), - type=ParameterType.INT, - ), - ParameterRule( - name="top_p", - use_template="top_p", - label=I18nObject(en_US="Top P", zh_Hans="Top P"), - type=ParameterType.FLOAT, - ), - ], - ) - - def _add_custom_parameters(self, credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = "https://api.stepfun.com/v1" - - def _add_function_call(self, model: str, credentials: dict) -> None: - model_schema = self.get_model_schema(model, credentials) - if model_schema and {ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL}.intersection( - model_schema.features or [] - ): - credentials["function_calling_type"] = "tool_call" - - def _convert_prompt_message_to_dict(self, message: PromptMessage, credentials: Optional[dict] = None) -> dict: - """ - Convert PromptMessage to dict for OpenAI API format - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(PromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": { - "url": message_content.data, - }, - } - sub_messages.append(sub_message_dict) - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - message_dict["tool_calls"] = [] - for function_call in message.tool_calls: - message_dict["tool_calls"].append( - { - "id": function_call.id, - "type": function_call.type, - "function": { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - }, - } - ) - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = {"role": "tool", "content": message.content, "tool_call_id": message.tool_call_id} - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _extract_response_tool_calls(self, response_tool_calls: list[dict]) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call["function"]["name"] - if response_tool_call.get("function", {}).get("name") - else "", - arguments=response_tool_call["function"]["arguments"] - if response_tool_call.get("function", {}).get("arguments") - else "", - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call["id"] if response_tool_call.get("id") else "", - type=response_tool_call["type"] if response_tool_call.get("type") else "", - function=function, - ) - tool_calls.append(tool_call) - - return tool_calls - - def _handle_generate_stream_response( - self, model: str, credentials: dict, response: requests.Response, prompt_messages: list[PromptMessage] - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: model credentials - :param response: streamed response - :param prompt_messages: prompt messages - :return: llm response chunk generator - """ - full_assistant_content = "" - chunk_index = 0 - - def create_final_llm_result_chunk( - index: int, message: AssistantPromptMessage, finish_reason: str - ) -> LLMResultChunk: - # calculate num tokens - prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content) - completion_tokens = self._num_tokens_from_string(model, full_assistant_content) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - return LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=message, finish_reason=finish_reason, usage=usage), - ) - - tools_calls: list[AssistantPromptMessage.ToolCall] = [] - finish_reason = "Unknown" - - 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 response.iter_lines(decode_unicode=True, delimiter="\n\n"): - if chunk: - # ignore sse comments - if chunk.startswith(":"): - continue - decoded_chunk = chunk.strip().lstrip("data: ").lstrip() - chunk_json = None - try: - chunk_json = json.loads(decoded_chunk) - # stream ended - except json.JSONDecodeError as e: - yield create_final_llm_result_chunk( - index=chunk_index + 1, - message=AssistantPromptMessage(content=""), - finish_reason="Non-JSON encountered.", - ) - break - if not chunk_json or len(chunk_json["choices"]) == 0: - continue - - choice = chunk_json["choices"][0] - finish_reason = chunk_json["choices"][0].get("finish_reason") - chunk_index += 1 - - if "delta" in choice: - delta = choice["delta"] - delta_content = delta.get("content") - - assistant_message_tool_calls = delta.get("tool_calls", None) - # assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if assistant_message_tool_calls: - tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - increase_tool_call(tool_calls) - - if delta_content is None or delta_content == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta_content, tool_calls=tool_calls if assistant_message_tool_calls else [] - ) - - full_assistant_content += delta_content - elif "text" in choice: - choice_text = choice.get("text", "") - if choice_text == "": - continue - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=choice_text) - full_assistant_content += choice_text - else: - continue - - # check payload indicator for completion - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=assistant_prompt_message, - ), - ) - - chunk_index += 1 - - if tools_calls: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=chunk_index, - message=AssistantPromptMessage(tool_calls=tools_calls, content=""), - ), - ) - - yield create_final_llm_result_chunk( - index=chunk_index, message=AssistantPromptMessage(content=""), finish_reason=finish_reason - ) diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1-128k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1-128k.yaml deleted file mode 100644 index 13f7b7fd26..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1-128k.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: step-1-128k -label: - zh_Hans: step-1-128k - en_US: step-1-128k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 128000 -pricing: - input: '0.04' - output: '0.20' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1-256k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1-256k.yaml deleted file mode 100644 index f80ec9851c..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1-256k.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: step-1-256k -label: - zh_Hans: step-1-256k - en_US: step-1-256k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 256000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 256000 -pricing: - input: '0.095' - output: '0.300' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1-32k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1-32k.yaml deleted file mode 100644 index 96132d14a8..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1-32k.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: step-1-32k -label: - zh_Hans: step-1-32k - en_US: step-1-32k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 -pricing: - input: '0.015' - output: '0.070' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1-8k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1-8k.yaml deleted file mode 100644 index 4a4ba8d178..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1-8k.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: step-1-8k -label: - zh_Hans: step-1-8k - en_US: step-1-8k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8000 -pricing: - input: '0.005' - output: '0.020' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1-flash.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1-flash.yaml deleted file mode 100644 index afb880f2a4..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1-flash.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: step-1-flash -label: - zh_Hans: step-1-flash - en_US: step-1-flash -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8000 -pricing: - input: '0.001' - output: '0.004' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1v-32k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1v-32k.yaml deleted file mode 100644 index 08d6ad245d..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1v-32k.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: step-1v-32k -label: - zh_Hans: step-1v-32k - en_US: step-1v-32k -model_type: llm -features: - - vision - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 32000 -pricing: - input: '0.015' - output: '0.070' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-1v-8k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-1v-8k.yaml deleted file mode 100644 index 843d14d9c6..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-1v-8k.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: step-1v-8k -label: - zh_Hans: step-1v-8k - en_US: step-1v-8k -model_type: llm -features: - - vision - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 8192 -pricing: - input: '0.005' - output: '0.020' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/llm/step-2-16k.yaml b/api/core/model_runtime/model_providers/stepfun/llm/step-2-16k.yaml deleted file mode 100644 index 6f2dabbfb0..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/llm/step-2-16k.yaml +++ /dev/null @@ -1,28 +0,0 @@ -model: step-2-16k -label: - zh_Hans: step-2-16k - en_US: step-2-16k -model_type: llm -features: - - agent-thought - - tool-call - - multi-tool-call - - stream-tool-call -model_properties: - mode: chat - context_size: 16000 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 16000 -pricing: - input: '0.038' - output: '0.120' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/stepfun/stepfun.py b/api/core/model_runtime/model_providers/stepfun/stepfun.py deleted file mode 100644 index e1c41a9153..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/stepfun.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class StepfunProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="step-1-8k", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/stepfun/stepfun.yaml b/api/core/model_runtime/model_providers/stepfun/stepfun.yaml deleted file mode 100644 index ccc8455adc..0000000000 --- a/api/core/model_runtime/model_providers/stepfun/stepfun.yaml +++ /dev/null @@ -1,81 +0,0 @@ -provider: stepfun -label: - zh_Hans: 阶跃星辰 - en_US: Stepfun -description: - en_US: Models provided by stepfun, such as step-1-8k, step-1-32k、step-1v-8k、step-1v-32k, step-1-128k and step-1-256k - zh_Hans: 阶跃星辰提供的模型,例如 step-1-8k、step-1-32k、step-1v-8k、step-1v-32k、step-1-128k 和 step-1-256k。 -icon_small: - en_US: icon_s_en.png -icon_large: - en_US: icon_l_en.png -background: "#FFFFFF" -help: - title: - en_US: Get your API Key from stepfun - zh_Hans: 从 stepfun 获取 API Key - url: - en_US: https://platform.stepfun.com/interface-key -supported_model_types: - - llm -configurate_methods: - - predefined-model - - customizable-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '8192' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - default: '8192' - type: text-input - - variable: function_calling_type - label: - en_US: Function calling - type: select - required: false - default: no_call - options: - - value: no_call - label: - en_US: Not supported - zh_Hans: 不支持 - - value: tool_call - label: - en_US: Tool Call - zh_Hans: Tool Call diff --git a/api/core/model_runtime/model_providers/tencent/__init__.py b/api/core/model_runtime/model_providers/tencent/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tencent/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/tencent/_assets/icon_l_en.svg deleted file mode 100644 index 63c7c8f988..0000000000 --- a/api/core/model_runtime/model_providers/tencent/_assets/icon_l_en.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - tencent-cloud - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/tencent/_assets/icon_l_zh.svg b/api/core/model_runtime/model_providers/tencent/_assets/icon_l_zh.svg deleted file mode 100644 index 63c7c8f988..0000000000 --- a/api/core/model_runtime/model_providers/tencent/_assets/icon_l_zh.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - tencent-cloud - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/tencent/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/tencent/_assets/icon_s_en.svg deleted file mode 100644 index a3299b9201..0000000000 --- a/api/core/model_runtime/model_providers/tencent/_assets/icon_s_en.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - tencent-cloud - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/tencent/speech2text/__init__.py b/api/core/model_runtime/model_providers/tencent/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tencent/speech2text/flash_recognizer.py b/api/core/model_runtime/model_providers/tencent/speech2text/flash_recognizer.py deleted file mode 100644 index c3c21793e8..0000000000 --- a/api/core/model_runtime/model_providers/tencent/speech2text/flash_recognizer.py +++ /dev/null @@ -1,164 +0,0 @@ -import base64 -import hashlib -import hmac -import operator -import time - -import requests - - -class Credential: - def __init__(self, secret_id, secret_key): - self.secret_id = secret_id - self.secret_key = secret_key - - -class FlashRecognitionRequest: - def __init__(self, voice_format="mp3", engine_type="16k_zh"): - self.engine_type = engine_type - self.speaker_diarization = 0 - self.hotword_id = "" - self.customization_id = "" - self.filter_dirty = 0 - self.filter_modal = 0 - self.filter_punc = 0 - self.convert_num_mode = 1 - self.word_info = 0 - self.voice_format = voice_format - self.first_channel_only = 1 - self.reinforce_hotword = 0 - self.sentence_max_length = 0 - - def set_first_channel_only(self, first_channel_only): - self.first_channel_only = first_channel_only - - def set_speaker_diarization(self, speaker_diarization): - self.speaker_diarization = speaker_diarization - - def set_filter_dirty(self, filter_dirty): - self.filter_dirty = filter_dirty - - def set_filter_modal(self, filter_modal): - self.filter_modal = filter_modal - - def set_filter_punc(self, filter_punc): - self.filter_punc = filter_punc - - def set_convert_num_mode(self, convert_num_mode): - self.convert_num_mode = convert_num_mode - - def set_word_info(self, word_info): - self.word_info = word_info - - def set_hotword_id(self, hotword_id): - self.hotword_id = hotword_id - - def set_customization_id(self, customization_id): - self.customization_id = customization_id - - def set_voice_format(self, voice_format): - self.voice_format = voice_format - - def set_sentence_max_length(self, sentence_max_length): - self.sentence_max_length = sentence_max_length - - def set_reinforce_hotword(self, reinforce_hotword): - self.reinforce_hotword = reinforce_hotword - - -class FlashRecognizer: - """ - response: - request_id string - status Integer - message String - audio_duration Integer - flash_result Result Array - - Result: - text String - channel_id Integer - sentence_list Sentence Array - - Sentence: - text String - start_time Integer - end_time Integer - speaker_id Integer - word_list Word Array - - Word: - word String - start_time Integer - end_time Integer - stable_flag: Integer - """ - - def __init__(self, appid, credential): - self.credential = credential - self.appid = appid - - def _format_sign_string(self, param): - signstr = "POSTasr.cloud.tencent.com/asr/flash/v1/" - for t in param: - if "appid" in t: - signstr += str(t[1]) - break - signstr += "?" - for x in param: - tmp = x - if "appid" in x: - continue - for t in tmp: - signstr += str(t) - signstr += "=" - signstr = signstr[:-1] - signstr += "&" - signstr = signstr[:-1] - return signstr - - def _build_header(self): - header = {"Host": "asr.cloud.tencent.com"} - return header - - def _sign(self, signstr, secret_key): - hmacstr = hmac.new(secret_key.encode("utf-8"), signstr.encode("utf-8"), hashlib.sha1).digest() - s = base64.b64encode(hmacstr) - s = s.decode("utf-8") - return s - - def _build_req_with_signature(self, secret_key, params, header): - query = sorted(params.items(), key=operator.itemgetter(0)) - signstr = self._format_sign_string(query) - signature = self._sign(signstr, secret_key) - header["Authorization"] = signature - req_url = "https://" - req_url += signstr[4::] - return req_url - - def _create_query_arr(self, req): - return { - "appid": self.appid, - "secretid": self.credential.secret_id, - "timestamp": str(int(time.time())), - "engine_type": req.engine_type, - "voice_format": req.voice_format, - "speaker_diarization": req.speaker_diarization, - "hotword_id": req.hotword_id, - "customization_id": req.customization_id, - "filter_dirty": req.filter_dirty, - "filter_modal": req.filter_modal, - "filter_punc": req.filter_punc, - "convert_num_mode": req.convert_num_mode, - "word_info": req.word_info, - "first_channel_only": req.first_channel_only, - "reinforce_hotword": req.reinforce_hotword, - "sentence_max_length": req.sentence_max_length, - } - - def recognize(self, req, data): - header = self._build_header() - query_arr = self._create_query_arr(req) - req_url = self._build_req_with_signature(self.credential.secret_key, query_arr, header) - r = requests.post(req_url, headers=header, data=data) - return r.text diff --git a/api/core/model_runtime/model_providers/tencent/speech2text/speech2text.py b/api/core/model_runtime/model_providers/tencent/speech2text/speech2text.py deleted file mode 100644 index 5b427663ca..0000000000 --- a/api/core/model_runtime/model_providers/tencent/speech2text/speech2text.py +++ /dev/null @@ -1,86 +0,0 @@ -import json -from typing import IO, Optional - -import requests - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeConnectionError, - InvokeError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from core.model_runtime.model_providers.tencent.speech2text.flash_recognizer import ( - Credential, - FlashRecognitionRequest, - FlashRecognizer, -) - - -class TencentSpeech2TextModel(Speech2TextModel): - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - return self._speech2text_invoke(model, credentials, file) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self._speech2text_invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _speech2text_invoke(self, model: str, credentials: dict, file: IO[bytes]) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :return: text for given audio file - """ - app_id = credentials["app_id"] - secret_id = credentials["secret_id"] - secret_key = credentials["secret_key"] - voice_format = file.voice_format if hasattr(file, "voice_format") else "mp3" - tencent_voice_recognizer = FlashRecognizer(app_id, Credential(secret_id, secret_key)) - resp = tencent_voice_recognizer.recognize(FlashRecognitionRequest(voice_format), file) - resp = json.loads(resp) - code = resp["code"] - message = resp["message"] - if code == 4002: - raise CredentialsValidateFailedError(str(message)) - elif code != 0: - return f"Tencent ASR Recognition failed with code {code} and message {message}" - return "\n".join(item["text"] for item in resp["flash_result"]) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [requests.exceptions.ConnectionError], - InvokeAuthorizationError: [CredentialsValidateFailedError], - } diff --git a/api/core/model_runtime/model_providers/tencent/speech2text/tencent.yaml b/api/core/model_runtime/model_providers/tencent/speech2text/tencent.yaml deleted file mode 100644 index 618d19ac7c..0000000000 --- a/api/core/model_runtime/model_providers/tencent/speech2text/tencent.yaml +++ /dev/null @@ -1,5 +0,0 @@ -model: tencent -model_type: speech2text -model_properties: - file_upload_limit: 25 - supported_file_extensions: flac,mp3,mp4,mpeg,mpga,m4a,ogg,wav,webm diff --git a/api/core/model_runtime/model_providers/tencent/tencent.py b/api/core/model_runtime/model_providers/tencent/tencent.py deleted file mode 100644 index 79c6f577b8..0000000000 --- a/api/core/model_runtime/model_providers/tencent/tencent.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class TencentProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.SPEECH2TEXT) - model_instance.validate_credentials(model="tencent", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/tencent/tencent.yaml b/api/core/model_runtime/model_providers/tencent/tencent.yaml deleted file mode 100644 index 7d8d5a1866..0000000000 --- a/api/core/model_runtime/model_providers/tencent/tencent.yaml +++ /dev/null @@ -1,49 +0,0 @@ -provider: tencent -label: - zh_Hans: 腾讯云 - en_US: Tencent -icon_small: - en_US: icon_s_en.svg -icon_large: - zh_Hans: icon_l_zh.svg - en_US: icon_l_en.svg -background: "#E5E7EB" -help: - title: - en_US: Get your API key from Tencent AI - zh_Hans: 从腾讯云获取 API Key - url: - en_US: https://cloud.tencent.com/product/asr -supported_model_types: - - speech2text -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: app_id - label: - zh_Hans: APPID - en_US: APPID - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的腾讯语音识别服务的 APPID - en_US: Enter the APPID of your Tencent Cloud ASR service - - variable: secret_id - label: - zh_Hans: SecretId - en_US: SecretId - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的腾讯语音识别服务的 SecretId - en_US: Enter the SecretId of your Tencent Cloud ASR service - - variable: secret_key - label: - zh_Hans: SecretKey - en_US: SecretKey - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的腾讯语音识别服务的 SecretKey - en_US: Enter the SecretKey of your Tencent Cloud ASR service diff --git a/api/core/model_runtime/model_providers/togetherai/__init__.py b/api/core/model_runtime/model_providers/togetherai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/togetherai/_assets/togetherai.svg b/api/core/model_runtime/model_providers/togetherai/_assets/togetherai.svg deleted file mode 100644 index e9d918b15e..0000000000 --- a/api/core/model_runtime/model_providers/togetherai/_assets/togetherai.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/togetherai/_assets/togetherai_square.svg b/api/core/model_runtime/model_providers/togetherai/_assets/togetherai_square.svg deleted file mode 100644 index 16bae5235f..0000000000 --- a/api/core/model_runtime/model_providers/togetherai/_assets/togetherai_square.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/togetherai/llm/__init__.py b/api/core/model_runtime/model_providers/togetherai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/togetherai/llm/llm.py b/api/core/model_runtime/model_providers/togetherai/llm/llm.py deleted file mode 100644 index b96d43979e..0000000000 --- a/api/core/model_runtime/model_providers/togetherai/llm/llm.py +++ /dev/null @@ -1,170 +0,0 @@ -from collections.abc import Generator -from decimal import Decimal -from typing import Optional, Union - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult -from core.model_runtime.entities.message_entities import ( - PromptMessage, - PromptMessageTool, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, - PriceConfig, -) -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class TogetherAILargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _update_endpoint_url(self, credentials: dict): - credentials["endpoint_url"] = "https://api.together.xyz/v1" - return credentials - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super()._invoke(model, cred_with_endpoint, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super().validate_credentials(model, cred_with_endpoint) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super()._generate( - model, cred_with_endpoint, prompt_messages, model_parameters, tools, stop, stream, user - ) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - REPETITION_PENALTY = "repetition_penalty" - TOP_K = "top_k" - features = [] - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - model_type=ModelType.LLM, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - features=features, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(cred_with_endpoint.get("context_size", "4096")), - ModelPropertyKey.MODE: cred_with_endpoint.get("mode"), - }, - parameter_rules=[ - ParameterRule( - name=DefaultParameterName.TEMPERATURE.value, - label=I18nObject(en_US="Temperature"), - type=ParameterType.FLOAT, - default=float(cred_with_endpoint.get("temperature", 0.7)), - min=0, - max=2, - precision=2, - ), - ParameterRule( - name=DefaultParameterName.TOP_P.value, - label=I18nObject(en_US="Top P"), - type=ParameterType.FLOAT, - default=float(cred_with_endpoint.get("top_p", 1)), - min=0, - max=1, - precision=2, - ), - ParameterRule( - name=TOP_K, - label=I18nObject(en_US="Top K"), - type=ParameterType.INT, - default=int(cred_with_endpoint.get("top_k", 50)), - min=-2147483647, - max=2147483647, - precision=0, - ), - ParameterRule( - name=REPETITION_PENALTY, - label=I18nObject(en_US="Repetition Penalty"), - type=ParameterType.FLOAT, - default=float(cred_with_endpoint.get("repetition_penalty", 1)), - min=-3.4, - max=3.4, - precision=1, - ), - ParameterRule( - name=DefaultParameterName.MAX_TOKENS.value, - label=I18nObject(en_US="Max Tokens"), - type=ParameterType.INT, - default=512, - min=1, - max=int(cred_with_endpoint.get("max_tokens_to_sample", 4096)), - ), - ParameterRule( - name=DefaultParameterName.FREQUENCY_PENALTY.value, - label=I18nObject(en_US="Frequency Penalty"), - type=ParameterType.FLOAT, - default=float(credentials.get("frequency_penalty", 0)), - min=-2, - max=2, - ), - ParameterRule( - name=DefaultParameterName.PRESENCE_PENALTY.value, - label=I18nObject(en_US="Presence Penalty"), - type=ParameterType.FLOAT, - default=float(credentials.get("presence_penalty", 0)), - min=-2, - max=2, - ), - ], - pricing=PriceConfig( - input=Decimal(cred_with_endpoint.get("input_price", 0)), - output=Decimal(cred_with_endpoint.get("output_price", 0)), - unit=Decimal(cred_with_endpoint.get("unit", 0)), - currency=cred_with_endpoint.get("currency", "USD"), - ), - ) - - if cred_with_endpoint["mode"] == "chat": - entity.model_properties[ModelPropertyKey.MODE] = LLMMode.CHAT.value - elif cred_with_endpoint["mode"] == "completion": - entity.model_properties[ModelPropertyKey.MODE] = LLMMode.COMPLETION.value - else: - raise ValueError(f"Unknown completion type {cred_with_endpoint['completion_type']}") - - return entity - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - cred_with_endpoint = self._update_endpoint_url(credentials=credentials) - - return super().get_num_tokens(model, cred_with_endpoint, prompt_messages, tools) diff --git a/api/core/model_runtime/model_providers/togetherai/togetherai.py b/api/core/model_runtime/model_providers/togetherai/togetherai.py deleted file mode 100644 index aa4100a7c9..0000000000 --- a/api/core/model_runtime/model_providers/togetherai/togetherai.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class TogetherAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/togetherai/togetherai.yaml b/api/core/model_runtime/model_providers/togetherai/togetherai.yaml deleted file mode 100644 index e69471b15d..0000000000 --- a/api/core/model_runtime/model_providers/togetherai/togetherai.yaml +++ /dev/null @@ -1,75 +0,0 @@ -provider: togetherai -label: - en_US: together.ai -icon_small: - en_US: togetherai_square.svg -icon_large: - en_US: togetherai.svg -background: "#F1EFED" -help: - title: - en_US: Get your API key from together.ai - zh_Hans: 从 together.ai 获取 API Key - url: - en_US: https://api.together.xyz/ -supported_model_types: - - llm -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter full model name - zh_Hans: 输入模型全称 - credential_form_schemas: - - variable: api_key - required: true - label: - en_US: API Key - type: secret-input - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: mode - show_on: - - variable: __model_type - value: llm - label: - en_US: Completion mode - type: select - required: false - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select completion mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens_to_sample - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - show_on: - - variable: __model_type - value: llm - default: '4096' - type: text-input diff --git a/api/core/model_runtime/model_providers/tongyi/__init__.py b/api/core/model_runtime/model_providers/tongyi/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tongyi/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/tongyi/_assets/icon_l_en.png deleted file mode 100644 index 94de01136a64b6c8fead0003f048fcb6058f07ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4741 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 T^40aD00000NkvXXu0mjf!iXSF diff --git a/api/core/model_runtime/model_providers/tongyi/_assets/icon_l_zh.png b/api/core/model_runtime/model_providers/tongyi/_assets/icon_l_zh.png deleted file mode 100644 index bd8f2762d18333f9c9f945abdaeadec98cc4e8d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7052 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 diff --git a/api/core/model_runtime/model_providers/tongyi/_common.py b/api/core/model_runtime/model_providers/tongyi/_common.py deleted file mode 100644 index 8a50c7aa05..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/_common.py +++ /dev/null @@ -1,55 +0,0 @@ -from dashscope.common.error import ( - AuthenticationError, - InvalidParameter, - RequestFailure, - ServiceUnavailableError, - UnsupportedHTTPMethod, - UnsupportedModel, -) - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonTongyi: - @staticmethod - def _to_credential_kwargs(credentials: dict) -> dict: - credentials_kwargs = { - "dashscope_api_key": credentials["dashscope_api_key"], - } - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - RequestFailure, - ], - InvokeServerUnavailableError: [ - ServiceUnavailableError, - ], - InvokeRateLimitError: [], - InvokeAuthorizationError: [ - AuthenticationError, - ], - InvokeBadRequestError: [ - InvalidParameter, - UnsupportedModel, - UnsupportedHTTPMethod, - ], - } diff --git a/api/core/model_runtime/model_providers/tongyi/llm/__init__.py b/api/core/model_runtime/model_providers/tongyi/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tongyi/llm/_position.yaml b/api/core/model_runtime/model_providers/tongyi/llm/_position.yaml deleted file mode 100644 index 8ce336d60c..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/_position.yaml +++ /dev/null @@ -1,51 +0,0 @@ -- qwen-vl-max-0809 -- qwen-vl-max-0201 -- qwen-vl-max -- qwen-max-latest -- qwen-max-1201 -- qwen-max-0919 -- qwen-max-0428 -- qwen-max-0403 -- qwen-max-0107 -- qwen-max -- qwen-max-longcontext -- qwen-plus-latest -- qwen-plus-0919 -- qwen-plus-0806 -- qwen-plus-0723 -- qwen-plus-0624 -- qwen-plus-0206 -- qwen-plus-chat -- qwen-plus -- qwen-vl-plus-0809 -- qwen-vl-plus-0201 -- qwen-vl-plus -- qwen-turbo-latest -- qwen-turbo-0919 -- qwen-turbo-0624 -- qwen-turbo-0206 -- qwen-turbo-chat -- qwen-turbo -- qwen2.5-72b-instruct -- qwen2.5-32b-instruct -- qwen2.5-14b-instruct -- qwen2.5-7b-instruct -- qwen2.5-3b-instruct -- qwen2.5-1.5b-instruct -- qwen2.5-0.5b-instruct -- qwen2.5-coder-7b-instruct -- qwen2-math-72b-instruct -- qwen2-math-7b-instruct -- qwen2-math-1.5b-instruct -- qwen-long -- qwen-math-plus-latest -- qwen-math-plus-0919 -- qwen-math-plus-0816 -- qwen-math-plus -- qwen-math-turbo-latest -- qwen-math-turbo-0919 -- qwen-math-turbo -- qwen-coder-turbo-latest -- qwen-coder-turbo-0919 -- qwen-coder-turbo -- farui-plus diff --git a/api/core/model_runtime/model_providers/tongyi/llm/farui-plus.yaml b/api/core/model_runtime/model_providers/tongyi/llm/farui-plus.yaml deleted file mode 100644 index 34a57d1fc0..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/farui-plus.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: farui-plus -label: - en_US: farui-plus -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 12288 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.02' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/llm.py b/api/core/model_runtime/model_providers/tongyi/llm/llm.py deleted file mode 100644 index 3e3585b30a..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/llm.py +++ /dev/null @@ -1,593 +0,0 @@ -import base64 -import os -import tempfile -import uuid -from collections.abc import Generator -from http import HTTPStatus -from pathlib import Path -from typing import Optional, Union, cast - -from dashscope import Generation, MultiModalConversation, get_tokenizer -from dashscope.api_entities.dashscope_response import GenerationResponse -from dashscope.common.error import ( - AuthenticationError, - InvalidParameter, - RequestFailure, - ServiceUnavailableError, - UnsupportedHTTPMethod, - UnsupportedModel, -) - -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - I18nObject, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - - -class TongyiLargeLanguageModel(LargeLanguageModel): - tokenizers = {} - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # invoke model without code wrapper - return self._generate(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - # Check if the model was added via get_customizable_model_schema - if self.get_customizable_model_schema(model, credentials) is not None: - # For custom models, tokens are not calculated. - return 0 - - if model in {"qwen-turbo-chat", "qwen-plus-chat"}: - model = model.replace("-chat", "") - if model == "farui-plus": - model = "qwen-farui-plus" - - if model in self.tokenizers: - tokenizer = self.tokenizers[model] - else: - tokenizer = get_tokenizer(model) - self.tokenizers[model] = tokenizer - - # convert string to token ids - tokens = tokenizer.encode(self._convert_messages_to_prompt(prompt_messages)) - - return len(tokens) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - self._generate( - model=model, - credentials=credentials, - prompt_messages=[ - UserPromptMessage(content="ping"), - ], - model_parameters={ - "temperature": 0.5, - }, - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model - - :param model: model name - :param credentials: credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - mode = self.get_model_mode(model, credentials) - - if model in {"qwen-turbo-chat", "qwen-plus-chat"}: - model = model.replace("-chat", "") - - extra_model_kwargs = {} - if tools: - extra_model_kwargs["tools"] = self._convert_tools(tools) - - if stop: - extra_model_kwargs["stop"] = stop - - params = { - "model": model, - **model_parameters, - **credentials_kwargs, - **extra_model_kwargs, - } - - model_schema = self.get_model_schema(model, credentials) - if ModelFeature.VISION in (model_schema.features or []): - params["messages"] = self._convert_prompt_messages_to_tongyi_messages(prompt_messages, rich_content=True) - - response = MultiModalConversation.call(**params, stream=stream) - else: - # nothing different between chat model and completion model in tongyi - params["messages"] = self._convert_prompt_messages_to_tongyi_messages(prompt_messages) - response = Generation.call(**params, result_format="message", stream=stream) - - if stream: - return self._handle_generate_stream_response(model, credentials, response, prompt_messages) - - return self._handle_generate_response(model, credentials, response, prompt_messages) - - def _handle_generate_response( - self, model: str, credentials: dict, response: GenerationResponse, prompt_messages: list[PromptMessage] - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - if response.status_code not in {200, HTTPStatus.OK}: - raise ServiceUnavailableError(response.message) - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=response.output.choices[0].message.content, - ) - - # transform usage - usage = self._calc_response_usage(model, credentials, response.usage.input_tokens, response.usage.output_tokens) - - # transform response - result = LLMResult( - model=model, - message=assistant_prompt_message, - prompt_messages=prompt_messages, - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, - model: str, - credentials: dict, - responses: Generator[GenerationResponse, None, None], - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param credentials: credentials - :param responses: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - full_text = "" - tool_calls = [] - for index, response in enumerate(responses): - if response.status_code not in {200, HTTPStatus.OK}: - raise ServiceUnavailableError( - f"Failed to invoke model {model}, status code: {response.status_code}, " - f"message: {response.message}" - ) - - resp_finish_reason = response.output.choices[0].finish_reason - - if resp_finish_reason is not None and resp_finish_reason != "null": - resp_content = response.output.choices[0].message.content - - assistant_prompt_message = AssistantPromptMessage( - content="", - ) - - if "tool_calls" in response.output.choices[0].message: - tool_calls = response.output.choices[0].message["tool_calls"] - elif resp_content: - # special for qwen-vl - if isinstance(resp_content, list): - resp_content = resp_content[0]["text"] - - # transform assistant message to prompt message - assistant_prompt_message.content = resp_content.replace(full_text, "", 1) - - full_text = resp_content - - if tool_calls: - message_tool_calls = [] - for tool_call_obj in tool_calls: - message_tool_call = AssistantPromptMessage.ToolCall( - id=tool_call_obj["function"]["name"], - type="function", - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_call_obj["function"]["name"], arguments=tool_call_obj["function"]["arguments"] - ), - ) - message_tool_calls.append(message_tool_call) - - assistant_prompt_message.tool_calls = message_tool_calls - - # transform usage - usage = response.usage - usage = self._calc_response_usage(model, credentials, usage.input_tokens, usage.output_tokens) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, message=assistant_prompt_message, finish_reason=resp_finish_reason, usage=usage - ), - ) - else: - resp_content = response.output.choices[0].message.content - if not resp_content: - if "tool_calls" in response.output.choices[0].message: - tool_calls = response.output.choices[0].message["tool_calls"] - continue - - # special for qwen-vl - if isinstance(resp_content, list): - resp_content = resp_content[0]["text"] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=resp_content.replace(full_text, "", 1), - ) - - full_text = resp_content - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=index, message=assistant_prompt_message), - ) - - def _to_credential_kwargs(self, credentials: dict) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["dashscope_api_key"], - } - - return credentials_kwargs - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - content = message.content - - if isinstance(message, UserPromptMessage): - if isinstance(content, str): - message_text = f"{human_prompt} {content}" - else: - message_text = "" - for sub_message in content: - if sub_message.type == PromptMessageContentType.TEXT: - message_text = f"{human_prompt} {sub_message.data}" - break - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage | ToolPromptMessage): - message_text = content - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - def _convert_messages_to_prompt(self, messages: list[PromptMessage]) -> str: - """ - Format a list of messages into a full prompt for the Anthropic model - - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() - - def _convert_prompt_messages_to_tongyi_messages( - self, prompt_messages: list[PromptMessage], rich_content: bool = False - ) -> list[dict]: - """ - Convert prompt messages to tongyi messages - - :param prompt_messages: prompt messages - :return: tongyi messages - """ - tongyi_messages = [] - for prompt_message in prompt_messages: - if isinstance(prompt_message, SystemPromptMessage): - tongyi_messages.append( - { - "role": "system", - "content": prompt_message.content if not rich_content else [{"text": prompt_message.content}], - } - ) - elif isinstance(prompt_message, UserPromptMessage): - if isinstance(prompt_message.content, str): - tongyi_messages.append( - { - "role": "user", - "content": prompt_message.content - if not rich_content - else [{"text": prompt_message.content}], - } - ) - else: - sub_messages = [] - for message_content in prompt_message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - - image_url = message_content.data - if message_content.data.startswith("data:"): - # convert image base64 data to file in /tmp - image_url = self._save_base64_image_to_file(message_content.data) - - sub_message_dict = {"image": image_url} - sub_messages.append(sub_message_dict) - - # resort sub_messages to ensure text is always at last - sub_messages = sorted(sub_messages, key=lambda x: "text" in x) - - tongyi_messages.append({"role": "user", "content": sub_messages}) - elif isinstance(prompt_message, AssistantPromptMessage): - content = prompt_message.content - if not content: - content = " " - message = {"role": "assistant", "content": content if not rich_content else [{"text": content}]} - if prompt_message.tool_calls: - message["tool_calls"] = [tool_call.model_dump() for tool_call in prompt_message.tool_calls] - tongyi_messages.append(message) - elif isinstance(prompt_message, ToolPromptMessage): - tongyi_messages.append( - {"role": "tool", "content": prompt_message.content, "name": prompt_message.tool_call_id} - ) - else: - raise ValueError(f"Got unknown type {prompt_message}") - - return tongyi_messages - - def _save_base64_image_to_file(self, base64_image: str) -> str: - """ - Save base64 image to file - 'data:{upload_file.mime_type};base64,{encoded_string}' - - :param base64_image: base64 image data - :return: image file path - """ - # get mime type and encoded string - mime_type, encoded_string = base64_image.split(",")[0].split(";")[0].split(":")[1], base64_image.split(",")[1] - - # save image to file - temp_dir = tempfile.gettempdir() - - file_path = os.path.join(temp_dir, f"{uuid.uuid4()}.{mime_type.split('/')[1]}") - - Path(file_path).write_bytes(base64.b64decode(encoded_string)) - - return f"file://{file_path}" - - def _convert_tools(self, tools: list[PromptMessageTool]) -> list[dict]: - """ - Convert tools - """ - tool_definitions = [] - for tool in tools: - properties = tool.parameters["properties"] - required_properties = tool.parameters["required"] - - properties_definitions = {} - for p_key, p_val in properties.items(): - desc = p_val["description"] - if "enum" in p_val: - desc += f"; Only accepts one of the following predefined options: [{', '.join(p_val['enum'])}]" - - properties_definitions[p_key] = { - "description": desc, - "type": p_val["type"], - } - - tool_definition = { - "type": "function", - "function": { - "name": tool.name, - "description": tool.description, - "parameters": properties_definitions, - "required": required_properties, - }, - } - - tool_definitions.append(tool_definition) - - return tool_definitions - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - RequestFailure, - ], - InvokeServerUnavailableError: [ - ServiceUnavailableError, - ], - InvokeRateLimitError: [], - InvokeAuthorizationError: [ - AuthenticationError, - ], - InvokeBadRequestError: [ - InvalidParameter, - UnsupportedModel, - UnsupportedHTTPMethod, - ], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - Architecture for defining customizable models - - :param model: model name - :param credentials: model credentials - :return: AIModelEntity or None - """ - return AIModelEntity( - model=model, - label=I18nObject(en_US=model, zh_Hans=model), - model_type=ModelType.LLM, - features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL, ModelFeature.STREAM_TOOL_CALL] - if credentials.get("function_calling_type") == "tool_call" - else [], - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_properties={ - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_size", 8000)), - ModelPropertyKey.MODE: LLMMode.CHAT.value, - }, - parameter_rules=[ - ParameterRule( - name="temperature", - use_template="temperature", - label=I18nObject(en_US="Temperature", zh_Hans="温度"), - type=ParameterType.FLOAT, - ), - ParameterRule( - name="max_tokens", - use_template="max_tokens", - default=512, - min=1, - max=int(credentials.get("max_tokens", 1024)), - label=I18nObject(en_US="Max Tokens", zh_Hans="最大标记"), - type=ParameterType.INT, - ), - ParameterRule( - name="top_p", - use_template="top_p", - label=I18nObject(en_US="Top P", zh_Hans="Top P"), - type=ParameterType.FLOAT, - ), - ParameterRule( - name="top_k", - use_template="top_k", - label=I18nObject(en_US="Top K", zh_Hans="Top K"), - type=ParameterType.FLOAT, - ), - ParameterRule( - name="frequency_penalty", - use_template="frequency_penalty", - label=I18nObject(en_US="Frequency Penalty", zh_Hans="重复惩罚"), - type=ParameterType.FLOAT, - ), - ], - ) diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-0919.yaml deleted file mode 100644 index 64a3f33133..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-0919.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-coder-turbo-0919 -label: - en_US: qwen-coder-turbo-0919 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-latest.yaml deleted file mode 100644 index a4c93f7047..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo-latest.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-coder-turbo-latest -label: - en_US: qwen-coder-turbo-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo.yaml deleted file mode 100644 index ff68faed80..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-coder-turbo.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-coder-turbo -label: - en_US: qwen-coder-turbo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-long.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-long.yaml deleted file mode 100644 index c3dbb3616f..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-long.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-long -label: - en_US: qwen-long -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 10000000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 6000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0005' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0816.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0816.yaml deleted file mode 100644 index 42fe1f6862..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0816.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-plus-0816 -label: - en_US: qwen-math-plus-0816 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0919.yaml deleted file mode 100644 index 9b6567b8cd..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-0919.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-plus-0919 -label: - en_US: qwen-math-plus-0919 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-latest.yaml deleted file mode 100644 index b2a2393b36..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus-latest.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-plus-latest -label: - en_US: qwen-math-plus-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus.yaml deleted file mode 100644 index 63f4b7ff0a..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-plus.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-plus -label: - en_US: qwen-math-plus -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-0919.yaml deleted file mode 100644 index 4da90eec3e..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-0919.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-turbo-0919 -label: - en_US: qwen-math-turbo-0919 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-latest.yaml deleted file mode 100644 index d29f8851dd..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo-latest.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-turbo-latest -label: - en_US: qwen-math-turbo-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo.yaml deleted file mode 100644 index 2a8f7f725e..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-math-turbo.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-math-turbo -label: - en_US: qwen-math-turbo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 3072 - min: 1 - max: 3072 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0107.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0107.yaml deleted file mode 100644 index ef1841b517..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0107.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-0107 -label: - en_US: qwen-max-0107 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.04' - output: '0.12' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0403.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0403.yaml deleted file mode 100644 index a2ea5df130..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0403.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max-0403, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-0403 -label: - en_US: qwen-max-0403 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.04' - output: '0.12' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0428.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0428.yaml deleted file mode 100644 index a467665f11..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0428.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max-0428, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-0428 -label: - en_US: qwen-max-0428 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.04' - output: '0.12' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0919.yaml deleted file mode 100644 index 78661eaea0..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-0919.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max-0919, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-0919 -label: - en_US: qwen-max-0919 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.06' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-1201.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-1201.yaml deleted file mode 100644 index 6f4674576b..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-1201.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-1201 -label: - en_US: qwen-max-1201 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.04' - output: '0.12' - unit: '0.001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-latest.yaml deleted file mode 100644 index 8b5f005473..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-latest.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-latest -label: - en_US: qwen-max-latest -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.06' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-longcontext.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-longcontext.yaml deleted file mode 100644 index 098494ff95..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max-longcontext.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# this model corresponds to qwen-max, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max-longcontext -label: - en_US: qwen-max-longcontext -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8000 - min: 1 - max: 8000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.04' - output: '0.12' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-max.yaml deleted file mode 100644 index 9d0d3f8db3..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-max.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# this model corresponds to qwen-max, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf) -model: qwen-max -label: - en_US: qwen-max -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: enable_search - type: boolean - default: false - label: - zh_Hans: 联网搜索 - en_US: Web Search - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.06' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0206.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0206.yaml deleted file mode 100644 index 0b1a6f81df..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0206.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-0206, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-0206 -label: - en_US: qwen-plus-0206 -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8000 - min: 1 - max: 8000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0624.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0624.yaml deleted file mode 100644 index 7706005bb5..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0624.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-0624, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-0624 -label: - en_US: qwen-plus-0624 -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8000 - min: 1 - max: 8000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0723.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0723.yaml deleted file mode 100644 index 348276fc08..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0723.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-0723, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-0723 -label: - en_US: qwen-plus-0723 -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8000 - min: 1 - max: 8000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0806.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0806.yaml deleted file mode 100644 index 29f125135e..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0806.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-0806, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-0806 -label: - en_US: qwen-plus-0806 -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0919.yaml deleted file mode 100644 index 905fa1e102..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-0919.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-0919, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-0919 -label: - en_US: qwen-plus-0919 -model_type: llm -features: - - agent-thought -model_properties: - mode: completion - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0008' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-chat.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-chat.yaml deleted file mode 100644 index c7a3549727..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-chat.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# this model corresponds to qwen-plus, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-chat -label: - en_US: qwen-plus-chat -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-latest.yaml deleted file mode 100644 index 608f52c296..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus-latest.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-plus-latest, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus-latest -label: - en_US: qwen-plus-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0008' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus.yaml deleted file mode 100644 index 9089e57255..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-plus.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# this model corresponds to qwen-plus, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) -model: qwen-plus -label: - en_US: qwen-plus -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: enable_search - type: boolean - default: false - label: - zh_Hans: 联网搜索 - en_US: Web Search - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. - - name: response_format - use_template: response_format -pricing: - input: '0.0008' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0206.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0206.yaml deleted file mode 100644 index 7ee0d44f2f..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0206.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# this model corresponds to qwen-turbo-0206, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) - -model: qwen-turbo-0206 -label: - en_US: qwen-turbo-0206 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0624.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0624.yaml deleted file mode 100644 index 20a3f7eb64..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0624.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-turbo-0624, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) -model: qwen-turbo-0624 -label: - en_US: qwen-turbo-0624 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0919.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0919.yaml deleted file mode 100644 index ba73dec363..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-0919.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-turbo-0919, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) -model: qwen-turbo-0919 -label: - en_US: qwen-turbo-0919 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0003' - output: '0.0006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-chat.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-chat.yaml deleted file mode 100644 index d785b7fe85..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-chat.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# this model corresponds to qwen-turbo, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) -model: qwen-turbo-chat -label: - en_US: qwen-turbo-chat -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 1500 - min: 1 - max: 1500 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-latest.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-latest.yaml deleted file mode 100644 index fe38a4283c..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo-latest.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# this model corresponds to qwen-turbo-latest, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) -model: qwen-turbo-latest -label: - en_US: qwen-turbo-latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0006' - output: '0.0003' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo.yaml deleted file mode 100644 index 215c9ec5fc..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-turbo.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# this model corresponds to qwen-turbo, for more details -# please refer to (https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub) -model: qwen-turbo -label: - en_US: qwen-turbo -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: enable_search - type: boolean - default: false - label: - zh_Hans: 联网搜索 - en_US: Web Search - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. - - name: response_format - use_template: response_format -pricing: - input: '0.0006' - output: '0.0003' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0201.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0201.yaml deleted file mode 100644 index d80168ffc3..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0201.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-max-0201 -label: - en_US: qwen-vl-max-0201 -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.02' - unit: '0.001' - currency: RMB -deprecated: true diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0809.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0809.yaml deleted file mode 100644 index 50e10226a5..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max-0809.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-max-0809 -label: - en_US: qwen-vl-max-0809 -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: max_tokens - required: false - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.02' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max.yaml deleted file mode 100644 index 21b127f56c..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-max.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-max -label: - en_US: qwen-vl-max -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: max_tokens - required: false - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.02' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0201.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0201.yaml deleted file mode 100644 index 03cb039d15..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0201.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-plus-0201 -label: - en_US: qwen-vl-plus-0201 -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: max_tokens - required: false - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.02' - output: '0.02' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0809.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0809.yaml deleted file mode 100644 index 67b2b2ebdd..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus-0809.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-plus-0809 -label: - en_US: qwen-vl-plus-0809 -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: max_tokens - required: false - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.008' - output: '0.008' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus.yaml deleted file mode 100644 index f55764c6c0..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen-vl-plus.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen-vl-plus -label: - en_US: qwen-vl-plus -model_type: llm -features: - - vision - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: max_tokens - required: false - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: response_format - use_template: response_format - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.008' - output: '0.008' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-1.5b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-1.5b-instruct.yaml deleted file mode 100644 index ea157f42de..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-1.5b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2-math-1.5b-instruct -label: - en_US: qwen2-math-1.5b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-72b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-72b-instruct.yaml deleted file mode 100644 index 37052a9233..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-72b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2-math-72b-instruct -label: - en_US: qwen2-math-72b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-7b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-7b-instruct.yaml deleted file mode 100644 index e182f1c27f..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2-math-7b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2-math-7b-instruct -label: - en_US: qwen2-math-7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 2000 - min: 1 - max: 2000 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-0.5b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-0.5b-instruct.yaml deleted file mode 100644 index 9e75ccc1f2..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-0.5b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-0.5b-instruct -label: - en_US: qwen2.5-0.5b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.000' - output: '0.000' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-1.5b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-1.5b-instruct.yaml deleted file mode 100644 index 67c9d31243..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-1.5b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-1.5b-instruct -label: - en_US: qwen2.5-1.5b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.000' - output: '0.000' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-14b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-14b-instruct.yaml deleted file mode 100644 index 2a38be921c..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-14b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-14b-instruct -label: - en_US: qwen2.5-14b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.002' - output: '0.006' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-32b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-32b-instruct.yaml deleted file mode 100644 index e6e4fbf978..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-32b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-32b-instruct -label: - en_US: qwen2.5-32b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.0035' - output: '0.007' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-3b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-3b-instruct.yaml deleted file mode 100644 index 8f250379a7..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-3b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-3b-instruct -label: - en_US: qwen2.5-3b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.000' - output: '0.000' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-72b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-72b-instruct.yaml deleted file mode 100644 index bb3cdd6141..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-72b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-72b-instruct -label: - en_US: qwen2.5-72b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.004' - output: '0.012' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-7b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-7b-instruct.yaml deleted file mode 100644 index fdcd3d4275..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-7b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-7b-instruct -label: - en_US: qwen2.5-7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.001' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-coder-7b-instruct.yaml b/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-coder-7b-instruct.yaml deleted file mode 100644 index 7ebeec3953..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/llm/qwen2.5-coder-7b-instruct.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models -model: qwen2.5-coder-7b-instruct -label: - en_US: qwen2.5-coder-7b-instruct -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 131072 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 - en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. - - name: max_tokens - use_template: max_tokens - type: int - default: 8192 - min: 1 - max: 8192 - help: - zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 - en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.1 - max: 0.9 - help: - zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 - en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. - - name: top_k - type: int - min: 0 - max: 99 - label: - zh_Hans: 取样数量 - en_US: Top k - help: - zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 - en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. - - name: seed - required: false - type: int - default: 1234 - label: - zh_Hans: 随机种子 - en_US: Random seed - help: - zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. - - name: repetition_penalty - required: false - type: float - default: 1.1 - label: - zh_Hans: 重复惩罚 - en_US: Repetition penalty - help: - zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 - en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - - name: response_format - use_template: response_format -pricing: - input: '0.001' - output: '0.002' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/text_embedding/__init__.py b/api/core/model_runtime/model_providers/tongyi/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v1.yaml b/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v1.yaml deleted file mode 100644 index 52e35d8b50..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v1.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw -model: text-embedding-v1 -model_type: text-embedding -model_properties: - context_size: 2048 - max_chunks: 25 -pricing: - input: "0.0007" - unit: "0.001" - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v2.yaml b/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v2.yaml deleted file mode 100644 index 5bb6a8f424..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v2.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw -model: text-embedding-v2 -model_type: text-embedding -model_properties: - context_size: 2048 - max_chunks: 25 -pricing: - input: "0.0007" - unit: "0.001" - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v3.yaml b/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v3.yaml deleted file mode 100644 index d8af0e2b63..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/text_embedding/text-embedding-v3.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# for more details, please refer to https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw -model: text-embedding-v3 -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 25 -pricing: - input: "0.0007" - unit: "0.001" - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/tongyi.py b/api/core/model_runtime/model_providers/tongyi/tongyi.py deleted file mode 100644 index a084512de9..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/tongyi.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class TongyiProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `qwen-turbo` model for validate, - model_instance.validate_credentials(model="qwen-turbo", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/tongyi/tongyi.yaml b/api/core/model_runtime/model_providers/tongyi/tongyi.yaml deleted file mode 100644 index 1a09c20fd9..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/tongyi.yaml +++ /dev/null @@ -1,87 +0,0 @@ -provider: tongyi -label: - zh_Hans: 通义千问 - en_US: TONGYI -icon_small: - en_US: icon_s_en.png -icon_large: - zh_Hans: icon_l_zh.png - en_US: icon_l_en.png -background: "#EFF1FE" -help: - title: - en_US: Get your API key from AliCloud - zh_Hans: 从阿里云百炼获取 API Key - url: - en_US: https://bailian.console.aliyun.com/?apiKey=1#/api-key -supported_model_types: - - llm - - tts - - text-embedding -configurate_methods: - - predefined-model - - customizable-model -provider_credential_schema: - credential_form_schemas: - - variable: dashscope_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: dashscope_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: context_size - label: - zh_Hans: 模型上下文长度 - en_US: Model context size - required: true - type: text-input - default: '4096' - placeholder: - zh_Hans: 在此输入您的模型上下文长度 - en_US: Enter your Model context size - - variable: max_tokens - label: - zh_Hans: 最大 token 上限 - en_US: Upper bound for max tokens - default: '4096' - type: text-input - show_on: - - variable: __model_type - value: llm - - variable: function_calling_type - label: - en_US: Function calling - type: select - required: false - default: no_call - options: - - value: no_call - label: - en_US: Not Support - zh_Hans: 不支持 - - value: function_call - label: - en_US: Support - zh_Hans: 支持 - show_on: - - variable: __model_type - value: llm diff --git a/api/core/model_runtime/model_providers/tongyi/tts/__init__.py b/api/core/model_runtime/model_providers/tongyi/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/tongyi/tts/tts-1.yaml b/api/core/model_runtime/model_providers/tongyi/tts/tts-1.yaml deleted file mode 100644 index 4eaa0ff361..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/tts/tts-1.yaml +++ /dev/null @@ -1,139 +0,0 @@ -model: tts-1 -model_type: tts -model_properties: - default_voice: 'sambert-zhiru-v1' - voices: - - mode: "sambert-zhinan-v1" - name: "知楠(广告男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiqi-v1" - name: "知琪(温柔女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhichu-v1" - name: "知厨(新闻播报)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhide-v1" - name: "知德(新闻男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhijia-v1" - name: "知佳(标准女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiru-v1" - name: "知茹(新闻女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiqian-v1" - name: "知倩(配音解说、新闻播报)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhixiang-v1" - name: "知祥(配音解说)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiwei-v1" - name: "知薇(萝莉女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhihao-v1" - name: "知浩(咨询男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhijing-v1" - name: "知婧(严厉女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiming-v1" - name: "知茗(诙谐男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhimo-v1" - name: "知墨(情感男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhina-v1" - name: "知娜(浙普女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhishu-v1" - name: "知树(资讯男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhistella-v1" - name: "知莎(知性女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiting-v1" - name: "知婷(电台女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhixiao-v1" - name: "知笑(资讯女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiya-v1" - name: "知雅(严厉女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiye-v1" - name: "知晔(青年男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiying-v1" - name: "知颖(软萌童声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhiyuan-v1" - name: "知媛(知心姐姐)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhigui-v1" - name: "知柜(直播女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhishuo-v1" - name: "知硕(自然男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhimiao-emo-v1" - name: "知妙(多种情感女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhimao-v1" - name: "知猫(直播女声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhilun-v1" - name: "知伦(悬疑解说)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhifei-v1" - name: "知飞(激昂解说)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-zhida-v1" - name: "知达(标准男声)" - language: [ "zh-Hans", "en-US" ] - - mode: "sambert-camila-v1" - name: "Camila(西班牙语女声)" - language: [ "es-ES" ] - - mode: "sambert-perla-v1" - name: "Perla(意大利语女声)" - language: [ "it-IT" ] - - mode: "sambert-indah-v1" - name: "Indah(印尼语女声)" - language: [ "id-ID" ] - - mode: "sambert-clara-v1" - name: "Clara(法语女声)" - language: [ "fr-FR" ] - - mode: "sambert-hanna-v1" - name: "Hanna(德语女声)" - language: [ "de-DE" ] - - mode: "sambert-beth-v1" - name: "Beth(咨询女声)" - language: [ "en-US" ] - - mode: "sambert-betty-v1" - name: "Betty(客服女声)" - language: [ "en-US" ] - - mode: "sambert-cally-v1" - name: "Cally(自然女声)" - language: [ "en-US" ] - - mode: "sambert-cindy-v1" - name: "Cindy(对话女声)" - language: [ "en-US" ] - - mode: "sambert-eva-v1" - name: "Eva(陪伴女声)" - language: [ "en-US" ] - - mode: "sambert-donna-v1" - name: "Donna(教育女声)" - language: [ "en-US" ] - - mode: "sambert-brian-v1" - name: "Brian(客服男声)" - language: [ "en-US" ] - - mode: "sambert-waan-v1" - name: "Waan(泰语女声)" - language: [ "th-TH" ] - word_limit: 7000 - audio_type: 'mp3' - max_workers: 5 -pricing: - input: '1' - output: '0' - unit: '0.0001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/tongyi/tts/tts.py b/api/core/model_runtime/model_providers/tongyi/tts/tts.py deleted file mode 100644 index 48a38897a8..0000000000 --- a/api/core/model_runtime/model_providers/tongyi/tts/tts.py +++ /dev/null @@ -1,152 +0,0 @@ -import threading -from queue import Queue -from typing import Optional - -import dashscope -from dashscope import SpeechSynthesizer -from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse -from dashscope.audio.tts import ResultCallback, SpeechSynthesisResult - -from core.model_runtime.errors.invoke import InvokeBadRequestError -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.tts_model import TTSModel -from core.model_runtime.model_providers.tongyi._common import _CommonTongyi - - -class TongyiText2SpeechModel(_CommonTongyi, TTSModel): - """ - Model class for Tongyi Speech to text model. - """ - - def _invoke( - self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ) -> any: - """ - _invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param voice: model timbre - :param content_text: text content to be translated - :param user: unique user id - :return: text translated to audio file - """ - if not voice or voice not in [ - d["value"] for d in self.get_tts_model_voices(model=model, credentials=credentials) - ]: - voice = self._get_model_default_voice(model, credentials) - - return self._tts_invoke_streaming(model=model, credentials=credentials, content_text=content_text, voice=voice) - - def validate_credentials(self, model: str, credentials: dict, user: Optional[str] = None) -> None: - """ - validate credentials text2speech model - - :param model: model name - :param credentials: model credentials - :param user: unique user id - :return: text translated to audio file - """ - try: - self._tts_invoke_streaming( - model=model, - credentials=credentials, - content_text="Hello Dify!", - voice=self._get_model_default_voice(model, credentials), - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: - """ - _tts_invoke_streaming text2speech model - - :param model: model name - :param credentials: model credentials - :param voice: model timbre - :param content_text: text content to be translated - :return: text translated to audio file - """ - word_limit = self._get_model_word_limit(model, credentials) - audio_type = self._get_model_audio_type(model, credentials) - try: - audio_queue: Queue = Queue() - callback = Callback(queue=audio_queue) - - def invoke_remote(content, v, api_key, cb, at, wl): - if len(content) < word_limit: - sentences = [content] - else: - sentences = list(self._split_text_into_sentences(org_text=content, max_length=wl)) - for sentence in sentences: - SpeechSynthesizer.call( - model=v, - sample_rate=16000, - api_key=api_key, - text=sentence.strip(), - callback=cb, - format=at, - word_timestamp_enabled=True, - phoneme_timestamp_enabled=True, - ) - - threading.Thread( - target=invoke_remote, - args=(content_text, voice, credentials.get("dashscope_api_key"), callback, audio_type, word_limit), - ).start() - - while True: - audio = audio_queue.get() - if audio is None: - break - yield audio - - except Exception as ex: - raise InvokeBadRequestError(str(ex)) - - @staticmethod - def _process_sentence(sentence: str, credentials: dict, voice: str, audio_type: str): - """ - _tts_invoke Tongyi text2speech model api - - :param credentials: model credentials - :param sentence: text content to be translated - :param voice: model timbre - :param audio_type: audio file type - :return: text translated to audio file - """ - response = dashscope.audio.tts.SpeechSynthesizer.call( - model=voice, - sample_rate=48000, - api_key=credentials.get("dashscope_api_key"), - text=sentence.strip(), - format=audio_type, - ) - if isinstance(response.get_audio_data(), bytes): - return response.get_audio_data() - - -class Callback(ResultCallback): - def __init__(self, queue: Queue): - self._queue = queue - - def on_open(self): - pass - - def on_complete(self): - self._queue.put(None) - self._queue.task_done() - - def on_error(self, response: SpeechSynthesisResponse): - self._queue.put(None) - self._queue.task_done() - - def on_close(self): - self._queue.put(None) - self._queue.task_done() - - def on_event(self, result: SpeechSynthesisResult): - ad = result.get_audio_frame() - if ad: - self._queue.put(ad) diff --git a/api/core/model_runtime/model_providers/triton_inference_server/__init__.py b/api/core/model_runtime/model_providers/triton_inference_server/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/triton_inference_server/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/triton_inference_server/_assets/icon_l_en.png deleted file mode 100644 index dd32d45803ee423f506fb3dc021e118ceccc0cfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79751 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 - - diff --git a/api/core/model_runtime/model_providers/triton_inference_server/llm/__init__.py b/api/core/model_runtime/model_providers/triton_inference_server/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/triton_inference_server/llm/llm.py b/api/core/model_runtime/model_providers/triton_inference_server/llm/llm.py deleted file mode 100644 index cf7e3f14be..0000000000 --- a/api/core/model_runtime/model_providers/triton_inference_server/llm/llm.py +++ /dev/null @@ -1,280 +0,0 @@ -from collections.abc import Generator - -from httpx import Response, post -from yarl import URL - -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, - SystemPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - - -class TritonInferenceAILargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - invoke LLM - - see `core.model_runtime.model_providers.__base.large_language_model.LargeLanguageModel._invoke` - """ - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - validate credentials - """ - if "server_url" not in credentials: - raise CredentialsValidateFailedError("server_url is required in credentials") - - try: - self._invoke( - model=model, - credentials=credentials, - prompt_messages=[UserPromptMessage(content="ping")], - model_parameters={}, - stream=False, - ) - except InvokeError as ex: - raise CredentialsValidateFailedError(f"An error occurred during connection: {str(ex)}") - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - """ - get number of tokens - - cause TritonInference LLM is a customized model, we could net detect which tokenizer to use - so we just take the GPT2 tokenizer as default - """ - return self._get_num_tokens_by_gpt2(self._convert_prompt_message_to_text(prompt_messages)) - - def _convert_prompt_message_to_text(self, message: list[PromptMessage]) -> str: - """ - convert prompt message to text - """ - text = "" - for item in message: - if isinstance(item, UserPromptMessage): - text += f"User: {item.content}" - elif isinstance(item, SystemPromptMessage): - text += f"System: {item.content}" - elif isinstance(item, AssistantPromptMessage): - text += f"Assistant: {item.content}" - else: - raise NotImplementedError(f"PromptMessage type {type(item)} is not supported") - return text - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - max=int(credentials.get("context_length", 2048)), - default=min(512, int(credentials.get("context_length", 2048))), - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ] - - completion_type = None - - if "completion_type" in credentials: - if credentials["completion_type"] == "chat": - completion_type = LLMMode.CHAT.value - elif credentials["completion_type"] == "completion": - completion_type = LLMMode.COMPLETION.value - else: - raise ValueError(f'completion_type {credentials["completion_type"]} is not supported') - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - parameter_rules=rules, - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties={ - ModelPropertyKey.MODE: completion_type, - ModelPropertyKey.CONTEXT_SIZE: int(credentials.get("context_length", 2048)), - }, - ) - - return entity - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - generate text from LLM - """ - if "server_url" not in credentials: - raise CredentialsValidateFailedError("server_url is required in credentials") - - if "stream" in credentials and not bool(credentials["stream"]) and stream: - raise ValueError(f"stream is not supported by model {model}") - - try: - parameters = {} - if "temperature" in model_parameters: - parameters["temperature"] = model_parameters["temperature"] - if "top_p" in model_parameters: - parameters["top_p"] = model_parameters["top_p"] - if "top_k" in model_parameters: - parameters["top_k"] = model_parameters["top_k"] - if "presence_penalty" in model_parameters: - parameters["presence_penalty"] = model_parameters["presence_penalty"] - if "frequency_penalty" in model_parameters: - parameters["frequency_penalty"] = model_parameters["frequency_penalty"] - - response = post( - str(URL(credentials["server_url"]) / "v2" / "models" / model / "generate"), - json={ - "text_input": self._convert_prompt_message_to_text(prompt_messages), - "max_tokens": model_parameters.get("max_tokens", 512), - "parameters": {"stream": False, **parameters}, - }, - timeout=(10, 120), - ) - response.raise_for_status() - if response.status_code != 200: - raise InvokeBadRequestError(f"Invoke failed with status code {response.status_code}, {response.text}") - - if stream: - return self._handle_chat_stream_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=response - ) - return self._handle_chat_generate_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=response - ) - except Exception as ex: - raise InvokeConnectionError(f"An error occurred during connection: {str(ex)}") - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: Response, - ) -> LLMResult: - """ - handle normal chat generate response - """ - text = resp.json()["text_output"] - - usage = LLMUsage.empty_usage() - usage.prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - usage.completion_tokens = self._get_num_tokens_by_gpt2(text) - - return LLMResult( - model=model, prompt_messages=prompt_messages, message=AssistantPromptMessage(content=text), usage=usage - ) - - def _handle_chat_stream_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: Response, - ) -> Generator: - """ - handle normal chat generate response - """ - text = resp.json()["text_output"] - - usage = LLMUsage.empty_usage() - usage.prompt_tokens = self.get_num_tokens(model, credentials, prompt_messages) - usage.completion_tokens = self._get_num_tokens_by_gpt2(text) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta(index=0, message=AssistantPromptMessage(content=text), usage=usage), - ) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [], - InvokeRateLimitError: [], - InvokeAuthorizationError: [], - InvokeBadRequestError: [ValueError], - } diff --git a/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.py b/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.py deleted file mode 100644 index d85f7c82e7..0000000000 --- a/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class XinferenceAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.yaml b/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.yaml deleted file mode 100644 index 218678b883..0000000000 --- a/api/core/model_runtime/model_providers/triton_inference_server/triton_inference_server.yaml +++ /dev/null @@ -1,84 +0,0 @@ -provider: triton_inference_server -label: - en_US: Triton Inference Server -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.png -background: "#EFFDFD" -help: - title: - en_US: How to deploy Triton Inference Server - zh_Hans: 如何部署 Triton Inference Server - url: - en_US: https://github.com/triton-inference-server/server -supported_model_types: - - llm -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: server_url - label: - zh_Hans: 服务器URL - en_US: Server url - type: text-input - required: true - placeholder: - zh_Hans: 在此输入 Triton Inference Server 的服务器地址,如 http://192.168.1.100:8000 - en_US: Enter the url of your Triton Inference Server, e.g. http://192.168.1.100:8000 - - variable: context_size - label: - zh_Hans: 上下文大小 - en_US: Context size - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的上下文大小 - en_US: Enter the context size - default: '2048' - - variable: completion_type - label: - zh_Hans: 补全类型 - en_US: Model type - type: select - required: true - default: chat - placeholder: - zh_Hans: 在此输入您的补全类型 - en_US: Enter the completion type - options: - - label: - zh_Hans: 补全模型 - en_US: Completion model - value: completion - - label: - zh_Hans: 对话模型 - en_US: Chat model - value: chat - - variable: stream - label: - zh_Hans: 流式输出 - en_US: Stream output - type: select - required: true - default: 'true' - placeholder: - zh_Hans: 是否支持流式输出 - en_US: Whether to support stream output - options: - - label: - zh_Hans: 是 - en_US: 'Yes' - value: 'true' - - label: - zh_Hans: 否 - en_US: 'No' - value: 'false' diff --git a/api/core/model_runtime/model_providers/upstage/__init__.py b/api/core/model_runtime/model_providers/upstage/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/upstage/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/upstage/_assets/icon_l_en.svg deleted file mode 100644 index 0761f85ba6..0000000000 --- a/api/core/model_runtime/model_providers/upstage/_assets/icon_l_en.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/upstage/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/upstage/_assets/icon_s_en.svg deleted file mode 100644 index 44ef12b730..0000000000 --- a/api/core/model_runtime/model_providers/upstage/_assets/icon_s_en.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/api/core/model_runtime/model_providers/upstage/_common.py b/api/core/model_runtime/model_providers/upstage/_common.py deleted file mode 100644 index 47ebaccd84..0000000000 --- a/api/core/model_runtime/model_providers/upstage/_common.py +++ /dev/null @@ -1,54 +0,0 @@ -from collections.abc import Mapping - -import openai -from httpx import Timeout - -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonUpstage: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["upstage_api_key"], - "base_url": "https://api.upstage.ai/v1/solar", - "timeout": Timeout(315.0, read=300.0, write=20.0, connect=10.0), - "max_retries": 1, - } - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [openai.APIConnectionError, openai.APITimeoutError], - InvokeServerUnavailableError: [openai.InternalServerError], - InvokeRateLimitError: [openai.RateLimitError], - InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError], - InvokeBadRequestError: [ - openai.BadRequestError, - openai.NotFoundError, - openai.UnprocessableEntityError, - openai.APIError, - ], - } diff --git a/api/core/model_runtime/model_providers/upstage/llm/__init__.py b/api/core/model_runtime/model_providers/upstage/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/upstage/llm/_position.yaml b/api/core/model_runtime/model_providers/upstage/llm/_position.yaml deleted file mode 100644 index 7992843dcb..0000000000 --- a/api/core/model_runtime/model_providers/upstage/llm/_position.yaml +++ /dev/null @@ -1 +0,0 @@ -- solar-1-mini-chat diff --git a/api/core/model_runtime/model_providers/upstage/llm/llm.py b/api/core/model_runtime/model_providers/upstage/llm/llm.py deleted file mode 100644 index a18ee90624..0000000000 --- a/api/core/model_runtime/model_providers/upstage/llm/llm.py +++ /dev/null @@ -1,603 +0,0 @@ -import logging -from collections.abc import Generator -from typing import Optional, Union, cast - -from openai import OpenAI, Stream -from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall -from openai.types.chat.chat_completion_message import FunctionCall -from tokenizers import Tokenizer - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - TextPromptMessageContent, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.upstage._common import _CommonUpstage - -logger = logging.getLogger(__name__) - -UPSTAGE_BLOCK_MODE_PROMPT = """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 - - -class UpstageLargeLanguageModel(_CommonUpstage, LargeLanguageModel): - """ - Model class for Upstage large language model. - """ - - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - - return self._chat_generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: Optional[list[Callback]] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - if "response_format" in model_parameters and model_parameters["response_format"] in {"JSON", "XML"}: - stop = stop or [] - self._transform_chat_json_prompts( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - response_format=model_parameters["response_format"], - ) - model_parameters.pop("response_format") - - return self._invoke( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _transform_chat_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts - """ - if stop is None: - stop = [] - if "```\n" not in stop: - stop.append("```\n") - if "\n```" not in stop: - stop.append("\n```") - - if len(prompt_messages) > 0 and isinstance(prompt_messages[0], SystemPromptMessage): - prompt_messages[0] = SystemPromptMessage( - content=UPSTAGE_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}\n")) - else: - prompt_messages.insert( - 0, - SystemPromptMessage( - content=UPSTAGE_BLOCK_MODE_PROMPT.replace( - "{{instructions}}", f"Please output a valid {response_format} object." - ).replace("{{block}}", response_format) - ), - ) - prompt_messages.append(AssistantPromptMessage(content=f"\n```{response_format}")) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - return self._num_tokens_from_messages(model, prompt_messages, tools) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - client.chat.completions.create( - messages=[{"role": "user", "content": "ping"}], model=model, temperature=0, max_tokens=10, stream=False - ) - except Exception as e: - raise CredentialsValidateFailedError(str(e)) - - def _chat_generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - credentials_kwargs = self._to_credential_kwargs(credentials) - client = OpenAI(**credentials_kwargs) - - extra_model_kwargs = {} - - if tools: - extra_model_kwargs["functions"] = [ - {"name": tool.name, "description": tool.description, "parameters": tool.parameters} for tool in tools - ] - - if stop: - extra_model_kwargs["stop"] = stop - - if user: - extra_model_kwargs["user"] = user - - # chat model - response = client.chat.completions.create( - messages=[self._convert_prompt_message_to_dict(m) for m in prompt_messages], - model=model, - stream=stream, - **model_parameters, - **extra_model_kwargs, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, credentials, response, prompt_messages, tools) - return self._handle_chat_generate_response(model, credentials, response, prompt_messages, tools) - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - response: ChatCompletion, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> LLMResult: - """ - Handle llm chat response - - :param model: model name - :param credentials: credentials - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response - """ - assistant_message = response.choices[0].message - # assistant_message_tool_calls = assistant_message.tool_calls - assistant_message_function_call = assistant_message.function_call - - # extract tool calls from response - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message.content, tool_calls=tool_calls) - - # calculate num tokens - if response.usage: - # transform usage - prompt_tokens = response.usage.prompt_tokens - completion_tokens = response.usage.completion_tokens - else: - # calculate num tokens - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - completion_tokens = self._num_tokens_from_messages(model, [assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - # transform response - response = LLMResult( - model=response.model, - prompt_messages=prompt_messages, - message=assistant_prompt_message, - usage=usage, - system_fingerprint=response.system_fingerprint, - ) - - return response - - def _handle_chat_generate_stream_response( - self, - model: str, - credentials: dict, - response: Stream[ChatCompletionChunk], - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> Generator: - """ - Handle llm chat stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: llm response chunk generator - """ - full_assistant_content = "" - delta_assistant_message_function_call_storage: Optional[ChoiceDeltaFunctionCall] = None - prompt_tokens = 0 - completion_tokens = 0 - final_tool_calls = [] - final_chunk = LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=""), - ), - ) - - for chunk in response: - if len(chunk.choices) == 0: - if chunk.usage: - # calculate num tokens - prompt_tokens = chunk.usage.prompt_tokens - completion_tokens = chunk.usage.completion_tokens - continue - - delta = chunk.choices[0] - has_finish_reason = delta.finish_reason is not None - - if ( - not has_finish_reason - and (delta.delta.content is None or delta.delta.content == "") - and delta.delta.function_call is None - ): - continue - - # assistant_message_tool_calls = delta.delta.tool_calls - assistant_message_function_call = delta.delta.function_call - - # extract tool calls from response - if delta_assistant_message_function_call_storage is not None: - # handle process of stream function call - if assistant_message_function_call: - # message has not ended ever - delta_assistant_message_function_call_storage.arguments += assistant_message_function_call.arguments - continue - else: - # message has ended - assistant_message_function_call = delta_assistant_message_function_call_storage - delta_assistant_message_function_call_storage = None - else: - if assistant_message_function_call: - # start of stream function call - delta_assistant_message_function_call_storage = assistant_message_function_call - if delta_assistant_message_function_call_storage.arguments is None: - delta_assistant_message_function_call_storage.arguments = "" - if not has_finish_reason: - continue - - # tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls) - function_call = self._extract_response_function_call(assistant_message_function_call) - tool_calls = [function_call] if function_call else [] - if tool_calls: - final_tool_calls.extend(tool_calls) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.delta.content or "", tool_calls=tool_calls) - - full_assistant_content += delta.delta.content or "" - - if has_finish_reason: - final_chunk = LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - ), - ) - - if not prompt_tokens: - prompt_tokens = self._num_tokens_from_messages(model, prompt_messages, tools) - - if not completion_tokens: - full_assistant_prompt_message = AssistantPromptMessage( - content=full_assistant_content, tool_calls=final_tool_calls - ) - completion_tokens = self._num_tokens_from_messages(model, [full_assistant_prompt_message]) - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - final_chunk.delta.usage = usage - - yield final_chunk - - def _extract_response_tool_calls( - self, response_tool_calls: list[ChatCompletionMessageToolCall | ChoiceDeltaToolCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - - return tool_calls - - def _extract_response_function_call( - self, response_function_call: FunctionCall | ChoiceDeltaFunctionCall - ) -> AssistantPromptMessage.ToolCall: - """ - Extract function call from response - - :param response_function_call: response function call - :return: tool call - """ - tool_call = None - if response_function_call: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function_call.name, arguments=response_function_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_function_call.name, type="function", function=function - ) - - return tool_call - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for Upstage API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(TextPromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - # message_dict["tool_calls"] = [tool_call.dict() for tool_call in - # message.tool_calls] - function_call = message.tool_calls[0] - message_dict["function_call"] = { - "name": function_call.function.name, - "arguments": function_call.function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - # message_dict = { - # "role": "tool", - # "content": message.content, - # "tool_call_id": message.tool_call_id - # } - message_dict = {"role": "function", "content": message.content, "name": message.tool_call_id} - else: - raise ValueError(f"Got unknown type {message}") - - if message.name: - message_dict["name"] = message.name - - return message_dict - - def _get_tokenizer(self) -> Tokenizer: - return Tokenizer.from_pretrained("upstage/solar-1-mini-tokenizer") - - def _num_tokens_from_messages( - self, model: str, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """ - Calculate num tokens for solar with Huggingface Solar tokenizer. - Solar tokenizer is opened in huggingface https://huggingface.co/upstage/solar-1-mini-tokenizer - """ - tokenizer = self._get_tokenizer() - tokens_per_message = 5 # <|im_start|>{role}\n{message}<|im_end|> - tokens_prefix = 1 # <|startoftext|> - tokens_suffix = 3 # <|im_start|>assistant\n - - num_tokens = 0 - num_tokens += tokens_prefix - - messages_dict = [self._convert_prompt_message_to_dict(message) for message in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += len(tokenizer.encode(t_key, add_special_tokens=False)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(tokenizer.encode(f_key, add_special_tokens=False)) - num_tokens += len(tokenizer.encode(f_value, add_special_tokens=False)) - else: - num_tokens += len(tokenizer.encode(t_key, add_special_tokens=False)) - num_tokens += len(tokenizer.encode(t_value, add_special_tokens=False)) - else: - num_tokens += len(tokenizer.encode(str(value), add_special_tokens=False)) - num_tokens += tokens_suffix - - if tools: - num_tokens += self._num_tokens_for_tools(tokenizer, tools) - - return num_tokens - - def _num_tokens_for_tools(self, tokenizer: Tokenizer, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling with upstage tokenizer. - - :param tokenizer: huggingface tokenizer - :param tools: tools for tool calling - :return: number of tokens - """ - num_tokens = 0 - for tool in tools: - num_tokens += len(tokenizer.encode("type")) - num_tokens += len(tokenizer.encode("function")) - - # calculate num tokens for function object - num_tokens += len(tokenizer.encode("name")) - num_tokens += len(tokenizer.encode(tool.name)) - num_tokens += len(tokenizer.encode("description")) - num_tokens += len(tokenizer.encode(tool.description)) - parameters = tool.parameters - num_tokens += len(tokenizer.encode("parameters")) - if "title" in parameters: - num_tokens += len(tokenizer.encode("title")) - num_tokens += len(tokenizer.encode(parameters.get("title"))) - num_tokens += len(tokenizer.encode("type")) - num_tokens += len(tokenizer.encode(parameters.get("type"))) - if "properties" in parameters: - num_tokens += len(tokenizer.encode("properties")) - for key, value in parameters.get("properties").items(): - num_tokens += len(tokenizer.encode(key)) - for field_key, field_value in value.items(): - num_tokens += len(tokenizer.encode(field_key)) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += len(tokenizer.encode(enum_field)) - else: - num_tokens += len(tokenizer.encode(field_key)) - num_tokens += len(tokenizer.encode(str(field_value))) - if "required" in parameters: - num_tokens += len(tokenizer.encode("required")) - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += len(tokenizer.encode(required_field)) - - return num_tokens diff --git a/api/core/model_runtime/model_providers/upstage/llm/solar-1-mini-chat.yaml b/api/core/model_runtime/model_providers/upstage/llm/solar-1-mini-chat.yaml deleted file mode 100644 index 787ac83f8a..0000000000 --- a/api/core/model_runtime/model_providers/upstage/llm/solar-1-mini-chat.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: solar-1-mini-chat -label: - zh_Hans: solar-1-mini-chat - en_US: solar-1-mini-chat - ko_KR: solar-1-mini-chat -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 32768 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 512 - min: 1 - max: 32768 - - name: seed - label: - zh_Hans: 种子 - en_US: Seed - type: int - help: - zh_Hans: - 如果指定,模型将尽最大努力进行确定性采样,使得重复的具有相同种子和参数的请求应该返回相同的结果。不能保证确定性,您应该参考 system_fingerprint - 响应参数来监视变化。 - en_US: - If specified, model will make a best effort to sample deterministically, - such that repeated requests with the same seed and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - system_fingerprint response parameter to monitor changes in the backend. - required: false -pricing: - input: "0.5" - output: "0.5" - unit: "0.000001" - currency: USD diff --git a/api/core/model_runtime/model_providers/upstage/text_embedding/__init__.py b/api/core/model_runtime/model_providers/upstage/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-passage.yaml b/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-passage.yaml deleted file mode 100644 index d838a5bbb1..0000000000 --- a/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-passage.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: solar-embedding-1-large-passage -model_type: text-embedding -model_properties: - context_size: 4000 - max_chunks: 32 -pricing: - input: '0.1' - unit: '0.000001' - currency: 'USD' diff --git a/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-query.yaml b/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-query.yaml deleted file mode 100644 index c77645cffd..0000000000 --- a/api/core/model_runtime/model_providers/upstage/text_embedding/solar-embedding-1-large-query.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: solar-embedding-1-large-query -model_type: text-embedding -model_properties: - context_size: 4000 - max_chunks: 32 -pricing: - input: '0.1' - unit: '0.000001' - currency: 'USD' diff --git a/api/core/model_runtime/model_providers/upstage/upstage.py b/api/core/model_runtime/model_providers/upstage/upstage.py deleted file mode 100644 index e45d4aae19..0000000000 --- a/api/core/model_runtime/model_providers/upstage/upstage.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class UpstageProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials from defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="solar-1-mini-chat", credentials=credentials) - except CredentialsValidateFailedError as e: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise e - except Exception as e: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise e diff --git a/api/core/model_runtime/model_providers/upstage/upstage.yaml b/api/core/model_runtime/model_providers/upstage/upstage.yaml deleted file mode 100644 index 837667cfa9..0000000000 --- a/api/core/model_runtime/model_providers/upstage/upstage.yaml +++ /dev/null @@ -1,49 +0,0 @@ -provider: upstage -label: - en_US: Upstage -description: - en_US: Models provided by Upstage, such as Solar-1-mini-chat. - zh_Hans: Upstage 提供的模型,例如 Solar-1-mini-chat. -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FFFFF" -help: - title: - en_US: Get your API Key from Upstage - zh_Hans: 从 Upstage 获取 API Key - url: - en_US: https://console.upstage.ai/api-keys -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: upstage_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key -provider_credential_schema: - credential_form_schemas: - - variable: upstage_api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/vertex_ai/__init__.py b/api/core/model_runtime/model_providers/vertex_ai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/vertex_ai/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/vertex_ai/_assets/icon_l_en.png deleted file mode 100644 index 9f8f05231a6733a138502ba7a254e2125ae8d02b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18078 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 diff --git a/api/core/model_runtime/model_providers/vertex_ai/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/vertex_ai/_assets/icon_s_en.svg deleted file mode 100644 index efc3589c07..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/_assets/icon_s_en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/vertex_ai/_common.py b/api/core/model_runtime/model_providers/vertex_ai/_common.py deleted file mode 100644 index 8f7c859e38..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/_common.py +++ /dev/null @@ -1,15 +0,0 @@ -from core.model_runtime.errors.invoke import InvokeError - - -class _CommonVertexAi: - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - pass diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/__init__.py b/api/core/model_runtime/model_providers/vertex_ai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml deleted file mode 100644 index 5613348695..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: claude-3-haiku@20240307 -label: - en_US: Claude 3 Haiku -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.00025' - output: '0.00125' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-opus.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-opus.yaml deleted file mode 100644 index ab084636b5..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-opus.yaml +++ /dev/null @@ -1,56 +0,0 @@ -model: claude-3-opus@20240229 -label: - en_US: Claude 3 Opus -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - # docs: https://docs.anthropic.com/claude/docs/system-prompts - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.015' - output: '0.075' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml deleted file mode 100644 index 0be0113ffd..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml +++ /dev/null @@ -1,55 +0,0 @@ -model: claude-3-sonnet@20240229 -label: - en_US: Claude 3 Sonnet -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml deleted file mode 100644 index c64384e6a2..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml +++ /dev/null @@ -1,55 +0,0 @@ -model: claude-3-5-sonnet@20240620 -label: - en_US: Claude 3.5 Sonnet -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: max_tokens - use_template: max_tokens - required: true - type: int - default: 4096 - min: 1 - max: 4096 - help: - zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 - en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. - - name: temperature - use_template: temperature - required: false - type: float - default: 1 - min: 0.0 - max: 1.0 - help: - zh_Hans: 生成内容的随机性。 - en_US: The amount of randomness injected into the response. - - name: top_p - required: false - type: float - default: 0.999 - min: 0.000 - max: 1.000 - help: - zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。 - en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both. - - name: top_k - required: false - type: int - default: 0 - min: 0 - # tip docs from aws has error, max value is 500 - max: 500 - help: - zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。 - en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses. -pricing: - input: '0.003' - output: '0.015' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml deleted file mode 100644 index ebb276b8af..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml +++ /dev/null @@ -1,37 +0,0 @@ -model: gemini-1.0-pro-vision-001 -label: - en_US: Gemini 1.0 Pro Vision -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - en_US: Top k - type: int - help: - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_output_tokens - use_template: max_tokens - required: true - default: 2048 - min: 1 - max: 2048 -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro.yaml deleted file mode 100644 index c325973846..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/gemini-1.0-pro.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: gemini-1.0-pro-002 -label: - en_US: Gemini 1.0 Pro -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32760 -parameter_rules: - - name: temperature - use_template: temperature - - name: top_p - use_template: top_p - - name: top_k - label: - en_US: Top k - type: int - help: - en_US: Only sample from the top K options for each subsequent token. - required: false - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: max_output_tokens - use_template: max_tokens - required: true - default: 8192 - min: 1 - max: 8192 -pricing: - input: '0.00' - output: '0.00' - unit: '0.000001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/text_embedding/__init__.py b/api/core/model_runtime/model_providers/vertex_ai/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-embedding-004.yaml b/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-embedding-004.yaml deleted file mode 100644 index 32db6faf89..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-embedding-004.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: text-embedding-004 -model_type: text-embedding -model_properties: - context_size: 2048 -pricing: - input: '0.00013' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-multilingual-embedding-002.yaml b/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-multilingual-embedding-002.yaml deleted file mode 100644 index 2ec0eea9f2..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/text_embedding/text-multilingual-embedding-002.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: text-multilingual-embedding-002 -model_type: text-embedding -model_properties: - context_size: 2048 -pricing: - input: '0.00013' - unit: '0.001' - currency: USD diff --git a/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.py b/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.py deleted file mode 100644 index 466a86fd36..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class VertexAiProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `gemini-1.0-pro-002` model for validate, - model_instance.validate_credentials(model="gemini-1.0-pro-002", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.yaml b/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.yaml deleted file mode 100644 index 27a4d03fe2..0000000000 --- a/api/core/model_runtime/model_providers/vertex_ai/vertex_ai.yaml +++ /dev/null @@ -1,43 +0,0 @@ -provider: vertex_ai -label: - en_US: Vertex AI | Google Cloud Platform -description: - en_US: Vertex AI in Google Cloud Platform. -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.png -background: "#FCFDFF" -help: - title: - en_US: Get your Access Details from Google - url: - en_US: https://cloud.google.com/vertex-ai/ -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: vertex_project_id - label: - en_US: Project ID - type: text-input - required: true - placeholder: - en_US: Enter your Google Cloud Project ID - - variable: vertex_location - label: - en_US: Location - type: text-input - required: true - placeholder: - en_US: Enter your Google Cloud Location - - variable: vertex_service_account_key - label: - en_US: Service Account Key (Leave blank if you use Application Default Credentials) - type: secret-input - required: false - placeholder: - en_US: Enter your Google Cloud Service Account Key in base64 format diff --git a/api/core/model_runtime/model_providers/volcengine_maas/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_en.svg deleted file mode 100644 index 616e90916b..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_en.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_zh.svg b/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_zh.svg deleted file mode 100644 index 24b92195bd..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_l_zh.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_s_en.svg deleted file mode 100644 index e6454a89b7..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/volcengine_maas/client.py b/api/core/model_runtime/model_providers/volcengine_maas/client.py deleted file mode 100644 index cfe21e4b9f..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/client.py +++ /dev/null @@ -1,216 +0,0 @@ -import re -from collections.abc import Generator -from typing import Optional, cast - -from volcenginesdkarkruntime import Ark -from volcenginesdkarkruntime.types.chat import ( - ChatCompletion, - ChatCompletionAssistantMessageParam, - ChatCompletionChunk, - ChatCompletionContentPartImageParam, - ChatCompletionContentPartTextParam, - ChatCompletionMessageParam, - ChatCompletionMessageToolCallParam, - ChatCompletionSystemMessageParam, - ChatCompletionToolMessageParam, - ChatCompletionToolParam, - ChatCompletionUserMessageParam, -) -from volcenginesdkarkruntime.types.chat.chat_completion_content_part_image_param import ImageURL -from volcenginesdkarkruntime.types.chat.chat_completion_message_tool_call_param import Function -from volcenginesdkarkruntime.types.create_embedding_response import CreateEmbeddingResponse -from volcenginesdkarkruntime.types.shared_params import FunctionDefinition - -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) - -DEFAULT_V2_ENDPOINT = "maas-api.ml-platform-cn-beijing.volces.com" -DEFAULT_V3_ENDPOINT = "https://ark.cn-beijing.volces.com/api/v3" - - -class ArkClientV3: - endpoint_id: Optional[str] = None - ark: Optional[Ark] = None - - def __init__(self, *args, **kwargs): - self.ark = Ark(*args, **kwargs) - self.endpoint_id = None - - @staticmethod - def is_legacy(credentials: dict) -> bool: - # match default v2 endpoint - if ArkClientV3.is_compatible_with_legacy(credentials): - return False - # match default v3 endpoint - if credentials.get("api_endpoint_host") == DEFAULT_V3_ENDPOINT: - return False - # only v3 support api_key - if credentials.get("auth_method") == "api_key": - return False - # these cases are considered as sdk v2 - # - modified default v2 endpoint - # - modified default v3 endpoint and auth without api_key - return True - - @staticmethod - def is_compatible_with_legacy(credentials: dict) -> bool: - endpoint = credentials.get("api_endpoint_host") - return endpoint == DEFAULT_V2_ENDPOINT - - @classmethod - def from_credentials(cls, credentials): - """Initialize the client using the credentials provided.""" - args = { - "base_url": credentials["api_endpoint_host"], - "region": credentials["volc_region"], - } - if credentials.get("auth_method") == "api_key": - args = { - **args, - "api_key": credentials["volc_api_key"], - } - else: - args = { - **args, - "ak": credentials["volc_access_key_id"], - "sk": credentials["volc_secret_access_key"], - } - - if cls.is_compatible_with_legacy(credentials): - args = {**args, "base_url": DEFAULT_V3_ENDPOINT} - - client = ArkClientV3(**args) - client.endpoint_id = credentials["endpoint_id"] - return client - - @staticmethod - def convert_prompt_message(message: PromptMessage) -> ChatCompletionMessageParam: - """Converts a PromptMessage to a ChatCompletionMessageParam""" - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - content = message.content - else: - content = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - content.append( - ChatCompletionContentPartTextParam( - text=message_content.text, - type="text", - ) - ) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - image_data = re.sub(r"^data:image\/[a-zA-Z]+;base64,", "", message_content.data) - content.append( - ChatCompletionContentPartImageParam( - image_url=ImageURL( - url=image_data, - detail=message_content.detail.value, - ), - type="image_url", - ) - ) - message_dict = ChatCompletionUserMessageParam(role="user", content=content) - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = ChatCompletionAssistantMessageParam( - content=message.content, - role="assistant", - tool_calls=None - if not message.tool_calls - else [ - ChatCompletionMessageToolCallParam( - id=call.id, - function=Function(name=call.function.name, arguments=call.function.arguments), - type="function", - ) - for call in message.tool_calls - ], - ) - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = ChatCompletionSystemMessageParam(content=message.content, role="system") - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = ChatCompletionToolMessageParam( - content=message.content, role="tool", tool_call_id=message.tool_call_id - ) - else: - raise ValueError(f"Got unknown PromptMessage type {message}") - - return message_dict - - @staticmethod - def _convert_tool_prompt(message: PromptMessageTool) -> ChatCompletionToolParam: - return ChatCompletionToolParam( - type="function", - function=FunctionDefinition( - name=message.name, - description=message.description, - parameters=message.parameters, - ), - ) - - def chat( - self, - messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - frequency_penalty: Optional[float] = None, - max_tokens: Optional[int] = None, - presence_penalty: Optional[float] = None, - top_p: Optional[float] = None, - temperature: Optional[float] = None, - ) -> ChatCompletion: - """Block chat""" - return self.ark.chat.completions.create( - model=self.endpoint_id, - messages=[self.convert_prompt_message(message) for message in messages], - tools=[self._convert_tool_prompt(tool) for tool in tools] if tools else None, - stop=stop, - frequency_penalty=frequency_penalty, - max_tokens=max_tokens, - presence_penalty=presence_penalty, - top_p=top_p, - temperature=temperature, - ) - - def stream_chat( - self, - messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - frequency_penalty: Optional[float] = None, - max_tokens: Optional[int] = None, - presence_penalty: Optional[float] = None, - top_p: Optional[float] = None, - temperature: Optional[float] = None, - ) -> Generator[ChatCompletionChunk]: - """Stream chat""" - chunks = self.ark.chat.completions.create( - stream=True, - model=self.endpoint_id, - messages=[self.convert_prompt_message(message) for message in messages], - tools=[self._convert_tool_prompt(tool) for tool in tools] if tools else None, - stop=stop, - frequency_penalty=frequency_penalty, - max_tokens=max_tokens, - presence_penalty=presence_penalty, - top_p=top_p, - temperature=temperature, - stream_options={"include_usage": True}, - ) - yield from chunks - - def embeddings(self, texts: list[str]) -> CreateEmbeddingResponse: - return self.ark.embeddings.create(model=self.endpoint_id, input=texts) diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/client.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/client.py deleted file mode 100644 index 266f1216f8..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/client.py +++ /dev/null @@ -1,123 +0,0 @@ -import re -from collections.abc import Callable, Generator -from typing import cast - -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.model_providers.volcengine_maas.legacy.errors import wrap_error -from core.model_runtime.model_providers.volcengine_maas.legacy.volc_sdk import ChatRole, MaasError, MaasService - - -class MaaSClient(MaasService): - def __init__(self, host: str, region: str): - self.endpoint_id = None - super().__init__(host, region) - - def set_endpoint_id(self, endpoint_id: str): - self.endpoint_id = endpoint_id - - @classmethod - def from_credential(cls, credentials: dict) -> "MaaSClient": - host = credentials["api_endpoint_host"] - region = credentials["volc_region"] - ak = credentials["volc_access_key_id"] - sk = credentials["volc_secret_access_key"] - endpoint_id = credentials["endpoint_id"] - - client = cls(host, region) - client.set_endpoint_id(endpoint_id) - client.set_ak(ak) - client.set_sk(sk) - return client - - def chat(self, params: dict, messages: list[PromptMessage], stream=False, **extra_model_kwargs) -> Generator | dict: - req = { - "parameters": params, - "messages": [self.convert_prompt_message_to_maas_message(prompt) for prompt in messages], - **extra_model_kwargs, - } - if not stream: - return super().chat( - self.endpoint_id, - req, - ) - return super().stream_chat( - self.endpoint_id, - req, - ) - - def embeddings(self, texts: list[str]) -> dict: - req = {"input": texts} - return super().embeddings(self.endpoint_id, req) - - @staticmethod - def convert_prompt_message_to_maas_message(message: PromptMessage) -> dict: - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": ChatRole.USER, "content": message.content} - else: - content = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - raise ValueError("Content object type only support image_url") - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - image_data = re.sub(r"^data:image\/[a-zA-Z]+;base64,", "", message_content.data) - content.append( - { - "type": "image_url", - "image_url": { - "url": "", - "image_bytes": image_data, - "detail": message_content.detail, - }, - } - ) - - message_dict = {"role": ChatRole.USER, "content": content} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": ChatRole.ASSISTANT, "content": message.content} - if message.tool_calls: - message_dict["tool_calls"] = [ - {"name": call.function.name, "arguments": call.function.arguments} for call in message.tool_calls - ] - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": ChatRole.SYSTEM, "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = {"role": ChatRole.FUNCTION, "content": message.content, "name": message.tool_call_id} - else: - raise ValueError(f"Got unknown PromptMessage type {message}") - - return message_dict - - @staticmethod - def wrap_exception(fn: Callable[[], dict | Generator]) -> dict | Generator: - try: - resp = fn() - except MaasError as e: - raise wrap_error(e) - - return resp - - @staticmethod - def transform_tool_prompt_to_maas_config(tool: PromptMessageTool): - return { - "type": "function", - "function": { - "name": tool.name, - "description": tool.description, - "parameters": tool.parameters, - }, - } diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py deleted file mode 100644 index 91dbe21a61..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py +++ /dev/null @@ -1,156 +0,0 @@ -from core.model_runtime.model_providers.volcengine_maas.legacy.volc_sdk import MaasError - - -class ClientSDKRequestError(MaasError): - pass - - -class SignatureDoesNotMatchError(MaasError): - pass - - -class RequestTimeoutError(MaasError): - pass - - -class ServiceConnectionTimeoutError(MaasError): - pass - - -class MissingAuthenticationHeaderError(MaasError): - pass - - -class AuthenticationHeaderIsInvalidError(MaasError): - pass - - -class InternalServiceError(MaasError): - pass - - -class MissingParameterError(MaasError): - pass - - -class InvalidParameterError(MaasError): - pass - - -class AuthenticationExpireError(MaasError): - pass - - -class EndpointIsInvalidError(MaasError): - pass - - -class EndpointIsNotEnableError(MaasError): - pass - - -class ModelNotSupportStreamModeError(MaasError): - pass - - -class ReqTextExistRiskError(MaasError): - pass - - -class RespTextExistRiskError(MaasError): - pass - - -class EndpointRateLimitExceededError(MaasError): - pass - - -class ServiceConnectionRefusedError(MaasError): - pass - - -class ServiceConnectionClosedError(MaasError): - pass - - -class UnauthorizedUserForEndpointError(MaasError): - pass - - -class InvalidEndpointWithNoURLError(MaasError): - pass - - -class EndpointAccountRpmRateLimitExceededError(MaasError): - pass - - -class EndpointAccountTpmRateLimitExceededError(MaasError): - pass - - -class ServiceResourceWaitQueueFullError(MaasError): - pass - - -class EndpointIsPendingError(MaasError): - pass - - -class ServiceNotOpenError(MaasError): - pass - - -AuthErrors = { - "SignatureDoesNotMatch": SignatureDoesNotMatchError, - "MissingAuthenticationHeader": MissingAuthenticationHeaderError, - "AuthenticationHeaderIsInvalid": AuthenticationHeaderIsInvalidError, - "AuthenticationExpire": AuthenticationExpireError, - "UnauthorizedUserForEndpoint": UnauthorizedUserForEndpointError, -} - -BadRequestErrors = { - "MissingParameter": MissingParameterError, - "InvalidParameter": InvalidParameterError, - "EndpointIsInvalid": EndpointIsInvalidError, - "EndpointIsNotEnable": EndpointIsNotEnableError, - "ModelNotSupportStreamMode": ModelNotSupportStreamModeError, - "ReqTextExistRisk": ReqTextExistRiskError, - "RespTextExistRisk": RespTextExistRiskError, - "InvalidEndpointWithNoURL": InvalidEndpointWithNoURLError, - "ServiceNotOpen": ServiceNotOpenError, -} - -RateLimitErrors = { - "EndpointRateLimitExceeded": EndpointRateLimitExceededError, - "EndpointAccountRpmRateLimitExceeded": EndpointAccountRpmRateLimitExceededError, - "EndpointAccountTpmRateLimitExceeded": EndpointAccountTpmRateLimitExceededError, -} - -ServerUnavailableErrors = { - "InternalServiceError": InternalServiceError, - "EndpointIsPending": EndpointIsPendingError, - "ServiceResourceWaitQueueFull": ServiceResourceWaitQueueFullError, -} - -ConnectionErrors = { - "ClientSDKRequestError": ClientSDKRequestError, - "RequestTimeout": RequestTimeoutError, - "ServiceConnectionTimeout": ServiceConnectionTimeoutError, - "ServiceConnectionRefused": ServiceConnectionRefusedError, - "ServiceConnectionClosed": ServiceConnectionClosedError, -} - -ErrorCodeMap = { - **AuthErrors, - **BadRequestErrors, - **RateLimitErrors, - **ServerUnavailableErrors, - **ConnectionErrors, -} - - -def wrap_error(e: MaasError) -> Exception: - if ErrorCodeMap.get(e.code): - return ErrorCodeMap.get(e.code)(e.code_n, e.code, e.message, e.req_id) - return e diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/__init__.py deleted file mode 100644 index 8b3eb157be..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .common import ChatRole -from .maas import MaasError, MaasService - -__all__ = ["MaasService", "ChatRole", "MaasError"] diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/auth.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/auth.py deleted file mode 100644 index c22bf8e76d..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/auth.py +++ /dev/null @@ -1,159 +0,0 @@ -# coding : utf-8 -import datetime -from itertools import starmap - -import pytz - -from .util import Util - - -class MetaData: - def __init__(self): - self.algorithm = "" - self.credential_scope = "" - self.signed_headers = "" - self.date = "" - self.region = "" - self.service = "" - - def set_date(self, date): - self.date = date - - def set_service(self, service): - self.service = service - - def set_region(self, region): - self.region = region - - def set_algorithm(self, algorithm): - self.algorithm = algorithm - - def set_credential_scope(self, credential_scope): - self.credential_scope = credential_scope - - def set_signed_headers(self, signed_headers): - self.signed_headers = signed_headers - - -class SignResult: - def __init__(self): - self.xdate = "" - self.xCredential = "" - self.xAlgorithm = "" - self.xSignedHeaders = "" - self.xSignedQueries = "" - self.xSignature = "" - self.xContextSha256 = "" - self.xSecurityToken = "" - - self.authorization = "" - - def __str__(self): - return "\n".join(list(starmap("{}:{}".format, self.__dict__.items()))) - - -class Credentials: - def __init__(self, ak, sk, service, region, session_token=""): - self.ak = ak - self.sk = sk - self.service = service - self.region = region - self.session_token = session_token - - def set_ak(self, ak): - self.ak = ak - - def set_sk(self, sk): - self.sk = sk - - def set_session_token(self, session_token): - self.session_token = session_token - - -class Signer: - @staticmethod - def sign(request, credentials): - if request.path == "": - request.path = "/" - if request.method != "GET" and "Content-Type" not in request.headers: - request.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8" - - format_date = Signer.get_current_format_date() - request.headers["X-Date"] = format_date - if credentials.session_token != "": - request.headers["X-Security-Token"] = credentials.session_token - - md = MetaData() - md.set_algorithm("HMAC-SHA256") - md.set_service(credentials.service) - md.set_region(credentials.region) - md.set_date(format_date[:8]) - - hashed_canon_req = Signer.hashed_canonical_request_v4(request, md) - md.set_credential_scope("/".join([md.date, md.region, md.service, "request"])) - - signing_str = "\n".join([md.algorithm, format_date, md.credential_scope, hashed_canon_req]) - signing_key = Signer.get_signing_secret_key_v4(credentials.sk, md.date, md.region, md.service) - sign = Util.to_hex(Util.hmac_sha256(signing_key, signing_str)) - request.headers["Authorization"] = Signer.build_auth_header_v4(sign, md, credentials) - - @staticmethod - def hashed_canonical_request_v4(request, meta): - body_hash = Util.sha256(request.body) - request.headers["X-Content-Sha256"] = body_hash - - signed_headers = {} - for key in request.headers: - if key in {"Content-Type", "Content-Md5", "Host"} or key.startswith("X-"): - signed_headers[key.lower()] = request.headers[key] - - if "host" in signed_headers: - v = signed_headers["host"] - if v.find(":") != -1: - split = v.split(":") - port = split[1] - if str(port) == "80" or str(port) == "443": - signed_headers["host"] = split[0] - - signed_str = "" - for key in sorted(signed_headers.keys()): - signed_str += key + ":" + signed_headers[key] + "\n" - - meta.set_signed_headers(";".join(sorted(signed_headers.keys()))) - - canonical_request = "\n".join( - [ - request.method, - Util.norm_uri(request.path), - Util.norm_query(request.query), - signed_str, - meta.signed_headers, - body_hash, - ] - ) - - return Util.sha256(canonical_request) - - @staticmethod - def get_signing_secret_key_v4(sk, date, region, service): - date = Util.hmac_sha256(bytes(sk, encoding="utf-8"), date) - region = Util.hmac_sha256(date, region) - service = Util.hmac_sha256(region, service) - return Util.hmac_sha256(service, "request") - - @staticmethod - def build_auth_header_v4(signature, meta, credentials): - credential = credentials.ak + "/" + meta.credential_scope - return ( - meta.algorithm - + " Credential=" - + credential - + ", SignedHeaders=" - + meta.signed_headers - + ", Signature=" - + signature - ) - - @staticmethod - def get_current_format_date(): - return datetime.datetime.now(tz=pytz.timezone("UTC")).strftime("%Y%m%dT%H%M%SZ") diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/service.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/service.py deleted file mode 100644 index 33c41f3eb3..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/service.py +++ /dev/null @@ -1,216 +0,0 @@ -import json -from collections import OrderedDict -from urllib.parse import urlencode - -import requests - -from .auth import Signer - -VERSION = "v1.0.137" - - -class Service: - def __init__(self, service_info, api_info): - self.service_info = service_info - self.api_info = api_info - self.session = requests.session() - - def set_ak(self, ak): - self.service_info.credentials.set_ak(ak) - - def set_sk(self, sk): - self.service_info.credentials.set_sk(sk) - - def set_session_token(self, session_token): - self.service_info.credentials.set_session_token(session_token) - - def set_host(self, host): - self.service_info.host = host - - def set_scheme(self, scheme): - self.service_info.scheme = scheme - - def get(self, api, params, doseq=0): - if api not in self.api_info: - raise Exception("no such api") - api_info = self.api_info[api] - - r = self.prepare_request(api_info, params, doseq) - - Signer.sign(r, self.service_info.credentials) - - url = r.build(doseq) - resp = self.session.get( - url, headers=r.headers, timeout=(self.service_info.connection_timeout, self.service_info.socket_timeout) - ) - if resp.status_code == 200: - return resp.text - else: - raise Exception(resp.text) - - def post(self, api, params, form): - if api not in self.api_info: - raise Exception("no such api") - api_info = self.api_info[api] - r = self.prepare_request(api_info, params) - r.headers["Content-Type"] = "application/x-www-form-urlencoded" - r.form = self.merge(api_info.form, form) - r.body = urlencode(r.form, True) - Signer.sign(r, self.service_info.credentials) - - url = r.build() - - resp = self.session.post( - url, - headers=r.headers, - data=r.form, - timeout=(self.service_info.connection_timeout, self.service_info.socket_timeout), - ) - if resp.status_code == 200: - return resp.text - else: - raise Exception(resp.text) - - def json(self, api, params, body): - if api not in self.api_info: - raise Exception("no such api") - api_info = self.api_info[api] - r = self.prepare_request(api_info, params) - r.headers["Content-Type"] = "application/json" - r.body = body - - Signer.sign(r, self.service_info.credentials) - - url = r.build() - resp = self.session.post( - url, - headers=r.headers, - data=r.body, - timeout=(self.service_info.connection_timeout, self.service_info.socket_timeout), - ) - if resp.status_code == 200: - return json.dumps(resp.json()) - else: - raise Exception(resp.text.encode("utf-8")) - - def put(self, url, file_path, headers): - with open(file_path, "rb") as f: - resp = self.session.put(url, headers=headers, data=f) - if resp.status_code == 200: - return True, resp.text.encode("utf-8") - else: - return False, resp.text.encode("utf-8") - - def put_data(self, url, data, headers): - resp = self.session.put(url, headers=headers, data=data) - if resp.status_code == 200: - return True, resp.text.encode("utf-8") - else: - return False, resp.text.encode("utf-8") - - def prepare_request(self, api_info, params, doseq=0): - for key in params: - if type(params[key]) == int or type(params[key]) == float or type(params[key]) == bool: - params[key] = str(params[key]) - elif type(params[key]) == list: - if not doseq: - params[key] = ",".join(params[key]) - - connection_timeout = self.service_info.connection_timeout - socket_timeout = self.service_info.socket_timeout - - r = Request() - r.set_schema(self.service_info.scheme) - r.set_method(api_info.method) - r.set_connection_timeout(connection_timeout) - r.set_socket_timeout(socket_timeout) - - headers = self.merge(api_info.header, self.service_info.header) - headers["Host"] = self.service_info.host - headers["User-Agent"] = "volc-sdk-python/" + VERSION - r.set_headers(headers) - - query = self.merge(api_info.query, params) - r.set_query(query) - - r.set_host(self.service_info.host) - r.set_path(api_info.path) - - return r - - @staticmethod - def merge(param1, param2): - od = OrderedDict() - for key in param1: - od[key] = param1[key] - - for key in param2: - od[key] = param2[key] - - return od - - -class Request: - def __init__(self): - self.schema = "" - self.method = "" - self.host = "" - self.path = "" - self.headers = OrderedDict() - self.query = OrderedDict() - self.body = "" - self.form = {} - self.connection_timeout = 0 - self.socket_timeout = 0 - - def set_schema(self, schema): - self.schema = schema - - def set_method(self, method): - self.method = method - - def set_host(self, host): - self.host = host - - def set_path(self, path): - self.path = path - - def set_headers(self, headers): - self.headers = headers - - def set_query(self, query): - self.query = query - - def set_body(self, body): - self.body = body - - def set_connection_timeout(self, connection_timeout): - self.connection_timeout = connection_timeout - - def set_socket_timeout(self, socket_timeout): - self.socket_timeout = socket_timeout - - def build(self, doseq=0): - return self.schema + "://" + self.host + self.path + "?" + urlencode(self.query, doseq) - - -class ServiceInfo: - def __init__(self, host, header, credentials, connection_timeout, socket_timeout, scheme="http"): - self.host = host - self.header = header - self.credentials = credentials - self.connection_timeout = connection_timeout - self.socket_timeout = socket_timeout - self.scheme = scheme - - -class ApiInfo: - def __init__(self, method, path, query, form, header): - self.method = method - self.path = path - self.query = query - self.form = form - self.header = header - - def __str__(self): - return "method: " + self.method + ", path: " + self.path diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/util.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/util.py deleted file mode 100644 index 178d63714e..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/base/util.py +++ /dev/null @@ -1,44 +0,0 @@ -import hashlib -import hmac -import operator -from functools import reduce -from urllib.parse import quote - - -class Util: - @staticmethod - def norm_uri(path): - return quote(path).replace("%2F", "/").replace("+", "%20") - - @staticmethod - def norm_query(params): - query = "" - for key in sorted(params.keys()): - if type(params[key]) == list: - for k in params[key]: - query = query + quote(key, safe="-_.~") + "=" + quote(k, safe="-_.~") + "&" - else: - query = query + quote(key, safe="-_.~") + "=" + quote(params[key], safe="-_.~") + "&" - query = query[:-1] - return query.replace("+", "%20") - - @staticmethod - def hmac_sha256(key, content): - return hmac.new(key, bytes(content, encoding="utf-8"), hashlib.sha256).digest() - - @staticmethod - def sha256(content): - if isinstance(content, str) is True: - return hashlib.sha256(content.encode("utf-8")).hexdigest() - else: - return hashlib.sha256(content).hexdigest() - - @staticmethod - def to_hex(content): - lst = [] - for ch in content: - hv = hex(ch).replace("0x", "") - if len(hv) == 1: - hv = "0" + hv - lst.append(hv) - return reduce(operator.add, lst) diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py deleted file mode 100644 index 3825fd6574..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py +++ /dev/null @@ -1,77 +0,0 @@ -import json -import random -from datetime import datetime - - -class ChatRole: - USER = "user" - ASSISTANT = "assistant" - SYSTEM = "system" - FUNCTION = "function" - - -class _Dict(dict): - __setattr__ = dict.__setitem__ - __getattr__ = dict.__getitem__ - - def __missing__(self, key): - return None - - -def dict_to_object(dict_obj): - # 支持嵌套类型 - if isinstance(dict_obj, list): - insts = [] - for i in dict_obj: - insts.append(dict_to_object(i)) - return insts - - if isinstance(dict_obj, dict): - inst = _Dict() - for k, v in dict_obj.items(): - inst[k] = dict_to_object(v) - return inst - - return dict_obj - - -def json_to_object(json_str, req_id=None): - obj = dict_to_object(json.loads(json_str)) - if obj and isinstance(obj, dict) and req_id: - obj["req_id"] = req_id - return obj - - -def gen_req_id(): - return datetime.now().strftime("%Y%m%d%H%M%S") + format(random.randint(0, 2**64 - 1), "020X") - - -class SSEDecoder: - def __init__(self, source): - self.source = source - - def _read(self): - data = b"" - for chunk in self.source: - for line in chunk.splitlines(True): - data += line - if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): - yield data - data = b"" - if data: - yield data - - def next(self): - for chunk in self._read(): - for line in chunk.splitlines(): - # skip comment - if line.startswith(b":"): - continue - - if b":" in line: - field, value = line.split(b":", 1) - else: - field, value = line, b"" - - if field == b"data" and len(value) > 0: - yield value diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/maas.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/maas.py deleted file mode 100644 index a3836685f1..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/maas.py +++ /dev/null @@ -1,198 +0,0 @@ -import copy -import json -from collections.abc import Iterator - -from .base.auth import Credentials, Signer -from .base.service import ApiInfo, Service, ServiceInfo -from .common import SSEDecoder, dict_to_object, gen_req_id, json_to_object - - -class MaasService(Service): - def __init__(self, host, region, connection_timeout=60, socket_timeout=60): - service_info = self.get_service_info(host, region, connection_timeout, socket_timeout) - self._apikey = None - api_info = self.get_api_info() - super().__init__(service_info, api_info) - - def set_apikey(self, apikey): - self._apikey = apikey - - @staticmethod - def get_service_info(host, region, connection_timeout, socket_timeout): - service_info = ServiceInfo( - host, - {"Accept": "application/json"}, - Credentials("", "", "ml_maas", region), - connection_timeout, - socket_timeout, - "https", - ) - return service_info - - @staticmethod - def get_api_info(): - api_info = { - "chat": ApiInfo("POST", "/api/v2/endpoint/{endpoint_id}/chat", {}, {}, {}), - "embeddings": ApiInfo("POST", "/api/v2/endpoint/{endpoint_id}/embeddings", {}, {}, {}), - } - return api_info - - def chat(self, endpoint_id, req): - req["stream"] = False - return self._request(endpoint_id, "chat", req) - - def stream_chat(self, endpoint_id, req): - req_id = gen_req_id() - self._validate("chat", req_id) - apikey = self._apikey - - try: - req["stream"] = True - res = self._call(endpoint_id, "chat", req_id, {}, json.dumps(req).encode("utf-8"), apikey, stream=True) - - decoder = SSEDecoder(res) - - def iter_fn(): - for data in decoder.next(): - if data == b"[DONE]": - return - - try: - res = json_to_object(str(data, encoding="utf-8"), req_id=req_id) - except Exception: - raise - - if res.error is not None and res.error.code_n != 0: - raise MaasError( - res.error.code_n, - res.error.code, - res.error.message, - req_id, - ) - yield res - - return iter_fn() - except MaasError: - raise - except Exception as e: - raise new_client_sdk_request_error(str(e)) - - def embeddings(self, endpoint_id, req): - return self._request(endpoint_id, "embeddings", req) - - def _request(self, endpoint_id, api, req, params={}): - req_id = gen_req_id() - - self._validate(api, req_id) - - apikey = self._apikey - - try: - res = self._call(endpoint_id, api, req_id, params, json.dumps(req).encode("utf-8"), apikey) - resp = dict_to_object(res.json()) - if resp and isinstance(resp, dict): - resp["req_id"] = req_id - return resp - - except MaasError as e: - raise e - except Exception as e: - raise new_client_sdk_request_error(str(e), req_id) - - def _validate(self, api, req_id): - credentials_exist = ( - self.service_info.credentials is not None - and self.service_info.credentials.sk is not None - and self.service_info.credentials.ak is not None - ) - - if not self._apikey and not credentials_exist: - raise new_client_sdk_request_error("no valid credential", req_id) - - if api not in self.api_info: - raise new_client_sdk_request_error("no such api", req_id) - - def _call(self, endpoint_id, api, req_id, params, body, apikey=None, stream=False): - api_info = copy.deepcopy(self.api_info[api]) - api_info.path = api_info.path.format(endpoint_id=endpoint_id) - - r = self.prepare_request(api_info, params) - r.headers["x-tt-logid"] = req_id - r.headers["Content-Type"] = "application/json" - r.body = body - - if apikey is None: - Signer.sign(r, self.service_info.credentials) - elif apikey is not None: - r.headers["Authorization"] = "Bearer " + apikey - - url = r.build() - res = self.session.post( - url, - headers=r.headers, - data=r.body, - timeout=( - self.service_info.connection_timeout, - self.service_info.socket_timeout, - ), - stream=stream, - ) - - if res.status_code != 200: - raw = res.text.encode() - res.close() - try: - resp = json_to_object(str(raw, encoding="utf-8"), req_id=req_id) - except Exception: - raise new_client_sdk_request_error(raw, req_id) - - if resp.error: - raise MaasError(resp.error.code_n, resp.error.code, resp.error.message, req_id) - else: - raise new_client_sdk_request_error(resp, req_id) - - return res - - -class MaasError(Exception): - def __init__(self, code_n, code, message, req_id): - self.code_n = code_n - self.code = code - self.message = message - self.req_id = req_id - - def __str__(self): - return ( - "Detailed exception information is listed below.\n" - + "req_id: {}\n" - + "code_n: {}\n" - + "code: {}\n" - + "message: {}" - ).format(self.req_id, self.code_n, self.code, self.message) - - -def new_client_sdk_request_error(raw, req_id=""): - return MaasError(1709701, "ClientSDKRequestError", "MaaS SDK request error: {}".format(raw), req_id) - - -class BinaryResponseContent: - def __init__(self, response, request_id) -> None: - self.response = response - self.request_id = request_id - - def stream_to_file(self, file: str) -> None: - is_first = True - error_bytes = b"" - with open(file, mode="wb") as f: - for data in self.response: - if len(error_bytes) > 0 or (is_first and '"error":' in str(data)): - error_bytes += data - else: - f.write(data) - - if len(error_bytes) > 0: - resp = json_to_object(str(error_bytes, encoding="utf-8"), req_id=self.request_id) - raise MaasError(resp.error.code_n, resp.error.code, resp.error.message, self.request_id) - - def iter_bytes(self) -> Iterator[bytes]: - yield from self.response diff --git a/api/core/model_runtime/model_providers/volcengine_maas/llm/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py b/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py deleted file mode 100644 index dec6c9d789..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py +++ /dev/null @@ -1,388 +0,0 @@ -import logging -from collections.abc import Generator - -from volcenginesdkarkruntime.types.chat import ChatCompletion, ChatCompletionChunk - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - FetchFrom, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.volcengine_maas.client import ArkClientV3 -from core.model_runtime.model_providers.volcengine_maas.legacy.client import MaaSClient -from core.model_runtime.model_providers.volcengine_maas.legacy.errors import ( - AuthErrors, - BadRequestErrors, - ConnectionErrors, - MaasError, - RateLimitErrors, - ServerUnavailableErrors, -) -from core.model_runtime.model_providers.volcengine_maas.llm.models import ( - get_model_config, - get_v2_req_params, - get_v3_req_params, -) - -logger = logging.getLogger(__name__) - - -class VolcengineMaaSLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - if ArkClientV3.is_legacy(credentials): - return self._generate_v2(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - return self._generate_v3(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate credentials - """ - if ArkClientV3.is_legacy(credentials): - return self._validate_credentials_v2(credentials) - return self._validate_credentials_v3(credentials) - - @staticmethod - def _validate_credentials_v2(credentials: dict) -> None: - client = MaaSClient.from_credential(credentials) - try: - client.chat( - { - "max_new_tokens": 16, - "temperature": 0.7, - "top_p": 0.9, - "top_k": 15, - }, - [UserPromptMessage(content="ping\nAnswer: ")], - ) - except MaasError as e: - raise CredentialsValidateFailedError(e.message) - - @staticmethod - def _validate_credentials_v3(credentials: dict) -> None: - client = ArkClientV3.from_credentials(credentials) - try: - client.chat( - max_tokens=16, - temperature=0.7, - top_p=0.9, - messages=[UserPromptMessage(content="ping\nAnswer: ")], - ) - except Exception as e: - raise CredentialsValidateFailedError(e) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - if ArkClientV3.is_legacy(credentials): - return self._get_num_tokens_v2(prompt_messages) - return self._get_num_tokens_v3(prompt_messages) - - def _get_num_tokens_v2(self, messages: list[PromptMessage]) -> int: - if len(messages) == 0: - return 0 - num_tokens = 0 - messages_dict = [MaaSClient.convert_prompt_message_to_maas_message(m) for m in messages] - for message in messages_dict: - for key, value in message.items(): - num_tokens += self._get_num_tokens_by_gpt2(str(key)) - num_tokens += self._get_num_tokens_by_gpt2(str(value)) - - return num_tokens - - def _get_num_tokens_v3(self, messages: list[PromptMessage]) -> int: - if len(messages) == 0: - return 0 - num_tokens = 0 - messages_dict = [ArkClientV3.convert_prompt_message(m) for m in messages] - for message in messages_dict: - for key, value in message.items(): - num_tokens += self._get_num_tokens_by_gpt2(str(key)) - num_tokens += self._get_num_tokens_by_gpt2(str(value)) - - return num_tokens - - def _generate_v2( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - client = MaaSClient.from_credential(credentials) - req_params = get_v2_req_params(credentials, model_parameters, stop) - extra_model_kwargs = {} - if tools: - extra_model_kwargs["tools"] = [MaaSClient.transform_tool_prompt_to_maas_config(tool) for tool in tools] - resp = MaaSClient.wrap_exception(lambda: client.chat(req_params, prompt_messages, stream, **extra_model_kwargs)) - - def _handle_stream_chat_response() -> Generator: - for index, r in enumerate(resp): - choices = r["choices"] - if not choices: - continue - choice = choices[0] - message = choice["message"] - usage = None - if r.get("usage"): - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=r["usage"]["prompt_tokens"], - completion_tokens=r["usage"]["completion_tokens"], - ) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=index, - message=AssistantPromptMessage(content=message["content"] or "", tool_calls=[]), - usage=usage, - finish_reason=choice.get("finish_reason"), - ), - ) - - def _handle_chat_response() -> LLMResult: - choices = resp["choices"] - if not choices: - raise ValueError("No choices found") - - choice = choices[0] - message = choice["message"] - - # parse tool calls - tool_calls = [] - if message["tool_calls"]: - for call in message["tool_calls"]: - tool_call = AssistantPromptMessage.ToolCall( - id=call["function"]["name"], - type=call["type"], - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=call["function"]["name"], arguments=call["function"]["arguments"] - ), - ) - tool_calls.append(tool_call) - - usage = resp["usage"] - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage( - content=message["content"] or "", - tool_calls=tool_calls, - ), - usage=self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=usage["prompt_tokens"], - completion_tokens=usage["completion_tokens"], - ), - ) - - if not stream: - return _handle_chat_response() - return _handle_stream_chat_response() - - def _generate_v3( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - client = ArkClientV3.from_credentials(credentials) - req_params = get_v3_req_params(credentials, model_parameters, stop) - if tools: - req_params["tools"] = tools - - def _handle_stream_chat_response(chunks: Generator[ChatCompletionChunk]) -> Generator: - for chunk in chunks: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage( - content=chunk.choices[0].delta.content if chunk.choices else "", tool_calls=[] - ), - usage=self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=chunk.usage.prompt_tokens, - completion_tokens=chunk.usage.completion_tokens, - ) - if chunk.usage - else None, - finish_reason=chunk.choices[0].finish_reason if chunk.choices else None, - ), - ) - - def _handle_chat_response(resp: ChatCompletion) -> LLMResult: - choice = resp.choices[0] - message = choice.message - # parse tool calls - tool_calls = [] - if message.tool_calls: - for call in message.tool_calls: - tool_call = AssistantPromptMessage.ToolCall( - id=call.id, - type=call.type, - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=call.function.name, arguments=call.function.arguments - ), - ) - tool_calls.append(tool_call) - - usage = resp.usage - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage( - content=message.content or "", - tool_calls=tool_calls, - ), - usage=self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=usage.prompt_tokens, - completion_tokens=usage.completion_tokens, - ), - ) - - if not stream: - resp = client.chat(prompt_messages, **req_params) - return _handle_chat_response(resp) - - chunks = client.stream_chat(prompt_messages, **req_params) - return _handle_stream_chat_response(chunks) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - model_config = get_model_config(credentials) - - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="top_k", type=ParameterType.INT, min=1, default=1, label=I18nObject(zh_Hans="Top K", en_US="Top K") - ), - ParameterRule( - name="presence_penalty", - type=ParameterType.FLOAT, - use_template="presence_penalty", - label=I18nObject( - en_US="Presence Penalty", - zh_Hans="存在惩罚", - ), - min=-2.0, - max=2.0, - ), - ParameterRule( - name="frequency_penalty", - type=ParameterType.FLOAT, - use_template="frequency_penalty", - label=I18nObject( - en_US="Frequency Penalty", - zh_Hans="频率惩罚", - ), - min=-2.0, - max=2.0, - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - max=model_config.properties.max_tokens, - default=512, - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ] - - model_properties = {} - model_properties[ModelPropertyKey.CONTEXT_SIZE] = model_config.properties.context_size - model_properties[ModelPropertyKey.MODE] = model_config.properties.mode.value - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - model_properties=model_properties, - parameter_rules=rules, - features=model_config.features, - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: ConnectionErrors.values(), - InvokeServerUnavailableError: ServerUnavailableErrors.values(), - InvokeRateLimitError: RateLimitErrors.values(), - InvokeAuthorizationError: AuthErrors.values(), - InvokeBadRequestError: BadRequestErrors.values(), - } diff --git a/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py b/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py deleted file mode 100644 index d8be14b024..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py +++ /dev/null @@ -1,142 +0,0 @@ -from pydantic import BaseModel - -from core.model_runtime.entities.llm_entities import LLMMode -from core.model_runtime.entities.model_entities import ModelFeature - - -class ModelProperties(BaseModel): - context_size: int - max_tokens: int - mode: LLMMode - - -class ModelConfig(BaseModel): - properties: ModelProperties - features: list[ModelFeature] - - -configs: dict[str, ModelConfig] = { - "Doubao-pro-4k": ModelConfig( - properties=ModelProperties(context_size=4096, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Doubao-lite-4k": ModelConfig( - properties=ModelProperties(context_size=4096, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Doubao-pro-32k": ModelConfig( - properties=ModelProperties(context_size=32768, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Doubao-lite-32k": ModelConfig( - properties=ModelProperties(context_size=32768, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Doubao-pro-128k": ModelConfig( - properties=ModelProperties(context_size=131072, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Doubao-lite-128k": ModelConfig( - properties=ModelProperties(context_size=131072, max_tokens=4096, mode=LLMMode.CHAT), features=[] - ), - "Skylark2-pro-4k": ModelConfig( - properties=ModelProperties(context_size=4096, max_tokens=4096, mode=LLMMode.CHAT), features=[] - ), - "Llama3-8B": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=8192, mode=LLMMode.CHAT), features=[] - ), - "Llama3-70B": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=8192, mode=LLMMode.CHAT), features=[] - ), - "Moonshot-v1-8k": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Moonshot-v1-32k": ModelConfig( - properties=ModelProperties(context_size=32768, max_tokens=16384, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Moonshot-v1-128k": ModelConfig( - properties=ModelProperties(context_size=131072, max_tokens=65536, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "GLM3-130B": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "GLM3-130B-Fin": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=4096, mode=LLMMode.CHAT), - features=[ModelFeature.TOOL_CALL], - ), - "Mistral-7B": ModelConfig( - properties=ModelProperties(context_size=8192, max_tokens=2048, mode=LLMMode.CHAT), features=[] - ), -} - - -def get_model_config(credentials: dict) -> ModelConfig: - base_model = credentials.get("base_model_name", "") - model_configs = configs.get(base_model) - if not model_configs: - return ModelConfig( - properties=ModelProperties( - context_size=int(credentials.get("context_size", 0)), - max_tokens=int(credentials.get("max_tokens", 0)), - mode=LLMMode.value_of(credentials.get("mode", "chat")), - ), - features=[], - ) - return model_configs - - -def get_v2_req_params(credentials: dict, model_parameters: dict, stop: list[str] | None = None): - req_params = {} - # predefined properties - model_configs = get_model_config(credentials) - if model_configs: - req_params["max_prompt_tokens"] = model_configs.properties.context_size - req_params["max_new_tokens"] = model_configs.properties.max_tokens - - # model parameters - if model_parameters.get("max_tokens"): - req_params["max_new_tokens"] = model_parameters.get("max_tokens") - if model_parameters.get("temperature"): - req_params["temperature"] = model_parameters.get("temperature") - if model_parameters.get("top_p"): - req_params["top_p"] = model_parameters.get("top_p") - if model_parameters.get("top_k"): - req_params["top_k"] = model_parameters.get("top_k") - if model_parameters.get("presence_penalty"): - req_params["presence_penalty"] = model_parameters.get("presence_penalty") - if model_parameters.get("frequency_penalty"): - req_params["frequency_penalty"] = model_parameters.get("frequency_penalty") - - if stop: - req_params["stop"] = stop - - return req_params - - -def get_v3_req_params(credentials: dict, model_parameters: dict, stop: list[str] | None = None): - req_params = {} - # predefined properties - model_configs = get_model_config(credentials) - if model_configs: - req_params["max_tokens"] = model_configs.properties.max_tokens - - # model parameters - if model_parameters.get("max_tokens"): - req_params["max_tokens"] = model_parameters.get("max_tokens") - if model_parameters.get("temperature"): - req_params["temperature"] = model_parameters.get("temperature") - if model_parameters.get("top_p"): - req_params["top_p"] = model_parameters.get("top_p") - if model_parameters.get("presence_penalty"): - req_params["presence_penalty"] = model_parameters.get("presence_penalty") - if model_parameters.get("frequency_penalty"): - req_params["frequency_penalty"] = model_parameters.get("frequency_penalty") - - if stop: - req_params["stop"] = stop - - return req_params diff --git a/api/core/model_runtime/model_providers/volcengine_maas/text_embedding/__init__.py b/api/core/model_runtime/model_providers/volcengine_maas/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/volcengine_maas/text_embedding/models.py b/api/core/model_runtime/model_providers/volcengine_maas/text_embedding/models.py deleted file mode 100644 index ce4f0c3ab1..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/text_embedding/models.py +++ /dev/null @@ -1,28 +0,0 @@ -from pydantic import BaseModel - - -class ModelProperties(BaseModel): - context_size: int - max_chunks: int - - -class ModelConfig(BaseModel): - properties: ModelProperties - - -ModelConfigs = { - "Doubao-embedding": ModelConfig(properties=ModelProperties(context_size=4096, max_chunks=32)), -} - - -def get_model_config(credentials: dict) -> ModelConfig: - base_model = credentials.get("base_model_name", "") - model_configs = ModelConfigs.get(base_model) - if not model_configs: - return ModelConfig( - properties=ModelProperties( - context_size=int(credentials.get("context_size", 0)), - max_chunks=int(credentials.get("max_chunks", 0)), - ) - ) - return model_configs diff --git a/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.py b/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.py deleted file mode 100644 index 10f9be2d08..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class VolcengineMaaSProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.yaml b/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.yaml deleted file mode 100644 index 13e00da76f..0000000000 --- a/api/core/model_runtime/model_providers/volcengine_maas/volcengine_maas.yaml +++ /dev/null @@ -1,266 +0,0 @@ -provider: volcengine_maas -label: - en_US: Volcengine -description: - en_US: Volcengine Ark models. - zh_Hans: 火山方舟提供的模型,例如 Doubao-pro-4k、Doubao-pro-32k 和 Doubao-pro-128k。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg - zh_Hans: icon_l_zh.svg -background: "#F9FAFB" -help: - title: - en_US: Get your Access Key and Secret Access Key from Volcengine Console - zh_Hans: 从火山引擎控制台获取您的 Access Key 和 Secret Access Key - url: - en_US: https://console.volcengine.com/iam/keymanage/ -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your Model Name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: auth_method - required: true - label: - en_US: Authentication Method - zh_Hans: 鉴权方式 - type: select - default: aksk - options: - - label: - en_US: API Key - value: api_key - - label: - en_US: Access Key / Secret Access Key - value: aksk - placeholder: - en_US: Enter your Authentication Method - zh_Hans: 选择鉴权方式 - - variable: volc_access_key_id - required: true - show_on: - - variable: auth_method - value: aksk - label: - en_US: Access Key - zh_Hans: Access Key - type: secret-input - placeholder: - en_US: Enter your Access Key - zh_Hans: 输入您的 Access Key - - variable: volc_secret_access_key - required: true - show_on: - - variable: auth_method - value: aksk - label: - en_US: Secret Access Key - zh_Hans: Secret Access Key - type: secret-input - placeholder: - en_US: Enter your Secret Access Key - zh_Hans: 输入您的 Secret Access Key - - variable: volc_api_key - required: true - show_on: - - variable: auth_method - value: api_key - label: - en_US: API Key - type: secret-input - placeholder: - en_US: Enter your API Key - zh_Hans: 输入您的 API Key - - variable: volc_region - required: true - label: - en_US: Volcengine Region - zh_Hans: 火山引擎地域 - type: text-input - default: cn-beijing - placeholder: - en_US: Enter Volcengine Region - zh_Hans: 输入火山引擎地域 - - variable: api_endpoint_host - required: true - label: - en_US: API Endpoint Host - zh_Hans: API Endpoint Host - type: text-input - default: https://ark.cn-beijing.volces.com/api/v3 - placeholder: - en_US: Enter your API Endpoint Host - zh_Hans: 输入 API Endpoint Host - - variable: endpoint_id - required: true - label: - en_US: Endpoint ID - zh_Hans: Endpoint ID - type: text-input - placeholder: - en_US: Enter your Endpoint ID - zh_Hans: 输入您的 Endpoint ID - - variable: base_model_name - label: - en_US: Base Model - zh_Hans: 基础模型 - type: select - required: true - options: - - label: - en_US: Doubao-pro-4k - value: Doubao-pro-4k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-4k - value: Doubao-lite-4k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-32k - value: Doubao-pro-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-32k - value: Doubao-lite-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-128k - value: Doubao-pro-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-128k - value: Doubao-lite-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Llama3-8B - value: Llama3-8B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Llama3-70B - value: Llama3-70B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-8k - value: Moonshot-v1-8k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-32k - value: Moonshot-v1-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-128k - value: Moonshot-v1-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: GLM3-130B - value: GLM3-130B - show_on: - - variable: __model_type - value: llm - - label: - en_US: GLM3-130B-Fin - value: GLM3-130B-Fin - show_on: - - variable: __model_type - value: llm - - label: - en_US: Mistral-7B - value: Mistral-7B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-embedding - value: Doubao-embedding - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: Custom - zh_Hans: 自定义 - value: Custom - - variable: mode - required: true - show_on: - - variable: __model_type - value: llm - - variable: base_model_name - value: Custom - label: - zh_Hans: 模型类型 - en_US: Completion Mode - type: select - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select Completion Mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - required: true - show_on: - - variable: base_model_name - value: Custom - label: - zh_Hans: 模型上下文长度 - en_US: Model Context Size - type: text-input - default: "4096" - placeholder: - zh_Hans: 输入您的模型上下文长度 - en_US: Enter your Model Context Size - - variable: max_tokens - required: true - show_on: - - variable: __model_type - value: llm - - variable: base_model_name - value: Custom - label: - zh_Hans: 最大 token 上限 - en_US: Upper Bound for Max Tokens - default: "4096" - type: text-input - placeholder: - zh_Hans: 输入您的模型最大 token 上限 - en_US: Enter your model Upper Bound for Max Tokens diff --git a/api/core/model_runtime/model_providers/wenxin/__init__.py b/api/core/model_runtime/model_providers/wenxin/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/wenxin/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/wenxin/_assets/icon_l_en.png deleted file mode 100644 index fb50487cceaa78bd265acc25d84cf797ff402b04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6615 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 V9l%)Y#!LVJ002ovPDHLkV1ij9WcdI9 diff --git a/api/core/model_runtime/model_providers/wenxin/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/wenxin/_assets/icon_s_en.png deleted file mode 100644 index 923919958a156a0a470ec0bcd228226b70500217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3350 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 diff --git a/api/core/model_runtime/model_providers/wenxin/_common.py b/api/core/model_runtime/model_providers/wenxin/_common.py deleted file mode 100644 index d72d1bd83a..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/_common.py +++ /dev/null @@ -1,194 +0,0 @@ -from datetime import datetime, timedelta -from threading import Lock - -from requests import post - -from core.model_runtime.model_providers.wenxin.wenxin_errors import ( - BadRequestError, - InternalServerError, - InvalidAPIKeyError, - InvalidAuthenticationError, - RateLimitReachedError, -) - -baidu_access_tokens: dict[str, "BaiduAccessToken"] = {} -baidu_access_tokens_lock = Lock() - - -class BaiduAccessToken: - api_key: str - access_token: str - expires: datetime - - def __init__(self, api_key: str) -> None: - self.api_key = api_key - self.access_token = "" - self.expires = datetime.now() + timedelta(days=3) - - @staticmethod - def _get_access_token(api_key: str, secret_key: str) -> str: - """ - request access token from Baidu - """ - try: - response = post( - url=f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={api_key}&client_secret={secret_key}", - headers={"Content-Type": "application/json", "Accept": "application/json"}, - ) - except Exception as e: - raise InvalidAuthenticationError(f"Failed to get access token from Baidu: {e}") - - resp = response.json() - if "error" in resp: - if resp["error"] == "invalid_client": - raise InvalidAPIKeyError(f'Invalid API key or secret key: {resp["error_description"]}') - elif resp["error"] == "unknown_error": - raise InternalServerError(f'Internal server error: {resp["error_description"]}') - elif resp["error"] == "invalid_request": - raise BadRequestError(f'Bad request: {resp["error_description"]}') - elif resp["error"] == "rate_limit_exceeded": - raise RateLimitReachedError(f'Rate limit reached: {resp["error_description"]}') - else: - raise Exception(f'Unknown error: {resp["error_description"]}') - - return resp["access_token"] - - @staticmethod - def get_access_token(api_key: str, secret_key: str) -> "BaiduAccessToken": - """ - LLM from Baidu requires access token to invoke the API. - however, we have api_key and secret_key, and access token is valid for 30 days. - so we can cache the access token for 3 days. (avoid memory leak) - - it may be more efficient to use a ticker to refresh access token, but it will cause - more complexity, so we just refresh access tokens when get_access_token is called. - """ - - # loop up cache, remove expired access token - baidu_access_tokens_lock.acquire() - now = datetime.now() - for key in list(baidu_access_tokens.keys()): - token = baidu_access_tokens[key] - if token.expires < now: - baidu_access_tokens.pop(key) - - if api_key not in baidu_access_tokens: - # if access token not in cache, request it - token = BaiduAccessToken(api_key) - baidu_access_tokens[api_key] = token - try: - # try to get access token - token_str = BaiduAccessToken._get_access_token(api_key, secret_key) - finally: - # release it to enhance performance - # btw, _get_access_token will raise exception if failed, release lock here to avoid deadlock - baidu_access_tokens_lock.release() - token.access_token = token_str - token.expires = now + timedelta(days=3) - return token - else: - # if access token in cache, return it - token = baidu_access_tokens[api_key] - baidu_access_tokens_lock.release() - return token - - -class _CommonWenxin: - api_bases = { - "ernie-bot": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-4k-0205", - "ernie-bot-4": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro", - "ernie-bot-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions", - "ernie-bot-turbo": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant", - "ernie-3.5-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions", - "ernie-3.5-8k-0205": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-0205", - "ernie-3.5-8k-1222": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-1222", - "ernie-3.5-4k-0205": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-4k-0205", - "ernie-3.5-128k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-128k", - "ernie-4.0-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro", - "ernie-4.0-8k-latest": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro", - "ernie-speed-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_speed", - "ernie-speed-128k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-speed-128k", - "ernie-speed-appbuilder": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ai_apaas", - "ernie-lite-8k-0922": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant", - "ernie-lite-8k-0308": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-lite-8k", - "ernie-character-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-char-8k", - "ernie-character-8k-0321": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-char-8k", - "ernie-4.0-turbo-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-4.0-turbo-8k", - "ernie-4.0-turbo-8k-preview": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-4.0-turbo-8k-preview", - "yi_34b_chat": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/yi_34b_chat", - "embedding-v1": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1", - "bge-large-en": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/bge_large_en", - "bge-large-zh": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/bge_large_zh", - "tao-8k": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/tao_8k", - } - - function_calling_supports = [ - "ernie-bot", - "ernie-bot-8k", - "ernie-3.5-8k", - "ernie-3.5-8k-0205", - "ernie-3.5-8k-1222", - "ernie-3.5-4k-0205", - "ernie-3.5-128k", - "ernie-4.0-8k", - "ernie-4.0-turbo-8k", - "ernie-4.0-turbo-8k-preview", - "yi_34b_chat", - ] - - api_key: str = "" - secret_key: str = "" - - def __init__(self, api_key: str, secret_key: str): - self.api_key = api_key - self.secret_key = secret_key - - @staticmethod - def _to_credential_kwargs(credentials: dict) -> dict: - credentials_kwargs = {"api_key": credentials["api_key"], "secret_key": credentials["secret_key"]} - return credentials_kwargs - - def _handle_error(self, code: int, msg: str): - error_map = { - 1: InternalServerError, - 2: InternalServerError, - 3: BadRequestError, - 4: RateLimitReachedError, - 6: InvalidAuthenticationError, - 13: InvalidAPIKeyError, - 14: InvalidAPIKeyError, - 15: InvalidAPIKeyError, - 17: RateLimitReachedError, - 18: RateLimitReachedError, - 19: RateLimitReachedError, - 100: InvalidAPIKeyError, - 111: InvalidAPIKeyError, - 200: InternalServerError, - 336000: InternalServerError, - 336001: BadRequestError, - 336002: BadRequestError, - 336003: BadRequestError, - 336004: InvalidAuthenticationError, - 336005: InvalidAPIKeyError, - 336006: BadRequestError, - 336007: BadRequestError, - 336008: BadRequestError, - 336100: InternalServerError, - 336101: BadRequestError, - 336102: BadRequestError, - 336103: BadRequestError, - 336104: BadRequestError, - 336105: BadRequestError, - 336200: InternalServerError, - 336303: BadRequestError, - 337006: BadRequestError, - } - - if code in error_map: - raise error_map[code](msg) - else: - raise InternalServerError(f"Unknown error: {msg}") - - def _get_access_token(self) -> str: - token = BaiduAccessToken.get_access_token(self.api_key, self.secret_key) - return token.access_token diff --git a/api/core/model_runtime/model_providers/wenxin/llm/__init__.py b/api/core/model_runtime/model_providers/wenxin/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-128k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-128k.yaml deleted file mode 100644 index b1b1ba1f69..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-128k.yaml +++ /dev/null @@ -1,37 +0,0 @@ -model: ernie-3.5-128k -label: - en_US: Ernie-3.5-128K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 2 - max: 4096 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-4k-0205.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-4k-0205.yaml deleted file mode 100644 index 1e8cf96440..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-4k-0205.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: ernie-3.5-4k-0205 -label: - en_US: Ernie-3.5-4k-0205 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-0205.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-0205.yaml deleted file mode 100644 index b308abcb32..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-0205.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: ernie-3.5-8k-0205 -label: - en_US: Ernie-3.5-8K-0205 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-1222.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-1222.yaml deleted file mode 100644 index c43588cfe1..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k-1222.yaml +++ /dev/null @@ -1,38 +0,0 @@ -model: ernie-3.5-8k-1222 -label: - en_US: Ernie-3.5-8K-1222 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k.yaml deleted file mode 100644 index 145844a4ff..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-3.5-8k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: ernie-3.5-8k -label: - en_US: Ernie-3.5-8K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k-latest.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k-latest.yaml deleted file mode 100644 index d23ae0dc48..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k-latest.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: ernie-4.0-8k-latest -label: - en_US: Ernie-4.0-8K-Latest -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k.yaml deleted file mode 100644 index 9ebb5c8c4f..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-8k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: ernie-4.0-8k -label: - en_US: Ernie-4.0-8K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k-preview.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k-preview.yaml deleted file mode 100644 index 16df540220..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k-preview.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: ernie-4.0-turbo-8k-preview -label: - en_US: Ernie-4.0-turbo-8k-preview -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k.yaml deleted file mode 100644 index 2887a510d0..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-4.0-turbo-8k.yaml +++ /dev/null @@ -1,40 +0,0 @@ -model: ernie-4.0-turbo-8k -label: - en_US: Ernie-4.0-turbo-8K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-4.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-4.yaml deleted file mode 100644 index f352787aec..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-4.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: ernie-bot-4 -label: - en_US: Ernie Bot 4 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4800 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 256 - min: 1 - max: 4800 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-8k.yaml deleted file mode 100644 index fa4b7dd800..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-8k.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: ernie-bot-8k -label: - en_US: Ernie Bot 8k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 1024 - min: 1 - max: 8000 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-turbo.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-turbo.yaml deleted file mode 100644 index c94aa2db88..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot-turbo.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: ernie-bot-turbo -label: - en_US: Ernie Bot Turbo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 11200 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 1024 - min: 1 - max: 11200 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: response_format - use_template: response_format -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot.yaml deleted file mode 100644 index 13985b7483..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-bot.yaml +++ /dev/null @@ -1,39 +0,0 @@ -model: ernie-bot -label: - en_US: Ernie Bot -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4800 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.8 - - name: top_p - use_template: top_p - - name: max_tokens - use_template: max_tokens - required: true - default: 256 - min: 1 - max: 4800 - - name: presence_penalty - use_template: presence_penalty - - name: frequency_penalty - use_template: frequency_penalty - - name: disable_search - label: - zh_Hans: 禁用搜索 - en_US: Disable Search - type: boolean - help: - zh_Hans: 禁用模型自行进行外部搜索。 - en_US: Disable the model to perform external search. - required: false - - name: response_format - use_template: response_format -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k-0321.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k-0321.yaml deleted file mode 100644 index 74451ff9e3..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k-0321.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: ernie-character-8k-0321 -label: - en_US: ERNIE-Character-8K-0321 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 1024 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k.yaml deleted file mode 100644 index 4b11b3e895..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-character-8k.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: ernie-character-8k-0321 -label: - en_US: ERNIE-Character-8K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 1024 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0308.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0308.yaml deleted file mode 100644 index 97ecb03f87..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0308.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: ernie-lite-8k-0308 -label: - en_US: ERNIE-Lite-8K-0308 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0922.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0922.yaml deleted file mode 100644 index 7410ce51df..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-lite-8k-0922.yaml +++ /dev/null @@ -1,31 +0,0 @@ -model: ernie-lite-8k-0922 -label: - en_US: ERNIE-Lite-8K-0922 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 1024 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 -deprecated: true diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-128k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-128k.yaml deleted file mode 100644 index 331639624c..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-128k.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: ernie-speed-128k -label: - en_US: ERNIE-Speed-128K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 128000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 2 - max: 4096 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-8k.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-8k.yaml deleted file mode 100644 index 304c6d1f7e..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-8k.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: ernie-speed-8k -label: - en_US: ERNIE-Speed-8K -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 2 - max: 2048 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-appbuilder.yaml b/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-appbuilder.yaml deleted file mode 100644 index c254ae0260..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie-speed-appbuilder.yaml +++ /dev/null @@ -1,25 +0,0 @@ -model: ernie-speed-appbuilder -label: - en_US: ERNIE-Speed-AppBuilder -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py b/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py deleted file mode 100644 index 07b970f810..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py +++ /dev/null @@ -1,245 +0,0 @@ -from collections.abc import Generator -from enum import Enum -from json import dumps, loads -from typing import Any, Union - -from requests import Response, post - -from core.model_runtime.entities.message_entities import PromptMessageTool -from core.model_runtime.model_providers.wenxin._common import _CommonWenxin -from core.model_runtime.model_providers.wenxin.wenxin_errors import ( - BadRequestError, - InternalServerError, -) - - -class ErnieMessage: - class Role(Enum): - USER = "user" - ASSISTANT = "assistant" - FUNCTION = "function" - SYSTEM = "system" - - role: str = Role.USER.value - content: str - usage: dict[str, int] = None - stop_reason: str = "" - - def to_dict(self) -> dict[str, Any]: - return { - "role": self.role, - "content": self.content, - } - - def __init__(self, content: str, role: str = "user") -> None: - self.content = content - self.role = role - - -class ErnieBotModel(_CommonWenxin): - def generate( - self, - model: str, - stream: bool, - messages: list[ErnieMessage], - parameters: dict[str, Any], - timeout: int, - tools: list[PromptMessageTool], - stop: list[str], - user: str, - ) -> Union[Generator[ErnieMessage, None, None], ErnieMessage]: - # check parameters - self._check_parameters(model, parameters, tools, stop) - - # get access token - access_token = self._get_access_token() - - # generate request body - url = f"{self.api_bases[model]}?access_token={access_token}" - - # clone messages - messages_cloned = self._copy_messages(messages=messages) - - # build body - body = self._build_request_body( - model, messages=messages_cloned, stream=stream, parameters=parameters, tools=tools, stop=stop, user=user - ) - headers = { - "Content-Type": "application/json", - } - - resp = post(url=url, data=dumps(body), headers=headers, stream=stream) - - if resp.status_code != 200: - raise InternalServerError(f"Failed to invoke ernie bot: {resp.text}") - - if stream: - return self._handle_chat_stream_generate_response(resp) - return self._handle_chat_generate_response(resp) - - def _copy_messages(self, messages: list[ErnieMessage]) -> list[ErnieMessage]: - return [ErnieMessage(message.content, message.role) for message in messages] - - def _check_parameters( - self, model: str, parameters: dict[str, Any], tools: list[PromptMessageTool], stop: list[str] - ) -> None: - if model not in self.api_bases: - raise BadRequestError(f"Invalid model: {model}") - - # if model not in self.function_calling_supports and tools is not None and len(tools) > 0: - # raise BadRequestError(f'Model {model} does not support calling function.') - # ErnieBot supports function calling, however, there is lots of limitations. - # such as, the messages should be ordered as user by assistant or function... - # so, we just disable function calling for now. - - if tools is not None and len(tools) > 0: - raise BadRequestError("function calling is not supported yet.") - - if stop is not None: - if len(stop) > 4: - raise BadRequestError("stop list should not exceed 4 items.") - - for s in stop: - if len(s) > 20: - raise BadRequestError("stop item should not exceed 20 characters.") - - def _build_request_body( - self, - model: str, - messages: list[ErnieMessage], - stream: bool, - parameters: dict[str, Any], - tools: list[PromptMessageTool], - stop: list[str], - user: str, - ) -> dict[str, Any]: - # if model in self.function_calling_supports: - # return self._build_function_calling_request_body(model, messages, parameters, tools, stop, user) - return self._build_chat_request_body(model, messages, stream, parameters, stop, user) - - def _build_function_calling_request_body( - self, - model: str, - messages: list[ErnieMessage], - stream: bool, - parameters: dict[str, Any], - tools: list[PromptMessageTool], - stop: list[str], - user: str, - ) -> dict[str, Any]: - if len(messages) % 2 == 0: - raise BadRequestError("The number of messages should be odd.") - if messages[0].role == "function": - raise BadRequestError("The first message should be user message.") - - """ - TODO: implement function calling - """ - - def _build_chat_request_body( - self, - model: str, - messages: list[ErnieMessage], - stream: bool, - parameters: dict[str, Any], - stop: list[str], - user: str, - ) -> dict[str, Any]: - if len(messages) == 0: - raise BadRequestError("The number of messages should not be zero.") - - # check if the first element is system, shift it - system_message = "" - if messages[0].role == "system": - message = messages.pop(0) - system_message = message.content - - if len(messages) % 2 == 0: - raise BadRequestError("The number of messages should be odd.") - if messages[0].role != "user": - raise BadRequestError("The first message should be user message.") - body = { - "messages": [message.to_dict() for message in messages], - "stream": stream, - "stop": stop, - "user_id": user, - **parameters, - } - - if "max_tokens" in parameters and type(parameters["max_tokens"]) == int: - body["max_output_tokens"] = parameters["max_tokens"] - - if "presence_penalty" in parameters and type(parameters["presence_penalty"]) == float: - body["penalty_score"] = parameters["presence_penalty"] - - if system_message: - body["system"] = system_message - - return body - - def _handle_chat_generate_response(self, response: Response) -> ErnieMessage: - data = response.json() - if "error_code" in data: - code = data["error_code"] - msg = data["error_msg"] - # raise error - self._handle_error(code, msg) - - result = data["result"] - usage = data["usage"] - - message = ErnieMessage(content=result, role="assistant") - message.usage = { - "prompt_tokens": usage["prompt_tokens"], - "completion_tokens": usage["completion_tokens"], - "total_tokens": usage["total_tokens"], - } - - return message - - def _handle_chat_stream_generate_response(self, response: Response) -> Generator[ErnieMessage, None, None]: - for line in response.iter_lines(): - if len(line) == 0: - continue - line = line.decode("utf-8") - if line[0] == "{": - try: - data = loads(line) - if "error_code" in data: - code = data["error_code"] - msg = data["error_msg"] - # raise error - self._handle_error(code, msg) - except Exception as e: - raise InternalServerError(f"Failed to parse response: {e}") - - if line.startswith("data:"): - line = line[5:].strip() - else: - continue - - if not line: - continue - try: - data = loads(line) - except Exception as e: - raise InternalServerError(f"Failed to parse response: {e}") - - result = data["result"] - is_end = data["is_end"] - - if is_end: - usage = data["usage"] - finish_reason = data.get("finish_reason", None) - message = ErnieMessage(content=result, role="assistant") - message.usage = { - "prompt_tokens": usage["prompt_tokens"], - "completion_tokens": usage["completion_tokens"], - "total_tokens": usage["total_tokens"], - } - message.stop_reason = finish_reason - - yield message - else: - message = ErnieMessage(content=result, role="assistant") - yield message diff --git a/api/core/model_runtime/model_providers/wenxin/llm/llm.py b/api/core/model_runtime/model_providers/wenxin/llm/llm.py deleted file mode 100644 index f7c160b6b4..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/llm.py +++ /dev/null @@ -1,316 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union, cast - -from core.model_runtime.callbacks.base_callback import Callback -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageTool, - SystemPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.invoke import ( - InvokeError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.wenxin._common import BaiduAccessToken -from core.model_runtime.model_providers.wenxin.llm.ernie_bot import ErnieBotModel, ErnieMessage -from core.model_runtime.model_providers.wenxin.wenxin_errors import invoke_error_mapping - -ERNIE_BOT_BLOCK_MODE_PROMPT = """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}} - - -You should also complete the text started with ``` but not tell ``` directly. -""" # noqa: E501 - - -class ErnieBotLargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ) - - def _code_block_mode_wrapper( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - callbacks: list[Callback] = None, - ) -> Union[LLMResult, Generator]: - """ - Code block mode wrapper for invoking large language model - """ - if "response_format" in model_parameters and model_parameters["response_format"] in {"JSON", "XML"}: - response_format = model_parameters["response_format"] - stop = stop or [] - self._transform_json_prompts( - model, credentials, prompt_messages, model_parameters, tools, stop, stream, user, response_format - ) - model_parameters.pop("response_format") - if stream: - return self._code_block_mode_stream_processor( - model=model, - prompt_messages=prompt_messages, - input_generator=self._invoke( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - ), - ) - - return self._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - - def _transform_json_prompts( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - response_format: str = "JSON", - ) -> None: - """ - Transform json prompts to model prompts - """ - - # 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=ERNIE_BOT_BLOCK_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content).replace( - "{{block}}", response_format - ) - ) - else: - # insert the system message - prompt_messages.insert( - 0, - SystemPromptMessage( - content=ERNIE_BOT_BLOCK_MODE_PROMPT.replace( - "{{instructions}}", f"Please output a valid {response_format} object." - ).replace("{{block}}", response_format) - ), - ) - - if len(prompt_messages) > 0 and isinstance(prompt_messages[-1], UserPromptMessage): - # add ```JSON\n to the last message - prompt_messages[-1].content += "\n```JSON\n{\n" - else: - # append a user message - prompt_messages.append(UserPromptMessage(content="```JSON\n{\n")) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - # tools is not supported yet - return self._num_tokens_from_messages(prompt_messages) - - def _num_tokens_from_messages( - self, - messages: list[PromptMessage], - ) -> int: - """Calculate num tokens for baichuan model""" - - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - tokens_per_message = 3 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - num_tokens += tokens(str(value)) - num_tokens += 3 - - return num_tokens - - def validate_credentials(self, model: str, credentials: dict) -> None: - api_key = credentials["api_key"] - secret_key = credentials["secret_key"] - try: - BaiduAccessToken.get_access_token(api_key, secret_key) - except Exception as e: - raise CredentialsValidateFailedError(f"Credentials validation failed: {e}") - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - instance = ErnieBotModel( - api_key=credentials["api_key"], - secret_key=credentials["secret_key"], - ) - - user = user or "ErnieBotDefault" - - # convert prompt messages to baichuan messages - messages = [ - ErnieMessage( - content=message.content - if isinstance(message.content, str) - else "".join([content.data for content in message.content]), - role=message.role.value, - ) - for message in prompt_messages - ] - - # invoke model - response = instance.generate( - model=model, - stream=stream, - messages=messages, - parameters=model_parameters, - timeout=60, - tools=tools, - stop=stop, - user=user, - ) - - if stream: - return self._handle_chat_generate_stream_response(model, prompt_messages, credentials, response) - else: - return self._handle_chat_generate_response(model, prompt_messages, credentials, response) - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for Baichuan - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - raise ValueError("User message content must be str") - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - else: - raise ValueError(f"Unknown message type {type(message)}") - - return message_dict - - def _handle_chat_generate_response( - self, model: str, prompt_messages: list[PromptMessage], credentials: dict, response: ErnieMessage - ) -> LLMResult: - # convert baichuan message to llm result - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=response.usage["prompt_tokens"], - completion_tokens=response.usage["completion_tokens"], - ) - return LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage(content=response.content, tool_calls=[]), - usage=usage, - ) - - def _handle_chat_generate_stream_response( - self, - model: str, - prompt_messages: list[PromptMessage], - credentials: dict, - response: Generator[ErnieMessage, None, None], - ) -> Generator: - for message in response: - if message.usage: - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=message.usage["prompt_tokens"], - completion_tokens=message.usage["completion_tokens"], - ) - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - usage=usage, - finish_reason=message.stop_reason or None, - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - delta=LLMResultChunkDelta( - index=0, - message=AssistantPromptMessage(content=message.content, tool_calls=[]), - finish_reason=message.stop_reason or None, - ), - ) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return invoke_error_mapping() diff --git a/api/core/model_runtime/model_providers/wenxin/llm/yi_34b_chat.yaml b/api/core/model_runtime/model_providers/wenxin/llm/yi_34b_chat.yaml deleted file mode 100644 index 0b247fbd22..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/llm/yi_34b_chat.yaml +++ /dev/null @@ -1,30 +0,0 @@ -model: yi_34b_chat -label: - en_US: yi_34b_chat -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 32000 -parameter_rules: - - name: temperature - use_template: temperature - min: 0.1 - max: 1.0 - default: 0.95 - - name: top_p - use_template: top_p - min: 0 - max: 1.0 - default: 0.7 - - name: max_tokens - use_template: max_tokens - default: 4096 - min: 2 - max: 4096 - - name: presence_penalty - use_template: presence_penalty - default: 1.0 - min: 1.0 - max: 2.0 diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/__init__.py b/api/core/model_runtime/model_providers/wenxin/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-en.yaml b/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-en.yaml deleted file mode 100644 index 74fadb7f9d..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-en.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: bge-large-en -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 16 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-zh.yaml b/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-zh.yaml deleted file mode 100644 index d4af27ec38..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/text_embedding/bge-large-zh.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: bge-large-zh -model_type: text-embedding -model_properties: - context_size: 512 - max_chunks: 16 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/embedding-v1.yaml b/api/core/model_runtime/model_providers/wenxin/text_embedding/embedding-v1.yaml deleted file mode 100644 index eda48d9655..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/text_embedding/embedding-v1.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: embedding-v1 -model_type: text-embedding -model_properties: - context_size: 384 - max_chunks: 16 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/tao-8k.yaml b/api/core/model_runtime/model_providers/wenxin/text_embedding/tao-8k.yaml deleted file mode 100644 index e28f253eb6..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/text_embedding/tao-8k.yaml +++ /dev/null @@ -1,9 +0,0 @@ -model: tao-8k -model_type: text-embedding -model_properties: - context_size: 8192 - max_chunks: 1 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/wenxin/wenxin.py b/api/core/model_runtime/model_providers/wenxin/wenxin.py deleted file mode 100644 index 895af20bc8..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/wenxin.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class WenxinProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `ernie-bot` model for validate, - model_instance.validate_credentials(model="ernie-bot", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/wenxin/wenxin.yaml b/api/core/model_runtime/model_providers/wenxin/wenxin.yaml deleted file mode 100644 index 6a6b38e6a1..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/wenxin.yaml +++ /dev/null @@ -1,40 +0,0 @@ -provider: wenxin -label: - en_US: WenXin - zh_Hans: 文心一言 -icon_small: - en_US: icon_s_en.png - zh_Hans: icon_s_en.png -icon_large: - en_US: icon_l_en.png - zh_Hans: icon_l_zh.png -background: "#E8F5FE" -help: - title: - en_US: Get your API Key from WenXin - zh_Hans: 从文心一言获取您的 API Key - url: - en_US: https://cloud.baidu.com/wenxin.html -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: secret_key - label: - en_US: Secret Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 Secret Key - en_US: Enter your Secret Key diff --git a/api/core/model_runtime/model_providers/wenxin/wenxin_errors.py b/api/core/model_runtime/model_providers/wenxin/wenxin_errors.py deleted file mode 100644 index bd074e0477..0000000000 --- a/api/core/model_runtime/model_providers/wenxin/wenxin_errors.py +++ /dev/null @@ -1,54 +0,0 @@ -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -def invoke_error_mapping() -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [InternalServerError], - InvokeRateLimitError: [RateLimitReachedError], - InvokeAuthorizationError: [ - InvalidAuthenticationError, - InsufficientAccountBalanceError, - InvalidAPIKeyError, - ], - InvokeBadRequestError: [BadRequestError, KeyError], - } - - -class InvalidAuthenticationError(Exception): - pass - - -class InvalidAPIKeyError(Exception): - pass - - -class RateLimitReachedError(Exception): - pass - - -class InsufficientAccountBalanceError(Exception): - pass - - -class InternalServerError(Exception): - pass - - -class BadRequestError(Exception): - pass diff --git a/api/core/model_runtime/model_providers/xinference/__init__.py b/api/core/model_runtime/model_providers/xinference/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/xinference/_assets/icon_l_en.svg deleted file mode 100644 index 8109176543..0000000000 --- a/api/core/model_runtime/model_providers/xinference/_assets/icon_l_en.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/xinference/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/xinference/_assets/icon_s_en.svg deleted file mode 100644 index f5c5f75ea8..0000000000 --- a/api/core/model_runtime/model_providers/xinference/_assets/icon_s_en.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/core/model_runtime/model_providers/xinference/llm/__init__.py b/api/core/model_runtime/model_providers/xinference/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/llm/llm.py b/api/core/model_runtime/model_providers/xinference/llm/llm.py deleted file mode 100644 index 286640079b..0000000000 --- a/api/core/model_runtime/model_providers/xinference/llm/llm.py +++ /dev/null @@ -1,816 +0,0 @@ -from collections.abc import Generator, Iterator -from typing import cast - -from openai import ( - APIConnectionError, - APITimeoutError, - AuthenticationError, - ConflictError, - InternalServerError, - NotFoundError, - OpenAI, - PermissionDeniedError, - RateLimitError, - UnprocessableEntityError, -) -from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall -from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall -from openai.types.chat.chat_completion_message import FunctionCall -from openai.types.completion import Completion -from xinference_client.client.restful.restful_client import ( - Client, - RESTfulChatModelHandle, - RESTfulGenerateModelHandle, -) - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - ImagePromptMessageContent, - PromptMessage, - PromptMessageContent, - PromptMessageContentType, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.entities.model_entities import ( - AIModelEntity, - DefaultParameterName, - FetchFrom, - ModelFeature, - ModelPropertyKey, - ModelType, - ParameterRule, - ParameterType, -) -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.xinference.xinference_helper import ( - XinferenceHelper, - XinferenceModelExtraParameter, -) -from core.model_runtime.utils import helper - - -class XinferenceAILargeLanguageModel(LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - invoke LLM - - see `core.model_runtime.model_providers.__base.large_language_model.LargeLanguageModel._invoke` - """ - if "temperature" in model_parameters: - if model_parameters["temperature"] < 0.01: - model_parameters["temperature"] = 0.01 - elif model_parameters["temperature"] > 1.0: - model_parameters["temperature"] = 0.99 - - return self._generate( - model=model, - credentials=credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - extra_model_kwargs=XinferenceHelper.get_xinference_extra_parameter( - server_url=credentials["server_url"], - model_uid=credentials["model_uid"], - api_key=credentials.get("api_key"), - ), - ) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - validate credentials - - credentials should be like: - { - 'model_type': 'text-generation', - 'server_url': 'server url', - 'model_uid': 'model uid', - } - """ - try: - if "/" in credentials["model_uid"] or "?" in credentials["model_uid"] or "#" in credentials["model_uid"]: - raise CredentialsValidateFailedError("model_uid should not contain /, ?, or #") - - extra_param = XinferenceHelper.get_xinference_extra_parameter( - server_url=credentials["server_url"], - model_uid=credentials["model_uid"], - api_key=credentials.get("api_key"), - ) - if "completion_type" not in credentials: - if "chat" in extra_param.model_ability: - credentials["completion_type"] = "chat" - elif "generate" in extra_param.model_ability: - credentials["completion_type"] = "completion" - else: - raise ValueError( - f"xinference model ability {extra_param.model_ability} is not supported," - f" check if you have the right model type" - ) - - if extra_param.support_function_call: - credentials["support_function_call"] = True - - if extra_param.support_vision: - credentials["support_vision"] = True - - if extra_param.context_length: - credentials["context_length"] = extra_param.context_length - - except RuntimeError as e: - raise CredentialsValidateFailedError(f"Xinference credentials validate failed: {e}") - except KeyError as e: - raise CredentialsValidateFailedError(f"Xinference credentials validate failed: {e}") - except Exception as e: - raise e - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool] | None = None, - ) -> int: - """ - get number of tokens - - cause XinferenceAI LLM is a customized model, we could net detect which tokenizer to use - so we just take the GPT2 tokenizer as default - """ - return self._num_tokens_from_messages(prompt_messages, tools) - - def _num_tokens_from_messages( - self, messages: list[PromptMessage], tools: list[PromptMessageTool], is_completion_model: bool = False - ) -> int: - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - if is_completion_model: - return sum(tokens(str(message.content)) for message in messages) - - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += tokens(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += tokens(f_key) - num_tokens += tokens(f_value) - else: - num_tokens += tokens(t_key) - num_tokens += tokens(t_value) - if key == "function_call": - for t_key, t_value in value.items(): - num_tokens += tokens(t_key) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += tokens(f_key) - num_tokens += tokens(f_value) - else: - num_tokens += tokens(t_key) - num_tokens += tokens(t_value) - else: - num_tokens += tokens(str(value)) - - if key == "name": - num_tokens += tokens_per_name - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(tools) - - return num_tokens - - def _num_tokens_for_tools(self, tools: list[PromptMessageTool]) -> int: - """ - Calculate num tokens for tool calling - - :param encoding: encoding - :param tools: tools for tool calling - :return: number of tokens - """ - - def tokens(text: str): - return self._get_num_tokens_by_gpt2(text) - - num_tokens = 0 - for tool in tools: - # calculate num tokens for function object - num_tokens += tokens("name") - num_tokens += tokens(tool.name) - num_tokens += tokens("description") - num_tokens += tokens(tool.description) - parameters = tool.parameters - num_tokens += tokens("parameters") - num_tokens += tokens("type") - num_tokens += tokens(parameters.get("type")) - if "properties" in parameters: - num_tokens += tokens("properties") - for key, value in parameters.get("properties").items(): - num_tokens += tokens(key) - for field_key, field_value in value.items(): - num_tokens += tokens(field_key) - if field_key == "enum": - for enum_field in field_value: - num_tokens += 3 - num_tokens += tokens(enum_field) - else: - num_tokens += tokens(field_key) - num_tokens += tokens(str(field_value)) - if "required" in parameters: - num_tokens += tokens("required") - for required_field in parameters["required"]: - num_tokens += 3 - num_tokens += tokens(required_field) - - return num_tokens - - def _convert_prompt_message_to_text(self, message: list[PromptMessage]) -> str: - """ - convert prompt message to text - """ - text = "" - for item in message: - if isinstance(item, UserPromptMessage | SystemPromptMessage | AssistantPromptMessage): - text += item.content - else: - raise NotImplementedError(f"PromptMessage type {type(item)} is not supported") - return text - - def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict: - """ - Convert PromptMessage to dict for OpenAI Compatibility API - """ - if isinstance(message, UserPromptMessage): - message = cast(UserPromptMessage, message) - if isinstance(message.content, str): - message_dict = {"role": "user", "content": message.content} - else: - sub_messages = [] - for message_content in message.content: - if message_content.type == PromptMessageContentType.TEXT: - message_content = cast(PromptMessageContent, message_content) - sub_message_dict = {"type": "text", "text": message_content.data} - sub_messages.append(sub_message_dict) - elif message_content.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, message_content) - sub_message_dict = { - "type": "image_url", - "image_url": {"url": message_content.data, "detail": message_content.detail.value}, - } - sub_messages.append(sub_message_dict) - message_dict = {"role": "user", "content": sub_messages} - elif isinstance(message, AssistantPromptMessage): - message = cast(AssistantPromptMessage, message) - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls and len(message.tool_calls) > 0: - message_dict["function_call"] = { - "name": message.tool_calls[0].function.name, - "arguments": message.tool_calls[0].function.arguments, - } - elif isinstance(message, SystemPromptMessage): - message = cast(SystemPromptMessage, message) - message_dict = {"role": "system", "content": message.content} - elif isinstance(message, ToolPromptMessage): - message = cast(ToolPromptMessage, message) - message_dict = {"tool_call_id": message.tool_call_id, "role": "tool", "content": message.content} - else: - raise ValueError(f"Unknown message type {type(message)}") - - return message_dict - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - rules = [ - ParameterRule( - name="temperature", - type=ParameterType.FLOAT, - use_template="temperature", - label=I18nObject(zh_Hans="温度", en_US="Temperature"), - ), - ParameterRule( - name="top_p", - type=ParameterType.FLOAT, - use_template="top_p", - label=I18nObject(zh_Hans="Top P", en_US="Top P"), - ), - ParameterRule( - name="max_tokens", - type=ParameterType.INT, - use_template="max_tokens", - min=1, - max=credentials.get("context_length", 2048), - default=512, - label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), - ), - ParameterRule( - name=DefaultParameterName.PRESENCE_PENALTY, - use_template=DefaultParameterName.PRESENCE_PENALTY, - type=ParameterType.FLOAT, - label=I18nObject( - en_US="Presence Penalty", - zh_Hans="存在惩罚", - ), - required=False, - help=I18nObject( - en_US="Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they " - "appear in the text so far, increasing the model's likelihood to talk about new topics.", - zh_Hans="介于 -2.0 和 2.0 之间的数字。正值会根据新词是否已出现在文本中对其进行惩罚," - "从而增加模型谈论新话题的可能性。", - ), - default=0.0, - min=-2.0, - max=2.0, - precision=2, - ), - ParameterRule( - name=DefaultParameterName.FREQUENCY_PENALTY, - use_template=DefaultParameterName.FREQUENCY_PENALTY, - type=ParameterType.FLOAT, - label=I18nObject( - en_US="Frequency Penalty", - zh_Hans="频率惩罚", - ), - required=False, - help=I18nObject( - en_US="Number between -2.0 and 2.0. Positive values penalize new tokens based on their " - "existing frequency in the text so far, decreasing the model's likelihood to repeat the " - "same line verbatim.", - zh_Hans="介于 -2.0 和 2.0 之间的数字。正值会根据新词在文本中的现有频率对其进行惩罚," - "从而降低模型逐字重复相同内容的可能性。", - ), - default=0.0, - min=-2.0, - max=2.0, - precision=2, - ), - ] - - completion_type = None - - if "completion_type" in credentials: - if credentials["completion_type"] == "chat": - completion_type = LLMMode.CHAT.value - elif credentials["completion_type"] == "completion": - completion_type = LLMMode.COMPLETION.value - else: - raise ValueError(f'completion_type {credentials["completion_type"]} is not supported') - else: - extra_args = XinferenceHelper.get_xinference_extra_parameter( - server_url=credentials["server_url"], - model_uid=credentials["model_uid"], - api_key=credentials.get("api_key"), - ) - - if "chat" in extra_args.model_ability: - completion_type = LLMMode.CHAT.value - elif "generate" in extra_args.model_ability: - completion_type = LLMMode.COMPLETION.value - else: - raise ValueError(f"xinference model ability {extra_args.model_ability} is not supported") - - features = [] - - support_function_call = credentials.get("support_function_call", False) - if support_function_call: - features.append(ModelFeature.TOOL_CALL) - - support_vision = credentials.get("support_vision", False) - if support_vision: - features.append(ModelFeature.VISION) - - context_length = credentials.get("context_length", 2048) - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.LLM, - features=features, - model_properties={ModelPropertyKey.MODE: completion_type, ModelPropertyKey.CONTEXT_SIZE: context_length}, - parameter_rules=rules, - ) - - return entity - - def _generate( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - extra_model_kwargs: XinferenceModelExtraParameter, - tools: list[PromptMessageTool] | None = None, - stop: list[str] | None = None, - stream: bool = True, - user: str | None = None, - ) -> LLMResult | Generator: - """ - generate text from LLM - - see `core.model_runtime.model_providers.__base.large_language_model.LargeLanguageModel._generate` - - extra_model_kwargs can be got by `XinferenceHelper.get_xinference_extra_parameter` - """ - if "server_url" not in credentials: - raise CredentialsValidateFailedError("server_url is required in credentials") - - credentials["server_url"] = credentials["server_url"].removesuffix("/") - - api_key = credentials.get("api_key") or "abc" - - client = OpenAI( - base_url=f'{credentials["server_url"]}/v1', - api_key=api_key, - max_retries=3, - timeout=60, - ) - - xinference_client = Client( - base_url=credentials["server_url"], - api_key=credentials.get("api_key"), - ) - - xinference_model = xinference_client.get_model(credentials["model_uid"]) - - generate_config = { - "temperature": model_parameters.get("temperature", 1.0), - "top_p": model_parameters.get("top_p", 0.7), - "max_tokens": model_parameters.get("max_tokens", 512), - "presence_penalty": model_parameters.get("presence_penalty", 0.0), - "frequency_penalty": model_parameters.get("frequency_penalty", 0.0), - } - - if stop: - generate_config["stop"] = stop - - if tools and len(tools) > 0: - generate_config["tools"] = [{"type": "function", "function": helper.dump_model(tool)} for tool in tools] - vision = credentials.get("support_vision", False) - if isinstance(xinference_model, RESTfulChatModelHandle): - resp = client.chat.completions.create( - model=credentials["model_uid"], - messages=[self._convert_prompt_message_to_dict(message) for message in prompt_messages], - stream=stream, - user=user, - **generate_config, - ) - if stream: - if tools and len(tools) > 0: - raise InvokeBadRequestError("xinference tool calls does not support stream mode") - return self._handle_chat_stream_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=resp - ) - return self._handle_chat_generate_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=resp - ) - elif isinstance(xinference_model, RESTfulGenerateModelHandle): - resp = client.completions.create( - model=credentials["model_uid"], - prompt=self._convert_prompt_message_to_text(prompt_messages), - stream=stream, - user=user, - **generate_config, - ) - if stream: - return self._handle_completion_stream_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=resp - ) - return self._handle_completion_generate_response( - model=model, credentials=credentials, prompt_messages=prompt_messages, tools=tools, resp=resp - ) - else: - raise NotImplementedError(f"xinference model handle type {type(xinference_model)} is not supported") - - def _extract_response_tool_calls( - self, response_tool_calls: list[ChatCompletionMessageToolCall | ChoiceDeltaToolCall] - ) -> list[AssistantPromptMessage.ToolCall]: - """ - Extract tool calls from response - - :param response_tool_calls: response tool calls - :return: list of tool calls - """ - tool_calls = [] - if response_tool_calls: - for response_tool_call in response_tool_calls: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_tool_call.function.name, arguments=response_tool_call.function.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_tool_call.id, type=response_tool_call.type, function=function - ) - tool_calls.append(tool_call) - - return tool_calls - - def _extract_response_function_call( - self, response_function_call: FunctionCall | ChoiceDeltaFunctionCall - ) -> AssistantPromptMessage.ToolCall: - """ - Extract function call from response - - :param response_function_call: response function call - :return: tool call - """ - tool_call = None - if response_function_call: - function = AssistantPromptMessage.ToolCall.ToolCallFunction( - name=response_function_call.name, arguments=response_function_call.arguments - ) - - tool_call = AssistantPromptMessage.ToolCall( - id=response_function_call.name, type="function", function=function - ) - - return tool_call - - def _handle_chat_generate_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: ChatCompletion, - ) -> LLMResult: - """ - handle normal chat generate response - """ - if len(resp.choices) == 0: - raise InvokeServerUnavailableError("Empty response") - - assistant_message = resp.choices[0].message - - # convert tool call to assistant message tool call - tool_calls = assistant_message.tool_calls - assistant_prompt_message_tool_calls = self._extract_response_tool_calls(tool_calls or []) - function_call = assistant_message.function_call - if function_call: - assistant_prompt_message_tool_calls += [self._extract_response_function_call(function_call)] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=assistant_message.content, tool_calls=assistant_prompt_message_tool_calls - ) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[assistant_prompt_message], tools=tools) - - usage = self._calc_response_usage( - model=model, credentials=credentials, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens - ) - - response = LLMResult( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=resp.system_fingerprint, - usage=usage, - message=assistant_prompt_message, - ) - - return response - - def _handle_chat_stream_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: Iterator[ChatCompletionChunk], - ) -> Generator: - """ - handle stream chat generate response - """ - full_response = "" - - for chunk in resp: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ""): - continue - - # check if there is a tool call in the response - function_call = None - tool_calls = [] - if delta.delta.tool_calls: - tool_calls += delta.delta.tool_calls - if delta.delta.function_call: - function_call = delta.delta.function_call - - assistant_message_tool_calls = self._extract_response_tool_calls(tool_calls) - if function_call: - assistant_message_tool_calls += [self._extract_response_function_call(function_call)] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta.delta.content or "", tool_calls=assistant_message_tool_calls - ) - - if delta.finish_reason is not None: - # temp_assistant_prompt_message is used to calculate usage - temp_assistant_prompt_message = AssistantPromptMessage( - content=full_response, tool_calls=assistant_message_tool_calls - ) - - prompt_tokens = self._num_tokens_from_messages(messages=prompt_messages, tools=tools) - completion_tokens = self._num_tokens_from_messages(messages=[temp_assistant_prompt_message], tools=[]) - - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, message=assistant_prompt_message, finish_reason=delta.finish_reason, usage=usage - ), - ) - else: - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, - message=assistant_prompt_message, - ), - ) - - full_response += delta.delta.content - - def _handle_completion_generate_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: Completion, - ) -> LLMResult: - """ - handle normal completion generate response - """ - if len(resp.choices) == 0: - raise InvokeServerUnavailableError("Empty response") - - assistant_message = resp.choices[0].text - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=assistant_message, tool_calls=[]) - - prompt_tokens = self._get_num_tokens_by_gpt2(self._convert_prompt_message_to_text(prompt_messages)) - completion_tokens = self._num_tokens_from_messages( - messages=[assistant_prompt_message], tools=[], is_completion_model=True - ) - usage = self._calc_response_usage( - model=model, credentials=credentials, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens - ) - - response = LLMResult( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=resp.system_fingerprint, - usage=usage, - message=assistant_prompt_message, - ) - - return response - - def _handle_completion_stream_response( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: list[PromptMessageTool], - resp: Iterator[Completion], - ) -> Generator: - """ - handle stream completion generate response - """ - full_response = "" - - for chunk in resp: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage(content=delta.text or "", tool_calls=[]) - - if delta.finish_reason is not None: - # temp_assistant_prompt_message is used to calculate usage - temp_assistant_prompt_message = AssistantPromptMessage(content=full_response, tool_calls=[]) - - prompt_tokens = self._get_num_tokens_by_gpt2(self._convert_prompt_message_to_text(prompt_messages)) - completion_tokens = self._num_tokens_from_messages( - messages=[temp_assistant_prompt_message], tools=[], is_completion_model=True - ) - usage = self._calc_response_usage( - model=model, - credentials=credentials, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, message=assistant_prompt_message, finish_reason=delta.finish_reason, usage=usage - ), - ) - else: - if delta.text is None or delta.text == "": - continue - - yield LLMResultChunk( - model=model, - prompt_messages=prompt_messages, - system_fingerprint=chunk.system_fingerprint, - delta=LLMResultChunkDelta( - index=0, - message=assistant_prompt_message, - ), - ) - - full_response += delta.text - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [ - APIConnectionError, - APITimeoutError, - ], - InvokeServerUnavailableError: [ - InternalServerError, - ConflictError, - NotFoundError, - UnprocessableEntityError, - PermissionDeniedError, - ], - InvokeRateLimitError: [RateLimitError], - InvokeAuthorizationError: [AuthenticationError], - InvokeBadRequestError: [ValueError], - } diff --git a/api/core/model_runtime/model_providers/xinference/rerank/__init__.py b/api/core/model_runtime/model_providers/xinference/rerank/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/rerank/rerank.py b/api/core/model_runtime/model_providers/xinference/rerank/rerank.py deleted file mode 100644 index 8f18bc42d2..0000000000 --- a/api/core/model_runtime/model_providers/xinference/rerank/rerank.py +++ /dev/null @@ -1,189 +0,0 @@ -from typing import Optional - -from xinference_client.client.restful.restful_client import Client, RESTfulRerankModelHandle - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.rerank_model import RerankModel - - -class XinferenceRerankModel(RerankModel): - """ - Model class for Xinference rerank model. - """ - - 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 - """ - if len(docs) == 0: - return RerankResult(model=model, docs=[]) - - server_url = credentials["server_url"] - model_uid = credentials["model_uid"] - api_key = credentials.get("api_key") - server_url = server_url.removesuffix("/") - auth_headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} - - params = {"documents": docs, "query": query, "top_n": top_n, "return_documents": True} - try: - handle = RESTfulRerankModelHandle(model_uid, server_url, auth_headers) - response = handle.rerank(**params) - except RuntimeError as e: - if "rerank hasn't support extra parameter" not in str(e): - raise InvokeServerUnavailableError(str(e)) - - # compatible xinference server between v0.10.1 - v0.12.1, not support 'return_len' - handle = RESTfulRerankModelHandleWithoutExtraParameter(model_uid, server_url, auth_headers) - response = handle.rerank(**params) - - rerank_documents = [] - for idx, result in enumerate(response["results"]): - # format document - index = result["index"] - page_content = result["document"] if isinstance(result["document"], str) else result["document"]["text"] - rerank_document = RerankDocument( - index=index, - text=page_content, - score=result["relevance_score"], - ) - - # score threshold check - if score_threshold is not None: - if result["relevance_score"] >= score_threshold: - rerank_documents.append(rerank_document) - else: - rerank_documents.append(rerank_document) - - return RerankResult(model=model, docs=rerank_documents) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - if "/" in credentials["model_uid"] or "?" in credentials["model_uid"] or "#" in credentials["model_uid"]: - raise CredentialsValidateFailedError("model_uid should not contain /, ?, or #") - - credentials["server_url"] = credentials["server_url"].removesuffix("/") - - # initialize client - client = Client( - base_url=credentials["server_url"], - api_key=credentials.get("api_key"), - ) - - xinference_client = client.get_model(model_uid=credentials["model_uid"]) - - if not isinstance(xinference_client, RESTfulRerankModelHandle): - raise InvokeBadRequestError( - "please check model type, the model you want to invoke is not a rerank model" - ) - - self.invoke( - model=model, - credentials=credentials, - query="Whose 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, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.RERANK, - model_properties={}, - parameter_rules=[], - ) - - return entity - - -class RESTfulRerankModelHandleWithoutExtraParameter(RESTfulRerankModelHandle): - def rerank( - self, - documents: list[str], - query: str, - top_n: Optional[int] = None, - max_chunks_per_doc: Optional[int] = None, - return_documents: Optional[bool] = None, - **kwargs, - ): - url = f"{self._base_url}/v1/rerank" - request_body = { - "model": self._model_uid, - "documents": documents, - "query": query, - "top_n": top_n, - "max_chunks_per_doc": max_chunks_per_doc, - "return_documents": return_documents, - } - - import requests - - response = requests.post(url, json=request_body, headers=self.auth_headers) - if response.status_code != 200: - raise InvokeServerUnavailableError(f"Failed to rerank documents, detail: {response.json()['detail']}") - response_data = response.json() - return response_data diff --git a/api/core/model_runtime/model_providers/xinference/speech2text/__init__.py b/api/core/model_runtime/model_providers/xinference/speech2text/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py b/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py deleted file mode 100644 index a6c5b8a0a5..0000000000 --- a/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py +++ /dev/null @@ -1,144 +0,0 @@ -from typing import IO, Optional - -from xinference_client.client.restful.restful_client import Client, RESTfulAudioModelHandle - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel - - -class XinferenceSpeech2TextModel(Speech2TextModel): - """ - Model class for Xinference speech to text model. - """ - - def _invoke(self, model: str, credentials: dict, file: IO[bytes], user: Optional[str] = None) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: audio file - :param user: unique user id - :return: text for given audio file - """ - return self._speech2text_invoke(model, credentials, file) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - if "/" in credentials["model_uid"] or "?" in credentials["model_uid"] or "#" in credentials["model_uid"]: - raise CredentialsValidateFailedError("model_uid should not contain /, ?, or #") - - credentials["server_url"] = credentials["server_url"].removesuffix("/") - - # initialize client - client = Client( - base_url=credentials["server_url"], - api_key=credentials.get("api_key"), - ) - - xinference_client = client.get_model(model_uid=credentials["model_uid"]) - - if not isinstance(xinference_client, RESTfulAudioModelHandle): - raise InvokeBadRequestError( - "please check model type, the model you want to invoke is not a audio model" - ) - - audio_file_path = self._get_demo_file_path() - - with open(audio_file_path, "rb") as audio_file: - self.invoke(model, credentials, audio_file) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def _speech2text_invoke( - self, - model: str, - credentials: dict, - file: IO[bytes], - language: Optional[str] = None, - prompt: Optional[str] = None, - response_format: Optional[str] = "json", - temperature: Optional[float] = 0, - ) -> str: - """ - Invoke speech2text model - - :param model: model name - :param credentials: model credentials - :param file: The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, - mpga, m4a, ogg, wav, or webm. - :param language: The language of the input audio. Supplying the input language in ISO-639-1 - :param prompt: An optional text to guide the model's style or continue a previous audio segment. - The prompt should match the audio language. - :param response_format: The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. - :param temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more - random,while lower values like 0.2 will make it more focused and deterministic.If set to 0, the model will use - log probability to automatically increase the temperature until certain thresholds are hit. - :return: text for given audio file - """ - server_url = credentials["server_url"] - model_uid = credentials["model_uid"] - api_key = credentials.get("api_key") - server_url = server_url.removesuffix("/") - auth_headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} - - try: - handle = RESTfulAudioModelHandle(model_uid, server_url, auth_headers) - response = handle.transcriptions( - audio=file, language=language, prompt=prompt, response_format=response_format, temperature=temperature - ) - except RuntimeError as e: - raise InvokeServerUnavailableError(str(e)) - - return response["text"] - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.SPEECH2TEXT, - model_properties={}, - parameter_rules=[], - ) - - return entity diff --git a/api/core/model_runtime/model_providers/xinference/text_embedding/__init__.py b/api/core/model_runtime/model_providers/xinference/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/tts/__init__.py b/api/core/model_runtime/model_providers/xinference/tts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/xinference/tts/tts.py b/api/core/model_runtime/model_providers/xinference/tts/tts.py deleted file mode 100644 index 81dbe397d2..0000000000 --- a/api/core/model_runtime/model_providers/xinference/tts/tts.py +++ /dev/null @@ -1,228 +0,0 @@ -import concurrent.futures -from typing import Optional - -from xinference_client.client.restful.restful_client import RESTfulAudioModelHandle - -from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.tts_model import TTSModel -from core.model_runtime.model_providers.xinference.xinference_helper import XinferenceHelper - - -class XinferenceText2SpeechModel(TTSModel): - def __init__(self): - # preset voices, need support custom voice - self.model_voices = { - "__default": { - "all": [ - {"name": "Default", "value": "default"}, - ] - }, - "ChatTTS": { - "all": [ - {"name": "Alloy", "value": "alloy"}, - {"name": "Echo", "value": "echo"}, - {"name": "Fable", "value": "fable"}, - {"name": "Onyx", "value": "onyx"}, - {"name": "Nova", "value": "nova"}, - {"name": "Shimmer", "value": "shimmer"}, - ] - }, - "CosyVoice": { - "zh-Hans": [ - {"name": "中文男", "value": "中文男"}, - {"name": "中文女", "value": "中文女"}, - {"name": "粤语女", "value": "粤语女"}, - ], - "zh-Hant": [ - {"name": "中文男", "value": "中文男"}, - {"name": "中文女", "value": "中文女"}, - {"name": "粤语女", "value": "粤语女"}, - ], - "en-US": [ - {"name": "英文男", "value": "英文男"}, - {"name": "英文女", "value": "英文女"}, - ], - "ja-JP": [ - {"name": "日语男", "value": "日语男"}, - ], - "ko-KR": [ - {"name": "韩语女", "value": "韩语女"}, - ], - }, - } - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - if "/" in credentials["model_uid"] or "?" in credentials["model_uid"] or "#" in credentials["model_uid"]: - raise CredentialsValidateFailedError("model_uid should not contain /, ?, or #") - - credentials["server_url"] = credentials["server_url"].removesuffix("/") - - extra_param = XinferenceHelper.get_xinference_extra_parameter( - server_url=credentials["server_url"], - model_uid=credentials["model_uid"], - api_key=credentials.get("api_key"), - ) - - if "text-to-audio" not in extra_param.model_ability: - raise InvokeBadRequestError( - "please check model type, the model you want to invoke is not a text-to-audio model" - ) - - if extra_param.model_family and extra_param.model_family in self.model_voices: - credentials["audio_model_name"] = extra_param.model_family - else: - credentials["audio_model_name"] = "__default" - - self._tts_invoke_streaming( - model=model, - credentials=credentials, - content_text="Hello Dify!", - voice=self._get_model_default_voice(model, credentials), - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _invoke( - self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ): - """ - _invoke text2speech model - - :param model: model name - :param tenant_id: user tenant id - :param credentials: model credentials - :param voice: model timbre - :param content_text: text content to be translated - :param user: unique user id - :return: text translated to audio file - """ - return self._tts_invoke_streaming(model, credentials, content_text, voice) - - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: - """ - used to define customizable model schema - """ - - entity = AIModelEntity( - model=model, - label=I18nObject(en_US=model), - fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - model_type=ModelType.TTS, - model_properties={}, - parameter_rules=[], - ) - - return entity - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [InvokeConnectionError], - InvokeServerUnavailableError: [InvokeServerUnavailableError], - InvokeRateLimitError: [InvokeRateLimitError], - InvokeAuthorizationError: [InvokeAuthorizationError], - InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError], - } - - def get_tts_model_voices(self, model: str, credentials: dict, language: Optional[str] = None) -> list: - audio_model_name = credentials.get("audio_model_name", "__default") - for key, voices in self.model_voices.items(): - if key in audio_model_name: - if language and language in voices: - return voices[language] - elif "all" in voices: - return voices["all"] - else: - all_voices = [] - for lang, lang_voices in voices.items(): - all_voices.extend(lang_voices) - return all_voices - - return self.model_voices["__default"]["all"] - - def _get_model_default_voice(self, model: str, credentials: dict) -> any: - return "" - - def _get_model_word_limit(self, model: str, credentials: dict) -> int: - return 3500 - - def _get_model_audio_type(self, model: str, credentials: dict) -> str: - return "mp3" - - def _get_model_workers_limit(self, model: str, credentials: dict) -> int: - return 5 - - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: - """ - _tts_invoke_streaming text2speech model - - :param model: model name - :param credentials: model credentials - :param content_text: text content to be translated - :param voice: model timbre - :return: text translated to audio file - """ - credentials["server_url"] = credentials["server_url"].removesuffix("/") - - try: - api_key = credentials.get("api_key") - auth_headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} - handle = RESTfulAudioModelHandle( - credentials["model_uid"], credentials["server_url"], auth_headers=auth_headers - ) - - model_support_voice = [ - x.get("value") for x in self.get_tts_model_voices(model=model, credentials=credentials) - ] - if not voice or voice not in model_support_voice: - voice = self._get_model_default_voice(model, credentials) - word_limit = self._get_model_word_limit(model, credentials) - if len(content_text) > word_limit: - sentences = self._split_text_into_sentences(content_text, max_length=word_limit) - executor = concurrent.futures.ThreadPoolExecutor(max_workers=min(3, len(sentences))) - futures = [ - executor.submit( - handle.speech, input=sentences[i], voice=voice, response_format="mp3", speed=1.0, stream=True - ) - for i in range(len(sentences)) - ] - - for future in futures: - response = future.result() - for chunk in response: - yield chunk - else: - response = handle.speech( - input=content_text.strip(), voice=voice, response_format="mp3", speed=1.0, stream=True - ) - - for chunk in response: - yield chunk - except Exception as ex: - raise InvokeBadRequestError(str(ex)) diff --git a/api/core/model_runtime/model_providers/xinference/xinference.py b/api/core/model_runtime/model_providers/xinference/xinference.py deleted file mode 100644 index d85f7c82e7..0000000000 --- a/api/core/model_runtime/model_providers/xinference/xinference.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class XinferenceAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - pass diff --git a/api/core/model_runtime/model_providers/xinference/xinference.yaml b/api/core/model_runtime/model_providers/xinference/xinference.yaml deleted file mode 100644 index be9073c1ca..0000000000 --- a/api/core/model_runtime/model_providers/xinference/xinference.yaml +++ /dev/null @@ -1,58 +0,0 @@ -provider: xinference -label: - en_US: Xorbits Inference -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#FAF5FF" -help: - title: - en_US: How to deploy Xinference - zh_Hans: 如何部署 Xinference - url: - en_US: https://github.com/xorbitsai/inference -supported_model_types: - - llm - - text-embedding - - rerank - - speech2text - - tts -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your model name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: server_url - label: - zh_Hans: 服务器URL - en_US: Server url - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入Xinference的服务器地址,如 http://192.168.1.100:9997 - en_US: Enter the url of your Xinference, e.g. http://192.168.1.100:9997 - - variable: model_uid - label: - zh_Hans: 模型UID - en_US: Model uid - type: text-input - required: true - placeholder: - zh_Hans: 在此输入您的Model UID - en_US: Enter the model uid - - variable: api_key - label: - zh_Hans: API密钥 - en_US: API key - type: secret-input - required: false - placeholder: - zh_Hans: 在此输入您的API密钥 - en_US: Enter the api key diff --git a/api/core/model_runtime/model_providers/xinference/xinference_helper.py b/api/core/model_runtime/model_providers/xinference/xinference_helper.py deleted file mode 100644 index 619ee1492a..0000000000 --- a/api/core/model_runtime/model_providers/xinference/xinference_helper.py +++ /dev/null @@ -1,134 +0,0 @@ -from threading import Lock -from time import time -from typing import Optional - -from requests.adapters import HTTPAdapter -from requests.exceptions import ConnectionError, MissingSchema, Timeout -from requests.sessions import Session -from yarl import URL - - -class XinferenceModelExtraParameter: - model_format: str - model_handle_type: str - model_ability: list[str] - max_tokens: int = 512 - context_length: int = 2048 - support_function_call: bool = False - support_vision: bool = False - model_family: Optional[str] - - def __init__( - self, - model_format: str, - model_handle_type: str, - model_ability: list[str], - support_function_call: bool, - support_vision: bool, - max_tokens: int, - context_length: int, - model_family: Optional[str], - ) -> None: - self.model_format = model_format - self.model_handle_type = model_handle_type - self.model_ability = model_ability - self.support_function_call = support_function_call - self.support_vision = support_vision - self.max_tokens = max_tokens - self.context_length = context_length - self.model_family = model_family - - -cache = {} -cache_lock = Lock() - - -class XinferenceHelper: - @staticmethod - def get_xinference_extra_parameter(server_url: str, model_uid: str, api_key: str) -> XinferenceModelExtraParameter: - XinferenceHelper._clean_cache() - with cache_lock: - if model_uid not in cache: - cache[model_uid] = { - "expires": time() + 300, - "value": XinferenceHelper._get_xinference_extra_parameter(server_url, model_uid, api_key), - } - return cache[model_uid]["value"] - - @staticmethod - def _clean_cache() -> None: - try: - with cache_lock: - expired_keys = [model_uid for model_uid, model in cache.items() if model["expires"] < time()] - for model_uid in expired_keys: - del cache[model_uid] - except RuntimeError as e: - pass - - @staticmethod - def _get_xinference_extra_parameter(server_url: str, model_uid: str, api_key: str) -> XinferenceModelExtraParameter: - """ - get xinference model extra parameter like model_format and model_handle_type - """ - - if not model_uid or not model_uid.strip() or not server_url or not server_url.strip(): - raise RuntimeError("model_uid is empty") - - url = str(URL(server_url) / "v1" / "models" / model_uid) - - # this method is surrounded by a lock, and default requests may hang forever, - # so we just set a Adapter with max_retries=3 - session = Session() - session.mount("http://", HTTPAdapter(max_retries=3)) - session.mount("https://", HTTPAdapter(max_retries=3)) - headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} - - try: - response = session.get(url, headers=headers, timeout=10) - except (MissingSchema, ConnectionError, Timeout) as e: - raise RuntimeError(f"get xinference model extra parameter failed, url: {url}, error: {e}") - if response.status_code != 200: - raise RuntimeError( - f"get xinference model extra parameter failed, status code: {response.status_code}," - f" response: {response.text}" - ) - - response_json = response.json() - - model_format = response_json.get("model_format", "ggmlv3") - model_ability = response_json.get("model_ability", []) - model_family = response_json.get("model_family", None) - - if response_json.get("model_type") == "embedding": - model_handle_type = "embedding" - elif response_json.get("model_type") == "audio": - model_handle_type = "audio" - if model_family and model_family in {"ChatTTS", "CosyVoice", "FishAudio"}: - model_ability.append("text-to-audio") - else: - model_ability.append("audio-to-text") - elif model_format == "ggmlv3" and "chatglm" in response_json["model_name"]: - model_handle_type = "chatglm" - elif "generate" in model_ability: - model_handle_type = "generate" - elif "chat" in model_ability: - model_handle_type = "chat" - else: - raise NotImplementedError("xinference model handle type is not supported") - - support_function_call = "tools" in model_ability - support_vision = "vision" in model_ability - max_tokens = response_json.get("max_tokens", 512) - - context_length = response_json.get("context_length", 2048) - - return XinferenceModelExtraParameter( - model_format=model_format, - model_handle_type=model_handle_type, - model_ability=model_ability, - support_function_call=support_function_call, - support_vision=support_vision, - max_tokens=max_tokens, - context_length=context_length, - model_family=model_family, - ) diff --git a/api/core/model_runtime/model_providers/yi/__init__.py b/api/core/model_runtime/model_providers/yi/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/yi/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/yi/_assets/icon_l_en.svg deleted file mode 100644 index 9ce3baddaa..0000000000 --- a/api/core/model_runtime/model_providers/yi/_assets/icon_l_en.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/yi/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/yi/_assets/icon_s_en.svg deleted file mode 100644 index eb0395a21c..0000000000 --- a/api/core/model_runtime/model_providers/yi/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/api/core/model_runtime/model_providers/yi/llm/__init__.py b/api/core/model_runtime/model_providers/yi/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/yi/llm/_position.yaml b/api/core/model_runtime/model_providers/yi/llm/_position.yaml deleted file mode 100644 index e876893b41..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/_position.yaml +++ /dev/null @@ -1,9 +0,0 @@ -- yi-34b-chat-0205 -- yi-34b-chat-200k -- yi-vl-plus -- yi-large -- yi-medium -- yi-vision -- yi-medium-200k -- yi-spark -- yi-large-turbo diff --git a/api/core/model_runtime/model_providers/yi/llm/llm.py b/api/core/model_runtime/model_providers/yi/llm/llm.py deleted file mode 100644 index 5ab7fd126e..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/llm.py +++ /dev/null @@ -1,127 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union -from urllib.parse import urlparse - -import tiktoken - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import ( - PromptMessage, - PromptMessageTool, - SystemPromptMessage, -) -from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel - - -class YiLargeLanguageModel(OpenAILargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - - # yi-vl-plus not support system prompt yet. - if model == "yi-vl-plus": - prompt_message_except_system: list[PromptMessage] = [] - for message in prompt_messages: - if not isinstance(message, SystemPromptMessage): - prompt_message_except_system.append(message) - return super()._invoke( - model, credentials, prompt_message_except_system, model_parameters, tools, stop, stream - ) - - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_string(self, model: str, text: str, tools: Optional[list[PromptMessageTool]] = None) -> int: - """ - Calculate num tokens for text completion model with tiktoken package. - - :param model: model name - :param text: prompt text - :param tools: tools for tool calling - :return: number of tokens - """ - encoding = tiktoken.get_encoding("cl100k_base") - num_tokens = len(encoding.encode(text)) - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - # refactored from openai model runtime, use cl100k_base for calculate token number - def _num_tokens_from_messages( - self, model: str, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> int: - """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. - - Official documentation: https://github.com/openai/openai-cookbook/blob/ - main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - encoding = tiktoken.get_encoding("cl100k_base") - tokens_per_message = 3 - tokens_per_name = 1 - - num_tokens = 0 - messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] - for message in messages_dict: - num_tokens += tokens_per_message - for key, value in message.items(): - # Cast str(value) in case the message value is not a string - # This occurs with function messages - # TODO: The current token calculation method for the image type is not implemented, - # which need to download the image and then get the resolution for calculation, - # and will increase the request delay - if isinstance(value, list): - text = "" - for item in value: - if isinstance(item, dict) and item["type"] == "text": - text += item["text"] - - value = text - - if key == "tool_calls": - for tool_call in value: - for t_key, t_value in tool_call.items(): - num_tokens += len(encoding.encode(t_key)) - if t_key == "function": - for f_key, f_value in t_value.items(): - num_tokens += len(encoding.encode(f_key)) - num_tokens += len(encoding.encode(f_value)) - else: - num_tokens += len(encoding.encode(t_key)) - num_tokens += len(encoding.encode(t_value)) - else: - num_tokens += len(encoding.encode(str(value))) - - if key == "name": - num_tokens += tokens_per_name - - # every reply is primed with assistant - num_tokens += 3 - - if tools: - num_tokens += self._num_tokens_for_tools(encoding, tools) - - return num_tokens - - @staticmethod - def _add_custom_parameters(credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["openai_api_key"] = credentials["api_key"] - if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": - credentials["openai_api_base"] = "https://api.lingyiwanwu.com" - else: - parsed_url = urlparse(credentials["endpoint_url"]) - credentials["openai_api_base"] = f"{parsed_url.scheme}://{parsed_url.netloc}" diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml deleted file mode 100644 index ea3d8f5dce..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-34b-chat-0205 -label: - zh_Hans: yi-34b-chat-0205 - en_US: yi-34b-chat-0205 -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4000 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '2.5' - output: '2.5' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml deleted file mode 100644 index d91f984d7f..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-34b-chat-200k -label: - zh_Hans: yi-34b-chat-200k - en_US: yi-34b-chat-200k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 200000 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.6 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 4096 - min: 1 - max: 199950 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '12' - output: '12' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-large-turbo.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-large-turbo.yaml deleted file mode 100644 index 1d00eca2ca..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-large-turbo.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-large-turbo -label: - zh_Hans: yi-large-turbo - en_US: yi-large-turbo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 16384 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '12' - output: '12' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-large.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-large.yaml deleted file mode 100644 index 347f511280..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-large.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-large -label: - zh_Hans: yi-large - en_US: yi-large -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 16384 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '20' - output: '20' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-medium-200k.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-medium-200k.yaml deleted file mode 100644 index e8ddbcba97..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-medium-200k.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-medium-200k -label: - zh_Hans: yi-medium-200k - en_US: yi-medium-200k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 204800 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 204800 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '12' - output: '12' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-medium.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-medium.yaml deleted file mode 100644 index 4f0244d1f5..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-medium.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-medium -label: - zh_Hans: yi-medium - en_US: yi-medium -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 16384 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '2.5' - output: '2.5' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-spark.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-spark.yaml deleted file mode 100644 index e28e9fd8c0..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-spark.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-spark -label: - zh_Hans: yi-spark - en_US: yi-spark -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 16384 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 16384 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '1' - output: '1' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-vision.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-vision.yaml deleted file mode 100644 index bce34f5836..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-vision.yaml +++ /dev/null @@ -1,44 +0,0 @@ -model: yi-vision -label: - zh_Hans: yi-vision - en_US: yi-vision -model_type: llm -features: - - agent-thought - - vision -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 1024 - min: 1 - max: 4096 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.9 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '6' - output: '6' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml deleted file mode 100644 index 461c68583f..0000000000 --- a/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml +++ /dev/null @@ -1,43 +0,0 @@ -model: yi-vl-plus -label: - zh_Hans: yi-vl-plus - en_US: yi-vl-plus -model_type: llm -features: - - vision -model_properties: - mode: chat - context_size: 4096 -parameter_rules: - - name: temperature - use_template: temperature - type: float - default: 0.3 - min: 0.0 - max: 2.0 - help: - zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 - en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - - name: max_tokens - use_template: max_tokens - type: int - default: 512 - min: 1 - max: 4000 - help: - zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 - en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. - - name: top_p - use_template: top_p - type: float - default: 0.8 - min: 0.01 - max: 1.00 - help: - zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 - en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. -pricing: - input: '6' - output: '6' - unit: '0.000001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/yi.py b/api/core/model_runtime/model_providers/yi/yi.py deleted file mode 100644 index 9599acb22b..0000000000 --- a/api/core/model_runtime/model_providers/yi/yi.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class YiProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `yi-34b-chat-0205` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="yi-34b-chat-0205", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/yi/yi.yaml b/api/core/model_runtime/model_providers/yi/yi.yaml deleted file mode 100644 index de741afb10..0000000000 --- a/api/core/model_runtime/model_providers/yi/yi.yaml +++ /dev/null @@ -1,41 +0,0 @@ -provider: yi -label: - en_US: 01.AI - zh_Hans: 零一万物 -description: - en_US: Models provided by 01.AI, such as yi-34b-chat and yi-vl-plus. - zh_Hans: 零一万物提供的模型,例如 yi-34b-chat 和 yi-vl-plus。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#E9F1EC" -help: - title: - en_US: Get your API Key from 01.ai - zh_Hans: 从零一万物获取 API Key - url: - en_US: https://platform.lingyiwanwu.com/apikeys -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key - - variable: endpoint_url - label: - zh_Hans: 自定义 API endpoint 地址 - en_US: Custom API endpoint URL - type: text-input - required: false - placeholder: - zh_Hans: Base URL, e.g. https://api.lingyiwanwu.com/v1 - en_US: Base URL, e.g. https://api.lingyiwanwu.com/v1 diff --git a/api/core/model_runtime/model_providers/zhinao/__init__.py b/api/core/model_runtime/model_providers/zhinao/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhinao/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/zhinao/_assets/icon_l_en.svg deleted file mode 100644 index b22b869441..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/_assets/icon_l_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/zhinao/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/zhinao/_assets/icon_s_en.svg deleted file mode 100644 index 8fe72b7d09..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo-responsibility-8k.yaml b/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo-responsibility-8k.yaml deleted file mode 100644 index f420df0001..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo-responsibility-8k.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: 360gpt-turbo-responsibility-8k -label: - zh_Hans: 360gpt-turbo-responsibility-8k - en_US: 360gpt-turbo-responsibility-8k -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 8192 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 8192 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo.yaml b/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo.yaml deleted file mode 100644 index a2658fbe4f..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/llm/360gpt-turbo.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: 360gpt-turbo -label: - zh_Hans: 360gpt-turbo - en_US: 360gpt-turbo -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/zhinao/llm/360gpt2-pro.yaml b/api/core/model_runtime/model_providers/zhinao/llm/360gpt2-pro.yaml deleted file mode 100644 index 00c81eb1da..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/llm/360gpt2-pro.yaml +++ /dev/null @@ -1,36 +0,0 @@ -model: 360gpt2-pro -label: - zh_Hans: 360gpt2-pro - en_US: 360gpt2-pro -model_type: llm -features: - - agent-thought -model_properties: - mode: chat - context_size: 2048 -parameter_rules: - - name: temperature - use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 - default: 1 - - name: max_tokens - use_template: max_tokens - min: 1 - max: 2048 - default: 1024 - - name: frequency_penalty - use_template: frequency_penalty - min: -2 - max: 2 - default: 0 - - name: presence_penalty - use_template: presence_penalty - min: -2 - max: 2 - default: 0 diff --git a/api/core/model_runtime/model_providers/zhinao/llm/__init__.py b/api/core/model_runtime/model_providers/zhinao/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhinao/llm/_position.yaml b/api/core/model_runtime/model_providers/zhinao/llm/_position.yaml deleted file mode 100644 index ab8dbf5182..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/llm/_position.yaml +++ /dev/null @@ -1,3 +0,0 @@ -- 360gpt2-pro -- 360gpt-turbo -- 360gpt-turbo-responsibility-8k diff --git a/api/core/model_runtime/model_providers/zhinao/llm/llm.py b/api/core/model_runtime/model_providers/zhinao/llm/llm.py deleted file mode 100644 index befc3de021..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/llm/llm.py +++ /dev/null @@ -1,31 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel - - -class ZhinaoLargeLanguageModel(OAIAPICompatLargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) - - def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials) - super().validate_credentials(model, credentials) - - @classmethod - def _add_custom_parameters(cls, credentials: dict) -> None: - credentials["mode"] = "chat" - credentials["endpoint_url"] = "https://api.360.cn/v1" diff --git a/api/core/model_runtime/model_providers/zhinao/zhinao.py b/api/core/model_runtime/model_providers/zhinao/zhinao.py deleted file mode 100644 index 2a263292f9..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/zhinao.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class ZhinaoProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - # Use `360gpt-turbo` model for validate, - # no matter what model you pass in, text completion model or chat model - model_instance.validate_credentials(model="360gpt-turbo", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/zhinao/zhinao.yaml b/api/core/model_runtime/model_providers/zhinao/zhinao.yaml deleted file mode 100644 index c5cb142c47..0000000000 --- a/api/core/model_runtime/model_providers/zhinao/zhinao.yaml +++ /dev/null @@ -1,32 +0,0 @@ -provider: zhinao -label: - en_US: 360 AI - zh_Hans: 360 智脑 -description: - en_US: Models provided by 360 AI. - zh_Hans: 360 智脑提供的模型。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg -background: "#e3f0ff" -help: - title: - en_US: Get your API Key from 360 AI. - zh_Hans: 从360 智脑获取 API Key - url: - en_US: https://ai.360.com/platform/keys -supported_model_types: - - llm -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: API Key - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 API Key - en_US: Enter your API Key diff --git a/api/core/model_runtime/model_providers/zhipuai/__init__.py b/api/core/model_runtime/model_providers/zhipuai/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_en.svg deleted file mode 100644 index d32499917d..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_en.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_zh.svg b/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_zh.svg deleted file mode 100644 index 067ea2c427..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_l_zh.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/zhipuai/_assets/icon_s_en.svg deleted file mode 100644 index 016f97ddab..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/_assets/icon_s_en.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/api/core/model_runtime/model_providers/zhipuai/_common.py b/api/core/model_runtime/model_providers/zhipuai/_common.py deleted file mode 100644 index fa95232f71..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/_common.py +++ /dev/null @@ -1,41 +0,0 @@ -from core.model_runtime.errors.invoke import ( - InvokeAuthorizationError, - InvokeBadRequestError, - InvokeConnectionError, - InvokeError, - InvokeRateLimitError, - InvokeServerUnavailableError, -) - - -class _CommonZhipuaiAI: - def _to_credential_kwargs(self, credentials: dict) -> dict: - """ - Transform credentials to kwargs for model instance - - :param credentials: - :return: - """ - credentials_kwargs = { - "api_key": credentials["api_key"] if "api_key" in credentials else credentials.get("zhipuai_api_key"), - } - - return credentials_kwargs - - @property - def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: - """ - Map model invoke error to unified error - The key is the error type thrown to the caller - The value is the error type thrown by the model, - which needs to be converted into a unified error type for the caller. - - :return: Invoke error mapping - """ - return { - InvokeConnectionError: [], - InvokeServerUnavailableError: [], - InvokeRateLimitError: [], - InvokeAuthorizationError: [], - InvokeBadRequestError: [], - } diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/__init__.py b/api/core/model_runtime/model_providers/zhipuai/llm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite.yaml deleted file mode 100644 index 9778de1a2e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm_lite -label: - en_US: chatglm_lite -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.9 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. -deprecated: true diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite_32k.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite_32k.yaml deleted file mode 100644 index 7836d964c6..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_lite_32k.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm_lite_32k -label: - en_US: chatglm_lite_32k -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.9 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. -deprecated: true diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_pro.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_pro.yaml deleted file mode 100644 index b3d53c812b..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_pro.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm_pro -label: - en_US: chatglm_pro -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.9 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. -deprecated: true diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_std.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_std.yaml deleted file mode 100644 index 7d8b9520a0..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_std.yaml +++ /dev/null @@ -1,22 +0,0 @@ -model: chatglm_std -label: - en_US: chatglm_std -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.9 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. -deprecated: true diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_turbo.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_turbo.yaml deleted file mode 100644 index fcd5c5ef64..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/chatglm_turbo.yaml +++ /dev/null @@ -1,51 +0,0 @@ -model: chatglm_turbo -label: - en_US: chatglm_turbo -model_type: llm -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: return_type - label: - zh_Hans: 回复类型 - en_US: Return Type - type: string - help: - zh_Hans: 用于控制每次返回内容的类型,空或者没有此字段时默认按照 json_string 返回,json_string 返回标准的 JSON 字符串,text 返回原始的文本内容。 - en_US: Used to control the type of content returned each time. When it is empty or does not have this field, it will be returned as json_string by default. json_string returns a standard JSON string, and text returns the original text content. - required: false - options: - - text - - json_string diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-0520.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-0520.yaml deleted file mode 100644 index 7fcf692202..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-0520.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4-0520 -label: - en_US: glm-4-0520 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.1' - output: '0.1' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-air.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-air.yaml deleted file mode 100644 index fcd7c7768c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-air.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4-air -label: - en_US: glm-4-air -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.001' - output: '0.001' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-airx.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-airx.yaml deleted file mode 100644 index c9ae5abf19..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-airx.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4-airx -label: - en_US: glm-4-airx -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.01' - output: '0.01' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-flash.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-flash.yaml deleted file mode 100644 index 98c4f72c72..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm-4-flash.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4-flash -label: - en_US: glm-4-flash -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0' - output: '0' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_3_turbo.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_3_turbo.yaml deleted file mode 100644 index 0b5391ce2f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_3_turbo.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-3-turbo -label: - en_US: glm-3-turbo -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 8192 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.001' - output: '0.001' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_4.yaml deleted file mode 100644 index 62f453fb77..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4 -label: - en_US: glm-4 -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.1' - output: '0.1' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_long.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_long.yaml deleted file mode 100644 index 350b080c3f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_long.yaml +++ /dev/null @@ -1,65 +0,0 @@ -model: glm-4-long -label: - en_US: glm-4-long -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat - context_size: 10240 -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - min: 0.0 - max: 1.0 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.001' - output: '0.001' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_plus.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_plus.yaml deleted file mode 100644 index 2d7ebd71cf..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4_plus.yaml +++ /dev/null @@ -1,62 +0,0 @@ -model: glm-4-plus -label: - en_US: glm-4-plus -model_type: llm -features: - - multi-tool-call - - agent-thought - - stream-tool-call -model_properties: - mode: chat -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.7 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 4095 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.05' - output: '0.05' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v.yaml deleted file mode 100644 index 3a1120ff37..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v.yaml +++ /dev/null @@ -1,60 +0,0 @@ -model: glm-4v -label: - en_US: glm-4v -model_type: llm -model_properties: - mode: chat -features: - - vision -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.6 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.05' - output: '0.05' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v_plus.yaml b/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v_plus.yaml deleted file mode 100644 index 14b9623e5a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/glm_4v_plus.yaml +++ /dev/null @@ -1,60 +0,0 @@ -model: glm-4v-plus -label: - en_US: glm-4v-plus -model_type: llm -model_properties: - mode: chat -features: - - vision -parameter_rules: - - name: temperature - use_template: temperature - default: 0.95 - min: 0.0 - max: 1.0 - help: - zh_Hans: 采样温度,控制输出的随机性,必须为正数取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95 值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Sampling temperature, controls the randomness of the output, must be a positive number. The value range is (0.0,1.0], which cannot be equal to 0. The default value is 0.95. The larger the value, the more random and creative the output will be; the smaller the value, The output will be more stable or certain. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: top_p - use_template: top_p - default: 0.6 - help: - zh_Hans: 用温度取样的另一种方法,称为核取样取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1,默认值为 0.7 模型考虑具有 top_p 概率质量tokens的结果例如:0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取 tokens 建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数。 - en_US: Another method of temperature sampling is called kernel sampling. The value range is (0.0, 1.0) open interval, which cannot be equal to 0 or 1. The default value is 0.7. The model considers the results with top_p probability mass tokens. For example 0.1 means The model decoder only considers tokens from the candidate set with the top 10% probability. It is recommended that you adjust the top_p or temperature parameters according to the application scenario, but do not adjust both parameters at the same time. - - name: do_sample - label: - zh_Hans: 采样策略 - en_US: Sampling strategy - type: boolean - help: - zh_Hans: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效。默认值为 true。 - en_US: When `do_sample` is set to true, the sampling strategy is enabled. When `do_sample` is set to false, the sampling strategies such as `temperature` and `top_p` will not take effect. The default value is true. - default: true - - name: stream - label: - zh_Hans: 流处理 - en_US: Event Stream - type: boolean - help: - zh_Hans: 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。默认值为 false。如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data:[DONE]消息。注意:在模型流式输出生成内容的过程中,我们会分批对模型生成内容进行检测,当检测到违法及不良信息时,API会返回错误码(1301)。开发者识别到错误码(1301),应及时采取(清屏、重启对话)等措施删除生成内容,并确保不将含有违法及不良信息的内容传递给模型继续生成,避免其造成负面影响。 - en_US: When using synchronous invocation, this parameter should be set to false or omitted. It indicates that the model will return all the generated content at once after the generation is complete. The default value is false. If set to true, the model will return the generated content in chunks via the standard Event Stream. A data:[DONE] message will be sent at the end of the Event Stream.Note:During the model's streaming output process, we will batch check the generated content. If illegal or harmful information is detected, the API will return an error code (1301). Developers who identify error code (1301) should promptly take actions such as clearing the screen or restarting the conversation to delete the generated content. They should also ensure that no illegal or harmful content is passed back to the model for continued generation to avoid negative impacts. - default: false - - name: max_tokens - use_template: max_tokens - default: 1024 - min: 1 - max: 1024 - - name: web_search - type: boolean - label: - zh_Hans: 联网搜索 - en_US: Web Search - default: false - help: - zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 - en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic. -pricing: - input: '0.01' - output: '0.01' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py b/api/core/model_runtime/model_providers/zhipuai/llm/llm.py deleted file mode 100644 index ea331701ab..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py +++ /dev/null @@ -1,486 +0,0 @@ -from collections.abc import Generator -from typing import Optional, Union - -from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta -from core.model_runtime.entities.message_entities import ( - AssistantPromptMessage, - PromptMessage, - PromptMessageContent, - PromptMessageContentType, - PromptMessageRole, - PromptMessageTool, - SystemPromptMessage, - ToolPromptMessage, - UserPromptMessage, -) -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from core.model_runtime.model_providers.zhipuai._common import _CommonZhipuaiAI -from core.model_runtime.model_providers.zhipuai.zhipuai_sdk._client import ZhipuAI -from core.model_runtime.model_providers.zhipuai.zhipuai_sdk.types.chat.chat_completion import Completion -from core.model_runtime.model_providers.zhipuai.zhipuai_sdk.types.chat.chat_completion_chunk import ChatCompletionChunk -from core.model_runtime.utils import helper - -GLM_JSON_MODE_PROMPT = """You should always follow the instructions and output a valid JSON object. -The structure of the JSON object you can found in the instructions, use {"answer": "$your_answer"} as the default structure -if you are not sure about the structure. - -And you should always end the block with a "```" to indicate the end of the JSON object. - - -{{instructions}} - - -```JSON""" # noqa: E501 - - -class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): - def _invoke( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[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 - """ - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - - # invoke model - # stop = stop or [] - # self._transform_json_prompts(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) - return self._generate(model, credentials_kwargs, prompt_messages, model_parameters, tools, stop, stream, user) - - # def _transform_json_prompts(self, model: str, credentials: dict, - # prompt_messages: list[PromptMessage], model_parameters: dict, - # tools: list[PromptMessageTool] | None = None, stop: list[str] | None = None, - # stream: bool = True, user: str | None = None) \ - # -> None: - # """ - # Transform json prompts to model prompts - # """ - # if "}\n\n" not in stop: - # stop.append("}\n\n") - - # # 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=GLM_JSON_MODE_PROMPT.replace("{{instructions}}", prompt_messages[0].content) - # ) - # else: - # # insert the system message - # prompt_messages.insert(0, SystemPromptMessage( - # content=GLM_JSON_MODE_PROMPT.replace("{{instructions}}", "Please output a valid JSON object.") - # )) - # # check if the last message is a user message - # if len(prompt_messages) > 0 and isinstance(prompt_messages[-1], UserPromptMessage): - # # add ```JSON\n to the last message - # prompt_messages[-1].content += "\n```JSON\n" - # else: - # # append a user message - # prompt_messages.append(UserPromptMessage( - # content="```JSON\n" - # )) - - def get_num_tokens( - self, - model: str, - credentials: dict, - prompt_messages: list[PromptMessage], - tools: Optional[list[PromptMessageTool]] = None, - ) -> int: - """ - Get number of tokens for given prompt messages - - :param model: model name - :param credentials: model credentials - :param prompt_messages: prompt messages - :param tools: tools for tool calling - :return: - """ - prompt = self._convert_messages_to_prompt(prompt_messages, tools) - - return self._get_num_tokens_by_gpt2(prompt) - - def validate_credentials(self, model: str, credentials: dict) -> None: - """ - Validate model credentials - - :param model: model name - :param credentials: model credentials - :return: - """ - try: - # transform credentials to kwargs for model instance - credentials_kwargs = self._to_credential_kwargs(credentials) - self._generate( - model=model, - credentials_kwargs=credentials_kwargs, - prompt_messages=[ - UserPromptMessage(content="ping"), - ], - model_parameters={ - "temperature": 0.5, - }, - tools=[], - stream=False, - ) - except Exception as ex: - raise CredentialsValidateFailedError(str(ex)) - - def _generate( - self, - model: str, - credentials_kwargs: dict, - prompt_messages: list[PromptMessage], - model_parameters: dict, - tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, - stream: bool = True, - user: Optional[str] = None, - ) -> Union[LLMResult, Generator]: - """ - Invoke large language model - - :param model: model name - :param credentials_kwargs: credentials kwargs - :param prompt_messages: prompt messages - :param model_parameters: model parameters - :param stop: stop words - :param stream: is stream response - :param user: unique user id - :return: full response or stream response chunk generator result - """ - extra_model_kwargs = {} - # request to glm-4v-plus with stop words will always response "finish_reason":"network_error" - if stop and model != "glm-4v-plus": - extra_model_kwargs["stop"] = stop - - client = ZhipuAI(api_key=credentials_kwargs["api_key"]) - - if len(prompt_messages) == 0: - raise ValueError("At least one message is required") - - if prompt_messages[0].role == PromptMessageRole.SYSTEM: - if not prompt_messages[0].content: - prompt_messages = prompt_messages[1:] - - # resolve zhipuai model not support system message and user message, assistant message must be in sequence - new_prompt_messages: list[PromptMessage] = [] - for prompt_message in prompt_messages: - copy_prompt_message = prompt_message.copy() - if copy_prompt_message.role in {PromptMessageRole.USER, PromptMessageRole.SYSTEM, PromptMessageRole.TOOL}: - if isinstance(copy_prompt_message.content, list): - # check if model is 'glm-4v' - if model not in {"glm-4v", "glm-4v-plus"}: - # not support list message - continue - # get image and - if not isinstance(copy_prompt_message, UserPromptMessage): - # not support system message - continue - new_prompt_messages.append(copy_prompt_message) - - if not isinstance(copy_prompt_message.content, str): - # not support image message - continue - - if ( - new_prompt_messages - and new_prompt_messages[-1].role == PromptMessageRole.USER - and copy_prompt_message.role == PromptMessageRole.USER - ): - new_prompt_messages[-1].content += "\n\n" + copy_prompt_message.content - else: - if copy_prompt_message.role in {PromptMessageRole.USER, PromptMessageRole.TOOL}: - new_prompt_messages.append(copy_prompt_message) - elif copy_prompt_message.role == PromptMessageRole.SYSTEM: - new_prompt_message = SystemPromptMessage(content=copy_prompt_message.content) - new_prompt_messages.append(new_prompt_message) - else: - new_prompt_message = UserPromptMessage(content=copy_prompt_message.content) - new_prompt_messages.append(new_prompt_message) - else: - if new_prompt_messages and new_prompt_messages[-1].role == PromptMessageRole.ASSISTANT: - new_prompt_messages[-1].content += "\n\n" + copy_prompt_message.content - else: - new_prompt_messages.append(copy_prompt_message) - - if model in {"glm-4v", "glm-4v-plus"}: - params = self._construct_glm_4v_parameter(model, new_prompt_messages, model_parameters) - else: - params = {"model": model, "messages": [], **model_parameters} - # glm model - if not model.startswith("chatglm"): - for prompt_message in new_prompt_messages: - if prompt_message.role == PromptMessageRole.TOOL: - params["messages"].append( - { - "role": "tool", - "content": prompt_message.content, - "tool_call_id": prompt_message.tool_call_id, - } - ) - elif isinstance(prompt_message, AssistantPromptMessage): - if prompt_message.tool_calls: - params["messages"].append( - { - "role": "assistant", - "content": prompt_message.content, - "tool_calls": [ - { - "id": tool_call.id, - "type": tool_call.type, - "function": { - "name": tool_call.function.name, - "arguments": tool_call.function.arguments, - }, - } - for tool_call in prompt_message.tool_calls - ], - } - ) - else: - params["messages"].append({"role": "assistant", "content": prompt_message.content}) - else: - params["messages"].append( - {"role": prompt_message.role.value, "content": prompt_message.content} - ) - else: - # chatglm model - for prompt_message in new_prompt_messages: - # merge system message to user message - if prompt_message.role in { - PromptMessageRole.SYSTEM, - PromptMessageRole.TOOL, - PromptMessageRole.USER, - }: - if len(params["messages"]) > 0 and params["messages"][-1]["role"] == "user": - params["messages"][-1]["content"] += "\n\n" + prompt_message.content - else: - params["messages"].append({"role": "user", "content": prompt_message.content}) - else: - params["messages"].append( - {"role": prompt_message.role.value, "content": prompt_message.content} - ) - - if tools and len(tools) > 0: - params["tools"] = [{"type": "function", "function": helper.dump_model(tool)} for tool in tools] - - if stream: - response = client.chat.completions.create(stream=stream, **params, **extra_model_kwargs) - return self._handle_generate_stream_response(model, credentials_kwargs, tools, response, prompt_messages) - - response = client.chat.completions.create(**params, **extra_model_kwargs) - return self._handle_generate_response(model, credentials_kwargs, tools, response, prompt_messages) - - def _construct_glm_4v_parameter(self, model: str, prompt_messages: list[PromptMessage], model_parameters: dict): - messages = [ - {"role": message.role.value, "content": self._construct_glm_4v_messages(message.content)} - for message in prompt_messages - ] - - params = {"model": model, "messages": messages, **model_parameters} - - return params - - def _construct_glm_4v_messages(self, prompt_message: Union[str, list[PromptMessageContent]]) -> list[dict]: - if isinstance(prompt_message, str): - return [{"type": "text", "text": prompt_message}] - - return [ - {"type": "image_url", "image_url": {"url": self._remove_image_header(item.data)}} - if item.type == PromptMessageContentType.IMAGE - else {"type": "text", "text": item.data} - for item in prompt_message - ] - - def _remove_image_header(self, image: str) -> str: - if image.startswith("data:image"): - return image.split(",")[1] - - return image - - def _handle_generate_response( - self, - model: str, - credentials: dict, - tools: Optional[list[PromptMessageTool]], - response: Completion, - prompt_messages: list[PromptMessage], - ) -> LLMResult: - """ - Handle llm response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :return: llm response - """ - text = "" - assistant_tool_calls: list[AssistantPromptMessage.ToolCall] = [] - for choice in response.choices: - if choice.message.tool_calls: - for tool_call in choice.message.tool_calls: - if tool_call.type == "function": - assistant_tool_calls.append( - AssistantPromptMessage.ToolCall( - id=tool_call.id, - type=tool_call.type, - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ), - ) - ) - - text += choice.message.content or "" - - prompt_usage = response.usage.prompt_tokens - completion_usage = response.usage.completion_tokens - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_usage, completion_usage) - - # transform response - result = LLMResult( - model=model, - prompt_messages=prompt_messages, - message=AssistantPromptMessage(content=text, tool_calls=assistant_tool_calls), - usage=usage, - ) - - return result - - def _handle_generate_stream_response( - self, - model: str, - credentials: dict, - tools: Optional[list[PromptMessageTool]], - responses: Generator[ChatCompletionChunk, None, None], - prompt_messages: list[PromptMessage], - ) -> Generator: - """ - Handle llm stream response - - :param model: model name - :param response: response - :param prompt_messages: prompt messages - :return: llm response chunk generator result - """ - full_assistant_content = "" - for chunk in responses: - if len(chunk.choices) == 0: - continue - - delta = chunk.choices[0] - - if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ""): - continue - - assistant_tool_calls: list[AssistantPromptMessage.ToolCall] = [] - for tool_call in delta.delta.tool_calls or []: - if tool_call.type == "function": - assistant_tool_calls.append( - AssistantPromptMessage.ToolCall( - id=tool_call.id, - type=tool_call.type, - function=AssistantPromptMessage.ToolCall.ToolCallFunction( - name=tool_call.function.name, - arguments=tool_call.function.arguments, - ), - ) - ) - - # transform assistant message to prompt message - assistant_prompt_message = AssistantPromptMessage( - content=delta.delta.content or "", tool_calls=assistant_tool_calls - ) - - full_assistant_content += delta.delta.content or "" - - if delta.finish_reason is not None and chunk.usage is not None: - completion_tokens = chunk.usage.completion_tokens - prompt_tokens = chunk.usage.prompt_tokens - - # transform usage - usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens) - - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint="", - delta=LLMResultChunkDelta( - index=delta.index, - message=assistant_prompt_message, - finish_reason=delta.finish_reason, - usage=usage, - ), - ) - else: - yield LLMResultChunk( - model=chunk.model, - prompt_messages=prompt_messages, - system_fingerprint="", - delta=LLMResultChunkDelta( - index=delta.index, message=assistant_prompt_message, finish_reason=delta.finish_reason - ), - ) - - def _convert_one_message_to_text(self, message: PromptMessage) -> str: - """ - Convert a single message to a string. - - :param message: PromptMessage to convert. - :return: String representation of the message. - """ - human_prompt = "\n\nHuman:" - ai_prompt = "\n\nAssistant:" - content = message.content - - if isinstance(message, UserPromptMessage): - message_text = f"{human_prompt} {content}" - elif isinstance(message, AssistantPromptMessage): - message_text = f"{ai_prompt} {content}" - elif isinstance(message, SystemPromptMessage | ToolPromptMessage): - message_text = content - else: - raise ValueError(f"Got unknown type {message}") - - return message_text - - def _convert_messages_to_prompt( - self, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None - ) -> str: - """ - :param messages: List of PromptMessage to combine. - :return: Combined string with necessary human_prompt and ai_prompt tags. - """ - messages = messages.copy() # don't mutate the original list - - text = "".join(self._convert_one_message_to_text(message) for message in messages) - - if tools and len(tools) > 0: - text += "\n\nTools:" - for tool in tools: - text += f"\n{tool.json()}" - - # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() diff --git a/api/core/model_runtime/model_providers/zhipuai/text_embedding/__init__.py b/api/core/model_runtime/model_providers/zhipuai/text_embedding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-2.yaml b/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-2.yaml deleted file mode 100644 index f1b8b35602..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-2.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: embedding-2 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-3.yaml b/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-3.yaml deleted file mode 100644 index 5c55c911c4..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/text_embedding/embedding-3.yaml +++ /dev/null @@ -1,8 +0,0 @@ -model: embedding-3 -model_type: text-embedding -model_properties: - context_size: 8192 -pricing: - input: '0.0005' - unit: '0.001' - currency: RMB diff --git a/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.yaml b/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.yaml deleted file mode 100644 index b9f5bc6397..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.yaml +++ /dev/null @@ -1,4 +0,0 @@ -model: text_embedding -model_type: text-embedding -model_properties: - context_size: 512 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai.py deleted file mode 100644 index e75aad6eb0..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -from core.model_runtime.entities.model_entities import ModelType -from core.model_runtime.errors.validate import CredentialsValidateFailedError -from core.model_runtime.model_providers.__base.model_provider import ModelProvider - -logger = logging.getLogger(__name__) - - -class ZhipuaiProvider(ModelProvider): - def validate_provider_credentials(self, credentials: dict) -> None: - """ - Validate provider credentials - - if validate failed, raise exception - - :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. - """ - try: - model_instance = self.get_model_instance(ModelType.LLM) - - model_instance.validate_credentials(model="glm-4", credentials=credentials) - except CredentialsValidateFailedError as ex: - raise ex - except Exception as ex: - logger.exception(f"{self.get_provider_schema().provider} credentials validate failed") - raise ex diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai.yaml b/api/core/model_runtime/model_providers/zhipuai/zhipuai.yaml deleted file mode 100644 index 303a549128..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai.yaml +++ /dev/null @@ -1,31 +0,0 @@ -provider: zhipuai -label: - zh_Hans: 智谱 AI - en_US: ZHIPU AI -icon_small: - en_US: icon_s_en.svg -icon_large: - zh_Hans: icon_l_zh.svg - en_US: icon_l_en.svg -background: "#EFF1FE" -help: - title: - en_US: Get your API key from ZHIPU AI - zh_Hans: 从智谱 AI 获取 API Key - url: - en_US: https://open.bigmodel.cn/usercenter/apikeys -supported_model_types: - - llm - - text-embedding -configurate_methods: - - predefined-model -provider_credential_schema: - credential_form_schemas: - - variable: api_key - label: - en_US: APIKey - type: secret-input - required: true - placeholder: - zh_Hans: 在此输入您的 APIKey - en_US: Enter your APIKey diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__init__.py deleted file mode 100644 index fc71d64714..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from .__version__ import __version__ -from ._client import ZhipuAI -from .core import ( - APIAuthenticationError, - APIConnectionError, - APIInternalError, - APIReachLimitError, - APIRequestFailedError, - APIResponseError, - APIResponseValidationError, - APIServerFlowExceedError, - APIStatusError, - APITimeoutError, - ZhipuAIError, -) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__version__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__version__.py deleted file mode 100644 index 51f8c49ecb..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/__version__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "v2.1.0" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/_client.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/_client.py deleted file mode 100644 index 705d371e62..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/_client.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -import os -from collections.abc import Mapping -from typing import Union - -import httpx -from httpx import Timeout -from typing_extensions import override - -from . import api_resource -from .core import NOT_GIVEN, ZHIPUAI_DEFAULT_MAX_RETRIES, HttpClient, NotGiven, ZhipuAIError, _jwt_token - - -class ZhipuAI(HttpClient): - chat: api_resource.chat.Chat - api_key: str - _disable_token_cache: bool = True - - def __init__( - self, - *, - api_key: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, - max_retries: int = ZHIPUAI_DEFAULT_MAX_RETRIES, - http_client: httpx.Client | None = None, - custom_headers: Mapping[str, str] | None = None, - disable_token_cache: bool = True, - _strict_response_validation: bool = False, - ) -> None: - if api_key is None: - api_key = os.environ.get("ZHIPUAI_API_KEY") - if api_key is None: - raise ZhipuAIError("未提供api_key,请通过参数或环境变量提供") - self.api_key = api_key - self._disable_token_cache = disable_token_cache - - if base_url is None: - base_url = os.environ.get("ZHIPUAI_BASE_URL") - if base_url is None: - base_url = "https://open.bigmodel.cn/api/paas/v4" - from .__version__ import __version__ - - super().__init__( - version=__version__, - base_url=base_url, - max_retries=max_retries, - timeout=timeout, - custom_httpx_client=http_client, - custom_headers=custom_headers, - _strict_response_validation=_strict_response_validation, - ) - self.chat = api_resource.chat.Chat(self) - self.images = api_resource.images.Images(self) - self.embeddings = api_resource.embeddings.Embeddings(self) - self.files = api_resource.files.Files(self) - self.fine_tuning = api_resource.fine_tuning.FineTuning(self) - self.batches = api_resource.Batches(self) - self.knowledge = api_resource.Knowledge(self) - self.tools = api_resource.Tools(self) - self.videos = api_resource.Videos(self) - self.assistant = api_resource.Assistant(self) - - @property - @override - def auth_headers(self) -> dict[str, str]: - api_key = self.api_key - if self._disable_token_cache: - return {"Authorization": f"Bearer {api_key}"} - else: - return {"Authorization": f"Bearer {_jwt_token.generate_token(api_key)}"} - - def __del__(self) -> None: - if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close") or not hasattr(self, "_client"): - # if the '__init__' method raised an error, self would not have client attr - return - - if self._has_custom_http_client: - return - - self.close() diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/__init__.py deleted file mode 100644 index 4fe0719dde..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -from .assistant import ( - Assistant, -) -from .batches import Batches -from .chat import ( - AsyncCompletions, - Chat, - Completions, -) -from .embeddings import Embeddings -from .files import Files, FilesWithRawResponse -from .fine_tuning import FineTuning -from .images import Images -from .knowledge import Knowledge -from .tools import Tools -from .videos import ( - Videos, -) - -__all__ = [ - "Videos", - "AsyncCompletions", - "Chat", - "Completions", - "Images", - "Embeddings", - "Files", - "FilesWithRawResponse", - "FineTuning", - "Batches", - "Knowledge", - "Tools", - "Assistant", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/__init__.py deleted file mode 100644 index ce619aa7f0..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .assistant import Assistant - -__all__ = ["Assistant"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/assistant.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/assistant.py deleted file mode 100644 index f772340a82..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/assistant/assistant.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - StreamResponse, - deepcopy_minimal, - make_request_options, - maybe_transform, -) -from ...types.assistant import AssistantCompletion -from ...types.assistant.assistant_conversation_resp import ConversationUsageListResp -from ...types.assistant.assistant_support_resp import AssistantSupportResp - -if TYPE_CHECKING: - from ..._client import ZhipuAI - -from ...types.assistant import assistant_conversation_params, assistant_create_params - -__all__ = ["Assistant"] - - -class Assistant(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def conversation( - self, - assistant_id: str, - model: str, - messages: list[assistant_create_params.ConversationMessage], - *, - stream: bool = True, - conversation_id: Optional[str] = None, - attachments: Optional[list[assistant_create_params.AssistantAttachments]] = None, - metadata: dict | None = None, - request_id: str = None, - user_id: str = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> StreamResponse[AssistantCompletion]: - body = deepcopy_minimal( - { - "assistant_id": assistant_id, - "model": model, - "messages": messages, - "stream": stream, - "conversation_id": conversation_id, - "attachments": attachments, - "metadata": metadata, - "request_id": request_id, - "user_id": user_id, - } - ) - return self._post( - "/assistant", - body=maybe_transform(body, assistant_create_params.AssistantParameters), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=AssistantCompletion, - stream=stream or True, - stream_cls=StreamResponse[AssistantCompletion], - ) - - def query_support( - self, - *, - assistant_id_list: list[str] = None, - request_id: str = None, - user_id: str = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantSupportResp: - body = deepcopy_minimal( - { - "assistant_id_list": assistant_id_list, - "request_id": request_id, - "user_id": user_id, - } - ) - return self._post( - "/assistant/list", - body=body, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=AssistantSupportResp, - ) - - def query_conversation_usage( - self, - assistant_id: str, - page: int = 1, - page_size: int = 10, - *, - request_id: str = None, - user_id: str = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ConversationUsageListResp: - body = deepcopy_minimal( - { - "assistant_id": assistant_id, - "page": page, - "page_size": page_size, - "request_id": request_id, - "user_id": user_id, - } - ) - return self._post( - "/assistant/conversation/list", - body=maybe_transform(body, assistant_conversation_params.ConversationParameters), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=ConversationUsageListResp, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/batches.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/batches.py deleted file mode 100644 index ae2f2be85e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/batches.py +++ /dev/null @@ -1,146 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Literal, Optional - -import httpx - -from ..core import NOT_GIVEN, BaseAPI, Body, Headers, NotGiven, make_request_options, maybe_transform -from ..core.pagination import SyncCursorPage -from ..types import batch_create_params, batch_list_params -from ..types.batch import Batch - -if TYPE_CHECKING: - from .._client import ZhipuAI - - -class Batches(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - completion_window: str | None = None, - endpoint: Literal["/v1/chat/completions", "/v1/embeddings"], - input_file_id: str, - metadata: Optional[dict[str, str]] | NotGiven = NOT_GIVEN, - auto_delete_input_file: bool = True, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Batch: - return self._post( - "/batches", - body=maybe_transform( - { - "completion_window": completion_window, - "endpoint": endpoint, - "input_file_id": input_file_id, - "metadata": metadata, - "auto_delete_input_file": auto_delete_input_file, - }, - batch_create_params.BatchCreateParams, - ), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=Batch, - ) - - def retrieve( - self, - batch_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Batch: - """ - Retrieves a batch. - - Args: - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not batch_id: - raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") - return self._get( - f"/batches/{batch_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=Batch, - ) - - def list( - self, - *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[Batch]: - """List your organization's batches. - - Args: - after: A cursor for use in pagination. - - `after` is an object ID that defines your place - in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include after=obj_foo in order to - fetch the next page of the list. - - limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. - - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/batches", - page=SyncCursorPage[Batch], - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after": after, - "limit": limit, - }, - batch_list_params.BatchListParams, - ), - ), - model=Batch, - ) - - def cancel( - self, - batch_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Batch: - """ - Cancels an in-progress batch. - - Args: - batch_id: The ID of the batch to cancel. - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - - """ - if not batch_id: - raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") - return self._post( - f"/batches/{batch_id}/cancel", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=Batch, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/__init__.py deleted file mode 100644 index 5cd8dc6f33..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .async_completions import AsyncCompletions -from .chat import Chat -from .completions import Completions - -__all__ = ["AsyncCompletions", "Chat", "Completions"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/async_completions.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/async_completions.py deleted file mode 100644 index 05510a3ec4..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/async_completions.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING, Literal, Optional, Union - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - drop_prefix_image_data, - make_request_options, - maybe_transform, -) -from ...types.chat.async_chat_completion import AsyncCompletion, AsyncTaskStatus -from ...types.chat.code_geex import code_geex_params -from ...types.sensitive_word_check import SensitiveWordCheckRequest - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - from ..._client import ZhipuAI - - -class AsyncCompletions(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - model: str, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - seed: int | NotGiven = NOT_GIVEN, - messages: Union[str, list[str], list[int], list[list[int]], None], - stop: Optional[Union[str, list[str], None]] | NotGiven = NOT_GIVEN, - sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN, - tools: Optional[object] | NotGiven = NOT_GIVEN, - tool_choice: str | NotGiven = NOT_GIVEN, - meta: Optional[dict[str, str]] | NotGiven = NOT_GIVEN, - extra: Optional[code_geex_params.CodeGeexExtra] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncTaskStatus: - _cast_type = AsyncTaskStatus - logger.debug(f"temperature:{temperature}, top_p:{top_p}") - if temperature is not None and temperature != NOT_GIVEN: - if temperature <= 0: - do_sample = False - temperature = 0.01 - # logger.warning("temperature:取值范围是:(0.0, 1.0) 开区间,do_sample重写为:false(参数top_p temperature不生效)") # noqa: E501 - if temperature >= 1: - temperature = 0.99 - # logger.warning("temperature:取值范围是:(0.0, 1.0) 开区间") - if top_p is not None and top_p != NOT_GIVEN: - if top_p >= 1: - top_p = 0.99 - # logger.warning("top_p:取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1") - if top_p <= 0: - top_p = 0.01 - # logger.warning("top_p:取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1") - - logger.debug(f"temperature:{temperature}, top_p:{top_p}") - if isinstance(messages, list): - for item in messages: - if item.get("content"): - item["content"] = drop_prefix_image_data(item["content"]) - - body = { - "model": model, - "request_id": request_id, - "user_id": user_id, - "temperature": temperature, - "top_p": top_p, - "do_sample": do_sample, - "max_tokens": max_tokens, - "seed": seed, - "messages": messages, - "stop": stop, - "sensitive_word_check": sensitive_word_check, - "tools": tools, - "tool_choice": tool_choice, - "meta": meta, - "extra": maybe_transform(extra, code_geex_params.CodeGeexExtra), - } - return self._post( - "/async/chat/completions", - body=body, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=_cast_type, - stream=False, - ) - - def retrieve_completion_result( - self, - id: str, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Union[AsyncCompletion, AsyncTaskStatus]: - _cast_type = Union[AsyncCompletion, AsyncTaskStatus] - return self._get( - path=f"/async-result/{id}", - cast_type=_cast_type, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/chat.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/chat.py deleted file mode 100644 index b3cc46566c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/chat.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import TYPE_CHECKING - -from ...core import BaseAPI, cached_property -from .async_completions import AsyncCompletions -from .completions import Completions - -if TYPE_CHECKING: - pass - - -class Chat(BaseAPI): - @cached_property - def completions(self) -> Completions: - return Completions(self._client) - - @cached_property - def asyncCompletions(self) -> AsyncCompletions: # noqa: N802 - return AsyncCompletions(self._client) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/completions.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/completions.py deleted file mode 100644 index 8e5bb454e6..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/chat/completions.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING, Literal, Optional, Union - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - StreamResponse, - deepcopy_minimal, - drop_prefix_image_data, - make_request_options, - maybe_transform, -) -from ...types.chat.chat_completion import Completion -from ...types.chat.chat_completion_chunk import ChatCompletionChunk -from ...types.chat.code_geex import code_geex_params -from ...types.sensitive_word_check import SensitiveWordCheckRequest - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - from ..._client import ZhipuAI - - -class Completions(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - model: str, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - max_tokens: int | NotGiven = NOT_GIVEN, - seed: int | NotGiven = NOT_GIVEN, - messages: Union[str, list[str], list[int], object, None], - stop: Optional[Union[str, list[str], None]] | NotGiven = NOT_GIVEN, - sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN, - tools: Optional[object] | NotGiven = NOT_GIVEN, - tool_choice: str | NotGiven = NOT_GIVEN, - meta: Optional[dict[str, str]] | NotGiven = NOT_GIVEN, - extra: Optional[code_geex_params.CodeGeexExtra] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Completion | StreamResponse[ChatCompletionChunk]: - logger.debug(f"temperature:{temperature}, top_p:{top_p}") - if temperature is not None and temperature != NOT_GIVEN: - if temperature <= 0: - do_sample = False - temperature = 0.01 - # logger.warning("temperature:取值范围是:(0.0, 1.0) 开区间,do_sample重写为:false(参数top_p temperature不生效)") # noqa: E501 - if temperature >= 1: - temperature = 0.99 - # logger.warning("temperature:取值范围是:(0.0, 1.0) 开区间") - if top_p is not None and top_p != NOT_GIVEN: - if top_p >= 1: - top_p = 0.99 - # logger.warning("top_p:取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1") - if top_p <= 0: - top_p = 0.01 - # logger.warning("top_p:取值范围是:(0.0, 1.0) 开区间,不能等于 0 或 1") - - logger.debug(f"temperature:{temperature}, top_p:{top_p}") - if isinstance(messages, list): - for item in messages: - if item.get("content"): - item["content"] = drop_prefix_image_data(item["content"]) - - body = deepcopy_minimal( - { - "model": model, - "request_id": request_id, - "user_id": user_id, - "temperature": temperature, - "top_p": top_p, - "do_sample": do_sample, - "max_tokens": max_tokens, - "seed": seed, - "messages": messages, - "stop": stop, - "sensitive_word_check": sensitive_word_check, - "stream": stream, - "tools": tools, - "tool_choice": tool_choice, - "meta": meta, - "extra": maybe_transform(extra, code_geex_params.CodeGeexExtra), - } - ) - return self._post( - "/chat/completions", - body=body, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=Completion, - stream=stream or False, - stream_cls=StreamResponse[ChatCompletionChunk], - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/embeddings.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/embeddings.py deleted file mode 100644 index 4b4baef942..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/embeddings.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -import httpx - -from ..core import NOT_GIVEN, BaseAPI, Body, Headers, NotGiven, make_request_options -from ..types.embeddings import EmbeddingsResponded - -if TYPE_CHECKING: - from .._client import ZhipuAI - - -class Embeddings(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - input: Union[str, list[str], list[int], list[list[int]]], - model: Union[str], - dimensions: Union[int] | NotGiven = NOT_GIVEN, - encoding_format: str | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - sensitive_word_check: Optional[object] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - disable_strict_validation: Optional[bool] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> EmbeddingsResponded: - _cast_type = EmbeddingsResponded - if disable_strict_validation: - _cast_type = object - return self._post( - "/embeddings", - body={ - "input": input, - "model": model, - "dimensions": dimensions, - "encoding_format": encoding_format, - "user": user, - "request_id": request_id, - "sensitive_word_check": sensitive_word_check, - }, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=_cast_type, - stream=False, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/files.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/files.py deleted file mode 100644 index ba9de75b7e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/files.py +++ /dev/null @@ -1,194 +0,0 @@ -from __future__ import annotations - -from collections.abc import Mapping -from typing import TYPE_CHECKING, Literal, cast - -import httpx - -from ..core import ( - NOT_GIVEN, - BaseAPI, - Body, - FileTypes, - Headers, - NotGiven, - _legacy_binary_response, - _legacy_response, - deepcopy_minimal, - extract_files, - make_request_options, - maybe_transform, -) -from ..types.files import FileDeleted, FileObject, ListOfFileObject, UploadDetail, file_create_params - -if TYPE_CHECKING: - from .._client import ZhipuAI - -__all__ = ["Files", "FilesWithRawResponse"] - - -class Files(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - file: FileTypes = None, - upload_detail: list[UploadDetail] = None, - purpose: Literal["fine-tune", "retrieval", "batch"], - knowledge_id: str = None, - sentence_size: int = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FileObject: - if not file and not upload_detail: - raise ValueError("At least one of `file` and `upload_detail` must be provided.") - body = deepcopy_minimal( - { - "file": file, - "upload_detail": upload_detail, - "purpose": purpose, - "knowledge_id": knowledge_id, - "sentence_size": sentence_size, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/files", - body=maybe_transform(body, file_create_params.FileCreateParams), - files=files, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FileObject, - ) - - # def retrieve( - # self, - # file_id: str, - # *, - # extra_headers: Headers | None = None, - # extra_body: Body | None = None, - # timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - # ) -> FileObject: - # """ - # Returns information about a specific file. - # - # Args: - # file_id: The ID of the file to retrieve information about - # extra_headers: Send extra headers - # - # extra_body: Add additional JSON properties to the request - # - # timeout: Override the client-level default timeout for this request, in seconds - # """ - # if not file_id: - # raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - # return self._get( - # f"/files/{file_id}", - # options=make_request_options( - # extra_headers=extra_headers, extra_body=extra_body, timeout=timeout - # ), - # cast_type=FileObject, - # ) - - def list( - self, - *, - purpose: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - after: str | NotGiven = NOT_GIVEN, - order: str | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ListOfFileObject: - return self._get( - "/files", - cast_type=ListOfFileObject, - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query={ - "purpose": purpose, - "limit": limit, - "after": after, - "order": order, - }, - ), - ) - - def delete( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FileDeleted: - """ - Delete a file. - - Args: - file_id: The ID of the file to delete - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - return self._delete( - f"/files/{file_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FileDeleted, - ) - - def content( - self, - file_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> _legacy_response.HttpxBinaryResponseContent: - """ - Returns the contents of the specified file. - - Args: - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - return self._get( - f"/files/{file_id}/content", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=_legacy_binary_response.HttpxBinaryResponseContent, - ) - - -class FilesWithRawResponse: - def __init__(self, files: Files) -> None: - self._files = files - - self.create = _legacy_response.to_raw_response_wrapper( - files.create, - ) - self.list = _legacy_response.to_raw_response_wrapper( - files.list, - ) - self.content = _legacy_response.to_raw_response_wrapper( - files.content, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/__init__.py deleted file mode 100644 index 7c309b8341..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .fine_tuning import FineTuning -from .jobs import Jobs -from .models import FineTunedModels - -__all__ = ["Jobs", "FineTunedModels", "FineTuning"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/fine_tuning.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/fine_tuning.py deleted file mode 100644 index 8670f7de00..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/fine_tuning.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import TYPE_CHECKING - -from ...core import BaseAPI, cached_property -from .jobs import Jobs -from .models import FineTunedModels - -if TYPE_CHECKING: - pass - - -class FineTuning(BaseAPI): - @cached_property - def jobs(self) -> Jobs: - return Jobs(self._client) - - @cached_property - def models(self) -> FineTunedModels: - return FineTunedModels(self._client) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/__init__.py deleted file mode 100644 index 40777a153f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .jobs import Jobs - -__all__ = ["Jobs"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/jobs.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/jobs.py deleted file mode 100644 index 8b038cadc0..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/jobs/jobs.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -import httpx - -from ....core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - make_request_options, -) -from ....types.fine_tuning import ( - FineTuningJob, - FineTuningJobEvent, - ListOfFineTuningJob, - job_create_params, -) - -if TYPE_CHECKING: - from ...._client import ZhipuAI - -__all__ = ["Jobs"] - - -class Jobs(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - model: str, - training_file: str, - hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTuningJob: - return self._post( - "/fine_tuning/jobs", - body={ - "model": model, - "training_file": training_file, - "hyperparameters": hyperparameters, - "suffix": suffix, - "validation_file": validation_file, - "request_id": request_id, - }, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FineTuningJob, - ) - - def retrieve( - self, - fine_tuning_job_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTuningJob: - return self._get( - f"/fine_tuning/jobs/{fine_tuning_job_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FineTuningJob, - ) - - def list( - self, - *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ListOfFineTuningJob: - return self._get( - "/fine_tuning/jobs", - cast_type=ListOfFineTuningJob, - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query={ - "after": after, - "limit": limit, - }, - ), - ) - - def cancel( - self, - fine_tuning_job_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # noqa: E501 - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTuningJob: - if not fine_tuning_job_id: - raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") - return self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FineTuningJob, - ) - - def list_events( - self, - fine_tuning_job_id: str, - *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTuningJobEvent: - return self._get( - f"/fine_tuning/jobs/{fine_tuning_job_id}/events", - cast_type=FineTuningJobEvent, - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query={ - "after": after, - "limit": limit, - }, - ), - ) - - def delete( - self, - fine_tuning_job_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTuningJob: - if not fine_tuning_job_id: - raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") - return self._delete( - f"/fine_tuning/jobs/{fine_tuning_job_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FineTuningJob, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/__init__.py deleted file mode 100644 index d832635baf..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .fine_tuned_models import FineTunedModels - -__all__ = ["FineTunedModels"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/fine_tuned_models.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/fine_tuned_models.py deleted file mode 100644 index 29c023e3b1..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/fine_tuning/models/fine_tuned_models.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import httpx - -from ....core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - make_request_options, -) -from ....types.fine_tuning.models import FineTunedModelsStatus - -if TYPE_CHECKING: - from ...._client import ZhipuAI - -__all__ = ["FineTunedModels"] - - -class FineTunedModels(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def delete( - self, - fine_tuned_model: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTunedModelsStatus: - if not fine_tuned_model: - raise ValueError(f"Expected a non-empty value for `fine_tuned_model` but received {fine_tuned_model!r}") - return self._delete( - f"fine_tuning/fine_tuned_models/{fine_tuned_model}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=FineTunedModelsStatus, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/images.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/images.py deleted file mode 100644 index 8ad411913f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/images.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -import httpx - -from ..core import NOT_GIVEN, BaseAPI, Body, Headers, NotGiven, make_request_options -from ..types.image import ImagesResponded -from ..types.sensitive_word_check import SensitiveWordCheckRequest - -if TYPE_CHECKING: - from .._client import ZhipuAI - - -class Images(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def generations( - self, - *, - prompt: str, - model: str | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[str] | NotGiven = NOT_GIVEN, - response_format: Optional[str] | NotGiven = NOT_GIVEN, - size: Optional[str] | NotGiven = NOT_GIVEN, - style: Optional[str] | NotGiven = NOT_GIVEN, - sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - user_id: Optional[str] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - disable_strict_validation: Optional[bool] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ImagesResponded: - _cast_type = ImagesResponded - if disable_strict_validation: - _cast_type = object - return self._post( - "/images/generations", - body={ - "prompt": prompt, - "model": model, - "n": n, - "quality": quality, - "response_format": response_format, - "sensitive_word_check": sensitive_word_check, - "size": size, - "style": style, - "user": user, - "user_id": user_id, - "request_id": request_id, - }, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=_cast_type, - stream=False, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/__init__.py deleted file mode 100644 index 5a67d743c3..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .knowledge import Knowledge - -__all__ = ["Knowledge"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/__init__.py deleted file mode 100644 index fd289e2232..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .document import Document - -__all__ = ["Document"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/document.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/document.py deleted file mode 100644 index 2c4066d893..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/document/document.py +++ /dev/null @@ -1,217 +0,0 @@ -from __future__ import annotations - -from collections.abc import Mapping -from typing import TYPE_CHECKING, Literal, Optional, cast - -import httpx - -from ....core import ( - NOT_GIVEN, - BaseAPI, - Body, - FileTypes, - Headers, - NotGiven, - deepcopy_minimal, - extract_files, - make_request_options, - maybe_transform, -) -from ....types.files import UploadDetail, file_create_params -from ....types.knowledge.document import DocumentData, DocumentObject, document_edit_params, document_list_params -from ....types.knowledge.document.document_list_resp import DocumentPage - -if TYPE_CHECKING: - from ...._client import ZhipuAI - -__all__ = ["Document"] - - -class Document(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def create( - self, - *, - file: FileTypes = None, - custom_separator: Optional[list[str]] = None, - upload_detail: list[UploadDetail] = None, - purpose: Literal["retrieval"], - knowledge_id: str = None, - sentence_size: int = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> DocumentObject: - if not file and not upload_detail: - raise ValueError("At least one of `file` and `upload_detail` must be provided.") - body = deepcopy_minimal( - { - "file": file, - "upload_detail": upload_detail, - "purpose": purpose, - "custom_separator": custom_separator, - "knowledge_id": knowledge_id, - "sentence_size": sentence_size, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/files", - body=maybe_transform(body, file_create_params.FileCreateParams), - files=files, - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=DocumentObject, - ) - - def edit( - self, - document_id: str, - knowledge_type: str, - *, - custom_separator: Optional[list[str]] = None, - sentence_size: Optional[int] = None, - callback_url: Optional[str] = None, - callback_header: Optional[dict[str, str]] = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> httpx.Response: - """ - - Args: - document_id: 知识id - knowledge_type: 知识类型: - 1:文章知识: 支持pdf,url,docx - 2.问答知识-文档: 支持pdf,url,docx - 3.问答知识-表格: 支持xlsx - 4.商品库-表格: 支持xlsx - 5.自定义: 支持pdf,url,docx - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - :param knowledge_type: - :param document_id: - :param timeout: - :param extra_body: - :param callback_header: - :param sentence_size: - :param extra_headers: - :param callback_url: - :param custom_separator: - """ - if not document_id: - raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}") - - body = deepcopy_minimal( - { - "id": document_id, - "knowledge_type": knowledge_type, - "custom_separator": custom_separator, - "sentence_size": sentence_size, - "callback_url": callback_url, - "callback_header": callback_header, - } - ) - - return self._put( - f"/document/{document_id}", - body=maybe_transform(body, document_edit_params.DocumentEditParams), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=httpx.Response, - ) - - def list( - self, - knowledge_id: str, - *, - purpose: str | NotGiven = NOT_GIVEN, - page: str | NotGiven = NOT_GIVEN, - limit: str | NotGiven = NOT_GIVEN, - order: Literal["desc", "asc"] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> DocumentPage: - return self._get( - "/files", - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "knowledge_id": knowledge_id, - "purpose": purpose, - "page": page, - "limit": limit, - "order": order, - }, - document_list_params.DocumentListParams, - ), - ), - cast_type=DocumentPage, - ) - - def delete( - self, - document_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> httpx.Response: - """ - Delete a file. - - Args: - - document_id: 知识id - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not document_id: - raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}") - - return self._delete( - f"/document/{document_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=httpx.Response, - ) - - def retrieve( - self, - document_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> DocumentData: - """ - - Args: - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not document_id: - raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}") - - return self._get( - f"/document/{document_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=DocumentData, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/knowledge.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/knowledge.py deleted file mode 100644 index fea4c73ac9..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/knowledge/knowledge.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Literal, Optional - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - cached_property, - deepcopy_minimal, - make_request_options, - maybe_transform, -) -from ...types.knowledge import KnowledgeInfo, KnowledgeUsed, knowledge_create_params, knowledge_list_params -from ...types.knowledge.knowledge_list_resp import KnowledgePage -from .document import Document - -if TYPE_CHECKING: - from ..._client import ZhipuAI - -__all__ = ["Knowledge"] - - -class Knowledge(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - @cached_property - def document(self) -> Document: - return Document(self._client) - - def create( - self, - embedding_id: int, - name: str, - *, - customer_identifier: Optional[str] = None, - description: Optional[str] = None, - background: Optional[Literal["blue", "red", "orange", "purple", "sky"]] = None, - icon: Optional[Literal["question", "book", "seal", "wrench", "tag", "horn", "house"]] = None, - bucket_id: Optional[str] = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KnowledgeInfo: - body = deepcopy_minimal( - { - "embedding_id": embedding_id, - "name": name, - "customer_identifier": customer_identifier, - "description": description, - "background": background, - "icon": icon, - "bucket_id": bucket_id, - } - ) - return self._post( - "/knowledge", - body=maybe_transform(body, knowledge_create_params.KnowledgeBaseParams), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=KnowledgeInfo, - ) - - def modify( - self, - knowledge_id: str, - embedding_id: int, - *, - name: str, - description: Optional[str] = None, - background: Optional[Literal["blue", "red", "orange", "purple", "sky"]] = None, - icon: Optional[Literal["question", "book", "seal", "wrench", "tag", "horn", "house"]] = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> httpx.Response: - body = deepcopy_minimal( - { - "id": knowledge_id, - "embedding_id": embedding_id, - "name": name, - "description": description, - "background": background, - "icon": icon, - } - ) - return self._put( - f"/knowledge/{knowledge_id}", - body=maybe_transform(body, knowledge_create_params.KnowledgeBaseParams), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=httpx.Response, - ) - - def query( - self, - *, - page: int | NotGiven = 1, - size: int | NotGiven = 10, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KnowledgePage: - return self._get( - "/knowledge", - options=make_request_options( - extra_headers=extra_headers, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "size": size, - }, - knowledge_list_params.KnowledgeListParams, - ), - ), - cast_type=KnowledgePage, - ) - - def delete( - self, - knowledge_id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> httpx.Response: - """ - Delete a file. - - Args: - knowledge_id: 知识库ID - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not knowledge_id: - raise ValueError("Expected a non-empty value for `knowledge_id`") - - return self._delete( - f"/knowledge/{knowledge_id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=httpx.Response, - ) - - def used( - self, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> KnowledgeUsed: - """ - Returns the contents of the specified file. - - Args: - extra_headers: Send extra headers - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/knowledge/capacity", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=KnowledgeUsed, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/__init__.py deleted file mode 100644 index 43e4e37da1..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .tools import Tools - -__all__ = ["Tools"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/tools.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/tools.py deleted file mode 100644 index 3c3a630aff..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/tools/tools.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING, Literal, Optional, Union - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - StreamResponse, - deepcopy_minimal, - make_request_options, - maybe_transform, -) -from ...types.tools import WebSearch, WebSearchChunk, tools_web_search_params - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - from ..._client import ZhipuAI - -__all__ = ["Tools"] - - -class Tools(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def web_search( - self, - *, - model: str, - request_id: Optional[str] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - messages: Union[str, list[str], list[int], object, None], - scope: Optional[str] | NotGiven = NOT_GIVEN, - location: Optional[str] | NotGiven = NOT_GIVEN, - recent_days: Optional[int] | NotGiven = NOT_GIVEN, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> WebSearch | StreamResponse[WebSearchChunk]: - body = deepcopy_minimal( - { - "model": model, - "request_id": request_id, - "messages": messages, - "stream": stream, - "scope": scope, - "location": location, - "recent_days": recent_days, - } - ) - return self._post( - "/tools", - body=maybe_transform(body, tools_web_search_params.WebSearchParams), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=WebSearch, - stream=stream or False, - stream_cls=StreamResponse[WebSearchChunk], - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/__init__.py deleted file mode 100644 index 6b0f99ed09..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .videos import ( - Videos, -) - -__all__ = [ - "Videos", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/videos.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/videos.py deleted file mode 100644 index f1f1c08036..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/api_resource/videos/videos.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -import httpx - -from ...core import ( - NOT_GIVEN, - BaseAPI, - Body, - Headers, - NotGiven, - deepcopy_minimal, - make_request_options, - maybe_transform, -) -from ...types.sensitive_word_check import SensitiveWordCheckRequest -from ...types.video import VideoObject, video_create_params - -if TYPE_CHECKING: - from ..._client import ZhipuAI - -__all__ = ["Videos"] - - -class Videos(BaseAPI): - def __init__(self, client: ZhipuAI) -> None: - super().__init__(client) - - def generations( - self, - model: str, - *, - prompt: str = None, - image_url: str = None, - sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN, - request_id: str = None, - user_id: str = None, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> VideoObject: - if not model and not model: - raise ValueError("At least one of `model` and `prompt` must be provided.") - body = deepcopy_minimal( - { - "model": model, - "prompt": prompt, - "image_url": image_url, - "sensitive_word_check": sensitive_word_check, - "request_id": request_id, - "user_id": user_id, - } - ) - return self._post( - "/videos/generations", - body=maybe_transform(body, video_create_params.VideoCreateParams), - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=VideoObject, - ) - - def retrieve_videos_result( - self, - id: str, - *, - extra_headers: Headers | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> VideoObject: - if not id: - raise ValueError("At least one of `id` must be provided.") - - return self._get( - f"/async-result/{id}", - options=make_request_options(extra_headers=extra_headers, extra_body=extra_body, timeout=timeout), - cast_type=VideoObject, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/__init__.py deleted file mode 100644 index 3d6466d279..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/__init__.py +++ /dev/null @@ -1,108 +0,0 @@ -from ._base_api import BaseAPI -from ._base_compat import ( - PYDANTIC_V2, - ConfigDict, - GenericModel, - cached_property, - field_get_default, - get_args, - get_model_config, - get_model_fields, - get_origin, - is_literal_type, - is_union, - parse_obj, -) -from ._base_models import BaseModel, construct_type -from ._base_type import ( - NOT_GIVEN, - Body, - FileTypes, - Headers, - IncEx, - ModelT, - NotGiven, - Query, -) -from ._constants import ( - ZHIPUAI_DEFAULT_LIMITS, - ZHIPUAI_DEFAULT_MAX_RETRIES, - ZHIPUAI_DEFAULT_TIMEOUT, -) -from ._errors import ( - APIAuthenticationError, - APIConnectionError, - APIInternalError, - APIReachLimitError, - APIRequestFailedError, - APIResponseError, - APIResponseValidationError, - APIServerFlowExceedError, - APIStatusError, - APITimeoutError, - ZhipuAIError, -) -from ._files import is_file_content -from ._http_client import HttpClient, make_request_options -from ._sse_client import StreamResponse -from ._utils import ( - deepcopy_minimal, - drop_prefix_image_data, - extract_files, - is_given, - is_list, - is_mapping, - maybe_transform, - parse_date, - parse_datetime, -) - -__all__ = [ - "BaseModel", - "construct_type", - "BaseAPI", - "NOT_GIVEN", - "Headers", - "NotGiven", - "Body", - "IncEx", - "ModelT", - "Query", - "FileTypes", - "PYDANTIC_V2", - "ConfigDict", - "GenericModel", - "get_args", - "is_union", - "parse_obj", - "get_origin", - "is_literal_type", - "get_model_config", - "get_model_fields", - "field_get_default", - "is_file_content", - "ZhipuAIError", - "APIStatusError", - "APIRequestFailedError", - "APIAuthenticationError", - "APIReachLimitError", - "APIInternalError", - "APIServerFlowExceedError", - "APIResponseError", - "APIResponseValidationError", - "APITimeoutError", - "make_request_options", - "HttpClient", - "ZHIPUAI_DEFAULT_TIMEOUT", - "ZHIPUAI_DEFAULT_MAX_RETRIES", - "ZHIPUAI_DEFAULT_LIMITS", - "is_list", - "is_mapping", - "parse_date", - "parse_datetime", - "is_given", - "maybe_transform", - "deepcopy_minimal", - "extract_files", - "StreamResponse", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_api.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_api.py deleted file mode 100644 index 3592ea6bac..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_api.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from .._client import ZhipuAI - - -class BaseAPI: - _client: ZhipuAI - - def __init__(self, client: ZhipuAI) -> None: - self._client = client - self._delete = client.delete - self._get = client.get - self._post = client.post - self._put = client.put - self._patch = client.patch - self._get_api_list = client.get_api_list diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_compat.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_compat.py deleted file mode 100644 index 92a5d683be..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_compat.py +++ /dev/null @@ -1,209 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable -from datetime import date, datetime -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast, overload - -import pydantic -from pydantic.fields import FieldInfo -from typing_extensions import Self - -from ._base_type import StrBytesIntFloat - -_T = TypeVar("_T") -_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) - -# --------------- Pydantic v2 compatibility --------------- - -# Pyright incorrectly reports some of our functions as overriding a method when they don't -# pyright: reportIncompatibleMethodOverride=false - -PYDANTIC_V2 = pydantic.VERSION.startswith("2.") - -# v1 re-exports -if TYPE_CHECKING: - - def parse_date(value: date | StrBytesIntFloat) -> date: ... - - def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: ... - - def get_args(t: type[Any]) -> tuple[Any, ...]: ... - - def is_union(tp: type[Any] | None) -> bool: ... - - def get_origin(t: type[Any]) -> type[Any] | None: ... - - def is_literal_type(type_: type[Any]) -> bool: ... - - def is_typeddict(type_: type[Any]) -> bool: ... - -else: - if PYDANTIC_V2: - from pydantic.v1.typing import ( # noqa: I001 - get_args as get_args, # noqa: PLC0414 - is_union as is_union, # noqa: PLC0414 - get_origin as get_origin, # noqa: PLC0414 - is_typeddict as is_typeddict, # noqa: PLC0414 - is_literal_type as is_literal_type, # noqa: PLC0414 - ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # noqa: PLC0414 - else: - from pydantic.typing import ( # noqa: I001 - get_args as get_args, # noqa: PLC0414 - is_union as is_union, # noqa: PLC0414 - get_origin as get_origin, # noqa: PLC0414 - is_typeddict as is_typeddict, # noqa: PLC0414 - is_literal_type as is_literal_type, # noqa: PLC0414 - ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # noqa: PLC0414 - - -# refactored config -if TYPE_CHECKING: - from pydantic import ConfigDict -else: - if PYDANTIC_V2: - from pydantic import ConfigDict - else: - # TODO: provide an error message here? - ConfigDict = None - - -# renamed methods / properties -def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(value) - else: - # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - return cast(_ModelT, model.parse_obj(value)) - - -def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V2: - return field.is_required() - return field.required # type: ignore - - -def field_get_default(field: FieldInfo) -> Any: - value = field.get_default() - if PYDANTIC_V2: - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None - return value - return value - - -def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V2: - return field.annotation - return field.outer_type_ # type: ignore - - -def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V2: - return model.model_config - return model.__config__ # type: ignore - - -def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V2: - return model.model_fields - return model.__fields__ # type: ignore - - -def model_copy(model: _ModelT) -> _ModelT: - if PYDANTIC_V2: - return model.model_copy() - return model.copy() # type: ignore - - -def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V2: - return model.model_dump_json(indent=indent) - return model.json(indent=indent) # type: ignore - - -def model_dump( - model: pydantic.BaseModel, - *, - exclude_unset: bool = False, - exclude_defaults: bool = False, -) -> dict[str, Any]: - if PYDANTIC_V2: - return model.model_dump( - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - ) - return cast( - "dict[str, Any]", - model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - ), - ) - - -def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(data) - return model.parse_obj(data) # pyright: ignore[reportDeprecated] - - -# generic models -if TYPE_CHECKING: - - class GenericModel(pydantic.BaseModel): ... - -else: - if PYDANTIC_V2: - # there no longer needs to be a distinction in v2 but - # we still have to create our own subclass to avoid - # inconsistent MRO ordering errors - class GenericModel(pydantic.BaseModel): ... - - else: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - - -# cached properties -if TYPE_CHECKING: - cached_property = property - - # we define a separate type (copied from typeshed) - # that represents that `cached_property` is `set`able - # at runtime, which differs from `@property`. - # - # this is a separate type as editors likely special case - # `@property` and we don't want to cause issues just to have - # more helpful internal types. - - class typed_cached_property(Generic[_T]): # noqa: N801 - func: Callable[[Any], _T] - attrname: str | None - - def __init__(self, func: Callable[[Any], _T]) -> None: ... - - @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... - - @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... - - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: - raise NotImplementedError() - - def __set_name__(self, owner: type[Any], name: str) -> None: ... - - # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: ... -else: - try: - from functools import cached_property - except ImportError: - from cached_property import cached_property - - typed_cached_property = cached_property diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_models.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_models.py deleted file mode 100644 index 6d8ba700b7..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_models.py +++ /dev/null @@ -1,670 +0,0 @@ -from __future__ import annotations - -import inspect -import os -from collections.abc import Callable -from datetime import date, datetime -from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, TypeGuard, TypeVar, cast - -import pydantic -import pydantic.generics -from pydantic.fields import FieldInfo -from typing_extensions import ( - ParamSpec, - Protocol, - override, - runtime_checkable, -) - -from ._base_compat import ( - PYDANTIC_V2, - ConfigDict, - field_get_default, - get_args, - get_model_config, - get_model_fields, - get_origin, - is_literal_type, - is_union, - parse_obj, -) -from ._base_compat import ( - GenericModel as BaseGenericModel, -) -from ._base_type import ( - IncEx, - ModelT, -) -from ._utils import ( - PropertyInfo, - coerce_boolean, - extract_type_arg, - is_annotated_type, - is_list, - is_mapping, - parse_date, - parse_datetime, - strip_annotated_type, -) - -if TYPE_CHECKING: - from pydantic_core.core_schema import LiteralSchema, ModelField, ModelFieldsSchema - -__all__ = ["BaseModel", "GenericModel"] -_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") - -_T = TypeVar("_T") -P = ParamSpec("P") - - -@runtime_checkable -class _ConfigProtocol(Protocol): - allow_population_by_field_name: bool - - -class BaseModel(pydantic.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) - ) - else: - - @property - @override - def model_fields_set(self) -> set[str]: - # a forwards-compat shim for pydantic v2 - return self.__fields_set__ # type: ignore - - class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] - extra: Any = pydantic.Extra.allow # type: ignore - - def to_dict( - self, - *, - mode: Literal["json", "python"] = "python", - use_api_names: bool = True, - exclude_unset: bool = True, - exclude_defaults: bool = False, - exclude_none: bool = False, - warnings: bool = True, - ) -> dict[str, object]: - """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. - - By default, fields that were not set by the API will not be included, - and keys will match the API response, *not* the property names from the model. - - For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, - the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). - - Args: - mode: - If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. - If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` - - use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. - """ # noqa: E501 - return self.model_dump( - mode=mode, - by_alias=use_api_names, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - warnings=warnings, - ) - - def to_json( - self, - *, - indent: int | None = 2, - use_api_names: bool = True, - exclude_unset: bool = True, - exclude_defaults: bool = False, - exclude_none: bool = False, - warnings: bool = True, - ) -> str: - """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). - - By default, fields that were not set by the API will not be included, - and keys will match the API response, *not* the property names from the model. - - For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, - the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). - - Args: - indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` - use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that have the default value. - exclude_none: Whether to exclude fields that have a value of `None`. - warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. - """ # noqa: E501 - return self.model_dump_json( - indent=indent, - by_alias=use_api_names, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - warnings=warnings, - ) - - @override - def __str__(self) -> str: - # mypy complains about an invalid self arg - return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc] - - # Override the 'construct' method in a way that supports recursive parsing without validation. - # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. - @classmethod - @override - def construct( - cls: type[ModelT], - _fields_set: set[str] | None = None, - **values: object, - ) -> ModelT: - m = cls.__new__(cls) - fields_values: dict[str, object] = {} - - config = get_model_config(cls) - populate_by_name = ( - config.allow_population_by_field_name - if isinstance(config, _ConfigProtocol) - else config.get("populate_by_name") - ) - - if _fields_set is None: - _fields_set = set() - - model_fields = get_model_fields(cls) - for name, field in model_fields.items(): - key = field.alias - if key is None or (key not in values and populate_by_name): - key = name - - if key in values: - fields_values[name] = _construct_field(value=values[key], field=field, key=key) - _fields_set.add(name) - else: - fields_values[name] = field_get_default(field) - - _extra = {} - for key, value in values.items(): - if key not in model_fields: - if PYDANTIC_V2: - _extra[key] = value - else: - _fields_set.add(key) - fields_values[key] = value - - object.__setattr__(m, "__dict__", fields_values) # noqa: PLC2801 - - if PYDANTIC_V2: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) # noqa: PLC2801 - object.__setattr__(m, "__pydantic_extra__", _extra) # noqa: PLC2801 - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) # noqa: PLC2801 - else: - # init_private_attributes() does not exist in v2 - m._init_private_attributes() # type: ignore - - # copied from Pydantic v1's `construct()` method - object.__setattr__(m, "__fields_set__", _fields_set) # noqa: PLC2801 - - return m - - if not TYPE_CHECKING: - # type checkers incorrectly complain about this assignment - # because the type signatures are technically different - # although not in practice - model_construct = construct - - if not PYDANTIC_V2: - # we define aliases for some of the new pydantic v2 methods so - # that we can just document these methods without having to specify - # a specific pydantic version as some users may not know which - # pydantic version they are currently using - - @override - def model_dump( - self, - *, - mode: Literal["json", "python"] | str = "python", - include: IncEx = None, - exclude: IncEx = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - round_trip: bool = False, - warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, - ) -> dict[str, Any]: - """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump - - Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. - - Args: - mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. - by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. - - Returns: - A dictionary representation of the model. - """ - if mode != "python": - raise ValueError("mode is only supported in Pydantic v2") - if round_trip != False: - raise ValueError("round_trip is only supported in Pydantic v2") - if warnings != True: - raise ValueError("warnings is only supported in Pydantic v2") - if context is not None: - raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any != False: - raise ValueError("serialize_as_any is only supported in Pydantic v2") - return super().dict( # pyright: ignore[reportDeprecated] - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - @override - def model_dump_json( - self, - *, - indent: int | None = None, - include: IncEx = None, - exclude: IncEx = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - round_trip: bool = False, - warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, - ) -> str: - """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json - - Generates a JSON representation of the model using Pydantic's `to_json` method. - - Args: - indent: Indentation to use in the JSON output. If None is passed, the output will be compact. - include: Field(s) to include in the JSON output. Can take either a string or set of strings. - exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. - by_alias: Whether to serialize using field aliases. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that have the default value. - exclude_none: Whether to exclude fields that have a value of `None`. - round_trip: Whether to use serialization/deserialization between JSON and class instance. - warnings: Whether to show any warnings that occurred during serialization. - - Returns: - A JSON string representation of the model. - """ - if round_trip != False: - raise ValueError("round_trip is only supported in Pydantic v2") - if warnings != True: - raise ValueError("warnings is only supported in Pydantic v2") - if context is not None: - raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any != False: - raise ValueError("serialize_as_any is only supported in Pydantic v2") - return super().json( # type: ignore[reportDeprecated] - indent=indent, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - -def _construct_field(value: object, field: FieldInfo, key: str) -> object: - if value is None: - return field_get_default(field) - - if PYDANTIC_V2: - type_ = field.annotation - else: - type_ = cast(type, field.outer_type_) # type: ignore - - if type_ is None: - raise RuntimeError(f"Unexpected field type is None for {key}") - - return construct_type(value=value, type_=type_) - - -def is_basemodel(type_: type) -> bool: - """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" - if is_union(type_): - return any(is_basemodel(variant) for variant in get_args(type_)) - - return is_basemodel_type(type_) - - -def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: - origin = get_origin(type_) or type_ - return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) - - -def build( - base_model_cls: Callable[P, _BaseModelT], - *args: P.args, - **kwargs: P.kwargs, -) -> _BaseModelT: - """Construct a BaseModel class without validation. - - This is useful for cases where you need to instantiate a `BaseModel` - from an API response as this provides type-safe params which isn't supported - by helpers like `construct_type()`. - - ```py - build(MyModel, my_field_a="foo", my_field_b=123) - ``` - """ - if args: - raise TypeError( - "Received positional arguments which are not supported; Keyword arguments must be used instead", - ) - - return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) - - -def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: - """Loose coercion to the expected type with construction of nested values. - - Note: the returned value from this function is not guaranteed to match the - given type. - """ - return cast(_T, construct_type(value=value, type_=type_)) - - -def construct_type(*, value: object, type_: type) -> object: - """Loose coercion to the expected type with construction of nested values. - - If the given value does not match the expected type then it is returned as-is. - """ - # we allow `object` as the input type because otherwise, passing things like - # `Literal['value']` will be reported as a type error by type checkers - type_ = cast("type[object]", type_) - - # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(type_): - meta: tuple[Any, ...] = get_args(type_)[1:] - type_ = extract_type_arg(type_, 0) - else: - meta = () - # we need to use the origin class for any types that are subscripted generics - # e.g. Dict[str, object] - origin = get_origin(type_) or type_ - args = get_args(type_) - - if is_union(origin): - try: - return validate_type(type_=cast("type[object]", type_), value=value) - except Exception: - pass - - # if the type is a discriminated union then we want to construct the right variant - # in the union, even if the data doesn't match exactly, otherwise we'd break code - # that relies on the constructed class types, e.g. - # - # class FooType: - # kind: Literal['foo'] - # value: str - # - # class BarType: - # kind: Literal['bar'] - # value: int - # - # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then - # we'd end up constructing `FooType` when it should be `BarType`. - discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) - if discriminator and is_mapping(value): - variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) - if variant_value and isinstance(variant_value, str): - variant_type = discriminator.mapping.get(variant_value) - if variant_type: - return construct_type(type_=variant_type, value=value) - - # if the data is not valid, use the first variant that doesn't fail while deserializing - for variant in args: - try: - return construct_type(value=value, type_=variant) - except Exception: - continue - - raise RuntimeError(f"Could not convert data into a valid instance of {type_}") - if origin == dict: - if not is_mapping(value): - return value - - _, items_type = get_args(type_) # Dict[_, items_type] - return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} - - if not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)): - if is_list(value): - return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] - - if is_mapping(value): - if issubclass(type_, BaseModel): - return type_.construct(**value) # type: ignore[arg-type] - - return cast(Any, type_).construct(**value) - - if origin == list: - if not is_list(value): - return value - - inner_type = args[0] # List[inner_type] - return [construct_type(value=entry, type_=inner_type) for entry in value] - - if origin == float: - if isinstance(value, int): - coerced = float(value) - if coerced != value: - return value - return coerced - - return value - - if type_ == datetime: - try: - return parse_datetime(value) # type: ignore - except Exception: - return value - - if type_ == date: - try: - return parse_date(value) # type: ignore - except Exception: - return value - - return value - - -@runtime_checkable -class CachedDiscriminatorType(Protocol): - __discriminator__: DiscriminatorDetails - - -class DiscriminatorDetails: - field_name: str - """The name of the discriminator field in the variant class, e.g. - - ```py - class Foo(BaseModel): - type: Literal['foo'] - ``` - - Will result in field_name='type' - """ - - field_alias_from: str | None - """The name of the discriminator field in the API response, e.g. - - ```py - class Foo(BaseModel): - type: Literal['foo'] = Field(alias='type_from_api') - ``` - - Will result in field_alias_from='type_from_api' - """ - - mapping: dict[str, type] - """Mapping of discriminator value to variant type, e.g. - - {'foo': FooVariant, 'bar': BarVariant} - """ - - def __init__( - self, - *, - mapping: dict[str, type], - discriminator_field: str, - discriminator_alias: str | None, - ) -> None: - self.mapping = mapping - self.field_name = discriminator_field - self.field_alias_from = discriminator_alias - - -def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ - - discriminator_field_name: str | None = None - - for annotation in meta_annotations: - if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: - discriminator_field_name = annotation.discriminator - break - - if not discriminator_field_name: - return None - - mapping: dict[str, type] = {} - discriminator_alias: str | None = None - - for variant in get_args(union): - variant = strip_annotated_type(variant) - if is_basemodel_type(variant): - if PYDANTIC_V2: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: - continue - - # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] - - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: - if isinstance(entry, str): - mapping[entry] = variant - else: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: - continue - - # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias - - if field_info.annotation and is_literal_type(field_info.annotation): - for entry in get_args(field_info.annotation): - if isinstance(entry, str): - mapping[entry] = variant - - if not mapping: - return None - - details = DiscriminatorDetails( - mapping=mapping, - discriminator_field=discriminator_field_name, - discriminator_alias=discriminator_alias, - ) - cast(CachedDiscriminatorType, union).__discriminator__ = details - return details - - -def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: - schema = model.__pydantic_core_schema__ - if schema["type"] != "model": - return None - - fields_schema = schema["schema"] - if fields_schema["type"] != "model-fields": - return None - - fields_schema = cast("ModelFieldsSchema", fields_schema) - - field = fields_schema["fields"].get(field_name) - if not field: - return None - - return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] - - -def validate_type(*, type_: type[_T], value: object) -> _T: - """Strict validation that the given value matches the expected type""" - if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): - return cast(_T, parse_obj(type_, value)) - - return cast(_T, _validate_non_model_type(type_=type_, value=value)) - - -# Subclassing here confuses type checkers, so we treat this class as non-inheriting. -if TYPE_CHECKING: - GenericModel = BaseModel -else: - - class GenericModel(BaseGenericModel, BaseModel): - pass - - -if PYDANTIC_V2: - from pydantic import TypeAdapter - - def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: - return TypeAdapter(type_).validate_python(value) - -elif not TYPE_CHECKING: - - class TypeAdapter(Generic[_T]): - """Used as a placeholder to easily convert runtime types to a Pydantic format - to provide validation. - - For example: - ```py - validated = RootModel[int](__root__="5").__root__ - # validated: 5 - ``` - """ - - def __init__(self, type_: type[_T]): - self.type_ = type_ - - def validate_python(self, value: Any) -> _T: - if not isinstance(value, self.type_): - raise ValueError(f"Invalid type: {value} is not of type {self.type_}") - return value - - def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: - return TypeAdapter(type_).validate_python(value) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_type.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_type.py deleted file mode 100644 index ea1d3f09dc..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_base_type.py +++ /dev/null @@ -1,170 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping, Sequence -from os import PathLike -from typing import ( - IO, - TYPE_CHECKING, - Any, - Literal, - Optional, - TypeAlias, - TypeVar, - Union, -) - -import pydantic -from httpx import Response -from typing_extensions import Protocol, TypedDict, override, runtime_checkable - -Query = Mapping[str, object] -Body = object -AnyMapping = Mapping[str, object] -PrimitiveData = Union[str, int, float, bool, None] -Data = Union[PrimitiveData, list[Any], tuple[Any], "Mapping[str, Any]"] -ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) -_T = TypeVar("_T") - -if TYPE_CHECKING: - NoneType: type[None] -else: - NoneType = type(None) - - -# Sentinel class used until PEP 0661 is accepted -class NotGiven: - """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). - - For example: - - ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... - - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - @override - def __repr__(self) -> str: - return "NOT_GIVEN" - - -NotGivenOr = Union[_T, NotGiven] -NOT_GIVEN = NotGiven() - - -class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: - - ```py - # as the default `Content-Type` header is `application/json` that will be sent - client.post('/upload/files', files={'file': b'my raw file content'}) - - # you can't explicitly override the header as it has to be dynamically generated - # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={'Content-Type': 'multipart/form-data'}) - - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={'Content-Type': Omit()}) - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - -@runtime_checkable -class ModelBuilderProtocol(Protocol): - @classmethod - def build( - cls: type[_T], - *, - response: Response, - data: object, - ) -> _T: ... - - -Headers = Mapping[str, Union[str, Omit]] - - -class HeadersLikeProtocol(Protocol): - def get(self, __key: str) -> str | None: ... - - -HeadersLike = Union[Headers, HeadersLikeProtocol] - -ResponseT = TypeVar( - "ResponseT", - bound="Union[str, None, BaseModel, list[Any], dict[str, Any], Response, UnknownResponse, ModelBuilderProtocol, BinaryResponseContent]", # noqa: E501 -) - -StrBytesIntFloat = Union[str, bytes, int, float] - -# Note: copied from Pydantic -# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49 -IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None" - -PostParser = Callable[[Any], Any] - - -@runtime_checkable -class InheritsGeneric(Protocol): - """Represents a type that has inherited from `Generic` - - The `__orig_bases__` property can be used to determine the resolved - type variable for a given base class. - """ - - __orig_bases__: tuple[_GenericAlias] - - -class _GenericAlias(Protocol): - __origin__: type[object] - - -class HttpxSendArgs(TypedDict, total=False): - auth: httpx.Auth - - -# for user input files -if TYPE_CHECKING: - Base64FileInput = Union[IO[bytes], PathLike[str]] - FileContent = Union[IO[bytes], bytes, PathLike[str]] -else: - Base64FileInput = Union[IO[bytes], PathLike] - FileContent = Union[IO[bytes], bytes, PathLike] - -FileTypes = Union[ - # file (or bytes) - FileContent, - # (filename, file (or bytes)) - tuple[Optional[str], FileContent], - # (filename, file (or bytes), content_type) - tuple[Optional[str], FileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], -] -RequestFiles = Union[Mapping[str, FileTypes], Sequence[tuple[str, FileTypes]]] - -# duplicate of the above but without our custom file support -HttpxFileContent = Union[bytes, IO[bytes]] -HttpxFileTypes = Union[ - # file (or bytes) - HttpxFileContent, - # (filename, file (or bytes)) - tuple[Optional[str], HttpxFileContent], - # (filename, file (or bytes), content_type) - tuple[Optional[str], HttpxFileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], -] - -HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[tuple[str, HttpxFileTypes]]] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_constants.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_constants.py deleted file mode 100644 index 8e43bdebec..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_constants.py +++ /dev/null @@ -1,12 +0,0 @@ -import httpx - -RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" -# 通过 `Timeout` 控制接口`connect` 和 `read` 超时时间,默认为`timeout=300.0, connect=8.0` -ZHIPUAI_DEFAULT_TIMEOUT = httpx.Timeout(timeout=300.0, connect=8.0) -# 通过 `retry` 参数控制重试次数,默认为3次 -ZHIPUAI_DEFAULT_MAX_RETRIES = 3 -# 通过 `Limits` 控制最大连接数和保持连接数,默认为`max_connections=50, max_keepalive_connections=10` -ZHIPUAI_DEFAULT_LIMITS = httpx.Limits(max_connections=50, max_keepalive_connections=10) - -INITIAL_RETRY_DELAY = 0.5 -MAX_RETRY_DELAY = 8.0 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_errors.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_errors.py deleted file mode 100644 index e2c9d24c6c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_errors.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -import httpx - -__all__ = [ - "ZhipuAIError", - "APIStatusError", - "APIRequestFailedError", - "APIAuthenticationError", - "APIReachLimitError", - "APIInternalError", - "APIServerFlowExceedError", - "APIResponseError", - "APIResponseValidationError", - "APITimeoutError", - "APIConnectionError", -] - - -class ZhipuAIError(Exception): - def __init__( - self, - message: str, - ) -> None: - super().__init__(message) - - -class APIStatusError(ZhipuAIError): - response: httpx.Response - status_code: int - - def __init__(self, message: str, *, response: httpx.Response) -> None: - super().__init__(message) - self.response = response - self.status_code = response.status_code - - -class APIRequestFailedError(APIStatusError): ... - - -class APIAuthenticationError(APIStatusError): ... - - -class APIReachLimitError(APIStatusError): ... - - -class APIInternalError(APIStatusError): ... - - -class APIServerFlowExceedError(APIStatusError): ... - - -class APIResponseError(ZhipuAIError): - message: str - request: httpx.Request - json_data: object - - def __init__(self, message: str, request: httpx.Request, json_data: object): - self.message = message - self.request = request - self.json_data = json_data - super().__init__(message) - - -class APIResponseValidationError(APIResponseError): - status_code: int - response: httpx.Response - - def __init__(self, response: httpx.Response, json_data: object | None, *, message: str | None = None) -> None: - super().__init__( - message=message or "Data returned by API invalid for expected schema.", - request=response.request, - json_data=json_data, - ) - self.response = response - self.status_code = response.status_code - - -class APIConnectionError(APIResponseError): - def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: - super().__init__(message, request, json_data=None) - - -class APITimeoutError(APIConnectionError): - def __init__(self, request: httpx.Request) -> None: - super().__init__(message="Request timed out.", request=request) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_files.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_files.py deleted file mode 100644 index f9d2e14d9e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_files.py +++ /dev/null @@ -1,75 +0,0 @@ -from __future__ import annotations - -import io -import os -import pathlib -from typing import TypeGuard, overload - -from ._base_type import ( - Base64FileInput, - FileContent, - FileTypes, - HttpxFileContent, - HttpxFileTypes, - HttpxRequestFiles, - RequestFiles, -) -from ._utils import is_mapping_t, is_sequence_t, is_tuple_t - - -def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: - return isinstance(obj, io.IOBase | os.PathLike) - - -def is_file_content(obj: object) -> TypeGuard[FileContent]: - return isinstance(obj, bytes | tuple | io.IOBase | os.PathLike) - - -def assert_is_file_content(obj: object, *, key: str | None = None) -> None: - if not is_file_content(obj): - prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" - raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads" - ) from None - - -@overload -def to_httpx_files(files: None) -> None: ... - - -@overload -def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... - - -def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: - if files is None: - return None - - if is_mapping_t(files): - files = {key: _transform_file(file) for key, file in files.items()} - elif is_sequence_t(files): - files = [(key, _transform_file(file)) for key, file in files] - else: - raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") - - return files - - -def _transform_file(file: FileTypes) -> HttpxFileTypes: - if is_file_content(file): - if isinstance(file, os.PathLike): - path = pathlib.Path(file) - return (path.name, path.read_bytes()) - - return file - - if is_tuple_t(file): - return (file[0], _read_file_content(file[1]), *file[2:]) - - raise TypeError("Expected file types input to be a FileContent type or to be a tuple") - - -def _read_file_content(file: FileContent) -> HttpxFileContent: - if isinstance(file, os.PathLike): - return pathlib.Path(file).read_bytes() - return file diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_http_client.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_http_client.py deleted file mode 100644 index ffdafb85d5..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_http_client.py +++ /dev/null @@ -1,910 +0,0 @@ -from __future__ import annotations - -import inspect -import logging -import time -import warnings -from collections.abc import Iterator, Mapping -from itertools import starmap -from random import random -from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, TypeVar, Union, cast, overload - -import httpx -import pydantic -from httpx import URL, Timeout - -from . import _errors, get_origin -from ._base_compat import model_copy -from ._base_models import GenericModel, construct_type, validate_type -from ._base_type import ( - NOT_GIVEN, - AnyMapping, - Body, - Data, - Headers, - HttpxSendArgs, - ModelBuilderProtocol, - NotGiven, - Omit, - PostParser, - Query, - RequestFiles, - ResponseT, -) -from ._constants import ( - INITIAL_RETRY_DELAY, - MAX_RETRY_DELAY, - RAW_RESPONSE_HEADER, - ZHIPUAI_DEFAULT_LIMITS, - ZHIPUAI_DEFAULT_MAX_RETRIES, - ZHIPUAI_DEFAULT_TIMEOUT, -) -from ._errors import APIConnectionError, APIResponseValidationError, APIStatusError, APITimeoutError -from ._files import to_httpx_files -from ._legacy_response import LegacyAPIResponse -from ._request_opt import FinalRequestOptions, UserRequestInput -from ._response import APIResponse, BaseAPIResponse, extract_response_type -from ._sse_client import StreamResponse -from ._utils import flatten, is_given, is_mapping - -log: logging.Logger = logging.getLogger(__name__) - -# TODO: make base page type vars covariant -SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") -# AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") - -_T = TypeVar("_T") -_T_co = TypeVar("_T_co", covariant=True) - -if TYPE_CHECKING: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT -else: - try: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT - except ImportError: - # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 - HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) - - -headers = { - "Accept": "application/json", - "Content-Type": "application/json; charset=UTF-8", -} - - -class PageInfo: - """Stores the necessary information to build the request to retrieve the next page. - - Either `url` or `params` must be set. - """ - - url: URL | NotGiven - params: Query | NotGiven - - @overload - def __init__( - self, - *, - url: URL, - ) -> None: ... - - @overload - def __init__( - self, - *, - params: Query, - ) -> None: ... - - def __init__( - self, - *, - url: URL | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, - ) -> None: - self.url = url - self.params = params - - -class BasePage(GenericModel, Generic[_T]): - """ - Defines the core interface for pagination. - - Type Args: - ModelT: The pydantic model that represents an item in the response. - - Methods: - has_next_page(): Check if there is another page available - next_page_info(): Get the necessary information to make a request for the next page - """ - - _options: FinalRequestOptions = pydantic.PrivateAttr() - _model: type[_T] = pydantic.PrivateAttr() - - def has_next_page(self) -> bool: - items = self._get_page_items() - if not items: - return False - return self.next_page_info() is not None - - def next_page_info(self) -> Optional[PageInfo]: ... - - def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] - ... - - def _params_from_url(self, url: URL) -> httpx.QueryParams: - # TODO: do we have to preprocess params here? - return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) - - def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: - options = model_copy(self._options) - options._strip_raw_response_header() - - if not isinstance(info.params, NotGiven): - options.params = {**options.params, **info.params} - return options - - if not isinstance(info.url, NotGiven): - params = self._params_from_url(info.url) - url = info.url.copy_with(params=params) - options.params = dict(url.params) - options.url = str(url) - return options - - raise ValueError("Unexpected PageInfo state") - - -class BaseSyncPage(BasePage[_T], Generic[_T]): - _client: HttpClient = pydantic.PrivateAttr() - - def _set_private_attributes( - self, - client: HttpClient, - model: type[_T], - options: FinalRequestOptions, - ) -> None: - self._model = model - self._client = client - self._options = options - - # Pydantic uses a custom `__iter__` method to support casting BaseModels - # to dictionaries. e.g. dict(model). - # As we want to support `for item in page`, this is inherently incompatible - # with the default pydantic behavior. It is not possible to support both - # use cases at once. Fortunately, this is not a big deal as all other pydantic - # methods should continue to work as expected as there is an alternative method - # to cast a model to a dictionary, model.dict(), which is used internally - # by pydantic. - def __iter__(self) -> Iterator[_T]: # type: ignore - for page in self.iter_pages(): - yield from page._get_page_items() - - def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: - page = self - while True: - yield page - if page.has_next_page(): - page = page.get_next_page() - else: - return - - def get_next_page(self: SyncPageT) -> SyncPageT: - info = self.next_page_info() - if not info: - raise RuntimeError( - "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." - ) - - options = self._info_to_options(info) - return self._client._request_api_list(self._model, page=self.__class__, options=options) - - -class HttpClient: - _client: httpx.Client - _version: str - _base_url: URL - max_retries: int - timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _has_custom_http_client: bool - _default_stream_cls: type[StreamResponse[Any]] | None = None - - _strict_response_validation: bool - - def __init__( - self, - *, - version: str, - base_url: URL, - _strict_response_validation: bool, - max_retries: int = ZHIPUAI_DEFAULT_MAX_RETRIES, - timeout: Union[float, Timeout, None], - limits: httpx.Limits | None = None, - custom_httpx_client: httpx.Client | None = None, - custom_headers: Mapping[str, str] | None = None, - ) -> None: - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", # noqa: E501 - category=DeprecationWarning, - stacklevel=3, - ) - if custom_httpx_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = ZHIPUAI_DEFAULT_LIMITS - - if not is_given(timeout): - if custom_httpx_client and custom_httpx_client.timeout != HTTPX_DEFAULT_TIMEOUT: - timeout = custom_httpx_client.timeout - else: - timeout = ZHIPUAI_DEFAULT_TIMEOUT - self.max_retries = max_retries - self.timeout = timeout - self._limits = limits - self._has_custom_http_client = bool(custom_httpx_client) - self._client = custom_httpx_client or httpx.Client( - base_url=base_url, - timeout=self.timeout, - limits=limits, - ) - self._version = version - url = URL(url=base_url) - if not url.raw_path.endswith(b"/"): - url = url.copy_with(raw_path=url.raw_path + b"/") - self._base_url = url - self._custom_headers = custom_headers or {} - self._strict_response_validation = _strict_response_validation - - def _prepare_url(self, url: str) -> URL: - sub_url = URL(url) - if sub_url.is_relative_url: - request_raw_url = self._base_url.raw_path + sub_url.raw_path.lstrip(b"/") - return self._base_url.copy_with(raw_path=request_raw_url) - - return sub_url - - @property - def _default_headers(self): - return { - "Accept": "application/json", - "Content-Type": "application/json; charset=UTF-8", - "ZhipuAI-SDK-Ver": self._version, - "source_type": "zhipu-sdk-python", - "x-request-sdk": "zhipu-sdk-python", - **self.auth_headers, - **self._custom_headers, - } - - @property - def custom_auth(self) -> httpx.Auth | None: - return None - - @property - def auth_headers(self): - return {} - - def _prepare_headers(self, options: FinalRequestOptions) -> httpx.Headers: - custom_headers = options.headers or {} - headers_dict = _merge_mappings(self._default_headers, custom_headers) - - httpx_headers = httpx.Headers(headers_dict) - - return httpx_headers - - def _remaining_retries( - self, - remaining_retries: Optional[int], - options: FinalRequestOptions, - ) -> int: - return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries) - - def _calculate_retry_timeout( - self, - remaining_retries: int, - options: FinalRequestOptions, - response_headers: Optional[httpx.Headers] = None, - ) -> float: - max_retries = options.get_max_retries(self.max_retries) - - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. - # retry_after = self._parse_retry_after_header(response_headers) - # if retry_after is not None and 0 < retry_after <= 60: - # return retry_after - - nb_retries = max_retries - remaining_retries - - # Apply exponential backoff, but not more than the max. - sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) - - # Apply some jitter, plus-or-minus half a second. - jitter = 1 - 0.25 * random() - timeout = sleep_seconds * jitter - return max(timeout, 0) - - def _build_request(self, options: FinalRequestOptions) -> httpx.Request: - kwargs: dict[str, Any] = {} - headers = self._prepare_headers(options) - url = self._prepare_url(options.url) - json_data = options.json_data - if options.extra_json is not None: - if json_data is None: - json_data = cast(Body, options.extra_json) - elif is_mapping(json_data): - json_data = _merge_mappings(json_data, options.extra_json) - else: - raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") - - content_type = headers.get("Content-Type") - # multipart/form-data; boundary=---abc-- - if headers.get("Content-Type") == "multipart/form-data": - if "boundary" not in content_type: - # only remove the header if the boundary hasn't been explicitly set - # as the caller doesn't want httpx to come up with their own boundary - headers.pop("Content-Type") - - if json_data: - kwargs["data"] = self._make_multipartform(json_data) - - return self._client.build_request( - headers=headers, - timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, - method=options.method, - url=url, - json=json_data, - files=options.files, - params=options.params, - **kwargs, - ) - - def _object_to_formdata(self, key: str, value: Data | Mapping[object, object]) -> list[tuple[str, str]]: - items = [] - - if isinstance(value, Mapping): - for k, v in value.items(): - items.extend(self._object_to_formdata(f"{key}[{k}]", v)) - return items - if isinstance(value, list | tuple): - for v in value: - items.extend(self._object_to_formdata(key + "[]", v)) - return items - - def _primitive_value_to_str(val) -> str: - # copied from httpx - if val is True: - return "true" - elif val is False: - return "false" - elif val is None: - return "" - return str(val) - - str_data = _primitive_value_to_str(value) - - if not str_data: - return [] - return [(key, str_data)] - - def _make_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: - items = flatten(list(starmap(self._object_to_formdata, data.items()))) - - serialized: dict[str, object] = {} - for key, value in items: - if key in serialized: - raise ValueError(f"存在重复的键: {key};") - serialized[key] = value - return serialized - - def _process_response_data( - self, - *, - data: object, - cast_type: type[ResponseT], - response: httpx.Response, - ) -> ResponseT: - if data is None: - return cast(ResponseT, None) - - if cast_type is object: - return cast(ResponseT, data) - - try: - if inspect.isclass(cast_type) and issubclass(cast_type, ModelBuilderProtocol): - return cast(ResponseT, cast_type.build(response=response, data=data)) - - if self._strict_response_validation: - return cast(ResponseT, validate_type(type_=cast_type, value=data)) - - return cast(ResponseT, construct_type(type_=cast_type, value=data)) - except pydantic.ValidationError as err: - raise APIResponseValidationError(response=response, json_data=data) from err - - def _should_stream_response_body(self, request: httpx.Request) -> bool: - return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] - - def _should_retry(self, response: httpx.Response) -> bool: - # Note: this is not a standard header - should_retry_header = response.headers.get("x-should-retry") - - # If the server explicitly says whether or not to retry, obey. - if should_retry_header == "true": - log.debug("Retrying as header `x-should-retry` is set to `true`") - return True - if should_retry_header == "false": - log.debug("Not retrying as header `x-should-retry` is set to `false`") - return False - - # Retry on request timeouts. - if response.status_code == 408: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry on lock timeouts. - if response.status_code == 409: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry on rate limits. - if response.status_code == 429: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry internal errors. - if response.status_code >= 500: - log.debug("Retrying due to status code %i", response.status_code) - return True - - log.debug("Not retrying") - return False - - def is_closed(self) -> bool: - return self._client.is_closed - - def close(self): - self._client.close() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - - def request( - self, - cast_type: type[ResponseT], - options: FinalRequestOptions, - remaining_retries: Optional[int] = None, - *, - stream: bool = False, - stream_cls: type[StreamResponse] | None = None, - ) -> ResponseT | StreamResponse: - return self._request( - cast_type=cast_type, - options=options, - stream=stream, - stream_cls=stream_cls, - remaining_retries=remaining_retries, - ) - - def _request( - self, - *, - cast_type: type[ResponseT], - options: FinalRequestOptions, - remaining_retries: int | None, - stream: bool, - stream_cls: type[StreamResponse] | None, - ) -> ResponseT | StreamResponse: - retries = self._remaining_retries(remaining_retries, options) - request = self._build_request(options) - - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth - try: - response = self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) - - if retries > 0: - return self._retry_request( - options, - cast_type, - retries, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) - - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) - - if retries > 0: - return self._retry_request( - options, - cast_type, - retries, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) - - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) - - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if retries > 0 and self._should_retry(err.response): - err.response.close() - return self._retry_request( - options, - cast_type, - retries, - err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) - - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() - - log.debug("Re-raising status error") - raise self._make_status_error(err.response) from None - - # return self._parse_response( - # cast_type=cast_type, - # options=options, - # response=response, - # stream=stream, - # stream_cls=stream_cls, - # ) - return self._process_response( - cast_type=cast_type, - options=options, - response=response, - stream=stream, - stream_cls=stream_cls, - ) - - def _retry_request( - self, - options: FinalRequestOptions, - cast_type: type[ResponseT], - remaining_retries: int, - response_headers: httpx.Headers | None, - *, - stream: bool, - stream_cls: type[StreamResponse] | None, - ) -> ResponseT | StreamResponse: - remaining = remaining_retries - 1 - if remaining == 1: - log.debug("1 retry left") - else: - log.debug("%i retries left", remaining) - - timeout = self._calculate_retry_timeout(remaining, options, response_headers) - log.info("Retrying request to %s in %f seconds", options.url, timeout) - - # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a - # different thread if necessary. - time.sleep(timeout) - - return self._request( - options=options, - cast_type=cast_type, - remaining_retries=remaining, - stream=stream, - stream_cls=stream_cls, - ) - - def _process_response( - self, - *, - cast_type: type[ResponseT], - options: FinalRequestOptions, - response: httpx.Response, - stream: bool, - stream_cls: type[StreamResponse] | None, - ) -> ResponseT: - # _legacy_response with raw_response_header to parser method - if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": - return cast( - ResponseT, - LegacyAPIResponse( - raw=response, - client=self, - cast_type=cast_type, - stream=stream, - stream_cls=stream_cls, - options=options, - ), - ) - - origin = get_origin(cast_type) or cast_type - - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): - if not issubclass(origin, APIResponse): - raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") - - response_cls = cast("type[BaseAPIResponse[Any]]", cast_type) - return cast( - ResponseT, - response_cls( - raw=response, - client=self, - cast_type=extract_response_type(response_cls), - stream=stream, - stream_cls=stream_cls, - options=options, - ), - ) - - if cast_type == httpx.Response: - return cast(ResponseT, response) - - api_response = APIResponse( - raw=response, - client=self, - cast_type=cast("type[ResponseT]", cast_type), # pyright: ignore[reportUnnecessaryCast] - stream=stream, - stream_cls=stream_cls, - options=options, - ) - if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): - return cast(ResponseT, api_response) - - return api_response.parse() - - def _request_api_list( - self, - model: type[object], - page: type[SyncPageT], - options: FinalRequestOptions, - ) -> SyncPageT: - def _parser(resp: SyncPageT) -> SyncPageT: - resp._set_private_attributes( - client=self, - model=model, - options=options, - ) - return resp - - options.post_parser = _parser - - return self.request(page, options, stream=False) - - @overload - def get( - self, - path: str, - *, - cast_type: type[ResponseT], - options: UserRequestInput = {}, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - def get( - self, - path: str, - *, - cast_type: type[ResponseT], - options: UserRequestInput = {}, - stream: Literal[True], - stream_cls: type[StreamResponse], - ) -> StreamResponse: ... - - @overload - def get( - self, - path: str, - *, - cast_type: type[ResponseT], - options: UserRequestInput = {}, - stream: bool, - stream_cls: type[StreamResponse] | None = None, - ) -> ResponseT | StreamResponse: ... - - def get( - self, - path: str, - *, - cast_type: type[ResponseT], - options: UserRequestInput = {}, - stream: bool = False, - stream_cls: type[StreamResponse] | None = None, - ) -> ResponseT: - opts = FinalRequestOptions.construct(method="get", url=path, **options) - return cast(ResponseT, self.request(cast_type, opts, stream=stream, stream_cls=stream_cls)) - - @overload - def post( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - files: RequestFiles | None = None, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - def post( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - files: RequestFiles | None = None, - stream: Literal[True], - stream_cls: type[StreamResponse], - ) -> StreamResponse: ... - - @overload - def post( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - files: RequestFiles | None = None, - stream: bool, - stream_cls: type[StreamResponse] | None = None, - ) -> ResponseT | StreamResponse: ... - - def post( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - files: RequestFiles | None = None, - stream: bool = False, - stream_cls: type[StreamResponse[Any]] | None = None, - ) -> ResponseT | StreamResponse: - opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=to_httpx_files(files), **options - ) - - return cast(ResponseT, self.request(cast_type, opts, stream=stream, stream_cls=stream_cls)) - - def patch( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) - - return self.request( - cast_type=cast_type, - options=opts, - ) - - def put( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - files: RequestFiles | None = None, - ) -> ResponseT | StreamResponse: - opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=to_httpx_files(files), **options - ) - - return self.request( - cast_type=cast_type, - options=opts, - ) - - def delete( - self, - path: str, - *, - cast_type: type[ResponseT], - body: Body | None = None, - options: UserRequestInput = {}, - ) -> ResponseT | StreamResponse: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) - - return self.request( - cast_type=cast_type, - options=opts, - ) - - def get_api_list( - self, - path: str, - *, - model: type[object], - page: type[SyncPageT], - body: Body | None = None, - options: UserRequestInput = {}, - method: str = "get", - ) -> SyncPageT: - opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) - return self._request_api_list(model, page, opts) - - def _make_status_error(self, response) -> APIStatusError: - response_text = response.text.strip() - status_code = response.status_code - error_msg = f"Error code: {status_code}, with error text {response_text}" - - if status_code == 400: - return _errors.APIRequestFailedError(message=error_msg, response=response) - elif status_code == 401: - return _errors.APIAuthenticationError(message=error_msg, response=response) - elif status_code == 429: - return _errors.APIReachLimitError(message=error_msg, response=response) - elif status_code == 500: - return _errors.APIInternalError(message=error_msg, response=response) - elif status_code == 503: - return _errors.APIServerFlowExceedError(message=error_msg, response=response) - return APIStatusError(message=error_msg, response=response) - - -def make_request_options( - *, - query: Query | None = None, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, -) -> UserRequestInput: - """Create a dict of type RequestOptions without keys of NotGiven values.""" - options: UserRequestInput = {} - if extra_headers is not None: - options["headers"] = extra_headers - - if extra_body is not None: - options["extra_json"] = cast(AnyMapping, extra_body) - - if query is not None: - options["params"] = query - - if extra_query is not None: - options["params"] = {**options.get("params", {}), **extra_query} - - if not isinstance(timeout, NotGiven): - options["timeout"] = timeout - - if is_given(post_parser): - # internal - options["post_parser"] = post_parser # type: ignore - - return options - - -def _merge_mappings( - obj1: Mapping[_T_co, Union[_T, Omit]], - obj2: Mapping[_T_co, Union[_T, Omit]], -) -> dict[_T_co, _T]: - """Merge two mappings of the same type, removing any values that are instances of `Omit`. - - In cases with duplicate keys the second mapping takes precedence. - """ - merged = {**obj1, **obj2} - return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_jwt_token.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_jwt_token.py deleted file mode 100644 index 21f158a5f4..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_jwt_token.py +++ /dev/null @@ -1,31 +0,0 @@ -import time - -import cachetools.func -import jwt - -# 缓存时间 3分钟 -CACHE_TTL_SECONDS = 3 * 60 - -# token 有效期比缓存时间 多30秒 -API_TOKEN_TTL_SECONDS = CACHE_TTL_SECONDS + 30 - - -@cachetools.func.ttl_cache(maxsize=10, ttl=CACHE_TTL_SECONDS) -def generate_token(apikey: str): - try: - api_key, secret = apikey.split(".") - except Exception as e: - raise Exception("invalid api_key", e) - - payload = { - "api_key": api_key, - "exp": int(round(time.time() * 1000)) + API_TOKEN_TTL_SECONDS * 1000, - "timestamp": int(round(time.time() * 1000)), - } - ret = jwt.encode( - payload, - secret, - algorithm="HS256", - headers={"alg": "HS256", "sign_type": "SIGN"}, - ) - return ret diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_binary_response.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_binary_response.py deleted file mode 100644 index 51623bd860..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_binary_response.py +++ /dev/null @@ -1,207 +0,0 @@ -from __future__ import annotations - -import os -from collections.abc import AsyncIterator, Iterator -from typing import Any - -import httpx - - -class HttpxResponseContent: - @property - def content(self) -> bytes: - raise NotImplementedError("This method is not implemented for this class.") - - @property - def text(self) -> str: - raise NotImplementedError("This method is not implemented for this class.") - - @property - def encoding(self) -> str | None: - raise NotImplementedError("This method is not implemented for this class.") - - @property - def charset_encoding(self) -> str | None: - raise NotImplementedError("This method is not implemented for this class.") - - def json(self, **kwargs: Any) -> Any: - raise NotImplementedError("This method is not implemented for this class.") - - def read(self) -> bytes: - raise NotImplementedError("This method is not implemented for this class.") - - def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: - raise NotImplementedError("This method is not implemented for this class.") - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - raise NotImplementedError("This method is not implemented for this class.") - - def iter_lines(self) -> Iterator[str]: - raise NotImplementedError("This method is not implemented for this class.") - - def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: - raise NotImplementedError("This method is not implemented for this class.") - - def write_to_file( - self, - file: str | os.PathLike[str], - ) -> None: - raise NotImplementedError("This method is not implemented for this class.") - - def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - raise NotImplementedError("This method is not implemented for this class.") - - def close(self) -> None: - raise NotImplementedError("This method is not implemented for this class.") - - async def aread(self) -> bytes: - raise NotImplementedError("This method is not implemented for this class.") - - async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - raise NotImplementedError("This method is not implemented for this class.") - - async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: - raise NotImplementedError("This method is not implemented for this class.") - - async def aiter_lines(self) -> AsyncIterator[str]: - raise NotImplementedError("This method is not implemented for this class.") - - async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - raise NotImplementedError("This method is not implemented for this class.") - - async def astream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - raise NotImplementedError("This method is not implemented for this class.") - - async def aclose(self) -> None: - raise NotImplementedError("This method is not implemented for this class.") - - -class HttpxBinaryResponseContent(HttpxResponseContent): - response: httpx.Response - - def __init__(self, response: httpx.Response) -> None: - self.response = response - - @property - def content(self) -> bytes: - return self.response.content - - @property - def encoding(self) -> str | None: - return self.response.encoding - - @property - def charset_encoding(self) -> str | None: - return self.response.charset_encoding - - def read(self) -> bytes: - return self.response.read() - - def text(self) -> str: - raise NotImplementedError("Not implemented for binary response content") - - def json(self, **kwargs: Any) -> Any: - raise NotImplementedError("Not implemented for binary response content") - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - raise NotImplementedError("Not implemented for binary response content") - - def iter_lines(self) -> Iterator[str]: - raise NotImplementedError("Not implemented for binary response content") - - async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: - raise NotImplementedError("Not implemented for binary response content") - - async def aiter_lines(self) -> AsyncIterator[str]: - raise NotImplementedError("Not implemented for binary response content") - - def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: - return self.response.iter_bytes(chunk_size) - - def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: - return self.response.iter_raw(chunk_size) - - def write_to_file( - self, - file: str | os.PathLike[str], - ) -> None: - """Write the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - - Note: if you want to stream the data to the file instead of writing - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')` - """ - with open(file, mode="wb") as f: - for data in self.response.iter_bytes(): - f.write(data) - - def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - with open(file, mode="wb") as f: - for data in self.response.iter_bytes(chunk_size): - f.write(data) - - def close(self) -> None: - return self.response.close() - - async def aread(self) -> bytes: - return await self.response.aread() - - async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - return self.response.aiter_bytes(chunk_size) - - async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - return self.response.aiter_raw(chunk_size) - - async def astream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - path = anyio.Path(file) - async with await path.open(mode="wb") as f: - async for data in self.response.aiter_bytes(chunk_size): - await f.write(data) - - async def aclose(self) -> None: - return await self.response.aclose() - - -class HttpxTextBinaryResponseContent(HttpxBinaryResponseContent): - response: httpx.Response - - @property - def text(self) -> str: - return self.response.text - - def json(self, **kwargs: Any) -> Any: - return self.response.json(**kwargs) - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - return self.response.iter_text(chunk_size) - - def iter_lines(self) -> Iterator[str]: - return self.response.iter_lines() - - async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: - return self.response.aiter_text(chunk_size) - - async def aiter_lines(self) -> AsyncIterator[str]: - return self.response.aiter_lines() diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_response.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_response.py deleted file mode 100644 index 51bf21bcdc..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_legacy_response.py +++ /dev/null @@ -1,341 +0,0 @@ -from __future__ import annotations - -import datetime -import functools -import inspect -import logging -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast, get_origin, overload - -import httpx -import pydantic -from typing_extensions import ParamSpec, override - -from ._base_models import BaseModel, is_basemodel -from ._base_type import NoneType -from ._constants import RAW_RESPONSE_HEADER -from ._errors import APIResponseValidationError -from ._legacy_binary_response import HttpxResponseContent, HttpxTextBinaryResponseContent -from ._sse_client import StreamResponse, extract_stream_chunk_type, is_stream_class_type -from ._utils import extract_type_arg, is_annotated_type, is_given - -if TYPE_CHECKING: - from ._http_client import HttpClient - from ._request_opt import FinalRequestOptions - -P = ParamSpec("P") -R = TypeVar("R") -_T = TypeVar("_T") - -log: logging.Logger = logging.getLogger(__name__) - - -class LegacyAPIResponse(Generic[R]): - """This is a legacy class as it will be replaced by `APIResponse` - and `AsyncAPIResponse` in the `_response.py` file in the next major - release. - - For the sync client this will mostly be the same with the exception - of `content` & `text` will be methods instead of properties. In the - async client, all methods will be async. - - A migration script will be provided & the migration in general should - be smooth. - """ - - _cast_type: type[R] - _client: HttpClient - _parsed_by_type: dict[type[Any], Any] - _stream: bool - _stream_cls: type[StreamResponse[Any]] | None - _options: FinalRequestOptions - - http_response: httpx.Response - - def __init__( - self, - *, - raw: httpx.Response, - cast_type: type[R], - client: HttpClient, - stream: bool, - stream_cls: type[StreamResponse[Any]] | None, - options: FinalRequestOptions, - ) -> None: - self._cast_type = cast_type - self._client = client - self._parsed_by_type = {} - self._stream = stream - self._stream_cls = stream_cls - self._options = options - self.http_response = raw - - @property - def request_id(self) -> str | None: - return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] - - @overload - def parse(self, *, to: type[_T]) -> _T: ... - - @overload - def parse(self) -> R: ... - - def parse(self, *, to: type[_T] | None = None) -> R | _T: - """Returns the rich python representation of this response's data. - - NOTE: For the async client: this will become a coroutine in the next major version. - - For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - - You can customize the type that the response is parsed into through - the `to` argument, e.g. - - ```py - from zhipuai import BaseModel - - - class MyModel(BaseModel): - foo: str - - - obj = response.parse(to=MyModel) - print(obj.foo) - ``` - - We support parsing: - - `BaseModel` - - `dict` - - `list` - - `Union` - - `str` - - `int` - - `float` - - `httpx.Response` - """ - cache_key = to if to is not None else self._cast_type - cached = self._parsed_by_type.get(cache_key) - if cached is not None: - return cached # type: ignore[no-any-return] - - parsed = self._parse(to=to) - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - self._parsed_by_type[cache_key] = parsed - return parsed - - @property - def headers(self) -> httpx.Headers: - return self.http_response.headers - - @property - def http_request(self) -> httpx.Request: - return self.http_response.request - - @property - def status_code(self) -> int: - return self.http_response.status_code - - @property - def url(self) -> httpx.URL: - return self.http_response.url - - @property - def method(self) -> str: - return self.http_request.method - - @property - def content(self) -> bytes: - """Return the binary response content. - - NOTE: this will be removed in favour of `.read()` in the - next major version. - """ - return self.http_response.content - - @property - def text(self) -> str: - """Return the decoded response content. - - NOTE: this will be turned into a method in the next major version. - """ - return self.http_response.text - - @property - def http_version(self) -> str: - return self.http_response.http_version - - @property - def is_closed(self) -> bool: - return self.http_response.is_closed - - @property - def elapsed(self) -> datetime.timedelta: - """The time taken for the complete request/response cycle to complete.""" - return self.http_response.elapsed - - def _parse(self, *, to: type[_T] | None = None) -> R | _T: - # unwrap `Annotated[T, ...]` -> `T` - if to and is_annotated_type(to): - to = extract_type_arg(to, 0) - - if self._stream: - if to: - if not is_stream_class_type(to): - raise TypeError(f"Expected custom parse type to be a subclass of {StreamResponse}") - - return cast( - _T, - to( - cast_type=extract_stream_chunk_type( - to, - failure_message="Expected custom stream type to be passed with a type argument, e.g. StreamResponse[ChunkType]", # noqa: E501 - ), - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - if self._stream_cls: - return cast( - R, - self._stream_cls( - cast_type=extract_stream_chunk_type(self._stream_cls), - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - stream_cls = cast("type[StreamResponse[Any]] | None", self._client._default_stream_cls) - if stream_cls is None: - raise MissingStreamClassError() - - return cast( - R, - stream_cls( - cast_type=self._cast_type, - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - cast_type = to if to is not None else self._cast_type - - # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(cast_type): - cast_type = extract_type_arg(cast_type, 0) - - if cast_type is NoneType: - return cast(R, None) - - response = self.http_response - if cast_type == str: - return cast(R, response.text) - - if cast_type == int: - return cast(R, int(response.text)) - - if cast_type == float: - return cast(R, float(response.text)) - - origin = get_origin(cast_type) or cast_type - - if inspect.isclass(origin) and issubclass(origin, HttpxResponseContent): - # in the response, e.g. mime file - *_, filename = response.headers.get("content-disposition", "").split("filename=") - # 判断文件类型是jsonl类型的使用HttpxTextBinaryResponseContent - if filename and filename.endswith(".jsonl") or filename and filename.endswith(".xlsx"): - return cast(R, HttpxTextBinaryResponseContent(response)) - else: - return cast(R, cast_type(response)) # type: ignore - - if origin == LegacyAPIResponse: - raise RuntimeError("Unexpected state - cast_type is `APIResponse`") - - if inspect.isclass(origin) and issubclass(origin, httpx.Response): - # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response - # and pass that class to our request functions. We cannot change the variance to be either - # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct - # the response class ourselves but that is something that should be supported directly in httpx - # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_type != httpx.Response: - raise ValueError("Subclasses of httpx.Response cannot be passed to `cast_type`") - return cast(R, response) - - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): - raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") - - if ( - cast_type is not object - and origin is not list - and origin is not dict - and origin is not Union - and not issubclass(origin, BaseModel) - ): - raise RuntimeError( - f"Unsupported type, expected {cast_type} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." # noqa: E501 - ) - - # split is required to handle cases where additional information is included - # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": - if is_basemodel(cast_type): - try: - data = response.json() - except Exception as exc: - log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) - else: - return self._client._process_response_data( - data=data, - cast_type=cast_type, # type: ignore - response=response, - ) - - if self._client._strict_response_validation: - raise APIResponseValidationError( - response=response, - message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", # noqa: E501 - json_data=response.text, - ) - - # If the API responds with content that isn't JSON then we just return - # the (decoded) text without performing any parsing so that you can still - # handle the response however you need to. - return response.text # type: ignore - - data = response.json() - - return self._client._process_response_data( - data=data, - cast_type=cast_type, # type: ignore - response=response, - ) - - @override - def __repr__(self) -> str: - return f"" - - -class MissingStreamClassError(TypeError): - def __init__(self) -> None: - super().__init__( - "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", # noqa: E501 - ) - - -def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: - """Higher order function that takes one of our bound API methods and wraps it - to support returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "true" - - kwargs["extra_headers"] = extra_headers - - return cast(LegacyAPIResponse[R], func(*args, **kwargs)) - - return wrapped diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_request_opt.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_request_opt.py deleted file mode 100644 index c3b894b3a3..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_request_opt.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, ClassVar, Union, cast - -import pydantic.generics -from httpx import Timeout -from typing_extensions import Required, TypedDict, Unpack, final - -from ._base_compat import PYDANTIC_V2, ConfigDict -from ._base_type import AnyMapping, Body, Headers, HttpxRequestFiles, NotGiven, Query -from ._constants import RAW_RESPONSE_HEADER -from ._utils import is_given, strip_not_given - - -class UserRequestInput(TypedDict, total=False): - headers: Headers - max_retries: int - timeout: float | Timeout | None - params: Query - extra_json: AnyMapping - - -class FinalRequestOptionsInput(TypedDict, total=False): - method: Required[str] - url: Required[str] - params: Query - headers: Headers - max_retries: int - timeout: float | Timeout | None - files: HttpxRequestFiles | None - json_data: Body - extra_json: AnyMapping - - -@final -class FinalRequestOptions(pydantic.BaseModel): - method: str - url: str - params: Query = {} - headers: Union[Headers, NotGiven] = NotGiven() - max_retries: Union[int, NotGiven] = NotGiven() - timeout: Union[float, Timeout, None, NotGiven] = NotGiven() - files: Union[HttpxRequestFiles, None] = None - idempotency_key: Union[str, None] = None - post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() - - # It should be noted that we cannot use `json` here as that would override - # a BaseModel method in an incompatible fashion. - json_data: Union[Body, None] = None - extra_json: Union[AnyMapping, None] = None - - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - else: - - class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] - arbitrary_types_allowed: bool = True - - def get_max_retries(self, max_retries: int) -> int: - if isinstance(self.max_retries, NotGiven): - return max_retries - return self.max_retries - - def _strip_raw_response_header(self) -> None: - if not is_given(self.headers): - return - - if self.headers.get(RAW_RESPONSE_HEADER): - self.headers = {**self.headers} - self.headers.pop(RAW_RESPONSE_HEADER) - - # override the `construct` method so that we can run custom transformations. - # this is necessary as we don't want to do any actual runtime type checking - # (which means we can't use validators) but we do want to ensure that `NotGiven` - # values are not present - # - # type ignore required because we're adding explicit types to `**values` - @classmethod - def construct( # type: ignore - cls, - _fields_set: set[str] | None = None, - **values: Unpack[UserRequestInput], - ) -> FinalRequestOptions: - kwargs: dict[str, Any] = { - # we unconditionally call `strip_not_given` on any value - # as it will just ignore any non-mapping types - key: strip_not_given(value) - for key, value in values.items() - } - if PYDANTIC_V2: - return super().model_construct(_fields_set, **kwargs) - return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] - - if not TYPE_CHECKING: - # type checkers incorrectly complain about this assignment - model_construct = construct diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_response.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_response.py deleted file mode 100644 index 92e6018055..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_response.py +++ /dev/null @@ -1,398 +0,0 @@ -from __future__ import annotations - -import datetime -import inspect -import logging -from collections.abc import Iterator -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast, get_origin, overload - -import httpx -import pydantic -from typing_extensions import ParamSpec, override - -from ._base_models import BaseModel, is_basemodel -from ._base_type import NoneType -from ._errors import APIResponseValidationError, ZhipuAIError -from ._sse_client import StreamResponse, extract_stream_chunk_type, is_stream_class_type -from ._utils import extract_type_arg, extract_type_var_from_base, is_annotated_type, is_given - -if TYPE_CHECKING: - from ._http_client import HttpClient - from ._request_opt import FinalRequestOptions - -P = ParamSpec("P") -R = TypeVar("R") -_T = TypeVar("_T") -_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") -log: logging.Logger = logging.getLogger(__name__) - - -class BaseAPIResponse(Generic[R]): - _cast_type: type[R] - _client: HttpClient - _parsed_by_type: dict[type[Any], Any] - _is_sse_stream: bool - _stream_cls: type[StreamResponse[Any]] - _options: FinalRequestOptions - http_response: httpx.Response - - def __init__( - self, - *, - raw: httpx.Response, - cast_type: type[R], - client: HttpClient, - stream: bool, - stream_cls: type[StreamResponse[Any]] | None = None, - options: FinalRequestOptions, - ) -> None: - self._cast_type = cast_type - self._client = client - self._parsed_by_type = {} - self._is_sse_stream = stream - self._stream_cls = stream_cls - self._options = options - self.http_response = raw - - def _parse(self, *, to: type[_T] | None = None) -> R | _T: - # unwrap `Annotated[T, ...]` -> `T` - if to and is_annotated_type(to): - to = extract_type_arg(to, 0) - - if self._is_sse_stream: - if to: - if not is_stream_class_type(to): - raise TypeError(f"Expected custom parse type to be a subclass of {StreamResponse}") - - return cast( - _T, - to( - cast_type=extract_stream_chunk_type( - to, - failure_message="Expected custom stream type to be passed with a type argument, e.g. StreamResponse[ChunkType]", # noqa: E501 - ), - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - if self._stream_cls: - return cast( - R, - self._stream_cls( - cast_type=extract_stream_chunk_type(self._stream_cls), - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - stream_cls = cast("type[Stream[Any]] | None", self._client._default_stream_cls) - if stream_cls is None: - raise MissingStreamClassError() - - return cast( - R, - stream_cls( - cast_type=self._cast_type, - response=self.http_response, - client=cast(Any, self._client), - ), - ) - - cast_type = to if to is not None else self._cast_type - - # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(cast_type): - cast_type = extract_type_arg(cast_type, 0) - - if cast_type is NoneType: - return cast(R, None) - - response = self.http_response - if cast_type == str: - return cast(R, response.text) - - if cast_type == bytes: - return cast(R, response.content) - - if cast_type == int: - return cast(R, int(response.text)) - - if cast_type == float: - return cast(R, float(response.text)) - - origin = get_origin(cast_type) or cast_type - - # handle the legacy binary response case - if inspect.isclass(cast_type) and cast_type.__name__ == "HttpxBinaryResponseContent": - return cast(R, cast_type(response)) # type: ignore - - if origin == APIResponse: - raise RuntimeError("Unexpected state - cast_type is `APIResponse`") - - if inspect.isclass(origin) and issubclass(origin, httpx.Response): - # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response - # and pass that class to our request functions. We cannot change the variance to be either - # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct - # the response class ourselves but that is something that should be supported directly in httpx - # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_type != httpx.Response: - raise ValueError("Subclasses of httpx.Response cannot be passed to `cast_type`") - return cast(R, response) - - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): - raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") - - if ( - cast_type is not object - and origin is not list - and origin is not dict - and origin is not Union - and not issubclass(origin, BaseModel) - ): - raise RuntimeError( - f"Unsupported type, expected {cast_type} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." # noqa: E501 - ) - - # split is required to handle cases where additional information is included - # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": - if is_basemodel(cast_type): - try: - data = response.json() - except Exception as exc: - log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) - else: - return self._client._process_response_data( - data=data, - cast_type=cast_type, # type: ignore - response=response, - ) - - if self._client._strict_response_validation: - raise APIResponseValidationError( - response=response, - message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", # noqa: E501 - json_data=response.text, - ) - - # If the API responds with content that isn't JSON then we just return - # the (decoded) text without performing any parsing so that you can still - # handle the response however you need to. - return response.text # type: ignore - - data = response.json() - - return self._client._process_response_data( - data=data, - cast_type=cast_type, # type: ignore - response=response, - ) - - @property - def headers(self) -> httpx.Headers: - return self.http_response.headers - - @property - def http_request(self) -> httpx.Request: - """Returns the httpx Request instance associated with the current response.""" - return self.http_response.request - - @property - def status_code(self) -> int: - return self.http_response.status_code - - @property - def url(self) -> httpx.URL: - """Returns the URL for which the request was made.""" - return self.http_response.url - - @property - def method(self) -> str: - return self.http_request.method - - @property - def http_version(self) -> str: - return self.http_response.http_version - - @property - def elapsed(self) -> datetime.timedelta: - """The time taken for the complete request/response cycle to complete.""" - return self.http_response.elapsed - - @property - def is_closed(self) -> bool: - """Whether or not the response body has been closed. - - If this is False then there is response data that has not been read yet. - You must either fully consume the response body or call `.close()` - before discarding the response to prevent resource leaks. - """ - return self.http_response.is_closed - - @override - def __repr__(self) -> str: - return f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_type}>" # noqa: E501 - - -class APIResponse(BaseAPIResponse[R]): - @property - def request_id(self) -> str | None: - return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] - - @overload - def parse(self, *, to: type[_T]) -> _T: ... - - @overload - def parse(self) -> R: ... - - def parse(self, *, to: type[_T] | None = None) -> R | _T: - """Returns the rich python representation of this response's data. - - For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - - You can customize the type that the response is parsed into through - the `to` argument, e.g. - - ```py - from openai import BaseModel - - - class MyModel(BaseModel): - foo: str - - - obj = response.parse(to=MyModel) - print(obj.foo) - ``` - - We support parsing: - - `BaseModel` - - `dict` - - `list` - - `Union` - - `str` - - `int` - - `float` - - `httpx.Response` - """ - cache_key = to if to is not None else self._cast_type - cached = self._parsed_by_type.get(cache_key) - if cached is not None: - return cached # type: ignore[no-any-return] - - if not self._is_sse_stream: - self.read() - - parsed = self._parse(to=to) - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - self._parsed_by_type[cache_key] = parsed - return parsed - - def read(self) -> bytes: - """Read and return the binary response content.""" - try: - return self.http_response.read() - except httpx.StreamConsumed as exc: - # The default error raised by httpx isn't very - # helpful in our case so we re-raise it with - # a different error message. - raise StreamAlreadyConsumed() from exc - - def text(self) -> str: - """Read and decode the response content into a string.""" - self.read() - return self.http_response.text - - def json(self) -> object: - """Read and decode the JSON response content.""" - self.read() - return self.http_response.json() - - def close(self) -> None: - """Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self.http_response.close() - - def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: - """ - A byte-iterator over the decoded response content. - - This automatically handles gzip, deflate and brotli encoded responses. - """ - yield from self.http_response.iter_bytes(chunk_size) - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - """A str-iterator over the decoded response content - that handles both gzip, deflate, etc but also detects the content's - string encoding. - """ - yield from self.http_response.iter_text(chunk_size) - - def iter_lines(self) -> Iterator[str]: - """Like `iter_text()` but will only yield chunks for each line""" - yield from self.http_response.iter_lines() - - -class MissingStreamClassError(TypeError): - def __init__(self) -> None: - super().__init__( - "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", # noqa: E501 - ) - - -class StreamAlreadyConsumed(ZhipuAIError): # noqa: N818 - """ - Attempted to read or stream content, but the content has already - been streamed. - - This can happen if you use a method like `.iter_lines()` and then attempt - to read th entire response body afterwards, e.g. - - ```py - response = await client.post(...) - async for line in response.iter_lines(): - ... # do something with `line` - - content = await response.read() - # ^ error - ``` - - If you want this behavior you'll need to either manually accumulate the response - content or call `await response.read()` before iterating over the stream. - """ - - def __init__(self) -> None: - message = ( - "Attempted to read or stream some content, but the content has " - "already been streamed. " - "This could be due to attempting to stream the response " - "content more than once." - "\n\n" - "You can fix this by manually accumulating the response content while streaming " - "or by calling `.read()` before starting to stream." - ) - super().__init__(message) - - -def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: - """Given a type like `APIResponse[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyResponse(APIResponse[bytes]): - ... - - extract_response_type(MyResponse) -> bytes - ``` - """ - return extract_type_var_from_base( - typ, - generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse)), - index=0, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_sse_client.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_sse_client.py deleted file mode 100644 index cbc449d244..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_sse_client.py +++ /dev/null @@ -1,206 +0,0 @@ -from __future__ import annotations - -import inspect -import json -from collections.abc import Iterator, Mapping -from typing import TYPE_CHECKING, Generic, TypeGuard, cast - -import httpx - -from . import get_origin -from ._base_type import ResponseT -from ._errors import APIResponseError -from ._utils import extract_type_var_from_base, is_mapping - -_FIELD_SEPARATOR = ":" - -if TYPE_CHECKING: - from ._http_client import HttpClient - - -class StreamResponse(Generic[ResponseT]): - response: httpx.Response - _cast_type: type[ResponseT] - - def __init__( - self, - *, - cast_type: type[ResponseT], - response: httpx.Response, - client: HttpClient, - ) -> None: - self.response = response - self._cast_type = cast_type - self._data_process_func = client._process_response_data - self._stream_chunks = self.__stream__() - - def __next__(self) -> ResponseT: - return self._stream_chunks.__next__() - - def __iter__(self) -> Iterator[ResponseT]: - yield from self._stream_chunks - - def __stream__(self) -> Iterator[ResponseT]: - sse_line_parser = SSELineParser() - iterator = sse_line_parser.iter_lines(self.response.iter_lines()) - - for sse in iterator: - if sse.data.startswith("[DONE]"): - break - - if sse.event is None: - data = sse.json_data() - if isinstance(data, Mapping) and data.get("error"): - raise APIResponseError( - message="An error occurred during streaming", - request=self.response.request, - json_data=data["error"], - ) - if sse.event is None: - data = sse.json_data() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIResponseError( - message=message, - request=self.response.request, - json_data=data["error"], - ) - yield self._data_process_func(data=data, cast_type=self._cast_type, response=self.response) - - else: - data = sse.json_data() - - if sse.event == "error" and is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIResponseError( - message=message, - request=self.response.request, - json_data=data["error"], - ) - yield self._data_process_func(data=data, cast_type=self._cast_type, response=self.response) - - for sse in iterator: - pass - - -class Event: - def __init__( - self, event: str | None = None, data: str | None = None, id: str | None = None, retry: int | None = None - ): - self._event = event - self._data = data - self._id = id - self._retry = retry - - def __repr__(self): - data_len = len(self._data) if self._data else 0 - return ( - f"Event(event={self._event}, data={self._data} ,data_length={data_len}, id={self._id}, retry={self._retry}" - ) - - @property - def event(self): - return self._event - - @property - def data(self): - return self._data - - def json_data(self): - return json.loads(self._data) - - @property - def id(self): - return self._id - - @property - def retry(self): - return self._retry - - -class SSELineParser: - _data: list[str] - _event: str | None - _retry: int | None - _id: str | None - - def __init__(self): - self._event = None - self._data = [] - self._id = None - self._retry = None - - def iter_lines(self, lines: Iterator[str]) -> Iterator[Event]: - for line in lines: - line = line.rstrip("\n") - if not line: - if self._event is None and not self._data and self._id is None and self._retry is None: - continue - sse_event = Event(event=self._event, data="\n".join(self._data), id=self._id, retry=self._retry) - self._event = None - self._data = [] - self._id = None - self._retry = None - - yield sse_event - self.decode_line(line) - - def decode_line(self, line: str): - if line.startswith(":") or not line: - return - - field, _p, value = line.partition(":") - - value = value.removeprefix(" ") - if field == "data": - self._data.append(value) - elif field == "event": - self._event = value - elif field == "retry": - try: - self._retry = int(value) - except (TypeError, ValueError): - pass - return - - -def is_stream_class_type(typ: type) -> TypeGuard[type[StreamResponse[object]]]: - """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" - origin = get_origin(typ) or typ - return inspect.isclass(origin) and issubclass(origin, StreamResponse) - - -def extract_stream_chunk_type( - stream_cls: type, - *, - failure_message: str | None = None, -) -> type: - """Given a type like `StreamResponse[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyStream(StreamResponse[bytes]): - ... - - extract_stream_chunk_type(MyStream) -> bytes - ``` - """ - - return extract_type_var_from_base( - stream_cls, - index=0, - generic_bases=cast("tuple[type, ...]", (StreamResponse,)), - failure_message=failure_message, - ) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/__init__.py deleted file mode 100644 index a66b095816..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -from ._utils import ( # noqa: I001 - remove_notgiven_indict as remove_notgiven_indict, # noqa: PLC0414 - flatten as flatten, # noqa: PLC0414 - is_dict as is_dict, # noqa: PLC0414 - is_list as is_list, # noqa: PLC0414 - is_given as is_given, # noqa: PLC0414 - is_tuple as is_tuple, # noqa: PLC0414 - is_mapping as is_mapping, # noqa: PLC0414 - is_tuple_t as is_tuple_t, # noqa: PLC0414 - parse_date as parse_date, # noqa: PLC0414 - is_iterable as is_iterable, # noqa: PLC0414 - is_sequence as is_sequence, # noqa: PLC0414 - coerce_float as coerce_float, # noqa: PLC0414 - is_mapping_t as is_mapping_t, # noqa: PLC0414 - removeprefix as removeprefix, # noqa: PLC0414 - removesuffix as removesuffix, # noqa: PLC0414 - extract_files as extract_files, # noqa: PLC0414 - is_sequence_t as is_sequence_t, # noqa: PLC0414 - required_args as required_args, # noqa: PLC0414 - coerce_boolean as coerce_boolean, # noqa: PLC0414 - coerce_integer as coerce_integer, # noqa: PLC0414 - file_from_path as file_from_path, # noqa: PLC0414 - parse_datetime as parse_datetime, # noqa: PLC0414 - strip_not_given as strip_not_given, # noqa: PLC0414 - deepcopy_minimal as deepcopy_minimal, # noqa: PLC0414 - get_async_library as get_async_library, # noqa: PLC0414 - maybe_coerce_float as maybe_coerce_float, # noqa: PLC0414 - get_required_header as get_required_header, # noqa: PLC0414 - maybe_coerce_boolean as maybe_coerce_boolean, # noqa: PLC0414 - maybe_coerce_integer as maybe_coerce_integer, # noqa: PLC0414 - drop_prefix_image_data as drop_prefix_image_data, # noqa: PLC0414 -) - - -from ._typing import ( - is_list_type as is_list_type, # noqa: PLC0414 - is_union_type as is_union_type, # noqa: PLC0414 - extract_type_arg as extract_type_arg, # noqa: PLC0414 - is_iterable_type as is_iterable_type, # noqa: PLC0414 - is_required_type as is_required_type, # noqa: PLC0414 - is_annotated_type as is_annotated_type, # noqa: PLC0414 - strip_annotated_type as strip_annotated_type, # noqa: PLC0414 - extract_type_var_from_base as extract_type_var_from_base, # noqa: PLC0414 -) - -from ._transform import ( - PropertyInfo as PropertyInfo, # noqa: PLC0414 - transform as transform, # noqa: PLC0414 - async_transform as async_transform, # noqa: PLC0414 - maybe_transform as maybe_transform, # noqa: PLC0414 - async_maybe_transform as async_maybe_transform, # noqa: PLC0414 -) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_transform.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_transform.py deleted file mode 100644 index e8ef1f7935..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_transform.py +++ /dev/null @@ -1,383 +0,0 @@ -from __future__ import annotations - -import base64 -import io -import pathlib -from collections.abc import Mapping -from datetime import date, datetime -from typing import Any, Literal, TypeVar, cast, get_args, get_type_hints - -import anyio -import pydantic -from typing_extensions import override - -from .._base_compat import is_typeddict, model_dump -from .._files import is_base64_file_input -from ._typing import ( - extract_type_arg, - is_annotated_type, - is_iterable_type, - is_list_type, - is_required_type, - is_union_type, - strip_annotated_type, -) -from ._utils import ( - is_iterable, - is_list, - is_mapping, -) - -_T = TypeVar("_T") - - -# TODO: support for drilling globals() and locals() -# TODO: ensure works correctly with forward references in all cases - - -PropertyFormat = Literal["iso8601", "base64", "custom"] - - -class PropertyInfo: - """Metadata class to be used in Annotated types to provide information about a given type. - - For example: - - class MyParams(TypedDict): - account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] - - This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. - """ # noqa: E501 - - alias: str | None - format: PropertyFormat | None - format_template: str | None - discriminator: str | None - - def __init__( - self, - *, - alias: str | None = None, - format: PropertyFormat | None = None, - format_template: str | None = None, - discriminator: str | None = None, - ) -> None: - self.alias = alias - self.format = format - self.format_template = format_template - self.discriminator = discriminator - - @override - def __repr__(self) -> str: - return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" # noqa: E501 - - -def maybe_transform( - data: object, - expected_type: object, -) -> Any | None: - """Wrapper over `transform()` that allows `None` to be passed. - - See `transform()` for more details. - """ - if data is None: - return None - return transform(data, expected_type) - - -# Wrapper over _transform_recursive providing fake types -def transform( - data: _T, - expected_type: object, -) -> _T: - """Transform dictionaries based off of type information from the given type, for example: - - ```py - class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - - - transformed = transform({"card_id": ""}, Params) - # {'cardID': ''} - ``` - - Any keys / data that does not have type information given will be included as is. - - It should be noted that the transformations that this function does are not represented in the type system. - """ - transformed = _transform_recursive(data, annotation=cast(type, expected_type)) - return cast(_T, transformed) - - -def _get_annotated_type(type_: type) -> type | None: - """If the given type is an `Annotated` type then it is returned, if not `None` is returned. - - This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` - """ - if is_required_type(type_): - # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` - type_ = get_args(type_)[0] - - if is_annotated_type(type_): - return type_ - - return None - - -def _maybe_transform_key(key: str, type_: type) -> str: - """Transform the given `data` based on the annotations provided in `type_`. - - Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. - """ - annotated_type = _get_annotated_type(type_) - if annotated_type is None: - # no `Annotated` definition for this type, no transformation needed - return key - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.alias is not None: - return annotation.alias - - return key - - -def _transform_recursive( - data: object, - *, - annotation: type, - inner_type: type | None = None, -) -> object: - """Transform the given data against the expected type. - - Args: - annotation: The direct type annotation given to the particular piece of data. - This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc - - inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type - is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in - the list can be transformed using the metadata from the container type. - - Defaults to the same value as the `annotation` argument. - """ - if inner_type is None: - inner_type = annotation - - stripped_type = strip_annotated_type(inner_type) - if is_typeddict(stripped_type) and is_mapping(data): - return _transform_typeddict(data, stripped_type) - - if ( - # List[T] - (is_list_type(stripped_type) and is_list(data)) - # Iterable[T] - or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) - ): - inner_type = extract_type_arg(stripped_type, 0) - return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] - - if is_union_type(stripped_type): - # For union types we run the transformation against all subtypes to ensure that everything is transformed. - # - # TODO: there may be edge cases where the same normalized field name will transform to two different names - # in different subtypes. - for subtype in get_args(stripped_type): - data = _transform_recursive(data, annotation=annotation, inner_type=subtype) - return data - - if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True) - - annotated_type = _get_annotated_type(annotation) - if annotated_type is None: - return data - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.format is not None: - return _format_data(data, annotation.format, annotation.format_template) - - return data - - -def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: - if isinstance(data, date | datetime): - if format_ == "iso8601": - return data.isoformat() - - if format_ == "custom" and format_template is not None: - return data.strftime(format_template) - - if format_ == "base64" and is_base64_file_input(data): - binary: str | bytes | None = None - - if isinstance(data, pathlib.Path): - binary = data.read_bytes() - elif isinstance(data, io.IOBase): - binary = data.read() - - if isinstance(binary, str): # type: ignore[unreachable] - binary = binary.encode() - - if not isinstance(binary, bytes): - raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") - - return base64.b64encode(binary).decode("ascii") - - return data - - -def _transform_typeddict( - data: Mapping[str, object], - expected_type: type, -) -> Mapping[str, object]: - result: dict[str, object] = {} - annotations = get_type_hints(expected_type, include_extras=True) - for key, value in data.items(): - type_ = annotations.get(key) - if type_ is None: - # we do not have a type annotation for this field, leave it as is - result[key] = value - else: - result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) - return result - - -async def async_maybe_transform( - data: object, - expected_type: object, -) -> Any | None: - """Wrapper over `async_transform()` that allows `None` to be passed. - - See `async_transform()` for more details. - """ - if data is None: - return None - return await async_transform(data, expected_type) - - -async def async_transform( - data: _T, - expected_type: object, -) -> _T: - """Transform dictionaries based off of type information from the given type, for example: - - ```py - class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - - - transformed = transform({"card_id": ""}, Params) - # {'cardID': ''} - ``` - - Any keys / data that does not have type information given will be included as is. - - It should be noted that the transformations that this function does are not represented in the type system. - """ - transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) - return cast(_T, transformed) - - -async def _async_transform_recursive( - data: object, - *, - annotation: type, - inner_type: type | None = None, -) -> object: - """Transform the given data against the expected type. - - Args: - annotation: The direct type annotation given to the particular piece of data. - This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc - - inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type - is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in - the list can be transformed using the metadata from the container type. - - Defaults to the same value as the `annotation` argument. - """ - if inner_type is None: - inner_type = annotation - - stripped_type = strip_annotated_type(inner_type) - if is_typeddict(stripped_type) and is_mapping(data): - return await _async_transform_typeddict(data, stripped_type) - - if ( - # List[T] - (is_list_type(stripped_type) and is_list(data)) - # Iterable[T] - or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) - ): - inner_type = extract_type_arg(stripped_type, 0) - return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] - - if is_union_type(stripped_type): - # For union types we run the transformation against all subtypes to ensure that everything is transformed. - # - # TODO: there may be edge cases where the same normalized field name will transform to two different names - # in different subtypes. - for subtype in get_args(stripped_type): - data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) - return data - - if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True) - - annotated_type = _get_annotated_type(annotation) - if annotated_type is None: - return data - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.format is not None: - return await _async_format_data(data, annotation.format, annotation.format_template) - - return data - - -async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: - if isinstance(data, date | datetime): - if format_ == "iso8601": - return data.isoformat() - - if format_ == "custom" and format_template is not None: - return data.strftime(format_template) - - if format_ == "base64" and is_base64_file_input(data): - binary: str | bytes | None = None - - if isinstance(data, pathlib.Path): - binary = await anyio.Path(data).read_bytes() - elif isinstance(data, io.IOBase): - binary = data.read() - - if isinstance(binary, str): # type: ignore[unreachable] - binary = binary.encode() - - if not isinstance(binary, bytes): - raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") - - return base64.b64encode(binary).decode("ascii") - - return data - - -async def _async_transform_typeddict( - data: Mapping[str, object], - expected_type: type, -) -> Mapping[str, object]: - result: dict[str, object] = {} - annotations = get_type_hints(expected_type, include_extras=True) - for key, value in data.items(): - type_ = annotations.get(key) - if type_ is None: - # we do not have a type annotation for this field, leave it as is - result[key] = value - else: - result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) - return result diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_typing.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_typing.py deleted file mode 100644 index c7c54dcc37..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_typing.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import annotations - -from collections import abc as _c_abc -from collections.abc import Iterable -from typing import Annotated, Any, TypeVar, cast, get_args, get_origin - -from typing_extensions import Required - -from .._base_compat import is_union as _is_union -from .._base_type import InheritsGeneric - - -def is_annotated_type(typ: type) -> bool: - return get_origin(typ) == Annotated - - -def is_list_type(typ: type) -> bool: - return (get_origin(typ) or typ) == list - - -def is_iterable_type(typ: type) -> bool: - """If the given type is `typing.Iterable[T]`""" - origin = get_origin(typ) or typ - return origin in {Iterable, _c_abc.Iterable} - - -def is_union_type(typ: type) -> bool: - return _is_union(get_origin(typ)) - - -def is_required_type(typ: type) -> bool: - return get_origin(typ) == Required - - -def is_typevar(typ: type) -> bool: - # type ignore is required because type checkers - # think this expression will always return False - return type(typ) == TypeVar # type: ignore - - -# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] -def strip_annotated_type(typ: type) -> type: - if is_required_type(typ) or is_annotated_type(typ): - return strip_annotated_type(cast(type, get_args(typ)[0])) - - return typ - - -def extract_type_arg(typ: type, index: int) -> type: - args = get_args(typ) - try: - return cast(type, args[index]) - except IndexError as err: - raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err - - -def extract_type_var_from_base( - typ: type, - *, - generic_bases: tuple[type, ...], - index: int, - failure_message: str | None = None, -) -> type: - """Given a type like `Foo[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyResponse(Foo[bytes]): - ... - - extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes - ``` - - And where a generic subclass is given: - ```py - _T = TypeVar('_T') - class MyResponse(Foo[_T]): - ... - - extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes - ``` - """ - cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: - # we're given the class directly - return extract_type_arg(typ, index) - - # if a subclass is given - # --- - # this is needed as __orig_bases__ is not present in the typeshed stubs - # because it is intended to be for internal use only, however there does - # not seem to be a way to resolve generic TypeVars for inherited subclasses - # without using it. - if isinstance(cls, InheritsGeneric): - target_base_class: Any | None = None - for base in cls.__orig_bases__: - if base.__origin__ in generic_bases: - target_base_class = base - break - - if target_base_class is None: - raise RuntimeError( - "Could not find the generic base class;\n" - "This should never happen;\n" - f"Does {cls} inherit from one of {generic_bases} ?" - ) - - extracted = extract_type_arg(target_base_class, index) - if is_typevar(extracted): - # If the extracted type argument is itself a type variable - # then that means the subclass itself is generic, so we have - # to resolve the type argument from the class itself, not - # the base class. - # - # Note: if there is more than 1 type argument, the subclass could - # change the ordering of the type arguments, this is not currently - # supported. - return extract_type_arg(typ, index) - - return extracted - - raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_utils.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_utils.py deleted file mode 100644 index ce5e7786aa..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/_utils/_utils.py +++ /dev/null @@ -1,409 +0,0 @@ -from __future__ import annotations - -import functools -import inspect -import os -import re -from collections.abc import Callable, Iterable, Mapping, Sequence -from pathlib import Path -from typing import ( - Any, - TypeGuard, - TypeVar, - Union, - cast, - overload, -) - -import sniffio - -from .._base_compat import parse_date as parse_date # noqa: PLC0414 -from .._base_compat import parse_datetime as parse_datetime # noqa: PLC0414 -from .._base_type import FileTypes, Headers, HeadersLike, NotGiven, NotGivenOr - - -def remove_notgiven_indict(obj): - if obj is None or (not isinstance(obj, Mapping)): - return obj - return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} - - -_T = TypeVar("_T") -_TupleT = TypeVar("_TupleT", bound=tuple[object, ...]) -_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) -_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) -CallableT = TypeVar("CallableT", bound=Callable[..., Any]) - - -def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: - return [item for sublist in t for item in sublist] - - -def extract_files( - # TODO: this needs to take Dict but variance issues..... - # create protocol type ? - query: Mapping[str, object], - *, - paths: Sequence[Sequence[str]], -) -> list[tuple[str, FileTypes]]: - """Recursively extract files from the given dictionary based on specified paths. - - A path may look like this ['foo', 'files', '', 'data']. - - Note: this mutates the given dictionary. - """ - files: list[tuple[str, FileTypes]] = [] - for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) - return files - - -def _extract_items( - obj: object, - path: Sequence[str], - *, - index: int, - flattened_key: str | None, -) -> list[tuple[str, FileTypes]]: - try: - key = path[index] - except IndexError: - if isinstance(obj, NotGiven): - # no value was provided - we can safely ignore - return [] - - # cyclical import - from .._files import assert_is_file_content - - # We have exhausted the path, return the entry we found. - assert_is_file_content(obj, key=flattened_key) - assert flattened_key is not None - return [(flattened_key, cast(FileTypes, obj))] - - index += 1 - if is_dict(obj): - try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: - item = obj.pop(key) - else: - item = obj[key] - except KeyError: - # Key was not present in the dictionary, this is not indicative of an error - # as the given path may not point to a required field. We also do not want - # to enforce required fields as the API may differ from the spec in some cases. - return [] - if flattened_key is None: - flattened_key = key - else: - flattened_key += f"[{key}]" - return _extract_items( - item, - path, - index=index, - flattened_key=flattened_key, - ) - elif is_list(obj): - if key != "": - return [] - - return flatten( - [ - _extract_items( - item, - path, - index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", - ) - for item in obj - ] - ) - - # Something unexpected was passed, just ignore it. - return [] - - -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) - - -# Type safe methods for narrowing types with TypeVars. -# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], -# however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. -# -# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. -# `is_*` is for when you're dealing with an unknown input -# `is_*_t` is for when you're narrowing a known union type to a specific subset - - -def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: - return isinstance(obj, tuple) - - -def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: - return isinstance(obj, tuple) - - -def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: - return isinstance(obj, Sequence) - - -def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: - return isinstance(obj, Sequence) - - -def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: - return isinstance(obj, Mapping) - - -def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: - return isinstance(obj, Mapping) - - -def is_dict(obj: object) -> TypeGuard[dict[object, object]]: - return isinstance(obj, dict) - - -def is_list(obj: object) -> TypeGuard[list[object]]: - return isinstance(obj, list) - - -def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: - return isinstance(obj, Iterable) - - -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - -# copied from https://github.com/Rapptz/RoboDanny -def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: - size = len(seq) - if size == 0: - return "" - - if size == 1: - return seq[0] - - if size == 2: - return f"{seq[0]} {final} {seq[1]}" - - return delim.join(seq[:-1]) + f" {final} {seq[-1]}" - - -def quote(string: str) -> str: - """Add single quotation marks around the given string. Does *not* do any escaping.""" - return f"'{string}'" - - -def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: - """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. - - Useful for enforcing runtime validation of overloaded functions. - - Example usage: - ```py - @overload - def foo(*, a: str) -> str: - ... - - - @overload - def foo(*, b: bool) -> str: - ... - - - # This enforces the same constraints that a static type checker would - # i.e. that either a or b must be passed to the function - @required_args(["a"], ["b"]) - def foo(*, a: str | None = None, b: bool | None = None) -> str: - ... - ``` - """ - - def inner(func: CallableT) -> CallableT: - params = inspect.signature(func).parameters - positional = [ - name - for name, param in params.items() - if param.kind - in { - param.POSITIONAL_ONLY, - param.POSITIONAL_OR_KEYWORD, - } - ] - - @functools.wraps(func) - def wrapper(*args: object, **kwargs: object) -> object: - given_params: set[str] = set() - for i, _ in enumerate(args): - try: - given_params.add(positional[i]) - except IndexError: - raise TypeError( - f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" - ) from None - - given_params.update(kwargs.keys()) - - for variant in variants: - matches = all(param in given_params for param in variant) - if matches: - break - else: # no break - if len(variants) > 1: - variations = human_join( - ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] - ) - msg = f"Missing required arguments; Expected either {variations} arguments to be given" - else: - # TODO: this error message is not deterministic - missing = list(set(variants[0]) - given_params) - if len(missing) > 1: - msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" - else: - msg = f"Missing required argument: {quote(missing[0])}" - raise TypeError(msg) - return func(*args, **kwargs) - - return wrapper # type: ignore - - return inner - - -_K = TypeVar("_K") -_V = TypeVar("_V") - - -@overload -def strip_not_given(obj: None) -> None: ... - - -@overload -def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... - - -@overload -def strip_not_given(obj: object) -> object: ... - - -def strip_not_given(obj: object | None) -> object: - """Remove all top-level keys where their values are instances of `NotGiven`""" - if obj is None: - return None - - if not is_mapping(obj): - return obj - - return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} - - -def coerce_integer(val: str) -> int: - return int(val, base=10) - - -def coerce_float(val: str) -> float: - return float(val) - - -def coerce_boolean(val: str) -> bool: - return val in {"true", "1", "on"} - - -def maybe_coerce_integer(val: str | None) -> int | None: - if val is None: - return None - return coerce_integer(val) - - -def maybe_coerce_float(val: str | None) -> float | None: - if val is None: - return None - return coerce_float(val) - - -def maybe_coerce_boolean(val: str | None) -> bool | None: - if val is None: - return None - return coerce_boolean(val) - - -def removeprefix(string: str, prefix: str) -> str: - """Remove a prefix from a string. - - Backport of `str.removeprefix` for Python < 3.9 - """ - if string.startswith(prefix): - return string[len(prefix) :] - return string - - -def removesuffix(string: str, suffix: str) -> str: - """Remove a suffix from a string. - - Backport of `str.removesuffix` for Python < 3.9 - """ - if string.endswith(suffix): - return string[: -len(suffix)] - return string - - -def file_from_path(path: str) -> FileTypes: - contents = Path(path).read_bytes() - file_name = os.path.basename(path) - return (file_name, contents) - - -def get_required_header(headers: HeadersLike, header: str) -> str: - lower_header = header.lower() - if isinstance(headers, Mapping): - headers = cast(Headers, headers) - for k, v in headers.items(): - if k.lower() == lower_header and isinstance(v, str): - return v - - """ to deal with the case where the header looks like Stainless-Event-Id """ - intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) - - for normalized_header in [header, lower_header, header.upper(), intercaps_header]: - value = headers.get(normalized_header) - if value: - return value - - raise ValueError(f"Could not find {header} header") - - -def get_async_library() -> str: - try: - return sniffio.current_async_library() - except Exception: - return "false" - - -def drop_prefix_image_data(content: Union[str, list[dict]]) -> Union[str, list[dict]]: - """ - 删除 ;base64, 前缀 - :param image_data: - :return: - """ - if isinstance(content, list): - for data in content: - if data.get("type") == "image_url": - image_data = data.get("image_url").get("url") - if image_data.startswith("data:image/"): - image_data = image_data.split("base64,")[-1] - data["image_url"]["url"] = image_data - - return content diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/logs.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/logs.py deleted file mode 100644 index e5fce94c00..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/logs.py +++ /dev/null @@ -1,78 +0,0 @@ -import logging -import os -import time - -logger = logging.getLogger(__name__) - - -class LoggerNameFilter(logging.Filter): - def filter(self, record): - # return record.name.startswith("loom_core") or record.name in "ERROR" or ( - # record.name.startswith("uvicorn.error") - # and record.getMessage().startswith("Uvicorn running on") - # ) - return True - - -def get_log_file(log_path: str, sub_dir: str): - """ - sub_dir should contain a timestamp. - """ - log_dir = os.path.join(log_path, sub_dir) - # Here should be creating a new directory each time, so `exist_ok=False` - os.makedirs(log_dir, exist_ok=False) - return os.path.join(log_dir, "zhipuai.log") - - -def get_config_dict(log_level: str, log_file_path: str, log_backup_count: int, log_max_bytes: int) -> dict: - # for windows, the path should be a raw string. - log_file_path = log_file_path.encode("unicode-escape").decode() if os.name == "nt" else log_file_path - log_level = log_level.upper() - config_dict = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "formatter": {"format": ("%(asctime)s %(name)-12s %(process)d %(levelname)-8s %(message)s")}, - }, - "filters": { - "logger_name_filter": { - "()": __name__ + ".LoggerNameFilter", - }, - }, - "handlers": { - "stream_handler": { - "class": "logging.StreamHandler", - "formatter": "formatter", - "level": log_level, - # "stream": "ext://sys.stdout", - # "filters": ["logger_name_filter"], - }, - "file_handler": { - "class": "logging.handlers.RotatingFileHandler", - "formatter": "formatter", - "level": log_level, - "filename": log_file_path, - "mode": "a", - "maxBytes": log_max_bytes, - "backupCount": log_backup_count, - "encoding": "utf8", - }, - }, - "loggers": { - "loom_core": { - "handlers": ["stream_handler", "file_handler"], - "level": log_level, - "propagate": False, - } - }, - "root": { - "level": log_level, - "handlers": ["stream_handler", "file_handler"], - }, - } - return config_dict - - -def get_timestamp_ms(): - t = time.time() - return int(round(t * 1000)) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/pagination.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/pagination.py deleted file mode 100644 index 7f0b1b91d9..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/core/pagination.py +++ /dev/null @@ -1,62 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Any, Generic, Optional, TypeVar, cast - -from typing_extensions import Protocol, override, runtime_checkable - -from ._http_client import BasePage, BaseSyncPage, PageInfo - -__all__ = ["SyncPage", "SyncCursorPage"] - -_T = TypeVar("_T") - - -@runtime_checkable -class CursorPageItem(Protocol): - id: Optional[str] - - -class SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" - - data: list[_T] - object: str - - @override - def _get_page_items(self) -> list[_T]: - data = self.data - if not data: - return [] - return data - - @override - def next_page_info(self) -> None: - """ - This page represents a response that isn't actually paginated at the API level - so there will never be a next page. - """ - return None - - -class SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - data: list[_T] - - @override - def _get_page_items(self) -> list[_T]: - data = self.data - if not data: - return [] - return data - - @override - def next_page_info(self) -> Optional[PageInfo]: - data = self.data - if not data: - return None - - item = cast(Any, data[-1]) - if not isinstance(item, CursorPageItem) or item.id is None: - # TODO emit warning log - return None - - return PageInfo(params={"after": item.id}) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/__init__.py deleted file mode 100644 index 9f941fb91c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .assistant_completion import AssistantCompletion - -__all__ = [ - "AssistantCompletion", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_completion.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_completion.py deleted file mode 100644 index cbfb6edaeb..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_completion.py +++ /dev/null @@ -1,40 +0,0 @@ -from typing import Any, Optional - -from ...core import BaseModel -from .message import MessageContent - -__all__ = ["AssistantCompletion", "CompletionUsage"] - - -class ErrorInfo(BaseModel): - code: str # 错误码 - message: str # 错误信息 - - -class AssistantChoice(BaseModel): - index: int # 结果下标 - delta: MessageContent # 当前会话输出消息体 - finish_reason: str - """ - # 推理结束原因 stop代表推理自然结束或触发停止词。 sensitive 代表模型推理内容被安全审核接口拦截。请注意,针对此类内容,请用户自行判断并决定是否撤回已公开的内容。 - # network_error 代表模型推理服务异常。 - """ # noqa: E501 - metadata: dict # 元信息,拓展字段 - - -class CompletionUsage(BaseModel): - prompt_tokens: int # 输入的 tokens 数量 - completion_tokens: int # 输出的 tokens 数量 - total_tokens: int # 总 tokens 数量 - - -class AssistantCompletion(BaseModel): - id: str # 请求 ID - conversation_id: str # 会话 ID - assistant_id: str # 智能体 ID - created: int # 请求创建时间,Unix 时间戳 - status: str # 返回状态,包括:`completed` 表示生成结束`in_progress`表示生成中 `failed` 表示生成异常 - last_error: Optional[ErrorInfo] # 异常信息 - choices: list[AssistantChoice] # 增量返回的信息 - metadata: Optional[dict[str, Any]] # 元信息,拓展字段 - usage: Optional[CompletionUsage] # tokens 数量统计 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_params.py deleted file mode 100644 index 03f14f4238..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_params.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import TypedDict - - -class ConversationParameters(TypedDict, total=False): - assistant_id: str # 智能体 ID - page: int # 当前分页 - page_size: int # 分页数量 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_resp.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_resp.py deleted file mode 100644 index d1833d220a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_conversation_resp.py +++ /dev/null @@ -1,29 +0,0 @@ -from ...core import BaseModel - -__all__ = ["ConversationUsageListResp"] - - -class Usage(BaseModel): - prompt_tokens: int # 用户输入的 tokens 数量 - completion_tokens: int # 模型输入的 tokens 数量 - total_tokens: int # 总 tokens 数量 - - -class ConversationUsage(BaseModel): - id: str # 会话 id - assistant_id: str # 智能体Assistant id - create_time: int # 创建时间 - update_time: int # 更新时间 - usage: Usage # 会话中 tokens 数量统计 - - -class ConversationUsageList(BaseModel): - assistant_id: str # 智能体id - has_more: bool # 是否还有更多页 - conversation_list: list[ConversationUsage] # 返回的 - - -class ConversationUsageListResp(BaseModel): - code: int - msg: str - data: ConversationUsageList diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_create_params.py deleted file mode 100644 index 2def1025cd..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_create_params.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Optional, TypedDict, Union - - -class AssistantAttachments: - file_id: str - - -class MessageTextContent: - type: str # 目前支持 type = text - text: str - - -MessageContent = Union[MessageTextContent] - - -class ConversationMessage(TypedDict): - """会话消息体""" - - role: str # 用户的输入角色,例如 'user' - content: list[MessageContent] # 会话消息体的内容 - - -class AssistantParameters(TypedDict, total=False): - """智能体参数类""" - - assistant_id: str # 智能体 ID - conversation_id: Optional[str] # 会话 ID,不传则创建新会话 - model: str # 模型名称,默认为 'GLM-4-Assistant' - stream: bool # 是否支持流式 SSE,需要传入 True - messages: list[ConversationMessage] # 会话消息体 - attachments: Optional[list[AssistantAttachments]] # 会话指定的文件,非必填 - metadata: Optional[dict] # 元信息,拓展字段,非必填 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_support_resp.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_support_resp.py deleted file mode 100644 index 0709cdbcad..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/assistant_support_resp.py +++ /dev/null @@ -1,21 +0,0 @@ -from ...core import BaseModel - -__all__ = ["AssistantSupportResp"] - - -class AssistantSupport(BaseModel): - assistant_id: str # 智能体的 Assistant id,用于智能体会话 - created_at: int # 创建时间 - updated_at: int # 更新时间 - name: str # 智能体名称 - avatar: str # 智能体头像 - description: str # 智能体描述 - status: str # 智能体状态,目前只有 publish - tools: list[str] # 智能体支持的工具名 - starter_prompts: list[str] # 智能体启动推荐的 prompt - - -class AssistantSupportResp(BaseModel): - code: int - msg: str - data: list[AssistantSupport] # 智能体列表 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/__init__.py deleted file mode 100644 index 562e0151e5..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .message_content import MessageContent - -__all__ = ["MessageContent"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/message_content.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/message_content.py deleted file mode 100644 index 6a1a438a6f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/message_content.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Annotated, TypeAlias, Union - -from ....core._utils import PropertyInfo -from .text_content_block import TextContentBlock -from .tools_delta_block import ToolsDeltaBlock - -__all__ = ["MessageContent"] - - -MessageContent: TypeAlias = Annotated[ - Union[ToolsDeltaBlock, TextContentBlock], - PropertyInfo(discriminator="type"), -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/text_content_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/text_content_block.py deleted file mode 100644 index 865fb1139e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/text_content_block.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import Literal - -from ....core import BaseModel - -__all__ = ["TextContentBlock"] - - -class TextContentBlock(BaseModel): - content: str - - role: str = "assistant" - - type: Literal["content"] = "content" - """Always `content`.""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/code_interpreter_delta_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/code_interpreter_delta_block.py deleted file mode 100644 index 9d569b282e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/code_interpreter_delta_block.py +++ /dev/null @@ -1,27 +0,0 @@ -from typing import Literal - -__all__ = ["CodeInterpreterToolBlock"] - -from .....core import BaseModel - - -class CodeInterpreterToolOutput(BaseModel): - """代码工具输出结果""" - - type: str # 代码执行日志,目前只有 logs - logs: str # 代码执行的日志结果 - error_msg: str # 错误信息 - - -class CodeInterpreter(BaseModel): - """代码解释器""" - - input: str # 生成的代码片段,输入给代码沙盒 - outputs: list[CodeInterpreterToolOutput] # 代码执行后的输出结果 - - -class CodeInterpreterToolBlock(BaseModel): - """代码工具块""" - - code_interpreter: CodeInterpreter # 代码解释器对象 - type: Literal["code_interpreter"] # 调用工具的类型,始终为 `code_interpreter` diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/drawing_tool_delta_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/drawing_tool_delta_block.py deleted file mode 100644 index 0b6895556b..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/drawing_tool_delta_block.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Literal - -from .....core import BaseModel - -__all__ = ["DrawingToolBlock"] - - -class DrawingToolOutput(BaseModel): - image: str - - -class DrawingTool(BaseModel): - input: str - outputs: list[DrawingToolOutput] - - -class DrawingToolBlock(BaseModel): - drawing_tool: DrawingTool - - type: Literal["drawing_tool"] - """Always `drawing_tool`.""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/function_delta_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/function_delta_block.py deleted file mode 100644 index c439bc4b3f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/function_delta_block.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Literal, Union - -__all__ = ["FunctionToolBlock"] - -from .....core import BaseModel - - -class FunctionToolOutput(BaseModel): - content: str - - -class FunctionTool(BaseModel): - name: str - arguments: Union[str, dict] - outputs: list[FunctionToolOutput] - - -class FunctionToolBlock(BaseModel): - function: FunctionTool - - type: Literal["function"] - """Always `drawing_tool`.""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/retrieval_delta_black.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/retrieval_delta_black.py deleted file mode 100644 index 4789e9378a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/retrieval_delta_black.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import Literal - -from .....core import BaseModel - - -class RetrievalToolOutput(BaseModel): - """ - This class represents the output of a retrieval tool. - - Attributes: - - text (str): The text snippet retrieved from the knowledge base. - - document (str): The name of the document from which the text snippet was retrieved, returned only in intelligent configuration. - """ # noqa: E501 - - text: str - document: str - - -class RetrievalTool(BaseModel): - """ - This class represents the outputs of a retrieval tool. - - Attributes: - - outputs (List[RetrievalToolOutput]): A list of text snippets and their respective document names retrieved from the knowledge base. - """ # noqa: E501 - - outputs: list[RetrievalToolOutput] - - -class RetrievalToolBlock(BaseModel): - """ - This class represents a block for invoking the retrieval tool. - - Attributes: - - retrieval (RetrievalTool): An instance of the RetrievalTool class containing the retrieval outputs. - - type (Literal["retrieval"]): The type of tool being used, always set to "retrieval". - """ - - retrieval: RetrievalTool - type: Literal["retrieval"] - """Always `retrieval`.""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/tools_type.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/tools_type.py deleted file mode 100644 index 98544053d4..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/tools_type.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Annotated, TypeAlias, Union - -from .....core._utils import PropertyInfo -from .code_interpreter_delta_block import CodeInterpreterToolBlock -from .drawing_tool_delta_block import DrawingToolBlock -from .function_delta_block import FunctionToolBlock -from .retrieval_delta_black import RetrievalToolBlock -from .web_browser_delta_block import WebBrowserToolBlock - -__all__ = ["ToolsType"] - - -ToolsType: TypeAlias = Annotated[ - Union[DrawingToolBlock, CodeInterpreterToolBlock, WebBrowserToolBlock, RetrievalToolBlock, FunctionToolBlock], - PropertyInfo(discriminator="type"), -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/web_browser_delta_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/web_browser_delta_block.py deleted file mode 100644 index 966e6fe0c8..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools/web_browser_delta_block.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import Literal - -from .....core import BaseModel - -__all__ = ["WebBrowserToolBlock"] - - -class WebBrowserOutput(BaseModel): - """ - This class represents the output of a web browser search result. - - Attributes: - - title (str): The title of the search result. - - link (str): The URL link to the search result's webpage. - - content (str): The textual content extracted from the search result. - - error_msg (str): Any error message encountered during the search or retrieval process. - """ - - title: str - link: str - content: str - error_msg: str - - -class WebBrowser(BaseModel): - """ - This class represents the input and outputs of a web browser search. - - Attributes: - - input (str): The input query for the web browser search. - - outputs (List[WebBrowserOutput]): A list of search results returned by the web browser. - """ - - input: str - outputs: list[WebBrowserOutput] - - -class WebBrowserToolBlock(BaseModel): - """ - This class represents a block for invoking the web browser tool. - - Attributes: - - web_browser (WebBrowser): An instance of the WebBrowser class containing the search input and outputs. - - type (Literal["web_browser"]): The type of tool being used, always set to "web_browser". - """ - - web_browser: WebBrowser - type: Literal["web_browser"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools_delta_block.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools_delta_block.py deleted file mode 100644 index 781a1ab819..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/assistant/message/tools_delta_block.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Literal - -from ....core import BaseModel -from .tools.tools_type import ToolsType - -__all__ = ["ToolsDeltaBlock"] - - -class ToolsDeltaBlock(BaseModel): - tool_calls: list[ToolsType] - """The index of the content part in the message.""" - - role: str = "tool" - - type: Literal["tool_calls"] = "tool_calls" - """Always `tool_calls`.""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch.py deleted file mode 100644 index 560562915c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch.py +++ /dev/null @@ -1,82 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import builtins -from typing import Literal, Optional - -from ..core import BaseModel -from .batch_error import BatchError -from .batch_request_counts import BatchRequestCounts - -__all__ = ["Batch", "Errors"] - - -class Errors(BaseModel): - data: Optional[list[BatchError]] = None - - object: Optional[str] = None - """这个类型,一直是`list`。""" - - -class Batch(BaseModel): - id: str - - completion_window: str - """用于执行请求的地址信息。""" - - created_at: int - """这是 Unix timestamp (in seconds) 表示的创建时间。""" - - endpoint: str - """这是ZhipuAI endpoint的地址。""" - - input_file_id: str - """标记为batch的输入文件的ID。""" - - object: Literal["batch"] - """这个类型,一直是`batch`.""" - - status: Literal[ - "validating", "failed", "in_progress", "finalizing", "completed", "expired", "cancelling", "cancelled" - ] - """batch 的状态。""" - - cancelled_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的取消时间。""" - - cancelling_at: Optional[int] = None - """Unix timestamp (in seconds) 表示发起取消的请求时间 """ - - completed_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的完成时间。""" - - error_file_id: Optional[str] = None - """这个文件id包含了执行请求失败的请求的输出。""" - - errors: Optional[Errors] = None - - expired_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的将在过期时间。""" - - expires_at: Optional[int] = None - """Unix timestamp (in seconds) 触发过期""" - - failed_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的失败时间。""" - - finalizing_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的最终时间。""" - - in_progress_at: Optional[int] = None - """Unix timestamp (in seconds) 表示的开始处理时间。""" - - metadata: Optional[builtins.object] = None - """ - key:value形式的元数据,以便将信息存储 - 结构化格式。键的长度是64个字符,值最长512个字符 - """ - - output_file_id: Optional[str] = None - """完成请求的输出文件的ID。""" - - request_counts: Optional[BatchRequestCounts] = None - """批次中不同状态的请求计数""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_create_params.py deleted file mode 100644 index 3dae65ea46..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_create_params.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Optional - -from typing_extensions import Required, TypedDict - -__all__ = ["BatchCreateParams"] - - -class BatchCreateParams(TypedDict, total=False): - completion_window: Required[str] - """The time frame within which the batch should be processed. - - Currently only `24h` is supported. - """ - - endpoint: Required[Literal["/v1/chat/completions", "/v1/embeddings"]] - """The endpoint to be used for all requests in the batch. - - Currently `/v1/chat/completions` and `/v1/embeddings` are supported. - """ - - input_file_id: Required[str] - """The ID of an uploaded file that contains requests for the new batch. - - See [upload file](https://platform.openai.com/docs/api-reference/files/create) - for how to upload a file. - - Your input file must be formatted as a - [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), - and must be uploaded with the purpose `batch`. - """ - - metadata: Optional[dict[str, str]] - """Optional custom metadata for the batch.""" - - auto_delete_input_file: Optional[bool] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_error.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_error.py deleted file mode 100644 index f934db1978..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_error.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..core import BaseModel - -__all__ = ["BatchError"] - - -class BatchError(BaseModel): - code: Optional[str] = None - """定义的业务错误码""" - - line: Optional[int] = None - """文件中的行号""" - - message: Optional[str] = None - """关于对话文件中的错误的描述""" - - param: Optional[str] = None - """参数名称,如果有的话""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_list_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_list_params.py deleted file mode 100644 index 1a68167132..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_list_params.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BatchListParams"] - - -class BatchListParams(TypedDict, total=False): - after: str - """分页的游标,用于获取下一页的数据。 - - `after` 是一个指向当前页面的游标,用于获取下一页的数据。如果没有提供 `after`,则返回第一页的数据。 - list. - """ - - limit: int - """这个参数用于限制返回的结果数量。 - - Limit 用于限制返回的结果数量。默认值为 10 - """ diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_request_counts.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_request_counts.py deleted file mode 100644 index ca3ccae625..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/batch_request_counts.py +++ /dev/null @@ -1,14 +0,0 @@ -from ..core import BaseModel - -__all__ = ["BatchRequestCounts"] - - -class BatchRequestCounts(BaseModel): - completed: int - """这个数字表示已经完成的请求。""" - - failed: int - """这个数字表示失败的请求。""" - - total: int - """这个数字表示总的请求。""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/async_chat_completion.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/async_chat_completion.py deleted file mode 100644 index c1eed070f3..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/async_chat_completion.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Optional - -from ...core import BaseModel -from .chat_completion import CompletionChoice, CompletionUsage - -__all__ = ["AsyncTaskStatus", "AsyncCompletion"] - - -class AsyncTaskStatus(BaseModel): - id: Optional[str] = None - request_id: Optional[str] = None - model: Optional[str] = None - task_status: Optional[str] = None - - -class AsyncCompletion(BaseModel): - id: Optional[str] = None - request_id: Optional[str] = None - model: Optional[str] = None - task_status: str - choices: list[CompletionChoice] - usage: CompletionUsage diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion.py deleted file mode 100644 index 1945a826cd..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = ["Completion", "CompletionUsage"] - - -class Function(BaseModel): - arguments: str - name: str - - -class CompletionMessageToolCall(BaseModel): - id: str - function: Function - type: str - - -class CompletionMessage(BaseModel): - content: Optional[str] = None - role: str - tool_calls: Optional[list[CompletionMessageToolCall]] = None - - -class CompletionUsage(BaseModel): - prompt_tokens: int - completion_tokens: int - total_tokens: int - - -class CompletionChoice(BaseModel): - index: int - finish_reason: str - message: CompletionMessage - - -class Completion(BaseModel): - model: Optional[str] = None - created: Optional[int] = None - choices: list[CompletionChoice] - request_id: Optional[str] = None - id: Optional[str] = None - usage: CompletionUsage diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion_chunk.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion_chunk.py deleted file mode 100644 index 27fad0008a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completion_chunk.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import Any, Optional - -from ...core import BaseModel - -__all__ = [ - "CompletionUsage", - "ChatCompletionChunk", - "Choice", - "ChoiceDelta", - "ChoiceDeltaFunctionCall", - "ChoiceDeltaToolCall", - "ChoiceDeltaToolCallFunction", -] - - -class ChoiceDeltaFunctionCall(BaseModel): - arguments: Optional[str] = None - name: Optional[str] = None - - -class ChoiceDeltaToolCallFunction(BaseModel): - arguments: Optional[str] = None - name: Optional[str] = None - - -class ChoiceDeltaToolCall(BaseModel): - index: int - id: Optional[str] = None - function: Optional[ChoiceDeltaToolCallFunction] = None - type: Optional[str] = None - - -class ChoiceDelta(BaseModel): - content: Optional[str] = None - role: Optional[str] = None - tool_calls: Optional[list[ChoiceDeltaToolCall]] = None - - -class Choice(BaseModel): - delta: ChoiceDelta - finish_reason: Optional[str] = None - index: int - - -class CompletionUsage(BaseModel): - prompt_tokens: int - completion_tokens: int - total_tokens: int - - -class ChatCompletionChunk(BaseModel): - id: Optional[str] = None - choices: list[Choice] - created: Optional[int] = None - model: Optional[str] = None - usage: Optional[CompletionUsage] = None - extra_json: dict[str, Any] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completions_create_param.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completions_create_param.py deleted file mode 100644 index 6ee4dc4794..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/chat_completions_create_param.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Optional - -from typing_extensions import TypedDict - - -class Reference(TypedDict, total=False): - enable: Optional[bool] - search_query: Optional[str] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/code_geex/code_geex_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/code_geex/code_geex_params.py deleted file mode 100644 index 666b38855c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/chat/code_geex/code_geex_params.py +++ /dev/null @@ -1,146 +0,0 @@ -from typing import Literal, Optional - -from typing_extensions import Required, TypedDict - -__all__ = [ - "CodeGeexTarget", - "CodeGeexContext", - "CodeGeexExtra", -] - - -class CodeGeexTarget(TypedDict, total=False): - """补全的内容参数""" - - path: Optional[str] - """文件路径""" - language: Required[ - Literal[ - "c", - "c++", - "cpp", - "c#", - "csharp", - "c-sharp", - "css", - "cuda", - "dart", - "lua", - "objectivec", - "objective-c", - "objective-c++", - "python", - "perl", - "prolog", - "swift", - "lisp", - "java", - "scala", - "tex", - "jsx", - "tsx", - "vue", - "markdown", - "html", - "php", - "js", - "javascript", - "typescript", - "go", - "shell", - "rust", - "sql", - "kotlin", - "vb", - "ruby", - "pascal", - "r", - "fortran", - "lean", - "matlab", - "delphi", - "scheme", - "basic", - "assembly", - "groovy", - "abap", - "gdscript", - "haskell", - "julia", - "elixir", - "excel", - "clojure", - "actionscript", - "solidity", - "powershell", - "erlang", - "cobol", - "alloy", - "awk", - "thrift", - "sparql", - "augeas", - "cmake", - "f-sharp", - "stan", - "isabelle", - "dockerfile", - "rmarkdown", - "literate-agda", - "tcl", - "glsl", - "antlr", - "verilog", - "racket", - "standard-ml", - "elm", - "yaml", - "smalltalk", - "ocaml", - "idris", - "visual-basic", - "protocol-buffer", - "bluespec", - "applescript", - "makefile", - "tcsh", - "maple", - "systemverilog", - "literate-coffeescript", - "vhdl", - "restructuredtext", - "sas", - "literate-haskell", - "java-server-pages", - "coffeescript", - "emacs-lisp", - "mathematica", - "xslt", - "zig", - "common-lisp", - "stata", - "agda", - "ada", - ] - ] - """代码语言类型,如python""" - code_prefix: Required[str] - """补全位置的前文""" - code_suffix: Required[str] - """补全位置的后文""" - - -class CodeGeexContext(TypedDict, total=False): - """附加代码""" - - path: Required[str] - """附加代码文件的路径""" - code: Required[str] - """附加的代码内容""" - - -class CodeGeexExtra(TypedDict, total=False): - target: Required[CodeGeexTarget] - """补全的内容参数""" - contexts: Optional[list[CodeGeexContext]] - """附加代码""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/embeddings.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/embeddings.py deleted file mode 100644 index 8425b5c866..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/embeddings.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from ..core import BaseModel -from .chat.chat_completion import CompletionUsage - -__all__ = ["Embedding", "EmbeddingsResponded"] - - -class Embedding(BaseModel): - object: str - index: Optional[int] = None - embedding: list[float] - - -class EmbeddingsResponded(BaseModel): - object: str - data: list[Embedding] - model: str - usage: CompletionUsage diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/__init__.py deleted file mode 100644 index bbaf59e4d7..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .file_deleted import FileDeleted -from .file_object import FileObject, ListOfFileObject -from .upload_detail import UploadDetail - -__all__ = ["FileObject", "ListOfFileObject", "UploadDetail", "FileDeleted"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_create_params.py deleted file mode 100644 index 4ef93b1c05..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_create_params.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Optional - -from typing_extensions import Required, TypedDict - -__all__ = ["FileCreateParams"] - -from ...core import FileTypes -from . import UploadDetail - - -class FileCreateParams(TypedDict, total=False): - file: FileTypes - """file和 upload_detail二选一必填""" - - upload_detail: list[UploadDetail] - """file和 upload_detail二选一必填""" - - purpose: Required[Literal["fine-tune", "retrieval", "batch"]] - """ - 上传文件的用途,支持 "fine-tune和 "retrieval" - retrieval支持上传Doc、Docx、PDF、Xlsx、URL类型文件,且单个文件的大小不超过 5MB。 - fine-tune支持上传.jsonl文件且当前单个文件的大小最大可为 100 MB ,文件中语料格式需满足微调指南中所描述的格式。 - """ - custom_separator: Optional[list[str]] - """ - 当 purpose 为 retrieval 且文件类型为 pdf, url, docx 时上传,切片规则默认为 `\n`。 - """ - knowledge_id: str - """ - 当文件上传目的为 retrieval 时,需要指定知识库ID进行上传。 - """ - - sentence_size: int - """ - 当文件上传目的为 retrieval 时,需要指定知识库ID进行上传。 - """ diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_deleted.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_deleted.py deleted file mode 100644 index a384b1a69a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_deleted.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Literal - -from ...core import BaseModel - -__all__ = ["FileDeleted"] - - -class FileDeleted(BaseModel): - id: str - - deleted: bool - - object: Literal["file"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_object.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_object.py deleted file mode 100644 index 8f9d0fbb8e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/file_object.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = ["FileObject", "ListOfFileObject"] - - -class FileObject(BaseModel): - id: Optional[str] = None - bytes: Optional[int] = None - created_at: Optional[int] = None - filename: Optional[str] = None - object: Optional[str] = None - purpose: Optional[str] = None - status: Optional[str] = None - status_details: Optional[str] = None - - -class ListOfFileObject(BaseModel): - object: Optional[str] = None - data: list[FileObject] - has_more: Optional[bool] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/upload_detail.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/upload_detail.py deleted file mode 100644 index 8f1ca5ce57..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/files/upload_detail.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - - -class UploadDetail(BaseModel): - url: str - knowledge_type: int - file_name: Optional[str] = None - sentence_size: Optional[int] = None - custom_separator: Optional[list[str]] = None - callback_url: Optional[str] = None - callback_header: Optional[dict[str, str]] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/__init__.py deleted file mode 100644 index 416f516ef7..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import annotations - -from .fine_tuning_job import FineTuningJob, ListOfFineTuningJob -from .fine_tuning_job_event import FineTuningJobEvent diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job.py deleted file mode 100644 index 75c7553dbe..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Optional, Union - -from ...core import BaseModel - -__all__ = ["FineTuningJob", "Error", "Hyperparameters", "ListOfFineTuningJob"] - - -class Error(BaseModel): - code: str - message: str - param: Optional[str] = None - - -class Hyperparameters(BaseModel): - n_epochs: Union[str, int, None] = None - - -class FineTuningJob(BaseModel): - id: Optional[str] = None - - request_id: Optional[str] = None - - created_at: Optional[int] = None - - error: Optional[Error] = None - - fine_tuned_model: Optional[str] = None - - finished_at: Optional[int] = None - - hyperparameters: Optional[Hyperparameters] = None - - model: Optional[str] = None - - object: Optional[str] = None - - result_files: list[str] - - status: str - - trained_tokens: Optional[int] = None - - training_file: str - - validation_file: Optional[str] = None - - -class ListOfFineTuningJob(BaseModel): - object: Optional[str] = None - data: list[FineTuningJob] - has_more: Optional[bool] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job_event.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job_event.py deleted file mode 100644 index f996cff114..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/fine_tuning_job_event.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Optional, Union - -from ...core import BaseModel - -__all__ = ["FineTuningJobEvent", "Metric", "JobEvent"] - - -class Metric(BaseModel): - epoch: Optional[Union[str, int, float]] = None - current_steps: Optional[int] = None - total_steps: Optional[int] = None - elapsed_time: Optional[str] = None - remaining_time: Optional[str] = None - trained_tokens: Optional[int] = None - loss: Optional[Union[str, int, float]] = None - eval_loss: Optional[Union[str, int, float]] = None - acc: Optional[Union[str, int, float]] = None - eval_acc: Optional[Union[str, int, float]] = None - learning_rate: Optional[Union[str, int, float]] = None - - -class JobEvent(BaseModel): - object: Optional[str] = None - id: Optional[str] = None - type: Optional[str] = None - created_at: Optional[int] = None - level: Optional[str] = None - message: Optional[str] = None - data: Optional[Metric] = None - - -class FineTuningJobEvent(BaseModel): - object: Optional[str] = None - data: list[JobEvent] - has_more: Optional[bool] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/job_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/job_create_params.py deleted file mode 100644 index e1ebc352bc..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/job_create_params.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Union - -from typing_extensions import TypedDict - -__all__ = ["Hyperparameters"] - - -class Hyperparameters(TypedDict, total=False): - batch_size: Union[Literal["auto"], int] - - learning_rate_multiplier: Union[Literal["auto"], float] - - n_epochs: Union[Literal["auto"], int] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/__init__.py deleted file mode 100644 index 57d0d2511d..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .fine_tuned_models import FineTunedModelsStatus diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/fine_tuned_models.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/fine_tuned_models.py deleted file mode 100644 index b286a5b577..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/fine_tuning/models/fine_tuned_models.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import ClassVar - -from ....core import PYDANTIC_V2, BaseModel, ConfigDict - -__all__ = ["FineTunedModelsStatus"] - - -class FineTunedModelsStatus(BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow", protected_namespaces=()) - request_id: str # 请求id - model_name: str # 模型名称 - delete_status: str # 删除状态 deleting(删除中), deleted (已删除) diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/image.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/image.py deleted file mode 100644 index 3bcad0acab..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/image.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from ..core import BaseModel - -__all__ = ["GeneratedImage", "ImagesResponded"] - - -class GeneratedImage(BaseModel): - b64_json: Optional[str] = None - url: Optional[str] = None - revised_prompt: Optional[str] = None - - -class ImagesResponded(BaseModel): - created: int - data: list[GeneratedImage] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/__init__.py deleted file mode 100644 index 8c81d703e2..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .knowledge import KnowledgeInfo -from .knowledge_used import KnowledgeStatistics, KnowledgeUsed - -__all__ = [ - "KnowledgeInfo", - "KnowledgeStatistics", - "KnowledgeUsed", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/__init__.py deleted file mode 100644 index 59cb41d712..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .document import DocumentData, DocumentFailedInfo, DocumentObject, DocumentSuccessInfo - -__all__ = [ - "DocumentData", - "DocumentObject", - "DocumentSuccessInfo", - "DocumentFailedInfo", -] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document.py deleted file mode 100644 index 980bc6f4a7..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Optional - -from ....core import BaseModel - -__all__ = ["DocumentData", "DocumentObject", "DocumentSuccessInfo", "DocumentFailedInfo"] - - -class DocumentSuccessInfo(BaseModel): - documentId: Optional[str] = None - """文件id""" - filename: Optional[str] = None - """文件名称""" - - -class DocumentFailedInfo(BaseModel): - failReason: Optional[str] = None - """上传失败的原因,包括:文件格式不支持、文件大小超出限制、知识库容量已满、容量上限为 50 万字。""" - filename: Optional[str] = None - """文件名称""" - documentId: Optional[str] = None - """知识库id""" - - -class DocumentObject(BaseModel): - """文档信息""" - - successInfos: Optional[list[DocumentSuccessInfo]] = None - """上传成功的文件信息""" - failedInfos: Optional[list[DocumentFailedInfo]] = None - """上传失败的文件信息""" - - -class DocumentDataFailInfo(BaseModel): - """失败原因""" - - embedding_code: Optional[int] = ( - None # 失败码 10001:知识不可用,知识库空间已达上限 10002:知识不可用,知识库空间已达上限(字数超出限制) - ) - embedding_msg: Optional[str] = None # 失败原因 - - -class DocumentData(BaseModel): - id: str = None # 知识唯一id - custom_separator: list[str] = None # 切片规则 - sentence_size: str = None # 切片大小 - length: int = None # 文件大小(字节) - word_num: int = None # 文件字数 - name: str = None # 文件名 - url: str = None # 文件下载链接 - embedding_stat: int = None # 0:向量化中 1:向量化完成 2:向量化失败 - failInfo: Optional[DocumentDataFailInfo] = None # 失败原因 向量化失败embedding_stat=2的时候 会有此值 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_edit_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_edit_params.py deleted file mode 100644 index 509cb3a451..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_edit_params.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Optional, TypedDict - -__all__ = ["DocumentEditParams"] - - -class DocumentEditParams(TypedDict): - """ - 知识参数类型定义 - - Attributes: - id (str): 知识ID - knowledge_type (int): 知识类型: - 1:文章知识: 支持pdf,url,docx - 2.问答知识-文档: 支持pdf,url,docx - 3.问答知识-表格: 支持xlsx - 4.商品库-表格: 支持xlsx - 5.自定义: 支持pdf,url,docx - custom_separator (Optional[List[str]]): 当前知识类型为自定义(knowledge_type=5)时的切片规则,默认\n - sentence_size (Optional[int]): 当前知识类型为自定义(knowledge_type=5)时的切片字数,取值范围: 20-2000,默认300 - callback_url (Optional[str]): 回调地址 - callback_header (Optional[dict]): 回调时携带的header - """ - - id: str - knowledge_type: int - custom_separator: Optional[list[str]] - sentence_size: Optional[int] - callback_url: Optional[str] - callback_header: Optional[dict[str, str]] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_params.py deleted file mode 100644 index 910c8c045e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_params.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from typing_extensions import TypedDict - - -class DocumentListParams(TypedDict, total=False): - """ - 文件查询参数类型定义 - - Attributes: - purpose (Optional[str]): 文件用途 - knowledge_id (Optional[str]): 当文件用途为 retrieval 时,需要提供查询的知识库ID - page (Optional[int]): 页,默认1 - limit (Optional[int]): 查询文件列表数,默认10 - after (Optional[str]): 查询指定fileID之后的文件列表(当文件用途为 fine-tune 时需要) - order (Optional[str]): 排序规则,可选值['desc', 'asc'],默认desc(当文件用途为 fine-tune 时需要) - """ - - purpose: Optional[str] - knowledge_id: Optional[str] - page: Optional[int] - limit: Optional[int] - after: Optional[str] - order: Optional[str] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_resp.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_resp.py deleted file mode 100644 index acae4fad9f..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/document/document_list_resp.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import annotations - -from ....core import BaseModel -from . import DocumentData - -__all__ = ["DocumentPage"] - - -class DocumentPage(BaseModel): - list: list[DocumentData] - object: str diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge.py deleted file mode 100644 index bc6f159eb2..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = ["KnowledgeInfo"] - - -class KnowledgeInfo(BaseModel): - id: Optional[str] = None - """知识库唯一 id""" - embedding_id: Optional[str] = ( - None # 知识库绑定的向量化模型 见模型列表 [内部服务开放接口文档](https://lslfd0slxc.feishu.cn/docx/YauWdbBiMopV0FxB7KncPWCEn8f#H15NduiQZo3ugmxnWQFcfAHpnQ4) - ) - name: Optional[str] = None # 知识库名称 100字限制 - customer_identifier: Optional[str] = None # 用户标识 长度32位以内 - description: Optional[str] = None # 知识库描述 500字限制 - background: Optional[str] = None # 背景颜色(给枚举)'blue', 'red', 'orange', 'purple', 'sky' - icon: Optional[str] = ( - None # 知识库图标(给枚举) question: 问号、book: 书籍、seal: 印章、wrench: 扳手、tag: 标签、horn: 喇叭、house: 房子 # noqa: E501 - ) - bucket_id: Optional[str] = None # 桶id 限制32位 diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_create_params.py deleted file mode 100644 index c3da201727..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_create_params.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Optional - -from typing_extensions import TypedDict - -__all__ = ["KnowledgeBaseParams"] - - -class KnowledgeBaseParams(TypedDict): - """ - 知识库参数类型定义 - - Attributes: - embedding_id (int): 知识库绑定的向量化模型ID - name (str): 知识库名称,限制100字 - customer_identifier (Optional[str]): 用户标识,长度32位以内 - description (Optional[str]): 知识库描述,限制500字 - background (Optional[Literal['blue', 'red', 'orange', 'purple', 'sky']]): 背景颜色 - icon (Optional[Literal['question', 'book', 'seal', 'wrench', 'tag', 'horn', 'house']]): 知识库图标 - bucket_id (Optional[str]): 桶ID,限制32位 - """ - - embedding_id: int - name: str - customer_identifier: Optional[str] - description: Optional[str] - background: Optional[Literal["blue", "red", "orange", "purple", "sky"]] = None - icon: Optional[Literal["question", "book", "seal", "wrench", "tag", "horn", "house"]] = None - bucket_id: Optional[str] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_params.py deleted file mode 100644 index a221b28e46..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_params.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["KnowledgeListParams"] - - -class KnowledgeListParams(TypedDict, total=False): - page: int = 1 - """ 页码,默认 1,第一页 - """ - - size: int = 10 - """每页数量 默认10 - """ diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_resp.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_resp.py deleted file mode 100644 index e462eddc55..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_list_resp.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import annotations - -from ...core import BaseModel -from . import KnowledgeInfo - -__all__ = ["KnowledgePage"] - - -class KnowledgePage(BaseModel): - list: list[KnowledgeInfo] - object: str diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_used.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_used.py deleted file mode 100644 index cfda709702..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/knowledge/knowledge_used.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = ["KnowledgeStatistics", "KnowledgeUsed"] - - -class KnowledgeStatistics(BaseModel): - """ - 使用量统计 - """ - - word_num: Optional[int] = None - length: Optional[int] = None - - -class KnowledgeUsed(BaseModel): - used: Optional[KnowledgeStatistics] = None - """已使用量""" - total: Optional[KnowledgeStatistics] = None - """知识库总量""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/__init__.py deleted file mode 100644 index c9bd60419c..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .sensitive_word_check import SensitiveWordCheckRequest - -__all__ = ["SensitiveWordCheckRequest"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/sensitive_word_check.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/sensitive_word_check.py deleted file mode 100644 index 0c37d99e65..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/sensitive_word_check/sensitive_word_check.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import Optional - -from typing_extensions import TypedDict - - -class SensitiveWordCheckRequest(TypedDict, total=False): - type: Optional[str] - """敏感词类型,当前仅支持ALL""" - status: Optional[str] - """敏感词启用禁用状态 - 启用:ENABLE - 禁用:DISABLE - 备注:默认开启敏感词校验,如果要关闭敏感词校验,需联系商务获取对应权限,否则敏感词禁用不生效。 - """ diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/__init__.py deleted file mode 100644 index 62f77344ee..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .web_search import ( - SearchIntent, - SearchRecommend, - SearchResult, - WebSearch, -) -from .web_search_chunk import WebSearchChunk - -__all__ = ["WebSearch", "SearchIntent", "SearchResult", "SearchRecommend", "WebSearchChunk"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/tools_web_search_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/tools_web_search_params.py deleted file mode 100644 index b3a3b26f07..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/tools_web_search_params.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import Optional, Union - -from typing_extensions import TypedDict - -__all__ = ["WebSearchParams"] - - -class WebSearchParams(TypedDict): - """ - 工具名:web-search-pro参数类型定义 - - Attributes: - :param model: str, 模型名称 - :param request_id: Optional[str], 请求ID - :param stream: Optional[bool], 是否流式 - :param messages: Union[str, List[str], List[int], object, None], - 包含历史对话上下文的内容,按照 {"role": "user", "content": "你好"} 的json 数组形式进行传参 - 当前版本仅支持 User Message 单轮对话,工具会理解User Message并进行搜索, - 请尽可能传入不带指令格式的用户原始提问,以提高搜索准确率。 - :param scope: Optional[str], 指定搜索范围,全网、学术等,默认全网 - :param location: Optional[str], 指定搜索用户地区 location 提高相关性 - :param recent_days: Optional[int],支持指定返回 N 天(1-30)更新的搜索结果 - - - """ - - model: str - request_id: Optional[str] - stream: Optional[bool] - messages: Union[str, list[str], list[int], object, None] - scope: Optional[str] = None - location: Optional[str] = None - recent_days: Optional[int] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search.py deleted file mode 100644 index ac9fa3821e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = [ - "WebSearch", - "SearchIntent", - "SearchResult", - "SearchRecommend", -] - - -class SearchIntent(BaseModel): - index: int - # 搜索轮次,默认为 0 - query: str - # 搜索优化 query - intent: str - # 判断的意图类型 - keywords: str - # 搜索关键词 - - -class SearchResult(BaseModel): - index: int - # 搜索轮次,默认为 0 - title: str - # 标题 - link: str - # 链接 - content: str - # 内容 - icon: str - # 图标 - media: str - # 来源媒体 - refer: str - # 角标序号 [ref_1] - - -class SearchRecommend(BaseModel): - index: int - # 搜索轮次,默认为 0 - query: str - # 推荐query - - -class WebSearchMessageToolCall(BaseModel): - id: str - search_intent: Optional[SearchIntent] - search_result: Optional[SearchResult] - search_recommend: Optional[SearchRecommend] - type: str - - -class WebSearchMessage(BaseModel): - role: str - tool_calls: Optional[list[WebSearchMessageToolCall]] = None - - -class WebSearchChoice(BaseModel): - index: int - finish_reason: str - message: WebSearchMessage - - -class WebSearch(BaseModel): - created: Optional[int] = None - choices: list[WebSearchChoice] - request_id: Optional[str] = None - id: Optional[str] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search_chunk.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search_chunk.py deleted file mode 100644 index 7fb0e02bb5..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/tools/web_search_chunk.py +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Optional - -from ...core import BaseModel -from .web_search import SearchIntent, SearchRecommend, SearchResult - -__all__ = ["WebSearchChunk"] - - -class ChoiceDeltaToolCall(BaseModel): - index: int - id: Optional[str] = None - - search_intent: Optional[SearchIntent] = None - search_result: Optional[SearchResult] = None - search_recommend: Optional[SearchRecommend] = None - type: Optional[str] = None - - -class ChoiceDelta(BaseModel): - role: Optional[str] = None - tool_calls: Optional[list[ChoiceDeltaToolCall]] = None - - -class Choice(BaseModel): - delta: ChoiceDelta - finish_reason: Optional[str] = None - index: int - - -class WebSearchChunk(BaseModel): - id: Optional[str] = None - choices: list[Choice] - created: Optional[int] = None diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/__init__.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/__init__.py deleted file mode 100644 index b14072b1a7..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .video_object import VideoObject, VideoResult - -__all__ = ["VideoObject", "VideoResult"] diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_create_params.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_create_params.py deleted file mode 100644 index f5489d708e..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_create_params.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from typing_extensions import TypedDict - -__all__ = ["VideoCreateParams"] - -from ..sensitive_word_check import SensitiveWordCheckRequest - - -class VideoCreateParams(TypedDict, total=False): - model: str - """模型编码""" - prompt: str - """所需视频的文本描述""" - image_url: str - """所需视频的文本描述""" - sensitive_word_check: Optional[SensitiveWordCheckRequest] - """支持 URL 或者 Base64、传入 image 奖进行图生视频 - * 图片格式: - * 图片大小:""" - request_id: str - """由用户端传参,需保证唯一性;用于区分每次请求的唯一标识,用户端不传时平台会默认生成。""" - - user_id: str - """用户端。""" diff --git a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_object.py b/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_object.py deleted file mode 100644 index 85c3844d8a..0000000000 --- a/api/core/model_runtime/model_providers/zhipuai/zhipuai_sdk/types/video/video_object.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Optional - -from ...core import BaseModel - -__all__ = ["VideoObject", "VideoResult"] - - -class VideoResult(BaseModel): - url: str - """视频url""" - cover_image_url: str - """预览图""" - - -class VideoObject(BaseModel): - id: Optional[str] = None - """智谱 AI 开放平台生成的任务订单号,调用请求结果接口时请使用此订单号""" - - model: str - """模型名称""" - - video_result: list[VideoResult] - """视频生成结果""" - - task_status: str - """处理状态,PROCESSING(处理中),SUCCESS(成功),FAIL(失败) - 注:处理中状态需通过查询获取结果""" - - request_id: str - """用户在客户端请求时提交的任务编号或者平台生成的任务编号""" diff --git a/api/core/plugin/entities/plugin_daemon.py b/api/core/plugin/entities/plugin_daemon.py index c5dced121f..554353c6c9 100644 --- a/api/core/plugin/entities/plugin_daemon.py +++ b/api/core/plugin/entities/plugin_daemon.py @@ -1,8 +1,11 @@ +from datetime import datetime from enum import Enum from typing import Generic, Optional, TypeVar -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict, Field +from core.model_runtime.entities.model_entities import AIModelEntity +from core.model_runtime.entities.provider_entities import ProviderEntity from core.tools.entities.tool_entities import ToolProviderEntityWithPlugin T = TypeVar("T", bound=(BaseModel | dict | list | bool)) @@ -22,6 +25,7 @@ class InstallPluginMessage(BaseModel): """ Message for installing a plugin. """ + class Event(Enum): Info = "info" Done = "done" @@ -42,4 +46,44 @@ class PluginBasicBooleanResponse(BaseModel): """ Basic boolean response from plugin daemon. """ - result: bool \ No newline at end of file + + result: bool + + +class PluginModelSchemaEntity(BaseModel): + model_schema: AIModelEntity = Field(description="The model schema.") + + # pydantic configs + model_config = ConfigDict(protected_namespaces=()) + + +class PluginModelProviderEntity(BaseModel): + id: str = Field(alias="ID", description="ID") + created_at: datetime = Field(alias="CreatedAt", description="The created at time of the model provider.") + updated_at: datetime = Field(alias="UpdatedAt", description="The updated at time of the model provider.") + provider: str = Field(description="The provider of the model.") + tenant_id: str = Field(description="The tenant ID.") + plugin_unique_identifier: str = Field(description="The plugin unique identifier.") + plugin_id: str = Field(description="The plugin ID.") + declaration: ProviderEntity = Field(description="The declaration of the model provider.") + + +class PluginNumTokensResponse(BaseModel): + """ + Response for number of tokens. + """ + + num_tokens: int = Field(description="The number of tokens.") + + +class PluginStringResultResponse(BaseModel): + result: str = Field(description="The result of the string.") + + +class PluginVoiceEntity(BaseModel): + name: str = Field(description="The name of the voice.") + value: str = Field(description="The value of the voice.") + + +class PluginVoicesResponse(BaseModel): + voices: list[PluginVoiceEntity] = Field(description="The result of the voices.") diff --git a/api/core/plugin/entities/request.py b/api/core/plugin/entities/request.py index 5fe9ea1dda..af40ebc5ca 100644 --- a/api/core/plugin/entities/request.py +++ b/api/core/plugin/entities/request.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from typing import Any, Literal, Optional -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from core.entities.provider_entities import BasicProviderConfig from core.model_runtime.entities.message_entities import ( @@ -39,6 +39,8 @@ class BaseRequestInvokeModel(BaseModel): model: str model_type: ModelType + model_config = ConfigDict(protected_namespaces=()) + class RequestInvokeLLM(BaseRequestInvokeModel): """ @@ -53,6 +55,8 @@ class RequestInvokeLLM(BaseRequestInvokeModel): stop: Optional[list[str]] = Field(default_factory=list) stream: Optional[bool] = False + model_config = ConfigDict(protected_namespaces=()) + @field_validator("prompt_messages", mode="before") @classmethod def convert_prompt_messages(cls, v): diff --git a/api/core/plugin/manager/asset.py b/api/core/plugin/manager/asset.py index df76f56a6d..fc4a99ad49 100644 --- a/api/core/plugin/manager/asset.py +++ b/api/core/plugin/manager/asset.py @@ -2,11 +2,11 @@ from core.plugin.manager.base import BasePluginManager class PluginAssetManager(BasePluginManager): - def fetch_asset(self, id: str) -> bytes: + def fetch_asset(self, tenant_id: str, id: str) -> bytes: """ Fetch an asset by id. """ - response = self._request(method="GET", path=f"/assets/plugin/{id}") + response = self._request(method="GET", path=f"plugin/{tenant_id}/assets/{id}") if response.status_code != 200: raise ValueError(f"can not found asset {id}") return response.content diff --git a/api/core/plugin/manager/base.py b/api/core/plugin/manager/base.py index fd18b3798e..1e050427d4 100644 --- a/api/core/plugin/manager/base.py +++ b/api/core/plugin/manager/base.py @@ -132,8 +132,19 @@ class BasePluginManager: line_data = json.loads(line) rep = PluginDaemonBasicResponse[type](**line_data) if rep.code != 0: - raise ValueError(f"got error from plugin daemon: {rep.message}, code: {rep.code}") + raise PluginDaemonRespError(rep.message, rep.code) if rep.data is None: raise ValueError("got empty data from plugin daemon") yield rep.data - \ No newline at end of file + + +class PluginDaemonRespError(Exception): + """ + Plugin daemon response error. + """ + + def __init__(self, resp_message: str, code: int): + super().__init__() + self.message = f"got error from plugin daemon: {resp_message}, code: {code}" + self.resp_message = resp_message + self.code = code diff --git a/api/core/plugin/manager/model.py b/api/core/plugin/manager/model.py index 4411d76fe1..e34e9b516e 100644 --- a/api/core/plugin/manager/model.py +++ b/api/core/plugin/manager/model.py @@ -1,13 +1,523 @@ -from core.model_runtime.entities.provider_entities import ProviderEntity -from core.plugin.manager.base import BasePluginManager +import binascii +from collections.abc import Generator, Sequence +from typing import IO, Optional + +from core.model_runtime.entities.llm_entities import LLMResultChunk +from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool +from core.model_runtime.entities.model_entities import AIModelEntity +from core.model_runtime.entities.rerank_entities import RerankResult +from core.model_runtime.entities.text_embedding_entities import TextEmbeddingResult +from core.model_runtime.utils.encoders import jsonable_encoder +from core.plugin.entities.plugin_daemon import ( + PluginBasicBooleanResponse, + PluginModelProviderEntity, + PluginModelSchemaEntity, + PluginNumTokensResponse, + PluginStringResultResponse, + PluginVoicesResponse, +) +from core.plugin.manager.base import BasePluginManager, PluginDaemonRespError class PluginModelManager(BasePluginManager): - def fetch_model_providers(self, tenant_id: str) -> list[ProviderEntity]: + def fetch_model_providers(self, tenant_id: str) -> Sequence[PluginModelProviderEntity]: """ Fetch model providers for the given tenant. """ response = self._request_with_plugin_daemon_response( - "GET", f"plugin/{tenant_id}/models", list[ProviderEntity], params={"page": 1, "page_size": 256} + "GET", + f"plugin/{tenant_id}/management/models", + list[PluginModelProviderEntity], + params={"page": 1, "page_size": 256}, ) return response + + 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 + """ + response = self._request_with_plugin_daemon_response_stream( + "POST", + f"plugin/{tenant_id}/dispatch/model/schema", + PluginModelSchemaEntity, + data={ + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + }, + }, + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.model_schema + + return None + + def validate_provider_credentials( + self, tenant_id: str, user_id: str, plugin_id: str, provider: str, credentials: dict + ) -> bool: + """ + validate the credentials of the provider + """ + response = self._request_with_plugin_daemon_response_stream( + "POST", + f"plugin/{tenant_id}/dispatch/model/validate_provider_credentials", + PluginBasicBooleanResponse, + data={ + "user_id": user_id, + "data": { + "provider": provider, + "credentials": credentials, + }, + }, + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.result + + return False + + def validate_model_credentials( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + ) -> bool: + """ + validate the credentials of the provider + """ + response = self._request_with_plugin_daemon_response_stream( + "POST", + f"plugin/{tenant_id}/dispatch/model/validate_model_credentials", + PluginBasicBooleanResponse, + data={ + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + }, + }, + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.result + + return False + + def invoke_llm( + self, + 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, + ) -> Generator[LLMResultChunk, None, None]: + """ + Invoke llm + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/llm/invoke", + type=LLMResultChunk, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": "llm", + "model": model, + "credentials": credentials, + "prompt_messages": prompt_messages, + "model_parameters": model_parameters, + "tools": tools, + "stop": stop, + "stream": stream, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + try: + yield from response + except PluginDaemonRespError as e: + raise ValueError(e.resp_message + str(e.code)) + + def get_llm_num_tokens( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + prompt_messages: list[PromptMessage], + tools: Optional[list[PromptMessageTool]] = None, + ) -> int: + """ + Get number of tokens for llm + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/llm/num_tokens", + type=PluginNumTokensResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "prompt_messages": prompt_messages, + "tools": tools, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.num_tokens + + return 0 + + def invoke_text_embedding( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model: str, + credentials: dict, + texts: list[str], + ) -> TextEmbeddingResult: + """ + Invoke text embedding + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/text_embedding/invoke", + type=TextEmbeddingResult, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": "text-embedding", + "model": model, + "credentials": credentials, + "texts": texts, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp + + raise ValueError("Failed to invoke text embedding") + + def get_text_embedding_num_tokens( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + texts: list[str], + ) -> int: + """ + Get number of tokens for text embedding + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/text_embedding/num_tokens", + type=PluginNumTokensResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "texts": texts, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.num_tokens + + return 0 + + def invoke_rerank( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + query: str, + docs: list[str], + score_threshold: Optional[float] = None, + top_n: Optional[int] = None, + ) -> RerankResult: + """ + Invoke rerank + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/rerank/invoke", + type=RerankResult, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "query": query, + "docs": docs, + "score_threshold": score_threshold, + "top_n": top_n, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp + + raise ValueError("Failed to invoke rerank") + + def invoke_tts( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + content_text: str, + voice: str, + ) -> Generator[bytes, None, None]: + """ + Invoke tts + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/tts/invoke", + type=PluginStringResultResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "content_text": content_text, + "voice": voice, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + try: + for result in response: + hex_str = result.result + yield binascii.unhexlify(hex_str) + except PluginDaemonRespError as e: + raise ValueError(e.resp_message + str(e.code)) + + def get_tts_model_voices( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + language: Optional[str] = None, + ) -> list[dict]: + """ + Get tts model voices + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/model/voices", + type=PluginVoicesResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "language": language, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + for voice in resp.voices: + return [{"name": voice.name, "value": voice.value}] + + return [] + + def invoke_speech_to_text( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + file: IO[bytes], + ) -> str: + """ + Invoke speech to text + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/speech2text/invoke", + type=PluginStringResultResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "file": binascii.hexlify(file.read()).decode(), + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.result + + raise ValueError("Failed to invoke speech to text") + + def invoke_moderation( + self, + tenant_id: str, + user_id: str, + plugin_id: str, + provider: str, + model_type: str, + model: str, + credentials: dict, + text: str, + ) -> bool: + """ + Invoke moderation + """ + response = self._request_with_plugin_daemon_response_stream( + method="POST", + path=f"plugin/{tenant_id}/dispatch/moderation/invoke", + type=PluginBasicBooleanResponse, + data=jsonable_encoder( + { + "user_id": user_id, + "data": { + "provider": provider, + "model_type": model_type, + "model": model, + "credentials": credentials, + "text": text, + }, + } + ), + headers={ + "X-Plugin-ID": plugin_id, + "Content-Type": "application/json", + }, + ) + + for resp in response: + return resp.result + + raise ValueError("Failed to invoke moderation") diff --git a/api/core/provider_manager.py b/api/core/provider_manager.py index 3a1fe300df..cb49a6cf56 100644 --- a/api/core/provider_manager.py +++ b/api/core/provider_manager.py @@ -22,7 +22,7 @@ from core.helper.model_provider_cache import ProviderCredentialsCache, ProviderC from core.helper.position_helper import is_filtered from core.model_runtime.entities.model_entities import ModelType from core.model_runtime.entities.provider_entities import CredentialFormSchema, FormType, ProviderEntity -from core.model_runtime.model_providers import model_provider_factory +from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from extensions import ext_hosting_provider from extensions.ext_database import db from extensions.ext_redis import redis_client @@ -97,6 +97,7 @@ class ProviderManager: provider_name_to_provider_model_records_dict = self._get_all_provider_models(tenant_id) # Get all provider entities + model_provider_factory = ModelProviderFactory(tenant_id) provider_entities = model_provider_factory.get_providers() # Get All preferred provider types of the workspace @@ -204,12 +205,10 @@ class ProviderManager: if not provider_configuration: raise ValueError(f"Provider {provider} does not exist.") - provider_instance = provider_configuration.get_provider_instance() - model_type_instance = provider_instance.get_model_instance(model_type) + model_type_instance = provider_configuration.get_model_type_instance(model_type) return ProviderModelBundle( configuration=provider_configuration, - provider_instance=provider_instance, model_type_instance=model_type_instance, ) @@ -257,8 +256,8 @@ class ProviderManager: if not default_model: return None - provider_instance = model_provider_factory.get_provider_instance(default_model.provider_name) - provider_schema = provider_instance.get_provider_schema() + model_provider_factory = ModelProviderFactory(tenant_id) + provider_schema = model_provider_factory.get_provider_schema(provider=default_model.provider_name) return DefaultModelEntity( model=default_model.model_name, diff --git a/api/core/tools/utils/model_invocation_utils.py b/api/core/tools/utils/model_invocation_utils.py index 4e226810d6..b3c3292f5d 100644 --- a/api/core/tools/utils/model_invocation_utils.py +++ b/api/core/tools/utils/model_invocation_utils.py @@ -10,7 +10,7 @@ from typing import cast 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 @@ from core.model_runtime.errors.invoke import ( 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/services/model_load_balancing_service.py b/api/services/model_load_balancing_service.py index e7b9422cfe..5c5c0c1607 100644 --- a/api/services/model_load_balancing_service.py +++ b/api/services/model_load_balancing_service.py @@ -14,7 +14,7 @@ from core.model_runtime.entities.provider_entities import ( 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 @@ class ModelLoadBalancingService: 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 384a072b37..630d575f82 100644 --- a/api/services/model_provider_service.py +++ b/api/services/model_provider_service.py @@ -1,16 +1,12 @@ import logging -import mimetypes import os -from pathlib import Path -from typing import Optional, cast +from typing import Optional import requests -from flask import current_app from core.entities.model_entities import ModelStatus, 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 ( @@ -100,7 +96,7 @@ class ModelProviderService: ModelWithProviderEntityResponse(model) for model in provider_configurations.get_models(provider=provider) ] - def get_provider_credentials(self, tenant_id: str, provider: str) -> dict: + def get_provider_credentials(self, tenant_id: str, provider: str) -> Optional[dict]: """ get provider credentials. @@ -176,7 +172,7 @@ class ModelProviderService: # Remove custom provider credentials. provider_configuration.delete_custom_credentials() - def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str) -> dict: + def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str) -> Optional[dict]: """ get model credentials. @@ -351,18 +347,17 @@ class ModelProviderService: 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 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]: """ @@ -410,52 +405,21 @@ class ModelProviderService: ) 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() + model_provider_factory = ModelProviderFactory(tenant_id) + byte_data = model_provider_factory.get_provider_icon(provider, icon_type, lang) - 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 - - 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 - - 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, "application/octet-stream" def switch_preferred_provider(self, tenant_id: str, provider: str, preferred_provider_type: str) -> None: """ @@ -525,6 +489,9 @@ class ModelProviderService: 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") + if not api_base_url: + raise Exception("FREE_QUOTA_APPLY_BASE_URL is not set") + api_url = api_base_url + "/api/v1/providers/apply" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} @@ -546,6 +513,9 @@ class ModelProviderService: 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") + if not api_base_url: + raise Exception("FREE_QUOTA_APPLY_BASE_URL is not set") + api_url = api_base_url + "/api/v1/providers/qualification-verify" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} 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 index 0ec4b0b724..5cb8a6252a 100644 --- a/api/tests/integration_tests/model_runtime/test_model_provider_factory.py +++ b/api/tests/integration_tests/model_runtime/test_model_provider_factory.py @@ -9,7 +9,7 @@ logger = logging.getLogger(__name__) def test_get_providers(): - factory = ModelProviderFactory() + factory = ModelProviderFactory("test_tenant") providers = factory.get_providers() for provider in providers: @@ -20,7 +20,7 @@ def test_get_providers(): def test_get_models(): - factory = ModelProviderFactory() + factory = ModelProviderFactory("test_tenant") providers = factory.get_models( model_type=ModelType.LLM, provider_configs=[ @@ -51,19 +51,7 @@ def test_get_models(): def test_provider_credentials_validate(): - factory = ModelProviderFactory() + factory = ModelProviderFactory("test_tenant") 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/workflow/nodes/test_llm.py b/api/tests/integration_tests/workflow/nodes/test_llm.py index dfb43650d2..aff6e1c481 100644 --- a/api/tests/integration_tests/workflow/nodes/test_llm.py +++ b/api/tests/integration_tests/workflow/nodes/test_llm.py @@ -115,7 +115,6 @@ def test_execute_llm(setup_openai_mock): 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") @@ -203,7 +202,6 @@ def test_execute_llm_with_jinja2(setup_code_executor_mock, setup_openai_mock): custom_configuration=CustomConfiguration(provider=CustomProviderConfiguration(credentials=credentials)), model_settings=[], ), - provider_instance=provider_instance, model_type_instance=model_type_instance, ) 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 88435c4022..90c0d54e17 100644 --- a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py +++ b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py @@ -35,23 +35,27 @@ def get_mocked_fetch_model_config( mode: str, credentials: dict, ): - provider_instance = ModelProviderFactory().get_provider_instance(provider) - model_type_instance = provider_instance.get_model_instance(ModelType.LLM) + 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=provider_instance.get_provider_schema(), + 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=[], ), - 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) + 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, diff --git a/api/tests/unit_tests/core/test_provider_manager.py b/api/tests/unit_tests/core/test_provider_manager.py index 2f4214a580..44284e03d0 100644 --- a/api/tests/unit_tests/core/test_provider_manager.py +++ b/api/tests/unit_tests/core/test_provider_manager.py @@ -1,12 +1,13 @@ 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.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 @@ -71,6 +72,7 @@ def test__to_model_settings(mocker): 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 @@ -123,6 +125,7 @@ def test__to_model_settings_only_one_lb(mocker): 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