mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
feat(ui): add TypeSafe and Strands Decider to the Add Model provider list (#45335)
* feat(ui): add TypeSafe and Strands Decider to the Add Model provider list * test(public_endpoints): type the provider fields helper against the route's model --------- Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
This commit is contained in:
parent
0e26edfdb9
commit
3a3b8cad80
5 changed files with 164 additions and 2 deletions
|
|
@ -3189,6 +3189,34 @@
|
|||
],
|
||||
"default_model_placeholder": "soniox/stt-async-v5"
|
||||
},
|
||||
{
|
||||
"provider": "StrandsDecider",
|
||||
"provider_display_name": "Strands Decider",
|
||||
"litellm_provider": "strands_decider",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "http://localhost:8000",
|
||||
"tooltip": "URL of your self-hosted Strands Decider server",
|
||||
"required": true,
|
||||
"field_type": "text",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"label": "API Key",
|
||||
"placeholder": null,
|
||||
"tooltip": null,
|
||||
"required": false,
|
||||
"field_type": "password",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
}
|
||||
],
|
||||
"default_model_placeholder": "strands_decider/strands-decider-2B-hobson-v19"
|
||||
},
|
||||
{
|
||||
"provider": "Tencent",
|
||||
"provider_display_name": "Tencent",
|
||||
|
|
@ -3319,6 +3347,34 @@
|
|||
],
|
||||
"default_model_placeholder": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"provider": "TypeSafe",
|
||||
"provider_display_name": "TypeSafe",
|
||||
"litellm_provider": "typesafe",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://api.typesafe.ai",
|
||||
"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": "typesafe/jev-latest"
|
||||
},
|
||||
{
|
||||
"provider": "V0",
|
||||
"provider_display_name": "V0",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import pytest
|
|||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.proxy.public_endpoints import router
|
||||
|
|
@ -16,6 +17,7 @@ from litellm.router_strategy.complexity_router.fuse_presets import get_fuse_pres
|
|||
from litellm.types.proxy.management_endpoints.model_management_endpoints import (
|
||||
ModelGroupInfoProxy,
|
||||
)
|
||||
from litellm.types.proxy.public_endpoints.public_endpoints import ProviderCreateInfo
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
|
||||
|
|
@ -402,6 +404,53 @@ def test_tencent_provider_fields():
|
|||
assert fields_by_key["api_base"]["required"] is False
|
||||
|
||||
|
||||
def _decisions_provider_entry(provider: str) -> ProviderCreateInfo:
|
||||
app_instance: Final = FastAPI()
|
||||
app_instance.include_router(router)
|
||||
test_client: Final = TestClient(app_instance)
|
||||
|
||||
response: Final = test_client.get("/public/providers/fields")
|
||||
assert response.status_code == 200
|
||||
providers: Final = TypeAdapter(list[ProviderCreateInfo]).validate_python(response.json())
|
||||
entry: Final = next((p for p in providers if p.provider == provider), None)
|
||||
assert entry is not None, f"{provider} provider entry not found"
|
||||
return entry
|
||||
|
||||
|
||||
def test_typesafe_provider_fields():
|
||||
typesafe: Final = _decisions_provider_entry("TypeSafe")
|
||||
|
||||
assert typesafe.provider_display_name == "TypeSafe"
|
||||
assert typesafe.litellm_provider == LlmProviders.TYPESAFE.value
|
||||
assert typesafe.default_model_placeholder is not None
|
||||
assert typesafe.default_model_placeholder.startswith("typesafe/")
|
||||
|
||||
fields_by_key: Final = {f.key: f for f in typesafe.credential_fields}
|
||||
|
||||
assert fields_by_key["api_key"].required is True
|
||||
assert fields_by_key["api_key"].field_type == "password"
|
||||
|
||||
assert fields_by_key["api_base"].required is False
|
||||
assert fields_by_key["api_base"].field_type == "text"
|
||||
|
||||
|
||||
def test_strands_decider_provider_fields():
|
||||
strands: Final = _decisions_provider_entry("StrandsDecider")
|
||||
|
||||
assert strands.provider_display_name == "Strands Decider"
|
||||
assert strands.litellm_provider == LlmProviders.STRANDS_DECIDER.value
|
||||
assert strands.default_model_placeholder is not None
|
||||
assert strands.default_model_placeholder.startswith("strands_decider/")
|
||||
|
||||
fields_by_key: Final = {f.key: f for f in strands.credential_fields}
|
||||
|
||||
assert fields_by_key["api_base"].required is True
|
||||
assert fields_by_key["api_base"].field_type == "text"
|
||||
|
||||
assert fields_by_key["api_key"].required is False
|
||||
assert fields_by_key["api_key"].field_type == "password"
|
||||
|
||||
|
||||
ADD_MODEL_UNLISTED_PROVIDERS: Final = frozenset(
|
||||
{
|
||||
"a2a",
|
||||
|
|
@ -436,12 +485,10 @@ ADD_MODEL_UNLISTED_PROVIDERS: Final = frozenset(
|
|||
"sagemaker_nova",
|
||||
"scaleway",
|
||||
"stability",
|
||||
"strands_decider",
|
||||
"synthetic",
|
||||
"tensormesh",
|
||||
"text-completion-inception",
|
||||
"transcribe",
|
||||
"typesafe",
|
||||
"valkey",
|
||||
"xiaomi_mimo",
|
||||
"zai",
|
||||
|
|
|
|||
BIN
ui/litellm-dashboard/public/assets/logos/typesafe.png
Normal file
BIN
ui/litellm-dashboard/public/assets/logos/typesafe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -214,6 +214,26 @@ describe("provider_info_helpers", () => {
|
|||
const { logo } = getProviderLogoAndName("tencent");
|
||||
expect(logo).toContain("tencent");
|
||||
});
|
||||
|
||||
it("should resolve the typesafe slug and TypeSafe enum key to the TypeSafe name and bundled logo", () => {
|
||||
const fromSlug = getProviderLogoAndName("typesafe");
|
||||
expect(fromSlug.displayName).toBe(Providers.TypeSafe);
|
||||
expect(fromSlug.logo).toContain("typesafe");
|
||||
|
||||
const fromEnumKey = getProviderLogoAndName("TypeSafe");
|
||||
expect(fromEnumKey.displayName).toBe(Providers.TypeSafe);
|
||||
expect(fromEnumKey.logo).toBe(fromSlug.logo);
|
||||
});
|
||||
|
||||
it("should resolve the strands_decider slug and StrandsDecider enum key to the Strands Decider name and bundled logo", () => {
|
||||
const fromSlug = getProviderLogoAndName("strands_decider");
|
||||
expect(fromSlug.displayName).toBe("Strands Decider");
|
||||
expect(fromSlug.logo).toContain("strands");
|
||||
|
||||
const fromEnumKey = getProviderLogoAndName("StrandsDecider");
|
||||
expect(fromEnumKey.displayName).toBe(Providers.StrandsDecider);
|
||||
expect(fromEnumKey.logo).toBe(fromSlug.logo);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPlaceholder", () => {
|
||||
|
|
@ -318,6 +338,11 @@ describe("provider_info_helpers", () => {
|
|||
expect(getPlaceholder(Providers.Tencent)).toBe("tencent/deepseek-v4-pro");
|
||||
});
|
||||
|
||||
it("should return decision model placeholders for the TypeSafe and StrandsDecider dropdown keys", () => {
|
||||
expect(getPlaceholder("TypeSafe")).toBe("typesafe/jev-latest");
|
||||
expect(getPlaceholder("StrandsDecider")).toBe("strands_decider/strands-decider-2B-hobson-v19");
|
||||
});
|
||||
|
||||
it("should return default gpt-3.5-turbo placeholder for unknown provider", () => {
|
||||
expect(getPlaceholder("UnknownProvider" as any)).toBe("gpt-3.5-turbo");
|
||||
});
|
||||
|
|
@ -421,6 +446,30 @@ describe("provider_info_helpers", () => {
|
|||
expect(getProviderModels("Sail" as Providers, modelMap)).toEqual(["sail/openai/gpt-oss-120b"]);
|
||||
});
|
||||
|
||||
it("should list only typesafe decision models for the 'TypeSafe' provider key, not the OpenRouter-hosted one", () => {
|
||||
const modelMap = {
|
||||
"typesafe/jev-latest": { litellm_provider: "typesafe", mode: "evaluation" },
|
||||
"typesafe/jev-preview": { litellm_provider: "typesafe", mode: "evaluation" },
|
||||
"openrouter/typesafe/jev-1.13": { litellm_provider: "openrouter", mode: "evaluation" },
|
||||
"strands_decider/strands-decider-2B-hobson-v19": { litellm_provider: "strands_decider", mode: "evaluation" },
|
||||
};
|
||||
expect(getProviderModels("TypeSafe" as Providers, modelMap)).toEqual([
|
||||
"typesafe/jev-latest",
|
||||
"typesafe/jev-preview",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should list only strands_decider models for the 'StrandsDecider' provider key", () => {
|
||||
const modelMap = {
|
||||
"strands_decider/strands-decider-2B-hobson-v19": { litellm_provider: "strands_decider", mode: "evaluation" },
|
||||
"typesafe/jev-latest": { litellm_provider: "typesafe", mode: "evaluation" },
|
||||
"openrouter/typesafe/jev-1.13": { litellm_provider: "openrouter", mode: "evaluation" },
|
||||
};
|
||||
expect(getProviderModels("StrandsDecider" as Providers, modelMap)).toEqual([
|
||||
"strands_decider/strands-decider-2B-hobson-v19",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should include bedrock converse but exclude standalone bedrock_mantle when called with 'Bedrock' provider key", () => {
|
||||
const modelMap = {
|
||||
"bedrock-base": { litellm_provider: "bedrock" },
|
||||
|
|
|
|||
|
|
@ -55,9 +55,11 @@ import sapLogo from "../../public/assets/logos/sap.png";
|
|||
import scxAiLogo from "../../public/assets/logos/scx_ai.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";
|
||||
import tencentLogo from "../../public/assets/logos/tencent.svg";
|
||||
import togetheraiLogo from "../../public/assets/logos/togetherai.svg";
|
||||
import topazLogo from "../../public/assets/logos/topaz.svg";
|
||||
import typesafeLogo from "../../public/assets/logos/typesafe.png";
|
||||
import v0Logo from "../../public/assets/logos/v0.svg";
|
||||
import vercelLogo from "../../public/assets/logos/vercel.svg";
|
||||
import vllmLogo from "../../public/assets/logos/vllm.png";
|
||||
|
|
@ -167,11 +169,13 @@ export enum Providers {
|
|||
SCX_AI = "SCX.ai",
|
||||
Snowflake = "Snowflake",
|
||||
Soniox = "Soniox",
|
||||
StrandsDecider = "Strands Decider",
|
||||
TEXT_COMPLETION_CODESTRAL = "Text-Completion-Codestral",
|
||||
Tencent = "Tencent",
|
||||
TogetherAI = "TogetherAI",
|
||||
TOPAZ = "Topaz",
|
||||
Triton = "Triton",
|
||||
TypeSafe = "TypeSafe",
|
||||
V0 = "V0",
|
||||
VERCEL_AI_GATEWAY = "Vercel Ai Gateway",
|
||||
Vertex_AI = "Vertex AI (Anthropic, Gemini, etc.)",
|
||||
|
|
@ -287,11 +291,13 @@ export const provider_map: Record<string, string> = {
|
|||
SCX_AI: "scx-ai",
|
||||
Snowflake: "snowflake",
|
||||
Soniox: "soniox",
|
||||
StrandsDecider: "strands_decider",
|
||||
TEXT_COMPLETION_CODESTRAL: "text-completion-codestral",
|
||||
Tencent: "tencent",
|
||||
TogetherAI: "together_ai",
|
||||
TOPAZ: "topaz",
|
||||
Triton: "triton",
|
||||
TypeSafe: "typesafe",
|
||||
V0: "v0",
|
||||
VERCEL_AI_GATEWAY: "vercel_ai_gateway",
|
||||
Vertex_AI: "vertex_ai",
|
||||
|
|
@ -387,11 +393,13 @@ export const providerLogoMap: Partial<Record<Providers, string>> = {
|
|||
[Providers.SCX_AI]: scxAiLogo.src,
|
||||
[Providers.Snowflake]: snowflakeLogo.src,
|
||||
[Providers.Soniox]: sonioxLogo.src,
|
||||
[Providers.StrandsDecider]: strandsLogo.src,
|
||||
[Providers.Tencent]: tencentLogo.src,
|
||||
[Providers.TEXT_COMPLETION_CODESTRAL]: mistralLogo.src,
|
||||
[Providers.TogetherAI]: togetheraiLogo.src,
|
||||
[Providers.TOPAZ]: topazLogo.src,
|
||||
[Providers.Triton]: nvidiaTritonLogo.src,
|
||||
[Providers.TypeSafe]: typesafeLogo.src,
|
||||
[Providers.V0]: v0Logo.src,
|
||||
[Providers.VERCEL_AI_GATEWAY]: vercelLogo.src,
|
||||
[Providers.Vertex_AI]: googleLogo.src,
|
||||
|
|
@ -457,7 +465,9 @@ const providerPlaceholderMap: Partial<Record<Providers, string>> = {
|
|||
[Providers.Sail]: "sail/openai/gpt-oss-120b",
|
||||
[Providers.SCX_AI]: "scx-ai/GLM-5.2",
|
||||
[Providers.Snowflake]: "snowflake/mistral-7b",
|
||||
[Providers.StrandsDecider]: "strands_decider/strands-decider-2B-hobson-v19",
|
||||
[Providers.Tencent]: "tencent/deepseek-v4-pro",
|
||||
[Providers.TypeSafe]: "typesafe/jev-latest",
|
||||
[Providers.Vertex_AI]: "gemini-pro",
|
||||
[Providers.VolcEngine]: "volcengine/<any-model-on-volcengine>",
|
||||
[Providers.Voyage]: "voyage/",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue