Repository: BerriAI/litellm
Pull request #8950: [FEAT] Support for Snowflake REST API LLMs #7979
Author: login: SunnyWan59
Created: 2025-03-03T06:43:30Z | Merged: 2025-03-13T23:51:58Z

##  Support for Snowflake REST API LLMs
Adds support for the LLMs in Snowflake REST API endpoint. Currently, Snowflake REST API does not support embeddings, but the Snowflake snowflake-arctic-embed models are hosted on HuggingFace so users should be able to call it already.

References: 
https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api 
https://docs.snowflake.com/en/user-guide/snowflake-cortex/llm-functions
https://huggingface.co/collections/Snowflake/arctic-embed-661fd57d50fab5fc314e4c18 
## Relevant issues
Implements:#7979


## Type
🆕 New Feature
📖 Documentation
✅ Test

## Changes

<!-- List of changes -->
Added support for Snowflake REST API completion. 
Added tests for the new providers

## [REQUIRED] Testing - Attach a screenshot of any new tests passing locally
Screenshots of tests passing:
<img width="1156" alt="Screenshot 2025-03-03 at 5 42 02 PM" src="https://github.com/user-attachments/assets/be40323a-2a0e-42f3-90ae-81b61abf5d26" />



Diff:
diff --git a/docs/my-website/docs/providers/snowflake.md b/docs/my-website/docs/providers/snowflake.md
new file mode 100644
index 00000000000..6a666e29531
--- /dev/null
+++ b/docs/my-website/docs/providers/snowflake.md
@@ -0,0 +1,89 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+# Snowflake
+| Property | Details |
+|-------|-------|
+| Description | The Snowflake Cortex LLM REST API lets you access the COMPLETE function via HTTP POST requests|
+| Provider Route on LiteLLM | `snowflake/` |
+| Link to Provider Doc | [Vertex AI ↗](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api) |
+| Base URL | [https://{account-id}.snowflakecomputing.com/api/v2/cortex/inference:complete/](https://{account-id}.snowflakecomputing.com/api/v2/cortex/inference:complete) |
+| Supported Operations | `/completions`|
+
+
+
+Currently, Snowflake's REST API does not have an endpoint for `snowflake-arctic-embed` embedding models. If you want to use these embedding models with Litellm, you can call them through our Hugging Face provider. 
+
+Find the Arctic Embed models [here](https://huggingface.co/collections/Snowflake/arctic-embed-661fd57d50fab5fc314e4c18) on Hugging Face.
+## Supported OpenAI Parameters
+```
+    "temperature",
+    "max_tokens",
+    "top_p",
+    "response_format"
+```
+
+## API KEYS
+
+Snowflake does have API keys. Instead, you access the Snowflake API with your JWT token and account identifier.
+
+```python
+import os 
+os.environ["SNOWFLAKE_JWT"] = "YOUR JWT"
+os.environ["SNOWFLAKE_ACCOUNT_ID"] = "YOUR ACCOUNT IDENTIFIER"
+```
+## Usage
+
+```python
+from litellm import completion
+
+## set ENV variables
+os.environ["SNOWFLAKE_JWT"] = "YOUR JWT"
+os.environ["SNOWFLAKE_ACCOUNT_ID"] = "YOUR ACCOUNT IDENTIFIER"
+
+# Snowflake call
+response = completion(
+    model="snowflake/mistral-7b", 
+    messages = [{ "content": "Hello, how are you?","role": "user"}]
+)
+```
+
+## Usage with LiteLLM Proxy 
+
+#### 1. Required env variables
+```bash
+export SNOWFLAKE_JWT=""
+export SNOWFLAKE_ACCOUNT_ID = ""
+```
+
+#### 2. Start the proxy~
+```yaml
+model_list:
+  - model_name: mistral-7b
+    litellm_params:
+        model: snowflake/mistral-7b
+        api_key: YOUR_API_KEY
+        api_base: https://YOUR-ACCOUNT-ID.snowflakecomputing.com/api/v2/cortex/inference:complete
+
+```
+
+```bash
+litellm --config /path/to/config.yaml
+```
+
+#### 3. Test it
+```shell
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+--header 'Content-Type: application/json' \
+--data ' {
+      "model": "snowflake/mistral-7b",
+      "messages": [
+        {
+          "role": "user",
+          "content": "Hello, how are you?"
+        }
+      ]
+    }
+'
+```
diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js
index 3bdd906c21d..385983fd339 100644
--- a/docs/my-website/sidebars.js
+++ b/docs/my-website/sidebars.js
@@ -231,6 +231,7 @@ const sidebars = {
         "providers/sambanova",
         "providers/custom_llm_server",
         "providers/petals",
+        "providers/snowflake"
       ],
     },
     {
diff --git a/litellm/__init__.py b/litellm/__init__.py
index 6d7a91dd5bc..762a058c7ec 100644
--- a/litellm/__init__.py
+++ b/litellm/__init__.py
@@ -182,6 +182,7 @@
 baseten_key: Optional[str] = None
 aleph_alpha_key: Optional[str] = None
 nlp_cloud_key: Optional[str] = None
+snowflake_key: Optional[str] = None
 common_cloud_provider_auth_params: dict = {
     "params": ["project", "region_name", "token"],
     "providers": ["vertex_ai", "bedrock", "watsonx", "azure", "vertex_ai_beta"],
@@ -416,6 +417,7 @@ def identify(event_details):
 galadriel_models: List = []
 sambanova_models: List = []
 assemblyai_models: List = []
+snowflake_models: List = []
 
 
 def is_bedrock_pricing_only_model(key: str) -> bool:
@@ -569,6 +571,8 @@ def add_known_models():
             assemblyai_models.append(key)
         elif value.get("litellm_provider") == "jina_ai":
             jina_ai_models.append(key)
+        elif value.get("litellm_provider") == "snowflake":
+            snowflake_models.append(key)
 
 
 add_known_models()
@@ -598,6 +602,7 @@ def add_known_models():
 
 maritalk_models = ["maritalk"]
 
+
 model_list = (
     open_ai_chat_completion_models
     + open_ai_text_completion_models
@@ -642,6 +647,7 @@ def add_known_models():
     + azure_text_models
     + assemblyai_models
     + jina_ai_models
+    + snowflake_models
 )
 
 model_list_set = set(model_list)
@@ -697,6 +703,7 @@ def add_known_models():
     "sambanova": sambanova_models,
     "assemblyai": assemblyai_models,
     "jina_ai": jina_ai_models,
+    "snowflake": snowflake_models,
 }
 
 # mapping for those models which have larger equivalents
@@ -813,6 +820,7 @@ def add_known_models():
 from .llms.predibase.chat.transformation import PredibaseConfig
 from .llms.replicate.chat.transformation import ReplicateConfig
 from .llms.cohere.completion.transformation import CohereTextConfig as CohereConfig
+from .llms.snowflake.chat.transformation import SnowflakeConfig
 from .llms.cohere.rerank.transformation import CohereRerankConfig
 from .llms.cohere.rerank_v2.transformation import CohereRerankV2Config
 from .llms.azure_ai.rerank.transformation import AzureAIRerankConfig
@@ -932,6 +940,8 @@ def add_known_models():
     OpenAIOSeriesConfig,
 )
 
+from .llms.snowflake.chat.transformation import SnowflakeConfig
+
 openaiOSeriesConfig = OpenAIOSeriesConfig()
 from .llms.openai.chat.gpt_transformation import (
     OpenAIGPTConfig,
diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py
index a64e7dd700d..0bf74c5dcaa 100644
--- a/litellm/litellm_core_utils/get_llm_provider_logic.py
+++ b/litellm/litellm_core_utils/get_llm_provider_logic.py
@@ -571,6 +571,14 @@ def _get_openai_compatible_provider_info(  # noqa: PLR0915
             or "https://api.galadriel.com/v1"
         )  # type: ignore
         dynamic_api_key = api_key or get_secret_str("GALADRIEL_API_KEY")
+    elif custom_llm_provider == "snowflake":
+        api_base = (
+                    api_base
+                    or get_secret("SNOWFLAKE_API_BASE")
+                    or f"https://{get_secret('SNOWFLAKE_ACCOUNT_ID')}.snowflakecomputing.com/api/v2/cortex/inference:complete"
+                )  # type: ignore
+        dynamic_api_key = api_key or get_secret("SNOWFLAKE_JWT")
+
     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))
     if dynamic_api_key is not None and not isinstance(dynamic_api_key, str):
diff --git a/litellm/llms/snowflake/chat/transformation.py b/litellm/llms/snowflake/chat/transformation.py
new file mode 100644
index 00000000000..77006079580
--- /dev/null
+++ b/litellm/llms/snowflake/chat/transformation.py
@@ -0,0 +1,176 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
+    """
+    source: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex
+    """
+
+    @classmethod
+    def get_config(cls):
+        return super().get_config()
+    
+    def get_supported_openai_params(self, model: str) -> List:
+        return [
+            "temperature",
+            "max_tokens",
+            "top_p",
+            "response_format"
+        ]
+            
+    def map_openai_params(
+        self,
+        non_default_params: dict,
+        optional_params: dict,
+        model: str,
+        drop_params: bool,
+    ) -> dict:
+        """
+        If any supported_openai_params are in non_default_params, add them to optional_params, so they are used in API call
+
+        Args:
+            non_default_params (dict): Non-default parameters to filter.
+            optional_params (dict): Optional parameters to update.
+            model (str): Model name for parameter support check.
+
+        Returns:
+            dict: Updated optional_params with supported non-default parameters.
+        """
+        supported_openai_params = self.get_supported_openai_params(model)
+        for param, value in non_default_params.items():
+            if param in supported_openai_params:
+                optional_params[param] = value
+        return optional_params
+
+    @staticmethod
+    def transform_response(
+        model: str,
+        raw_response: httpx.Response,
+        model_response: ModelResponse,
+        logging_obj: LiteLLMLoggingObj,
+        request_data: dict,
+        messages: List[AllMessageValues],
+        optional_params: dict,
+        litellm_params: dict,
+        encoding: Any,
+        api_key: Optional[str] = None,
+        json_mode: Optional[bool] = None,
+    ) -> ModelResponse:
+        response_json = raw_response.json()
+        logging_obj.post_call(
+            input=messages,
+            api_key="",
+            original_response=response_json,
+            additional_args={"complete_input_dict": request_data},
+        )
+
+
+        returned_response = ModelResponse(**response_json)
+
+        returned_response.model = (
+            "snowflake/" + (returned_response.model or "")
+        )
+
+        if model is not None:
+            returned_response._hidden_params["model"] = model
+        return returned_response
+
+
+    def validate_environment(
+        self,
+        headers: dict,
+        model: str,
+        api_base: str = None,
+        api_key: Optional[str] = None,
+        messages: dict = None,
+        optional_params: dict = None,
+    ) -> dict:
+        """
+        Return headers to use for Snowflake completion request
+
+        Snowflake REST API Ref: https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api#api-reference
+        Expected headers:
+        {
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+            "Authorization": "Bearer " + <JWT>,
+            "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+        } 
+        """
+
+        if api_key is None:
+                raise ValueError(
+                    "Missing Snowflake JWT key"
+                )
+
+        headers.update(
+            {
+                "Content-Type": "application/json",
+                "Accept": "application/json",
+                "Authorization": "Bearer " + api_key,
+                "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+            }
+        )
+        return headers
+    
+    def _get_openai_compatible_provider_info(
+        self, api_base: Optional[str], api_key: Optional[str]
+    ) -> Tuple[Optional[str], Optional[str]]:
+        api_base = (
+            api_base
+            or f"""https://{get_secret("SNOWFLAKE_ACCOUNT_ID")}.snowflakecomputing.com/api/v2/cortex/inference:complete"""
+            or get_secret("SNOWFLAKE_API_BASE")
+        )  # type: ignore
+        dynamic_api_key = api_key or get_secret("SNOWFLAKE_JWT")
+        return api_base, dynamic_api_key
+    
+    def get_complete_url(
+        self,
+        api_base: Optional[str],
+        model: str,
+        optional_params: dict,
+        stream: Optional[bool] = None,
+    ) -> str:
+        """
+        If api_base is not provided, use the default DeepSeek /chat/completions endpoint.
+        """
+        if not api_base:
+            api_base = f"""https://{get_secret("SNOWFLAKE_ACCOUNT_ID")}.snowflakecomputing.com/api/v2/cortex/inference:complete"""
+
+        return api_base
+    
+    def transform_request(
+        self, 
+        model: str, 
+        messages: dict , 
+        optional_params: dict, 
+        litellm_params: dict, 
+        headers: dict
+    ) -> dict:
+        stream: bool = optional_params.pop("stream", None) or False
+        extra_body = optional_params.pop("extra_body", {})
+        return {
+            "model": model,
+            "messages": messages,
+            "stream": stream,
+            **optional_params,
+            **extra_body,
+        }
\ No newline at end of file
diff --git a/litellm/llms/snowflake/common_utils.py b/litellm/llms/snowflake/common_utils.py
new file mode 100644
index 00000000000..ab8d37bcf13
--- /dev/null
+++ b/litellm/llms/snowflake/common_utils.py
@@ -0,0 +1,38 @@
+import httpx
+from typing import List, Optional
+
+class SnowflakeBase:
+    def validate_environment(
+        self,
+        headers: dict,
+        JWT: Optional[str] = None,
+    ) -> dict:
+        """
+        Return headers to use for Snowflake completion request
+
+        Snowflake REST API Ref: https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api#api-reference
+        Expected headers:
+        {
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+            "Authorization": "Bearer " + <JWT>,
+            "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+        } 
+        """
+
+        if JWT is None:
+                raise ValueError(
+                    "Missing Snowflake JWT key"
+                )
+
+        headers.update(
+            {
+                "Content-Type": "application/json",
+                "Accept": "application/json",
+                "Authorization": "Bearer " + JWT,
+                "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+            }
+        )
+        return headers
+    
+    
\ No newline at end of file
diff --git a/litellm/main.py b/litellm/main.py
index 84ad92dfe06..6ae2df517d2 100644
--- a/litellm/main.py
+++ b/litellm/main.py
@@ -2986,6 +2986,38 @@ def completion(  # type: ignore # noqa: PLR0915
                 )
                 return response
             response = model_response
+        elif custom_llm_provider == "snowflake" or model in litellm.snowflake_models: 
+            try:
+                client = HTTPHandler(timeout=timeout) if stream is False else None # Keep this here, otherwise, the httpx.client closes and streaming is impossible
+                response = base_llm_http_handler.completion(
+                    model=model,
+                    messages=messages,
+                    headers=headers,
+                    model_response=model_response,
+                    api_key=api_key,
+                    api_base=api_base,
+                    acompletion=acompletion,
+                    logging_obj=logging,
+                    optional_params=optional_params,
+                    litellm_params=litellm_params,
+                    timeout=timeout,  # type: ignore
+                    client= client,
+                    custom_llm_provider=custom_llm_provider,
+                    encoding=encoding,
+                    stream=stream,
+                )   
+
+
+            except Exception as e:
+                ## LOGGING - log the original exception returned
+                logging.post_call(
+                    input=messages,
+                    api_key=api_key,
+                    original_response=str(e),
+                    additional_args={"headers": headers},
+                )
+                raise e
+            
         elif custom_llm_provider == "custom":
             url = litellm.api_base or api_base or ""
             if url is None or url == "":
@@ -3044,6 +3076,7 @@ def completion(  # type: ignore # noqa: PLR0915
             model_response.created = int(time.time())
             model_response.model = model
             response = model_response
+
         elif (
             custom_llm_provider in litellm._custom_providers
         ):  # Assume custom LLM provider
diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json
index 3b9bd946f57..fa9c7ffbd55 100644
--- a/litellm/model_prices_and_context_window_backup.json
+++ b/litellm/model_prices_and_context_window_backup.json
@@ -10067,5 +10067,173 @@
         "output_cost_per_token": 0.000000018,
         "litellm_provider": "jina_ai",
         "mode": "rerank"
+    },
+    "snowflake/deepseek-r1": {
+        "max_tokens": 32768,
+        "max_input_tokens": 32768,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-arctic": {
+        "max_tokens": 4096,
+        "max_input_tokens": 4096,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/claude-3-5-sonnet": {
+        "max_tokens": 18000,
+        "max_input_tokens": 18000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-large": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-large2": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/reka-flash": {
+        "max_tokens": 100000,
+        "max_input_tokens": 100000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/reka-core": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-instruct": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-1.5-mini": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-1.5-large": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mixtral-8x7b": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama2-70b-chat": {
+        "max_tokens": 4096,
+        "max_input_tokens": 4096,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3-8b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3-70b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-8b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-70b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.3-70b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-llama-3.3-70b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-405b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-llama-3.1-405b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.2-1b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.2-3b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-7b": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/gemma-7b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
     }
 }
diff --git a/litellm/types/utils.py b/litellm/types/utils.py
index db315e26963..9608c099a33 100644
--- a/litellm/types/utils.py
+++ b/litellm/types/utils.py
@@ -1967,6 +1967,7 @@ class LlmProviders(str, Enum):
     HUMANLOOP = "humanloop"
     TOPAZ = "topaz"
     ASSEMBLYAI = "assemblyai"
+    SNOWFLAKE = "snowflake"
 
 
 # Create a set of all provider values for quick lookup
diff --git a/litellm/utils.py b/litellm/utils.py
index db1a3c7f301..423c950a1ce 100644
--- a/litellm/utils.py
+++ b/litellm/utils.py
@@ -6107,6 +6107,8 @@ def get_provider_chat_config(  # noqa: PLR0915
             return litellm.CohereChatConfig()
         elif litellm.LlmProviders.COHERE == provider:
             return litellm.CohereConfig()
+        elif litellm.LlmProviders.SNOWFLAKE == provider:
+            return litellm.SnowflakeConfig()
         elif litellm.LlmProviders.CLARIFAI == provider:
             return litellm.ClarifaiConfig()
         elif litellm.LlmProviders.ANTHROPIC == provider:
diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json
index 3b9bd946f57..fa9c7ffbd55 100644
--- a/model_prices_and_context_window.json
+++ b/model_prices_and_context_window.json
@@ -10067,5 +10067,173 @@
         "output_cost_per_token": 0.000000018,
         "litellm_provider": "jina_ai",
         "mode": "rerank"
+    },
+    "snowflake/deepseek-r1": {
+        "max_tokens": 32768,
+        "max_input_tokens": 32768,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-arctic": {
+        "max_tokens": 4096,
+        "max_input_tokens": 4096,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/claude-3-5-sonnet": {
+        "max_tokens": 18000,
+        "max_input_tokens": 18000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-large": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-large2": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/reka-flash": {
+        "max_tokens": 100000,
+        "max_input_tokens": 100000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/reka-core": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-instruct": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-1.5-mini": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/jamba-1.5-large": {
+        "max_tokens": 256000,
+        "max_input_tokens": 256000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mixtral-8x7b": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama2-70b-chat": {
+        "max_tokens": 4096,
+        "max_input_tokens": 4096,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3-8b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3-70b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-8b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-70b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.3-70b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-llama-3.3-70b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.1-405b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/snowflake-llama-3.1-405b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.2-1b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/llama3.2-3b": {
+        "max_tokens": 128000,
+        "max_input_tokens": 128000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/mistral-7b": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
+    },
+    "snowflake/gemma-7b": {
+        "max_tokens": 8000,
+        "max_input_tokens": 8000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "chat"
     }
 }
diff --git a/tests/llm_translation/test_snowflake.py b/tests/llm_translation/test_snowflake.py
new file mode 100644
index 00000000000..139fa16d7ae
--- /dev/null
+++ b/tests/llm_translation/test_snowflake.py
@@ -0,0 +1,76 @@
+import os
+import sys
+import traceback
+from dotenv import load_dotenv
+
+load_dotenv()
+import pytest
+
+from litellm import completion, acompletion
+
+@pytest.mark.parametrize("sync_mode", [True, False])
+@pytest.mark.asyncio
+async def test_chat_completion_snowflake(sync_mode):
+    try:
+        messages = [
+            {
+                "role": "user",
+                "content": "Write me a poem about the blue sky",
+            },
+        ]
+
+        if sync_mode:
+            response = completion(
+                model="snowflake/mistral-7b",
+                messages=messages,
+                api_base = "https://exampleopenaiendpoint-production.up.railway.app/v1/chat/completions"
+            )
+            print(response)
+            assert response is not None
+        else:
+            response = await acompletion(
+                model="snowflake/mistral-7b",
+                messages=messages,
+                api_base = "https://exampleopenaiendpoint-production.up.railway.app/v1/chat/completions"
+            )
+            print(response)
+            assert response is not None
+    except Exception as e:
+        pytest.fail(f"Error occurred: {e}")
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("sync_mode", [True, False])
+async def test_chat_completion_snowflake_stream(sync_mode):
+    try:
+        set_verbose = True
+        messages = [
+            {
+                "role": "user",
+                "content": "Write me a poem about the blue sky",
+            },
+        ]
+        
+        if sync_mode is False:
+            response = await acompletion(
+                model="snowflake/mistral-7b",
+                messages=messages,
+                max_tokens=100,
+                stream=True,
+                api_base = "https://exampleopenaiendpoint-production.up.railway.app/v1/chat/completions"
+            )
+            
+            async for chunk in response:
+                print(chunk)
+        else:
+            response = completion(
+                model="snowflake/mistral-7b",
+                messages=messages,
+                max_tokens=100,
+                stream=True,
+                api_base = "https://exampleopenaiendpoint-production.up.railway.app/v1/chat/completions"
+            )
+
+            for chunk in response:
+                print(chunk)
+    except Exception as e:
+        pytest.fail(f"Error occurred: {e}")


Reviews:
user: login: ishaan-jaff
body: reviewed 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T21:15:58Z
user: login: ishaan-jaff
body: minor changes 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T15:12:38Z
user: login: SunnyWan59
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T20:17:23Z
user: login: ishaan-jaff
body: reviewed 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T21:52:36Z
user: login: ishaan-jaff
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T23:51:50Z

Review comments:
diff_hunk: @@ -596,6 +600,29 @@ def add_known_models():
 
 maritalk_models = ["maritalk"]
 
+snowflake_models = [
path: litellm/__init__.py
user: login: ishaan-jaff
body: don't hardcode this list, you only need to add it to the model_prices json file 
created_at: 2025-03-10T21:07:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,86 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Union, Any
+
+import litellm
+from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
+from litellm.types.llms.openai import AllMessageValues
+from litellm.types.utils import Choices, Message, ModelResponse, TextCompletionResponse
+from litellm.litellm_core_utils.prompt_templates.common_utils import (
+    convert_content_list_to_str,
+)
+from ...openai_like.chat.transformation import OpenAILikeChatConfig
+
+
+class SnowflakeConfig(OpenAILikeChatConfig):
path: litellm/llms/snowflake/completion/transformation.py
user: login: ishaan-jaff
body: Is Snowflake an OpenAI compatible endpoint @SunnyWan59  ? 
created_at: 2025-03-10T21:08:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+# Snowflake
+LiteLLM supports all Snowflake models.
+- `snowflake/deepseek-r1`
path: docs/my-website/docs/providers/snowflake.md
user: login: ishaan-jaff
body: I don't think the docs needs a list like this, snowflake can change / update this list 
created_at: 2025-03-10T21:09:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,86 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Union, Any
+
+import litellm
+from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
+from litellm.types.llms.openai import AllMessageValues
+from litellm.types.utils import Choices, Message, ModelResponse, TextCompletionResponse
+from litellm.litellm_core_utils.prompt_templates.common_utils import (
+    convert_content_list_to_str,
+)
+from ...openai_like.chat.transformation import OpenAILikeChatConfig
+
+
+class SnowflakeConfig(OpenAILikeChatConfig):
path: litellm/llms/snowflake/completion/transformation.py
user: login: ishaan-jaff
body: This should inherit from `OpenAIGPTConfig`  
created_at: 2025-03-10T21:13:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,86 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Union, Any
+
+import litellm
+from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
+from litellm.types.llms.openai import AllMessageValues
+from litellm.types.utils import Choices, Message, ModelResponse, TextCompletionResponse
+from litellm.litellm_core_utils.prompt_templates.common_utils import (
+    convert_content_list_to_str,
+)
+from ...openai_like.chat.transformation import OpenAILikeChatConfig
+
+
+class SnowflakeConfig(OpenAILikeChatConfig):
path: litellm/llms/snowflake/completion/transformation.py
user: login: ishaan-jaff
body: see `DeepSeekChatConfig` as an example 
created_at: 2025-03-10T21:13:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+from litellm.llms.base import BaseLLM
+from typing import Any, List, Optional
+from typing import List, Dict, Callable, Optional, Any, cast, Union
+
+import litellm
+from litellm.utils import ModelResponse
+from litellm.types.llms.openai import AllMessageValues
+from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
+from litellm.llms.openai_like.chat.handler import OpenAILikeChatHandler
+from ..common_utils import SnowflakeBase
+
+class SnowflakeChatCompletion(OpenAILikeChatHandler,SnowflakeBase):
path: litellm/llms/snowflake/completion/handler.py
user: login: ishaan-jaff
body: you don't need a handler for this if it's OpenAI compatible. See llms/deepseek/chat, just follow that same format 
created_at: 2025-03-10T21:14:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,217 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
path: litellm/llms/snowflake/completion/transformation.py
user: login: ishaan-jaff
body: This should be in snowflake/chat/transformation.py 

this is a chat endpoint correct ? I can see it supports messages 
created_at: 2025-03-11T14:26:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9461,5 +9461,173 @@
         "output_cost_per_token": 0.000000018,
         "litellm_provider": "jina_ai",
         "mode": "rerank"
+    },
+    "snowflake/deepseek-r1": {
+        "max_tokens": 32768,
+        "max_input_tokens": 32768,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "completion"
+    },
+    "snowflake/snowflake-arctic": {
+        "max_tokens": 4096,
+        "max_input_tokens": 4096,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "completion" 
+    },
+    "snowflake/claude-3-5-sonnet": {
+        "max_tokens": 18000,
+        "max_input_tokens": 18000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "completion"
+    },
+    "snowflake/mistral-large": {
+        "max_tokens": 32000,
+        "max_input_tokens": 32000,
+        "max_output_tokens": 8192,
+        "litellm_provider": "snowflake",
+        "mode": "completion"
path: model_prices_and_context_window.json
user: login: ishaan-jaff
body: mode should be "chat" these are all chat models 
created_at: 2025-03-11T15:11:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+# Snowflake
+
+Currently, Snowflake's REST API does not have an endpoint for `snowflake-arctic-embed` embedding models. If you want to use these embedding models with Litellm, you can call them through our Hugging Face provider. 
+
+Find the Arctic Embed models [here](https://huggingface.co/collections/Snowflake/arctic-embed-661fd57d50fab5fc314e4c18) on Hugging Face.
+## Supported OpenAI Parameters
+```
path: docs/my-website/docs/providers/snowflake.md
user: login: ishaan-jaff
body: Can you add an overview section at the top like this 
<img width="556" alt="Screenshot 2025-03-11 at 8 12 14 AM" src="https://github.com/user-attachments/assets/72097e48-a20e-4cee-9ff6-020c47b025d1" />

created_at: 2025-03-11T15:12:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,217 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
path: litellm/llms/snowflake/completion/transformation.py
user: login: SunnyWan59
body: I think they are completion endpoints base on the snowflake docs. I'm not super sure though, and if it makes more sense to put them in chat, I can do that.  My understanding is that you can use the completion endpoint for chat as well.


Source:
https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api#model-availability
created_at: 2025-03-11T20:17:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,217 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
+    """
+    source: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex
+    """
+
+    @classmethod
+    def get_config(cls):
+        return super().get_config()
+    
+    def get_supported_openai_params(self, model: str) -> List:
+        return [
+            "temperature",
+            "max_tokens",
+            "top_p",
+            "response_format"
+        ]
+            
+    def map_openai_params(
+        self,
+        non_default_params: dict,
+        optional_params: dict,
+        model: str,
+        drop_params: bool,
+    ) -> dict:
+        """
+        If any supported_openai_params are in non_default_params, add them to optional_params, so they are used in API call
+
+        Args:
+            non_default_params (dict): Non-default parameters to filter.
+            optional_params (dict): Optional parameters to update.
+            model (str): Model name for parameter support check.
+
+        Returns:
+            dict: Updated optional_params with supported non-default parameters.
+        """
+        supported_openai_params = self.get_supported_openai_params(model)
+        for param, value in non_default_params.items():
+            if param in supported_openai_params:
+                optional_params[param] = value
+        return optional_params
+    
+    def _convert_tool_response_to_message(
+        message: ChatCompletionAssistantMessage, json_mode: bool
+    ) -> ChatCompletionAssistantMessage:
+        """
+        if json_mode is true, convert the returned tool call response to a content with json str
+
+        e.g. input:
+
+        {"role": "assistant", "tool_calls": [{"id": "call_5ms4", "type": "function", "function": {"name": "json_tool_call", "arguments": "{\"key\": \"question\", \"value\": \"What is the capital of France?\"}"}}]}
+
+        output:
+
+        {"role": "assistant", "content": "{\"key\": \"question\", \"value\": \"What is the capital of France?\"}"}
+        """
+        if not json_mode:
+            return message
+
+        _tool_calls = message.get("tool_calls")
+
+        if _tool_calls is None or len(_tool_calls) != 1:
+            return message
+
+        message["content"] = _tool_calls[0]["function"].get("arguments") or ""
+        message["tool_calls"] = None
+
+        return message
+
+
+    @staticmethod
+    def transform_response(
+        model: str,
+        raw_response: httpx.Response,
+        model_response: ModelResponse,
+        logging_obj: LiteLLMLoggingObj,
+        request_data: dict,
+        messages: List[AllMessageValues],
+        optional_params: dict,
+        litellm_params: dict,
+        encoding: Any,
+        api_key: Optional[str] = None,
+        json_mode: Optional[bool] = None,
+    ) -> ModelResponse:
+        response_json = raw_response.json()
+        logging_obj.post_call(
+            input=messages,
+            api_key="",
+            original_response=response_json,
+            additional_args={"complete_input_dict": request_data},
+        )
+
+        if json_mode:
+            for choice in response_json["choices"]:
+                message = SnowflakeConfig._convert_tool_response_to_message(
path: litellm/llms/snowflake/chat/transformation.py
user: login: ishaan-jaff
body: do you need this? they don't support tool calling: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex 
created_at: 2025-03-13T21:50:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,217 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
+    """
+    source: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex
+    """
+
+    @classmethod
+    def get_config(cls):
+        return super().get_config()
+    
+    def get_supported_openai_params(self, model: str) -> List:
+        return [
+            "temperature",
+            "max_tokens",
+            "top_p",
+            "response_format"
+        ]
+            
+    def map_openai_params(
+        self,
+        non_default_params: dict,
+        optional_params: dict,
+        model: str,
+        drop_params: bool,
+    ) -> dict:
+        """
+        If any supported_openai_params are in non_default_params, add them to optional_params, so they are used in API call
+
+        Args:
+            non_default_params (dict): Non-default parameters to filter.
+            optional_params (dict): Optional parameters to update.
+            model (str): Model name for parameter support check.
+
+        Returns:
+            dict: Updated optional_params with supported non-default parameters.
+        """
+        supported_openai_params = self.get_supported_openai_params(model)
+        for param, value in non_default_params.items():
+            if param in supported_openai_params:
+                optional_params[param] = value
+        return optional_params
+    
+    def _convert_tool_response_to_message(
path: litellm/llms/snowflake/chat/transformation.py
user: login: ishaan-jaff
body: not needed imo, snowflake does not support tool calling: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex 
created_at: 2025-03-13T21:50:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,217 @@
+'''
+Support for Snowflake REST API 
+'''
+import httpx
+from typing import List, Optional, Tuple, Any, TYPE_CHECKING
+
+from litellm.types.llms.openai import AllMessageValues
+from litellm.utils import get_secret
+from litellm.types.utils import ModelResponse
+from litellm.types.llms.openai import ChatCompletionAssistantMessage
+from litellm.llms.databricks.streaming_utils import ModelResponseIterator
+
+from ...openai_like.chat.transformation import OpenAIGPTConfig
+
+if TYPE_CHECKING:
+    from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+    LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+    LiteLLMLoggingObj = Any
+
+class SnowflakeConfig(OpenAIGPTConfig):
+    """
+    source: https://docs.snowflake.com/en/sql-reference/functions/complete-snowflake-cortex
+    """
+
+    @classmethod
+    def get_config(cls):
+        return super().get_config()
+    
+    def get_supported_openai_params(self, model: str) -> List:
+        return [
+            "temperature",
+            "max_tokens",
+            "top_p",
+            "response_format"
+        ]
+            
+    def map_openai_params(
+        self,
+        non_default_params: dict,
+        optional_params: dict,
+        model: str,
+        drop_params: bool,
+    ) -> dict:
+        """
+        If any supported_openai_params are in non_default_params, add them to optional_params, so they are used in API call
+
+        Args:
+            non_default_params (dict): Non-default parameters to filter.
+            optional_params (dict): Optional parameters to update.
+            model (str): Model name for parameter support check.
+
+        Returns:
+            dict: Updated optional_params with supported non-default parameters.
+        """
+        supported_openai_params = self.get_supported_openai_params(model)
+        for param, value in non_default_params.items():
+            if param in supported_openai_params:
+                optional_params[param] = value
+        return optional_params
+    
+    def _convert_tool_response_to_message(
+        message: ChatCompletionAssistantMessage, json_mode: bool
+    ) -> ChatCompletionAssistantMessage:
+        """
+        if json_mode is true, convert the returned tool call response to a content with json str
+
+        e.g. input:
+
+        {"role": "assistant", "tool_calls": [{"id": "call_5ms4", "type": "function", "function": {"name": "json_tool_call", "arguments": "{\"key\": \"question\", \"value\": \"What is the capital of France?\"}"}}]}
+
+        output:
+
+        {"role": "assistant", "content": "{\"key\": \"question\", \"value\": \"What is the capital of France?\"}"}
+        """
+        if not json_mode:
+            return message
+
+        _tool_calls = message.get("tool_calls")
+
+        if _tool_calls is None or len(_tool_calls) != 1:
+            return message
+
+        message["content"] = _tool_calls[0]["function"].get("arguments") or ""
+        message["tool_calls"] = None
+
+        return message
+
+
+    @staticmethod
+    def transform_response(
+        model: str,
+        raw_response: httpx.Response,
+        model_response: ModelResponse,
+        logging_obj: LiteLLMLoggingObj,
+        request_data: dict,
+        messages: List[AllMessageValues],
+        optional_params: dict,
+        litellm_params: dict,
+        encoding: Any,
+        api_key: Optional[str] = None,
+        json_mode: Optional[bool] = None,
+    ) -> ModelResponse:
+        response_json = raw_response.json()
+        logging_obj.post_call(
+            input=messages,
+            api_key="",
+            original_response=response_json,
+            additional_args={"complete_input_dict": request_data},
+        )
+
+        if json_mode:
+            for choice in response_json["choices"]:
+                message = SnowflakeConfig._convert_tool_response_to_message(
+                    choice.get("message"), json_mode
+                )
+                choice["message"] = message
+
+        returned_response = ModelResponse(**response_json)
+
+        returned_response.model = (
+            "snowflake/" + (returned_response.model or "")
+        )
+
+        if model is not None:
+            returned_response._hidden_params["model"] = model
+        return returned_response
+
+
+    def validate_environment(
+        self,
+        headers: dict,
+        model: str,
+        api_base: str = None,
+        api_key: Optional[str] = None,
+        messages: dict = None,
+        optional_params: dict = None,
+    ) -> dict:
+        """
+        Return headers to use for Snowflake completion request
+
+        Snowflake REST API Ref: https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api#api-reference
+        Expected headers:
+        {
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+            "Authorization": "Bearer " + <JWT>,
+            "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+        } 
+        """
+
+        if api_key is None:
+                raise ValueError(
+                    "Missing Snowflake JWT key"
+                )
+
+        headers.update(
+            {
+                "Content-Type": "application/json",
+                "Accept": "application/json",
+                "Authorization": "Bearer " + api_key,
+                "X-Snowflake-Authorization-Token-Type": "KEYPAIR_JWT"
+            }
+        )
+        return headers
+    
+    def _get_openai_compatible_provider_info(
+        self, api_base: Optional[str], api_key: Optional[str]
+    ) -> Tuple[Optional[str], Optional[str]]:
+        api_base = (
+            api_base
+            or f"""https://{get_secret("SNOWFLAKE_ACCOUNT_ID")}.snowflakecomputing.com/api/v2/cortex/inference:complete"""
+            or get_secret("SNOWFLAKE_API_BASE")
+        )  # type: ignore
+        dynamic_api_key = api_key or get_secret("SNOWFLAKE_JWT")
+        return api_base, dynamic_api_key
+    
+    def get_complete_url(
+        self,
+        api_base: Optional[str],
+        model: str,
+        optional_params: dict,
+        stream: Optional[bool] = None,
+    ) -> str:
+        """
+        If api_base is not provided, use the default DeepSeek /chat/completions endpoint.
+        """
+        if not api_base:
+            api_base = f"""https://{get_secret("SNOWFLAKE_ACCOUNT_ID")}.snowflakecomputing.com/api/v2/cortex/inference:complete"""
+
+        return api_base
+    
+    def transform_request(
+        self, 
+        model: str, 
+        messages: dict , 
+        optional_params: dict, 
+        litellm_params: dict, 
+        headers: dict
+    ) -> dict:
+        stream: bool = optional_params.pop("stream", None) or False
+        extra_body = optional_params.pop("extra_body", {})
+        return {
+            "model": model,
+            "messages": messages,
+            "stream": stream,
+            **optional_params,
+            **extra_body,
+        }
+    
+    def get_model_response_iterator(
path: litellm/llms/snowflake/chat/transformation.py
user: login: ishaan-jaff
body: you don't need this, `OpenAIGPTConfig` already has this defined 
created_at: 2025-03-13T21:51:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-03T06:43:35Z
body: [vc]: #fSC1CIgsiSsL/eV2DW3ikXAbz+FRdq05KDiesr23TAE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJsaXRlbGxtIiwicm9vdERpcmVjdG9yeSI6ImRvY3MvbXktd2Vic2l0ZSIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9jbGVya2llYWkvbGl0ZWxsbS85dnExS3JjSnRWNlZFeGc2UExnUFZpWlJxYkZMIiwicHJldmlld1VybCI6ImxpdGVsbG0tZ2l0LWZvcmstc3Vubnl3YW41OS1tYWluLWNsZXJraWVhaS52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImxpdGVsbG0tZ2l0LWZvcmstc3Vubnl3YW41OS1tYWluLWNsZXJraWVhaS52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **litellm** | ✅ Ready ([Inspect](https://vercel.com/clerkieai/litellm/9vq1KrcJtV6VExg6PLgPViZRqbFL)) | [Visit Preview](https://litellm-git-fork-sunnywan59-main-clerkieai.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/litellm-git-fork-sunnywan59-main-clerkieai.vercel.app?via=pr-comment-feedback-link) | Mar 13, 2025 11:43pm |


author_association: NONE
user: login: SunnyWan59
created_at: 2025-03-03T23:32:17Z
body: The snowflake cost is based on sowflake credits (which doesn't have a set price) rather than price per million tokens, so I wasn't sure how to add cost per token.
author_association: CONTRIBUTOR
user: login: sumitdas66
created_at: 2025-03-04T03:08:44Z
body: @SunnyWan59 
It would be great if you can add a doc on how the add snowflake models to litellm. 
You can find references here: https://github.com/BerriAI/litellm/tree/main/docs/my-website/docs/providers
author_association: NONE
user: login: SunnyWan59
created_at: 2025-03-04T06:22:32Z
body: Alright! I'll get started on that.


author_association: CONTRIBUTOR
user: login: SunnyWan59
created_at: 2025-03-04T22:13:22Z
body: Just added documentation
author_association: CONTRIBUTOR
user: login: SunnyWan59
created_at: 2025-03-11T06:05:32Z
body: Just address the comments! @ishaan-jaff 
author_association: CONTRIBUTOR
user: login: SunnyWan59
created_at: 2025-03-11T20:33:05Z
body: Just added mock tests and changed the docs. @ishaan-jaff 
author_association: CONTRIBUTOR