From ab37d696c3dbb8feb5976f893ae5519cbca3b4d2 Mon Sep 17 00:00:00 2001 From: mogith-pn <143642606+mogith-pn@users.noreply.github.com> Date: Fri, 17 Oct 2025 04:15:34 +0530 Subject: [PATCH] updated clarifai functions to openai compatible (#15615) --- docs/my-website/docs/providers/clarifai.md | 368 +++++++++++------- litellm/__init__.py | 2 + litellm/constants.py | 97 ++--- .../get_llm_provider_logic.py | 9 + litellm/llms/clarifai/chat/transformation.py | 309 +++++---------- litellm/llms/clarifai/common_utils.py | 6 - litellm/main.py | 37 +- litellm/utils.py | 2 + 8 files changed, 364 insertions(+), 466 deletions(-) delete mode 100644 litellm/llms/clarifai/common_utils.py diff --git a/docs/my-website/docs/providers/clarifai.md b/docs/my-website/docs/providers/clarifai.md index cb498650385..7c3f8607684 100644 --- a/docs/my-website/docs/providers/clarifai.md +++ b/docs/my-website/docs/providers/clarifai.md @@ -1,21 +1,24 @@ # Clarifai -Anthropic, OpenAI, Mistral, Llama and Gemini LLMs are Supported on Clarifai. +Anthropic, OpenAI, Qwen, xAI, Gemini and most of Open soured LLMs are Supported on Clarifai. -:::warning - -Streaming is not yet supported on using clarifai and litellm. Tracking support here: https://github.com/BerriAI/litellm/issues/4162 - -::: +| Property | Details | +|-------|-------| +| Description | Clarifai is a powerful AI platform that provides access to a wide range of LLMs through a unified API. LiteLLM enables seamless integration with Clarifai's models using an OpenAI-compatible interface. | +| Provider Doc | [Clarifai ↗](https://docs.clarifai.com/) | +|OpenAI compatible Endpoint for Provider | `https://api.clarifai.com/v2/ext/openai/v1` | +| Supported Endpoints | `/chat/completions` | ## Pre-Requisites -`pip install litellm` + +```bash +pip install litellm +``` ## Required Environment Variables -To obtain your Clarifai Personal access token follow this [link](https://docs.clarifai.com/clarifai-basics/authentication/personal-access-tokens/). Optionally the PAT can also be passed in `completion` function. +To obtain your Clarifai Personal access token follow this [link](https://docs.clarifai.com/clarifai-basics/authentication/personal-access-tokens/). ```python -os.environ["CLARIFAI_API_KEY"] = "YOUR_CLARIFAI_PAT" # CLARIFAI_PAT - +os.environ["CLARIFAI_PAT"] = "CLARIFAI_API_KEY" # CLARIFAI_PAT ``` ## Usage @@ -27,154 +30,231 @@ from litellm import completion os.environ["CLARIFAI_API_KEY"] = "" response = completion( - model="clarifai/mistralai.completion.mistral-large", + model="clarifai/openai.chat-completion.gpt-oss-20b", messages=[{ "content": "Tell me a joke about physics?","role": "user"}] ) ``` +## Streaming Support -**Output** -```json -{ - "id": "chatcmpl-572701ee-9ab2-411c-ac75-46c1ba18e781", - "choices": [ - { - "finish_reason": "stop", - "index": 1, - "message": { - "content": "Sure, here's a physics joke for you:\n\nWhy can't you trust an atom?\n\nBecause they make up everything!", - "role": "assistant" - } - } +LiteLLM supports streaming responses with Clarifai models: + +```python +import litellm + +for chunk in litellm.completion( + model="clarifai/openai.chat-completion.gpt-oss-20b", + api_key="CLARIFAI_API_KEY", + messages=[ + {"role": "user", "content": "Tell me a fun fact about space."} ], - "created": 1714410197, - "model": "https://api.clarifai.com/v2/users/mistralai/apps/completion/models/mistral-large/outputs", - "object": "chat.completion", - "system_fingerprint": null, - "usage": { - "prompt_tokens": 14, - "completion_tokens": 24, - "total_tokens": 38 + stream=True, +): + print(chunk.choices[0].delta) +``` + +## Tool Calling (Function Calling) + +Clarifai models accessed via LiteLLM support function calling: + +```python +import litellm + +tools = [{ + "type": "function", + "function": { + "name": "get_weather", + "description": "Get current temperature for a given location.", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City and country e.g. Tokyo, Japan" + } + }, + "required": ["location"], + "additionalProperties": False + }, } } +}] + +response = litellm.completion( + model="clarifai/openai.chat-completion.gpt-oss-20b", + api_key="CLARIFAI_API_KEY", + messages=[{"role": "user", "content": "What is the weather in Paris today?"}], + tools=tools, +) + +print(response.choices[0].message.tool_calls) ``` ## Clarifai models liteLLM supports all models on [Clarifai community](https://clarifai.com/explore/models?filterData=%5B%7B%22field%22%3A%22use_cases%22%2C%22value%22%3A%5B%22llm%22%5D%7D%5D&page=1&perPage=24) -Example Usage - Note: liteLLM supports all models deployed on Clarifai - -## Llama LLMs -| Model Name | Function Call | ----------------------------|---------------------------------| -| clarifai/meta.Llama-2.llama2-7b-chat | `completion('clarifai/meta.Llama-2.llama2-7b-chat', messages)` -| clarifai/meta.Llama-2.llama2-13b-chat | `completion('clarifai/meta.Llama-2.llama2-13b-chat', messages)` -| clarifai/meta.Llama-2.llama2-70b-chat | `completion('clarifai/meta.Llama-2.llama2-70b-chat', messages)` | -| clarifai/meta.Llama-2.codeLlama-70b-Python | `completion('clarifai/meta.Llama-2.codeLlama-70b-Python', messages)`| -| clarifai/meta.Llama-2.codeLlama-70b-Instruct | `completion('clarifai/meta.Llama-2.codeLlama-70b-Instruct', messages)` | - -## Mistral LLMs -| Model Name | Function Call | -|---------------------------------------------|------------------------------------------------------------------------| -| clarifai/mistralai.completion.mixtral-8x22B | `completion('clarifai/mistralai.completion.mixtral-8x22B', messages)` | -| clarifai/mistralai.completion.mistral-large | `completion('clarifai/mistralai.completion.mistral-large', messages)` | -| clarifai/mistralai.completion.mistral-medium | `completion('clarifai/mistralai.completion.mistral-medium', messages)` | -| clarifai/mistralai.completion.mistral-small | `completion('clarifai/mistralai.completion.mistral-small', messages)` | -| clarifai/mistralai.completion.mixtral-8x7B-Instruct-v0_1 | `completion('clarifai/mistralai.completion.mixtral-8x7B-Instruct-v0_1', messages)` -| clarifai/mistralai.completion.mistral-7B-OpenOrca | `completion('clarifai/mistralai.completion.mistral-7B-OpenOrca', messages)` | -| clarifai/mistralai.completion.openHermes-2-mistral-7B | `completion('clarifai/mistralai.completion.openHermes-2-mistral-7B', messages)` | +### 🧠 OpenAI Models +- [gpt-oss-20b](https://clarifai.com/openai/chat-completion/models/gpt-oss-20b) +- [gpt-oss-120b](https://clarifai.com/openai/chat-completion/models/gpt-oss-120b) +- [gpt-5-nano](https://clarifai.com/openai/chat-completion/models/gpt-5-nano) +- [gpt-5-mini](https://clarifai.com/openai/chat-completion/models/gpt-5-mini) +- [gpt-5](https://clarifai.com/openai/chat-completion/models/gpt-5) +- [gpt-4o](https://clarifai.com/openai/chat-completion/models/gpt-4o) +- [o3](https://clarifai.com/openai/chat-completion/models/o3) +- Many more... -## Jurassic LLMs -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/ai21.complete.Jurassic2-Grande | `completion('clarifai/ai21.complete.Jurassic2-Grande', messages)` | -| clarifai/ai21.complete.Jurassic2-Grande-Instruct | `completion('clarifai/ai21.complete.Jurassic2-Grande-Instruct', messages)` | -| clarifai/ai21.complete.Jurassic2-Jumbo-Instruct | `completion('clarifai/ai21.complete.Jurassic2-Jumbo-Instruct', messages)` | -| clarifai/ai21.complete.Jurassic2-Jumbo | `completion('clarifai/ai21.complete.Jurassic2-Jumbo', messages)` | -| clarifai/ai21.complete.Jurassic2-Large | `completion('clarifai/ai21.complete.Jurassic2-Large', messages)` | - -## Wizard LLMs - -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/wizardlm.generate.wizardCoder-Python-34B | `completion('clarifai/wizardlm.generate.wizardCoder-Python-34B', messages)` | -| clarifai/wizardlm.generate.wizardLM-70B | `completion('clarifai/wizardlm.generate.wizardLM-70B', messages)` | -| clarifai/wizardlm.generate.wizardLM-13B | `completion('clarifai/wizardlm.generate.wizardLM-13B', messages)` | -| clarifai/wizardlm.generate.wizardCoder-15B | `completion('clarifai/wizardlm.generate.wizardCoder-15B', messages)` | - -## Anthropic models - -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/anthropic.completion.claude-v1 | `completion('clarifai/anthropic.completion.claude-v1', messages)` | -| clarifai/anthropic.completion.claude-instant-1_2 | `completion('clarifai/anthropic.completion.claude-instant-1_2', messages)` | -| clarifai/anthropic.completion.claude-instant | `completion('clarifai/anthropic.completion.claude-instant', messages)` | -| clarifai/anthropic.completion.claude-v2 | `completion('clarifai/anthropic.completion.claude-v2', messages)` | -| clarifai/anthropic.completion.claude-2_1 | `completion('clarifai/anthropic.completion.claude-2_1', messages)` | -| clarifai/anthropic.completion.claude-3-opus | `completion('clarifai/anthropic.completion.claude-3-opus', messages)` | -| clarifai/anthropic.completion.claude-3-sonnet | `completion('clarifai/anthropic.completion.claude-3-sonnet', messages)` | - -## OpenAI GPT LLMs - -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/openai.chat-completion.GPT-4 | `completion('clarifai/openai.chat-completion.GPT-4', messages)` | -| clarifai/openai.chat-completion.GPT-3_5-turbo | `completion('clarifai/openai.chat-completion.GPT-3_5-turbo', messages)` | -| clarifai/openai.chat-completion.gpt-4-turbo | `completion('clarifai/openai.chat-completion.gpt-4-turbo', messages)` | -| clarifai/openai.completion.gpt-3_5-turbo-instruct | `completion('clarifai/openai.completion.gpt-3_5-turbo-instruct', messages)` | - -## GCP LLMs - -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/gcp.generate.gemini-1_5-pro | `completion('clarifai/gcp.generate.gemini-1_5-pro', messages)` | -| clarifai/gcp.generate.imagen-2 | `completion('clarifai/gcp.generate.imagen-2', messages)` | -| clarifai/gcp.generate.code-gecko | `completion('clarifai/gcp.generate.code-gecko', messages)` | -| clarifai/gcp.generate.code-bison | `completion('clarifai/gcp.generate.code-bison', messages)` | -| clarifai/gcp.generate.text-bison | `completion('clarifai/gcp.generate.text-bison', messages)` | -| clarifai/gcp.generate.gemma-2b-it | `completion('clarifai/gcp.generate.gemma-2b-it', messages)` | -| clarifai/gcp.generate.gemma-7b-it | `completion('clarifai/gcp.generate.gemma-7b-it', messages)` | -| clarifai/gcp.generate.gemini-pro | `completion('clarifai/gcp.generate.gemini-pro', messages)` | -| clarifai/gcp.generate.gemma-1_1-7b-it | `completion('clarifai/gcp.generate.gemma-1_1-7b-it', messages)` | - -## Cohere LLMs -| Model Name | Function Call | -|-----------------------------------------------|---------------------------------------------------------------------| -| clarifai/cohere.generate.cohere-generate-command | `completion('clarifai/cohere.generate.cohere-generate-command', messages)` | - clarifai/cohere.generate.command-r-plus' | `completion('clarifai/clarifai/cohere.generate.command-r-plus', messages)`| - -## Databricks LLMs - -| Model Name | Function Call | -|---------------------------------------------------|---------------------------------------------------------------------| -| clarifai/databricks.drbx.dbrx-instruct | `completion('clarifai/databricks.drbx.dbrx-instruct', messages)` | -| clarifai/databricks.Dolly-v2.dolly-v2-12b | `completion('clarifai/databricks.Dolly-v2.dolly-v2-12b', messages)`| - -## Microsoft LLMs - -| Model Name | Function Call | -|---------------------------------------------------|---------------------------------------------------------------------| -| clarifai/microsoft.text-generation.phi-2 | `completion('clarifai/microsoft.text-generation.phi-2', messages)` | -| clarifai/microsoft.text-generation.phi-1_5 | `completion('clarifai/microsoft.text-generation.phi-1_5', messages)`| - -## Salesforce models - -| Model Name | Function Call | -|-----------------------------------------------------------|-------------------------------------------------------------------------------| -| clarifai/salesforce.blip.general-english-image-caption-blip-2 | `completion('clarifai/salesforce.blip.general-english-image-caption-blip-2', messages)` | -| clarifai/salesforce.xgen.xgen-7b-8k-instruct | `completion('clarifai/salesforce.xgen.xgen-7b-8k-instruct', messages)` | +### 🤖 Anthropic Models +- [claude-sonnet-4](https://clarifai.com/anthropic/completion/models/claude-sonnet-4) +- [claude-opus-4](https://clarifai.com/anthropic/completion/models/claude-opus-4) +- [claude-3_5-haiku](https://clarifai.com/anthropic/completion/models/claude-3_5-haiku) +- [claude-3_7-sonnet](https://clarifai.com/anthropic/completion/models/claude-3_7-sonnet) +- Many more... -## Other Top performing LLMs +### 🪄 xAI Models +- [grok-3](https://clarifai.com/xai/chat-completion/models/grok-3) +- [grok-2-vision-1212](https://clarifai.com/xai/chat-completion/models/grok-2-vision-1212) +- [grok-2-1212](https://clarifai.com/xai/chat-completion/models/grok-2-1212) +- [grok-code-fast-1](https://clarifai.com/xai/chat-completion/models/grok-code-fast-1) +- [grok-2-image-1212](https://clarifai.com/xai/image-generation/models/grok-2-image-1212) +- Many more... -| Model Name | Function Call | -|---------------------------------------------------|---------------------------------------------------------------------| -| clarifai/deci.decilm.deciLM-7B-instruct | `completion('clarifai/deci.decilm.deciLM-7B-instruct', messages)` | -| clarifai/upstage.solar.solar-10_7b-instruct | `completion('clarifai/upstage.solar.solar-10_7b-instruct', messages)` | -| clarifai/openchat.openchat.openchat-3_5-1210 | `completion('clarifai/openchat.openchat.openchat-3_5-1210', messages)` | -| clarifai/togethercomputer.stripedHyena.stripedHyena-Nous-7B | `completion('clarifai/togethercomputer.stripedHyena.stripedHyena-Nous-7B', messages)` | -| clarifai/fblgit.una-cybertron.una-cybertron-7b-v2 | `completion('clarifai/fblgit.una-cybertron.una-cybertron-7b-v2', messages)` | -| clarifai/tiiuae.falcon.falcon-40b-instruct | `completion('clarifai/tiiuae.falcon.falcon-40b-instruct', messages)` | -| clarifai/togethercomputer.RedPajama.RedPajama-INCITE-7B-Chat | `completion('clarifai/togethercomputer.RedPajama.RedPajama-INCITE-7B-Chat', messages)` | -| clarifai/bigcode.code.StarCoder | `completion('clarifai/bigcode.code.StarCoder', messages)` | -| clarifai/mosaicml.mpt.mpt-7b-instruct | `completion('clarifai/mosaicml.mpt.mpt-7b-instruct', messages)` | + +### 🔷 Google Gemini Models +- [gemini-2_5-pro](https://clarifai.com/gcp/generate/models/gemini-2_5-pro) +- [gemini-2_5-flash-lite](https://clarifai.com/gcp/generate/models/gemini-2_5-flash-lite) +- [gemini-2_0-flash](https://clarifai.com/gcp/generate/models/gemini-2_0-flash) +- [gemini-2_0-flash-lite](https://clarifai.com/gcp/generate/models/gemini-2_0-flash-lite) +- Many more... + + +### 🧩 Qwen Models +- [Qwen3-30B-A3B-Instruct-2507](https://clarifai.com/qwen/qwenLM/models/Qwen3-30B-A3B-Instruct-2507) +- [Qwen3-30B-A3B-Thinking-2507](https://clarifai.com/qwen/qwenLM/models/Qwen3-30B-A3B-Thinking-2507) +- [Qwen3-14B](https://clarifai.com/qwen/qwenLM/models/Qwen3-14B) +- [QwQ-32B-AWQ](https://clarifai.com/qwen/qwenLM/models/QwQ-32B-AWQ) +- [Qwen2_5-VL-7B-Instruct](https://clarifai.com/qwen/qwen-VL/models/Qwen2_5-VL-7B-Instruct) +- [Qwen3-Coder-30B-A3B-Instruct](https://clarifai.com/qwen/qwenCoder/models/Qwen3-Coder-30B-A3B-Instruct) +- Many more... + + +### 💡 MiniCPM (OpenBMB) Models +- [MiniCPM-o-2_6-language](https://clarifai.com/openbmb/miniCPM/models/MiniCPM-o-2_6-language) +- [MiniCPM3-4B](https://clarifai.com/openbmb/miniCPM/models/MiniCPM3-4B) +- [MiniCPM4-8B](https://clarifai.com/openbmb/miniCPM/models/MiniCPM4-8B) +- Many more... + + +### 🧬 Microsoft Phi Models +- [Phi-4-reasoning-plus](https://clarifai.com/microsoft/text-generation/models/Phi-4-reasoning-plus) +- [phi-4](https://clarifai.com/microsoft/text-generation/models/phi-4) +- Many more... + + +### 🦙 Meta Llama Models +- [Llama-3_2-3B-Instruct](https://clarifai.com/meta/Llama-3/models/Llama-3_2-3B-Instruct) +- Many more... + + +### 🔍 DeepSeek Models +- [DeepSeek-R1-0528-Qwen3-8B](https://clarifai.com/deepseek-ai/deepseek-chat/models/DeepSeek-R1-0528-Qwen3-8B) +- Many more... + +## Usage with LiteLLM Proxy + +Here's how to call Clarifai with the LiteLLM Proxy Server + +### 1. Save key in your environment + +```bash +export CLARIFAI_PAT="CLARIFAI_API_KEY" +``` + +### 2. Start the proxy + + + + +```yaml +model_list: + - model_name: clarifai-model + litellm_params: + model: clarifai/openai.chat-completion.gpt-oss-20b + api_key: os.environ/CLARIFAI_PAT +``` + +```bash +litellm --config /path/to/config.yaml + +# Server running on http://0.0.0.0:4000 +``` + + + +### 3. Test it + + + + +```shell +curl --location 'http://0.0.0.0:4000/chat/completions' \ +--header 'Content-Type: application/json' \ +--data ' { + "model": "clarifai-model", + "messages": [ + { + "role": "user", + "content": "what llm are you" + } + ] + } +' +``` + + + +```python +import openai +client = openai.OpenAI( + api_key="anything", + base_url="http://0.0.0.0:4000" +) + +response = client.chat.completions.create( + model="clarifai-model", + messages = [ + { + "role": "user", + "content": "this is a test request, write a short poem" + } + ] +) + +print(response) +``` + + + +## Important Notes + +- Always prefix Clarifai model IDs with `clarifai/` when specifying the model name +- Use your Clarifai Personal Access Token (PAT) as the API key +- Usage is tracked and billed through Clarifai +- API rate limits are subject to your Clarifai account settings +- Most OpenAI parameters are supported, but some advanced features may vary by model + + +## FAQs + +| Question | Answer | +|----------|---------| +| Can I use all Clarifai models with LiteLLM? | Most chat-completion models are supported. Use the Clarifai model URL as the `model`. | +| Do I need a separate Clarifai PAT? | Yes, you must use a valid Clarifai Personal Access Token. | +| Is tool calling supported? | Yes, provided the underlying Clarifai model supports function/tool calling. | +| How is billing handled? | Clarifai usage is billed independently via Clarifai. | + +## Additional Resources + +- [Clarifai Documentation](https://docs.clarifai.com/) +- [LiteLLM GitHub](https://github.com/BerriAI/litellm) +- [Clarifai Runners Examples](https://github.com/Clarifai/runners-examples) \ No newline at end of file diff --git a/litellm/__init__.py b/litellm/__init__.py index 02bfaaf72f9..dddcc6c2574 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -849,6 +849,7 @@ model_list = list( | wandb_models | ovhcloud_models | lemonade_models + | set(clarifai_models) ) model_list_set = set(model_list) @@ -934,6 +935,7 @@ models_by_provider: dict = { "wandb": wandb_models, "ovhcloud": ovhcloud_models | ovhcloud_embedding_models, "lemonade": lemonade_models, + "clarifai": clarifai_models, } # mapping for those models which have larger equivalents diff --git a/litellm/constants.py b/litellm/constants.py index d1858bdfd84..24657c90014 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -480,6 +480,7 @@ openai_compatible_endpoints: List = [ "https://api.hyperbolic.xyz/v1", "https://ai-gateway.vercel.sh/v1", "https://api.inference.wandb.ai/v1", + "https://api.clarifai.com/v2/ext/openai/v1", ] @@ -526,6 +527,7 @@ openai_compatible_providers: List = [ "aiml", "wandb", "cometapi", + "clarifai", ] openai_text_completion_compatible_providers: List = ( [ # providers that support `/v1/completions` @@ -569,69 +571,37 @@ replicate_models: set = set( clarifai_models: set = set( [ - "clarifai/meta.Llama-3.Llama-3-8B-Instruct", - "clarifai/gcp.generate.gemma-1_1-7b-it", - "clarifai/mistralai.completion.mixtral-8x22B", - "clarifai/cohere.generate.command-r-plus", - "clarifai/databricks.drbx.dbrx-instruct", - "clarifai/mistralai.completion.mistral-large", - "clarifai/mistralai.completion.mistral-medium", - "clarifai/mistralai.completion.mistral-small", - "clarifai/mistralai.completion.mixtral-8x7B-Instruct-v0_1", - "clarifai/gcp.generate.gemma-2b-it", - "clarifai/gcp.generate.gemma-7b-it", - "clarifai/deci.decilm.deciLM-7B-instruct", - "clarifai/mistralai.completion.mistral-7B-Instruct", - "clarifai/gcp.generate.gemini-pro", - "clarifai/anthropic.completion.claude-v1", - "clarifai/anthropic.completion.claude-instant-1_2", - "clarifai/anthropic.completion.claude-instant", - "clarifai/anthropic.completion.claude-v2", - "clarifai/anthropic.completion.claude-2_1", - "clarifai/meta.Llama-2.codeLlama-70b-Python", - "clarifai/meta.Llama-2.codeLlama-70b-Instruct", - "clarifai/openai.completion.gpt-3_5-turbo-instruct", - "clarifai/meta.Llama-2.llama2-7b-chat", - "clarifai/meta.Llama-2.llama2-13b-chat", - "clarifai/meta.Llama-2.llama2-70b-chat", - "clarifai/openai.chat-completion.gpt-4-turbo", - "clarifai/microsoft.text-generation.phi-2", - "clarifai/meta.Llama-2.llama2-7b-chat-vllm", - "clarifai/upstage.solar.solar-10_7b-instruct", - "clarifai/openchat.openchat.openchat-3_5-1210", - "clarifai/togethercomputer.stripedHyena.stripedHyena-Nous-7B", - "clarifai/gcp.generate.text-bison", - "clarifai/meta.Llama-2.llamaGuard-7b", - "clarifai/fblgit.una-cybertron.una-cybertron-7b-v2", - "clarifai/openai.chat-completion.GPT-4", - "clarifai/openai.chat-completion.GPT-3_5-turbo", - "clarifai/ai21.complete.Jurassic2-Grande", - "clarifai/ai21.complete.Jurassic2-Grande-Instruct", - "clarifai/ai21.complete.Jurassic2-Jumbo-Instruct", - "clarifai/ai21.complete.Jurassic2-Jumbo", - "clarifai/ai21.complete.Jurassic2-Large", - "clarifai/cohere.generate.cohere-generate-command", - "clarifai/wizardlm.generate.wizardCoder-Python-34B", - "clarifai/wizardlm.generate.wizardLM-70B", - "clarifai/tiiuae.falcon.falcon-40b-instruct", - "clarifai/togethercomputer.RedPajama.RedPajama-INCITE-7B-Chat", - "clarifai/gcp.generate.code-gecko", - "clarifai/gcp.generate.code-bison", - "clarifai/mistralai.completion.mistral-7B-OpenOrca", - "clarifai/mistralai.completion.openHermes-2-mistral-7B", - "clarifai/wizardlm.generate.wizardLM-13B", - "clarifai/huggingface-research.zephyr.zephyr-7B-alpha", - "clarifai/wizardlm.generate.wizardCoder-15B", - "clarifai/microsoft.text-generation.phi-1_5", - "clarifai/databricks.Dolly-v2.dolly-v2-12b", - "clarifai/bigcode.code.StarCoder", - "clarifai/salesforce.xgen.xgen-7b-8k-instruct", - "clarifai/mosaicml.mpt.mpt-7b-instruct", - "clarifai/anthropic.completion.claude-3-opus", - "clarifai/anthropic.completion.claude-3-sonnet", - "clarifai/gcp.generate.gemini-1_5-pro", - "clarifai/gcp.generate.imagen-2", - "clarifai/salesforce.blip.general-english-image-caption-blip-2", + "clarifai/openai.chat-completion.gpt-oss-20b", + "clarifai/qwen.qwenLM.Qwen3-30B-A3B-Instruct-2507", + "clarifai/qwen.qwen3.qwen3-next-80B-A3B-Thinking", + "clarifai/openai.chat-completion.gpt-oss-120b", + "clarifai/qwen.qwenLM.Qwen3-30B-A3B-Thinking-2507" + "clarifai/openai.chat-completion.gpt-5-nano", + "clarifai/openai.chat-completion.gpt-4o", + "clarifai/gcp.generate.gemini-2_5-pro", + "clarifai/anthropic.completion.claude-sonnet-4", + "clarifai/xai.chat-completion.grok-2-vision-1212", + "clarifai/openbmb.miniCPM.MiniCPM-o-2_6-language", + "clarifai/microsoft.text-generation.Phi-4-reasoning-plus", + "clarifai/openbmb.miniCPM.MiniCPM3-4B", + "clarifai/openbmb.miniCPM.MiniCPM4-8B", + "clarifai/xai.chat-completion.grok-2-1212", + "clarifai/anthropic.completion.claude-opus-4", + "clarifai/xai.chat-completion.grok-code-fast-1", + "clarifai/qwen.qwenCoder.Qwen3-Coder-30B-A3B-Instruct", + "clarifai/deepseek-ai.deepseek-chat.DeepSeek-R1-0528-Qwen3-8B", + "clarifai/openai.chat-completion.gpt-5-mini", + "clarifai/microsoft.text-generation.phi-4", + "clarifai/openai.chat-completion.gpt-5", + "clarifai/meta.Llama-3.Llama-3_2-3B-Instruct", + "clarifai/xai.image-generation.grok-2-image-1212", + "clarifai/xai.chat-completion.grok-3", + "clarifai/openai.chat-completion.o3", + "clarifai/qwen.qwen-VL.Qwen2_5-VL-7B-Instruct", + "clarifai/qwen.qwenLM.Qwen3-14B", + "clarifai/qwen.qwenLM.QwQ-32B-AWQ", + "clarifai/anthropic.completion.claude-3_5-haiku", + "clarifai/anthropic.completion.claude-3_7-sonnet", ] ) @@ -1118,6 +1088,7 @@ SENTRY_DENYLIST = [ "FIREWORKS_AI_API_KEY", "FIREWORKSAI_API_KEY", "OVHCLOUD_API_KEY", + "CLARIFAI_API_KEY", # Database and Connection Strings "database_url", "redis_url", diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index f209aed483c..275f2a63a1b 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -383,6 +383,8 @@ def get_llm_provider( # noqa: PLR0915 custom_llm_provider = "ovhcloud" elif model.startswith("lemonade/"): custom_llm_provider = "lemonade" + elif model.startswith("clarifai/"): + custom_llm_provider = "clarifai" if not custom_llm_provider: if litellm.suppress_debug_info is False: print() # noqa @@ -794,6 +796,13 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915 ) = litellm.LemonadeChatConfig()._get_openai_compatible_provider_info( api_base, api_key ) + elif custom_llm_provider == "clarifai": + ( + api_base, + dynamic_api_key, + ) = litellm.ClarifaiConfig()._get_openai_compatible_provider_info( + api_base, api_key + ) if api_base is not None and not isinstance(api_base, str): raise Exception("api base needs to be a string. api_base={}".format(api_base)) diff --git a/litellm/llms/clarifai/chat/transformation.py b/litellm/llms/clarifai/chat/transformation.py index 73be89fc6e7..48884ff0139 100644 --- a/litellm/llms/clarifai/chat/transformation.py +++ b/litellm/llms/clarifai/chat/transformation.py @@ -1,262 +1,133 @@ -import json -from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, List, Optional, Union +from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union import httpx -from litellm.litellm_core_utils.prompt_templates.common_utils import ( - convert_content_list_to_str, +from litellm.secret_managers.main import get_secret_str +from litellm.types.utils import ModelResponse +from litellm.types.llms.openai import ( + AllMessageValues, ) -from litellm.llms.base_llm.base_model_iterator import FakeStreamResponseIterator -from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException -from litellm.types.llms.openai import AllMessageValues -from litellm.types.utils import ( - ChatCompletionToolCallChunk, - ChatCompletionUsageBlock, - Choices, - GenericStreamingChunk, - Message, - ModelResponse, - Usage, -) -from litellm.utils import token_counter +from litellm.llms.openai.common_utils import OpenAIError +from litellm.llms.base_llm.chat.transformation import BaseLLMException -from ..common_utils import ClarifaiError +from ...openai.chat.gpt_transformation import OpenAIGPTConfig if TYPE_CHECKING: - from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj - LoggingClass = LiteLLMLoggingObj + LiteLLMLoggingObj = _LiteLLMLoggingObj else: - LoggingClass = Any + LiteLLMLoggingObj = Any -class ClarifaiConfig(BaseConfig): +class ClarifaiConfig(OpenAIGPTConfig): """ - Reference: https://clarifai.com/meta/Llama-2/models/llama2-70b-chat + Configuration class for Clarifai chat completions. + Since Clarifai is OpenAI-compatible, we extend OpenAIGPTConfig. """ - - max_tokens: Optional[int] = None - temperature: Optional[int] = None - top_k: Optional[int] = None - - def __init__( - self, - max_tokens: Optional[int] = None, - temperature: Optional[int] = None, - top_k: Optional[int] = None, - ) -> None: - locals_ = locals().copy() - for key, value in locals_.items(): - if key != "self" and value is not None: - setattr(self.__class__, key, value) - - @classmethod - def get_config(cls): - return super().get_config() - def get_supported_openai_params(self, model: str) -> list: + """ + Get the supported OpenAI params for the given model + """ return [ - "temperature", "max_tokens", + "max_completion_tokens", + "response_format", + "stream", + "temperature", + "top_p", + "tool_choice", + "tools", + "presence_penalty", + "frequency_penalty", + "stream_options", ] - - def map_openai_params( - self, - non_default_params: dict, - optional_params: dict, - model: str, - drop_params: bool, - ) -> dict: - for param, value in non_default_params.items(): - if param == "temperature": - optional_params["temperature"] = value - elif param == "max_tokens": - optional_params["max_tokens"] = value - - return optional_params - - def _completions_to_model(self, prompt: str, optional_params: dict) -> dict: - params = {} - if temperature := optional_params.get("temperature"): - params["temperature"] = temperature - if max_tokens := optional_params.get("max_tokens"): - params["max_tokens"] = max_tokens - return { - "inputs": [{"data": {"text": {"raw": prompt}}}], - "model": {"output_info": {"params": params}}, - } - - def _convert_model_to_url(self, model: str, api_base: str): - user_id, app_id, model_id = model.split(".") - return f"{api_base}/users/{user_id}/apps/{app_id}/models/{model_id}/outputs" - - def transform_request( - self, - model: str, - messages: List[AllMessageValues], - optional_params: dict, - litellm_params: dict, - headers: dict, - ) -> dict: - prompt = " ".join(convert_content_list_to_str(message) for message in messages) - - ## Load Config - config = self.get_config() - for k, v in config.items(): - if k not in optional_params: - optional_params[k] = v - - data = self._completions_to_model( - prompt=prompt, optional_params=optional_params + + @staticmethod + def get_api_key(api_key: Optional[str] = None) -> Optional[str]: + return ( + api_key + or get_secret_str("CLARIFAI_API_KEY") ) + + @staticmethod + def get_api_base(api_base: Optional[str] = None) -> Optional[str]: + return api_base or "https://api.clarifai.com/v2/ext/openai/v1" + + @staticmethod + def get_base_model(model: Optional[str] = None) -> Optional[str]: + if model: + user_id, app_id, model_id = model.split(".") + return f"https://clarifai.com/{user_id}/{app_id}/models/{model_id}" + return None - return data - - def validate_environment( + def _get_openai_compatible_provider_info( self, - headers: dict, - model: str, - messages: List[AllMessageValues], - optional_params: dict, - litellm_params: dict, - api_key: Optional[str] = None, - api_base: Optional[str] = None, - ) -> dict: - headers = { - "accept": "application/json", - "content-type": "application/json", - } - - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - return headers - - def get_error_class( - self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers] - ) -> BaseLLMException: - return ClarifaiError(message=error_message, status_code=status_code) - + api_base: Optional[str], + api_key: Optional[str], + ) -> Tuple[Optional[str], Optional[str]]: + """ + Get API base and key for Clarifai provider. + """ + api_base = api_base or "https://api.clarifai.com/v2/ext/openai/v1" + dynamic_api_key = api_key or get_secret_str("CLARIFAI_API_KEY") or "" + return api_base, dynamic_api_key + + def transform_request(self, model, messages, optional_params, litellm_params, headers): + model = self.get_base_model(model) or model + return super().transform_request(model, messages, optional_params, litellm_params, headers) + def transform_response( self, model: str, raw_response: httpx.Response, model_response: ModelResponse, - logging_obj: LoggingClass, + logging_obj: LiteLLMLoggingObj, request_data: dict, messages: List[AllMessageValues], optional_params: dict, litellm_params: dict, - encoding: str, + encoding: Any, api_key: Optional[str] = None, json_mode: Optional[bool] = None, ) -> ModelResponse: + """ + Transform the Clarifai response to a standard ModelResponse. + Since Clarifai is OpenAI-compatible, we use OpenAI response transformation. + """ + ## Logging logging_obj.post_call( input=messages, api_key=api_key, original_response=raw_response.text, additional_args={"complete_input_dict": request_data}, ) - ## RESPONSE OBJECT + ## Reponse try: completion_response = raw_response.json() - except httpx.HTTPStatusError as e: - raise ClarifaiError( - message=str(e), + except Exception as e: + raise OpenAIError( status_code=raw_response.status_code, - ) - except Exception as e: - raise ClarifaiError( - message=str(e), - status_code=422, - ) - # print(completion_response) - try: - choices_list = [] - for idx, item in enumerate(completion_response["outputs"]): - if len(item["data"]["text"]["raw"]) > 0: - message_obj = Message(content=item["data"]["text"]["raw"]) - else: - message_obj = Message(content=None) - choice_obj = Choices( - finish_reason="stop", - index=idx + 1, # check - message=message_obj, - ) - choices_list.append(choice_obj) - model_response.choices = choices_list # type: ignore + message=f"Failed to parse Clarifai response: {str(e)}", + headers=raw_response.headers, + ) from e + + response = ModelResponse(**completion_response) + + if response.model is not None: + response.model = "clarifai/" + model - except Exception as e: - raise ClarifaiError( - message=str(e), - status_code=422, - ) + return response - # Calculate Usage - prompt_tokens = token_counter(model=model, messages=messages) - completion_tokens = len( - encoding.encode(model_response["choices"][0]["message"].get("content")) - ) - model_response.model = model - setattr( - model_response, - "usage", - Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, - ), - ) - return model_response - - def get_model_response_iterator( - self, - streaming_response: Union[Iterator[str], AsyncIterator[str], ModelResponse], - sync_stream: bool, - json_mode: Optional[bool] = False, - ) -> Any: - return ClarifaiModelResponseIterator( - model_response=streaming_response, - json_mode=json_mode, - ) - - -class ClarifaiModelResponseIterator(FakeStreamResponseIterator): - def __init__( - self, - model_response: Union[Iterator[str], AsyncIterator[str], ModelResponse], - json_mode: Optional[bool] = False, - ): - super().__init__( - model_response=model_response, - json_mode=json_mode, - ) - - def chunk_parser(self, chunk: dict) -> GenericStreamingChunk: - try: - text = "" - tool_use: Optional[ChatCompletionToolCallChunk] = None - is_finished = False - finish_reason = "" - usage: Optional[ChatCompletionUsageBlock] = None - provider_specific_fields = None - - text = ( - chunk.get("outputs", "")[0] - .get("data", "") - .get("text", "") - .get("raw", "") - ) - - index: int = 0 - - return GenericStreamingChunk( - text=text, - tool_use=tool_use, - is_finished=is_finished, - finish_reason=finish_reason, - usage=usage, - index=index, - provider_specific_fields=provider_specific_fields, - ) - except json.JSONDecodeError: - raise ValueError(f"Failed to decode JSON from chunk: {chunk}") + def get_error_class( + self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers] + ) -> BaseLLMException: + """ + Get the appropriate error class for Clarifai errors. + Since Clarifai is OpenAI-compatible, we use OpenAI error handling. + """ + return OpenAIError( + status_code=status_code, + message=error_message, + headers=headers, + ) \ No newline at end of file diff --git a/litellm/llms/clarifai/common_utils.py b/litellm/llms/clarifai/common_utils.py deleted file mode 100644 index 611d2ccf30b..00000000000 --- a/litellm/llms/clarifai/common_utils.py +++ /dev/null @@ -1,6 +0,0 @@ -from litellm.llms.base_llm.chat.transformation import BaseLLMException - - -class ClarifaiError(BaseLLMException): - def __init__(self, status_code: int, message: str): - super().__init__(status_code=status_code, message=message) diff --git a/litellm/main.py b/litellm/main.py index 5c50f096460..fa505f3fac5 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -154,6 +154,7 @@ from .llms.bedrock.chat import BedrockConverseLLM, BedrockLLM from .llms.bedrock.embed.embedding import BedrockEmbedding from .llms.bedrock.image.image_handler import BedrockImageGeneration from .llms.bytez.chat.transformation import BytezChatConfig +from .llms.clarifai.chat.transformation import ClarifaiConfig from .llms.codestral.completion.handler import CodestralTextCompletion from .llms.cohere.embed import handler as cohere_embed from .llms.custom_httpx.aiohttp_handler import BaseLLMAIOHTTPHandler @@ -2027,6 +2028,7 @@ def completion( # type: ignore # noqa: PLR0915 or custom_llm_provider == "together_ai" or custom_llm_provider == "nebius" or custom_llm_provider == "wandb" + or custom_llm_provider == "clarifai" or custom_llm_provider in litellm.openai_compatible_providers or "ft:gpt-3.5-turbo" in model # finetune gpt-3.5-turbo ): # allow user to make an openai call with a custom base @@ -2221,40 +2223,7 @@ def completion( # type: ignore # noqa: PLR0915 or custom_llm_provider == "clarifai" or model in litellm.clarifai_models ): - clarifai_key = None - clarifai_key = ( - api_key - or litellm.clarifai_key - or litellm.api_key - or get_secret("CLARIFAI_API_KEY") - or get_secret("CLARIFAI_API_TOKEN") - ) - - api_base = ( - api_base - or litellm.api_base - or get_secret("CLARIFAI_API_BASE") - or "https://api.clarifai.com/v2" - ) - api_base = litellm.ClarifaiConfig()._convert_model_to_url(model, api_base) - response = base_llm_http_handler.completion( - model=model, - stream=stream, - fake_stream=True, # clarifai does not support streaming, we fake it - messages=messages, - acompletion=acompletion, - api_base=api_base, - model_response=model_response, - optional_params=optional_params, - litellm_params=litellm_params, - shared_session=shared_session, - custom_llm_provider="clarifai", - timeout=timeout, - headers=headers, - encoding=encoding, - api_key=clarifai_key, - logging_obj=logging, # model call logging done inside the class as we make need to modify I/O to fit aleph alpha's requirements - ) + pass # Deprecated - handled in the openai compatible provider section above elif custom_llm_provider == "anthropic_text": api_key = ( api_key diff --git a/litellm/utils.py b/litellm/utils.py index f017543ceaa..dd42f4aebf1 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7360,6 +7360,8 @@ class ProviderConfigManager: return VLLMModelInfo() elif LlmProviders.LEMONADE == provider: return litellm.LemonadeChatConfig() + elif LlmProviders.CLARIFAI == provider: + return litellm.ClarifaiConfig() return None @staticmethod