diff --git a/litellm/constants.py b/litellm/constants.py index 32b47bc6b46..a2da0567d4c 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1093,6 +1093,7 @@ openai_compatible_endpoints: Final[list[str]] = [ "https://api.scx.ai/v1", "https://api.prisminference.com/v1", "https://api.reka.ai/v1", + "https://inference.gitgot.ai/v1", "https://inference.runanywhere.ai/v1", "https://gigachat.devices.sberbank.ru/api/v1", "https://api.openinfer.ai/v1", @@ -1180,6 +1181,7 @@ openai_compatible_providers: Final[list[str]] = [ "hostinger_router", "sail", "reka", + "gitgot", "streamlake", "wally", ] diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index c89ede6e77e..a4422a05d1a 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -252,6 +252,12 @@ "api_key_env": "REKA_API_KEY", "api_base_env": "REKA_API_BASE" }, + "gitgot": { + "base_url": "https://inference.gitgot.ai/v1", + "api_key_env": "GITGOT_API_KEY", + "api_base_env": "GITGOT_API_BASE", + "supported_endpoints": ["/v1/chat/completions"] + }, "openinfer": { "base_url": "https://api.openinfer.ai/v1", "api_key_env": "OPENINFER_API_KEY", diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index e771cf12124..7a7a908f121 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -77934,6 +77934,104 @@ "supports_audio_output": true, "supports_function_calling": true }, + "gitgot/deepseek-ai/DeepSeek-V4-Flash": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000000882, + "output_cost_per_token": 0.0000001764, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/deepseek-ai/DeepSeek-V4-Flash-0731": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000000588, + "output_cost_per_token": 0.0000001764, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/deepseek-ai/DeepSeek-V4-Pro": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.000001274, + "output_cost_per_token": 0.000002548, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": true + }, + "gitgot/meta-llama/Llama-3.3-70B-Instruct": { + "max_input_tokens": 131072, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000001323, + "output_cost_per_token": 0.000000392, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/moonshotai/Kimi-K2.6": { + "max_input_tokens": 262144, + "max_output_tokens": 64000, + "input_cost_per_token": 0.000000735, + "output_cost_per_token": 0.00000343, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/moonshotai/Kimi-K2.7-Code": { + "max_input_tokens": 262144, + "max_output_tokens": 64000, + "input_cost_per_token": 0.0000006664, + "output_cost_per_token": 0.000003332, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/openai/gpt-oss-120b": { + "max_input_tokens": 131072, + "max_output_tokens": 32768, + "input_cost_per_token": 0.00000003626, + "output_cost_per_token": 0.0000001666, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, "together_ai/nvidia/NVIDIA-Nemotron-3.5-Super-VL": { "cache_read_input_token_cost": 2e-08, "input_cost_per_token": 1.5e-07, diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index 672a2739964..d3ae43034b2 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -3708,6 +3708,32 @@ "generateContent": true } }, + "gitgot": { + "display_name": "GitGot (`gitgot`)", + "url": "https://gitgot.ai/inference", + "endpoints": { + "chat_completions": true, + "responses": true, + "text_completion": true, + "embeddings": false, + "image_generations": false, + "image_edits": false, + "audio_transcriptions": false, + "audio_speech": false, + "rerank": false, + "ocr": false, + "search": false, + "moderations": false, + "realtime": false, + "video_generations": false, + "batches": false, + "messages": true, + "interactions": true, + "systemone": false, + "a2a": true, + "generateContent": true + } + }, "scx-ai": { "display_name": "SCX.ai (`scx-ai`)", "url": "https://docs.litellm.ai/docs/providers/scx_ai", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 9d4ede7b90d..e563e87e3ca 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3659,6 +3659,34 @@ ], "default_model_placeholder": "sap/gpt-4" }, + { + "provider": "GITGOT", + "provider_display_name": "GitGot", + "litellm_provider": "gitgot", + "credential_fields": [ + { + "key": "api_base", + "label": "API Base", + "placeholder": "https://inference.gitgot.ai/v1", + "tooltip": null, + "required": false, + "field_type": "text", + "options": null, + "default_value": null + }, + { + "key": "api_key", + "label": "API Key", + "placeholder": null, + "tooltip": null, + "required": true, + "field_type": "password", + "options": null, + "default_value": null + } + ], + "default_model_placeholder": "gitgot/openai/gpt-oss-120b" + }, { "provider": "SCX_AI", "provider_display_name": "SCX.ai", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index ee80b63f93b..e47ee774a47 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4765,6 +4765,7 @@ class LlmProviders(str, Enum): CORTECS = "cortecs" SCX_AI = "scx-ai" PRISM = "prism" + GITGOT = "gitgot" OPENINFER = "openinfer" INFERENCEAPIS = "inferenceapis" HOSTINGER_ROUTER = "hostinger_router" diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index e771cf12124..7a7a908f121 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -77934,6 +77934,104 @@ "supports_audio_output": true, "supports_function_calling": true }, + "gitgot/deepseek-ai/DeepSeek-V4-Flash": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000000882, + "output_cost_per_token": 0.0000001764, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/deepseek-ai/DeepSeek-V4-Flash-0731": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000000588, + "output_cost_per_token": 0.0000001764, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/deepseek-ai/DeepSeek-V4-Pro": { + "max_input_tokens": 163840, + "max_output_tokens": 32768, + "input_cost_per_token": 0.000001274, + "output_cost_per_token": 0.000002548, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": true + }, + "gitgot/meta-llama/Llama-3.3-70B-Instruct": { + "max_input_tokens": 131072, + "max_output_tokens": 32768, + "input_cost_per_token": 0.0000001323, + "output_cost_per_token": 0.000000392, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/moonshotai/Kimi-K2.6": { + "max_input_tokens": 262144, + "max_output_tokens": 64000, + "input_cost_per_token": 0.000000735, + "output_cost_per_token": 0.00000343, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/moonshotai/Kimi-K2.7-Code": { + "max_input_tokens": 262144, + "max_output_tokens": 64000, + "input_cost_per_token": 0.0000006664, + "output_cost_per_token": 0.000003332, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, + "gitgot/openai/gpt-oss-120b": { + "max_input_tokens": 131072, + "max_output_tokens": 32768, + "input_cost_per_token": 0.00000003626, + "output_cost_per_token": 0.0000001666, + "litellm_provider": "gitgot", + "mode": "chat", + "source": "https://inference.gitgot.ai/v1/pricing", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_reasoning": false + }, "together_ai/nvidia/NVIDIA-Nemotron-3.5-Super-VL": { "cache_read_input_token_cost": 2e-08, "input_cost_per_token": 1.5e-07, diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index 672a2739964..d3ae43034b2 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -3708,6 +3708,32 @@ "generateContent": true } }, + "gitgot": { + "display_name": "GitGot (`gitgot`)", + "url": "https://gitgot.ai/inference", + "endpoints": { + "chat_completions": true, + "responses": true, + "text_completion": true, + "embeddings": false, + "image_generations": false, + "image_edits": false, + "audio_transcriptions": false, + "audio_speech": false, + "rerank": false, + "ocr": false, + "search": false, + "moderations": false, + "realtime": false, + "video_generations": false, + "batches": false, + "messages": true, + "interactions": true, + "systemone": false, + "a2a": true, + "generateContent": true + } + }, "scx-ai": { "display_name": "SCX.ai (`scx-ai`)", "url": "https://docs.litellm.ai/docs/providers/scx_ai", diff --git a/tests/unit/llms/openai_like/test_gitgot_provider.py b/tests/unit/llms/openai_like/test_gitgot_provider.py new file mode 100644 index 00000000000..32ce27436b3 --- /dev/null +++ b/tests/unit/llms/openai_like/test_gitgot_provider.py @@ -0,0 +1,141 @@ +import json +from pathlib import Path +from typing import Final + +import pytest +import respx + +import litellm +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.types.utils import Choices, Message, ModelResponse, Usage + +_MODEL: Final = "gitgot/openai/gpt-oss-120b" +_UPSTREAM_MODEL: Final = "openai/gpt-oss-120b" +_DEFAULT_API_BASE: Final = "https://inference.gitgot.ai/v1" +_CHAT_COMPLETION: Final = { + "id": "chatcmpl_gitgot", + "object": "chat.completion", + "created": 1_790_000_000, + "model": _UPSTREAM_MODEL, + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "Hello from GitGot"}, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7}, +} + + +def test_gitgot_is_a_registered_provider(): + assert litellm.LlmProviders.GITGOT.value == "gitgot" + assert "gitgot" in litellm.provider_list + assert "gitgot" in litellm.constants.openai_compatible_providers + + +def test_gitgot_provider_resolution_keeps_the_vendor_prefix(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("GITGOT_API_KEY", "gitgot-env-key") + monkeypatch.delenv("GITGOT_API_BASE", raising=False) + + model, provider, api_key, api_base = get_llm_provider( + model=_MODEL, + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == _UPSTREAM_MODEL + assert provider == "gitgot" + assert api_key == "gitgot-env-key" + assert api_base == _DEFAULT_API_BASE + + +def test_gitgot_provider_keeps_explicit_credentials(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("GITGOT_API_KEY", "gitgot-env-key") + monkeypatch.setenv("GITGOT_API_BASE", "https://gitgot.env.example/v1") + + _, provider, api_key, api_base = get_llm_provider( + model=_MODEL, + custom_llm_provider=None, + api_base="https://gitgot.internal.example/v1", + api_key="gitgot-explicit-key", + ) + + assert provider == "gitgot" + assert api_key == "gitgot-explicit-key" + assert api_base == "https://gitgot.internal.example/v1" + + +def test_gitgot_api_base_env_overrides_default(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("GITGOT_API_KEY", "gitgot-env-key") + monkeypatch.setenv("GITGOT_API_BASE", "https://gitgot.env.example/v1") + + _, provider, _, api_base = get_llm_provider( + model=_MODEL, + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert provider == "gitgot" + assert api_base == "https://gitgot.env.example/v1" + + +def test_gitgot_is_available_in_add_model_form(): + fields_path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + providers = json.loads(fields_path.read_text()) + gitgot = next(provider for provider in providers if provider["litellm_provider"] == "gitgot") + + assert gitgot["provider"] == "GITGOT" + assert gitgot["default_model_placeholder"] == _MODEL + assert {field["key"]: field["required"] for field in gitgot["credential_fields"]} == { + "api_base": False, + "api_key": True, + } + + +def test_gitgot_endpoint_support_files_agree(): + backup_path = Path(litellm.__file__).parent / "provider_endpoints_support_backup.json" + root_path = Path(litellm.__file__).parent.parent / "provider_endpoints_support.json" + backup = json.loads(backup_path.read_text())["providers"]["gitgot"] + root = json.loads(root_path.read_text())["providers"]["gitgot"] + + assert backup == root + assert root["endpoints"]["chat_completions"] is True + + +def test_gitgot_chat_completion_request_sends_the_vendor_prefixed_model(): + with respx.mock() as upstream: + route: Final = upstream.post(f"{_DEFAULT_API_BASE}/chat/completions").respond(200, json=_CHAT_COMPLETION) + response: Final = litellm.completion( + model=_MODEL, + messages=[{"role": "user", "content": "Say hello"}], + api_key="gitgot-test-key", + ) + + request: Final = route.calls.last.request + body: Final = json.loads(request.content) + assert route.call_count == 1 + assert request.headers["authorization"] == "Bearer gitgot-test-key" + assert body["model"] == _UPSTREAM_MODEL + assert body["messages"] == [{"role": "user", "content": "Say hello"}] + assert response.choices[0].message.content == "Hello from GitGot" + + +def test_gitgot_cost_is_derived_from_its_cost_map_entry(local_model_cost_map): + entry: Final = litellm.model_cost[_MODEL] + response: Final = ModelResponse( + id="chatcmpl-test", + choices=[Choices(finish_reason="stop", index=0, message=Message(content="hello", role="assistant"))], + created=1_790_000_000, + model=_MODEL, + object="chat.completion", + usage=Usage(prompt_tokens=1_000_000, completion_tokens=500_000, total_tokens=1_500_000), + ) + + cost: Final = litellm.completion_cost(completion_response=response) + + assert entry["litellm_provider"] == "gitgot" + assert cost == pytest.approx(1_000_000 * entry["input_cost_per_token"] + 500_000 * entry["output_cost_per_token"]) + assert 0 < cost < 10 diff --git a/ui/litellm-dashboard/public/assets/logos/gitgot.svg b/ui/litellm-dashboard/public/assets/logos/gitgot.svg new file mode 100644 index 00000000000..c03cb6f12f9 --- /dev/null +++ b/ui/litellm-dashboard/public/assets/logos/gitgot.svg @@ -0,0 +1 @@ + diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx index 1680258e10b..a916b5390bc 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx @@ -59,6 +59,7 @@ import runwayLogo from "../../public/assets/logos/runway.png"; import sambanovaLogo from "../../public/assets/logos/sambanova.svg"; import sapLogo from "../../public/assets/logos/sap.png"; import scxAiLogo from "../../public/assets/logos/scx_ai.svg"; +import gitgotLogo from "../../public/assets/logos/gitgot.svg"; import snowflakeLogo from "../../public/assets/logos/snowflake.svg"; import sonioxLogo from "../../public/assets/logos/soniox.svg"; import strandsLogo from "../../public/assets/logos/strands.svg"; @@ -181,6 +182,7 @@ export enum Providers { Sambanova = "Sambanova", SAP = "SAP Generative AI Hub", SCX_AI = "SCX.ai", + GitGot = "GitGot", Snowflake = "Snowflake", Soniox = "Soniox", StrandsDecider = "Strands Decider", @@ -316,6 +318,7 @@ export const provider_map: Record = { Sambanova: "sambanova", SAP: "sap", SCX_AI: "scx-ai", + GitGot: "gitgot", Snowflake: "snowflake", Soniox: "soniox", StrandsDecider: "strands_decider", @@ -433,6 +436,7 @@ export const providerLogoMap: Partial> = { [Providers.Sambanova]: sambanovaLogo.src, [Providers.SAP]: sapLogo.src, [Providers.SCX_AI]: scxAiLogo.src, + [Providers.GitGot]: gitgotLogo.src, [Providers.Snowflake]: snowflakeLogo.src, [Providers.Soniox]: sonioxLogo.src, [Providers.StrandsDecider]: strandsLogo.src, @@ -513,6 +517,7 @@ const providerPlaceholderMap: Partial> = { [Providers.SCX_AI]: "scx-ai/GLM-5.2", [Providers.Bespoke]: "bespoke/nimble", [Providers.Laya]: "laya/english", + [Providers.GitGot]: "gitgot/openai/gpt-oss-120b", [Providers.Snowflake]: "snowflake/mistral-7b", [Providers.StrandsDecider]: "strands_decider/strands-decider-2B-hobson-v19", [Providers.Tencent]: "tencent/deepseek-v4-pro",