mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Merge 3fe3618fc8 into d295f76c4a
This commit is contained in:
commit
f002a2fa2c
17 changed files with 407 additions and 0 deletions
|
|
@ -404,6 +404,7 @@ Set `LITELLM_PROXY_API_BASE` and `LITELLM_PROXY_API_KEY` and every model call th
|
|||
| [Fal AI (`fal_ai`)](https://docs.litellm.ai/docs/providers/fal_ai) | ✅ | ✅ | ✅ | | ✅ | | | | | |
|
||||
| [Featherless AI (`featherless_ai`)](https://docs.litellm.ai/docs/providers/featherless_ai) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Fireworks AI (`fireworks_ai`)](https://docs.litellm.ai/docs/providers/fireworks_ai) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Flonno (`flonno`)](https://docs.litellm.ai/docs/providers/flonno) | ✅ | ✅ | | | | | | | | |
|
||||
| [FriendliAI (`friendliai`)](https://docs.litellm.ai/docs/providers/friendliai) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Galadriel (`galadriel`)](https://docs.litellm.ai/docs/providers/galadriel) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [GitHub Copilot (`github_copilot`)](https://docs.litellm.ai/docs/providers/github_copilot) | ✅ | ✅ | ✅ | ✅ | | | | | | |
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const OPENAI_COMPATIBLE_PROVIDERS: &[&str] = &[
|
|||
"parasail",
|
||||
"libertai",
|
||||
"featherless_ai",
|
||||
"flonno",
|
||||
"nscale",
|
||||
"nebius",
|
||||
"dashscope",
|
||||
|
|
|
|||
|
|
@ -690,6 +690,7 @@ kyma_models: Final[set[str]] = set() # mutable-ok: populated when the shared mo
|
|||
streamlake_models: Final[Set[str]] = set()
|
||||
wally_models: Final[Set[str]] = set()
|
||||
zerogpu_models: Final[Set[str]] = set()
|
||||
flonno_models: Final[Set[str]] = set()
|
||||
v0_models: Set = set()
|
||||
morph_models: Set = set()
|
||||
opencode_go_models: Final[set[str]] = set() # mutable-ok: populated when the shared model catalog loads
|
||||
|
|
@ -989,6 +990,8 @@ def _populate_provider_model_sets(model_cost_map: Mapping[str, Mapping[str, obje
|
|||
wally_models.add(key)
|
||||
elif value.get("litellm_provider") == "zerogpu":
|
||||
zerogpu_models.add(key)
|
||||
elif value.get("litellm_provider") == "flonno":
|
||||
flonno_models.add(key)
|
||||
elif value.get("litellm_provider") == "v0":
|
||||
v0_models.add(key)
|
||||
elif value.get("litellm_provider") == "morph":
|
||||
|
|
@ -1170,6 +1173,7 @@ model_list = list(
|
|||
| streamlake_models
|
||||
| wally_models
|
||||
| zerogpu_models
|
||||
| flonno_models
|
||||
| v0_models
|
||||
| morph_models
|
||||
| opencode_go_models
|
||||
|
|
@ -1304,6 +1308,7 @@ def _build_models_by_provider() -> dict:
|
|||
"streamlake": streamlake_models,
|
||||
"wally": wally_models,
|
||||
"zerogpu": zerogpu_models,
|
||||
"flonno": flonno_models,
|
||||
"v0": v0_models,
|
||||
"morph": morph_models,
|
||||
"opencode_go": opencode_go_models,
|
||||
|
|
|
|||
|
|
@ -1096,6 +1096,7 @@ openai_compatible_endpoints: Final[list[str]] = [
|
|||
"https://inference.runanywhere.ai/v1",
|
||||
"https://gigachat.devices.sberbank.ru/api/v1",
|
||||
"https://api.openinfer.ai/v1",
|
||||
"https://www.flonno.com/v1",
|
||||
"https://vanchin.streamlake.ai/api/gateway/v1/endpoints",
|
||||
]
|
||||
|
||||
|
|
@ -1146,6 +1147,7 @@ openai_compatible_providers: Final[list[str]] = [
|
|||
"parasail", # Parasail - JSON-configured provider
|
||||
"libertai", # LibertAI - JSON-configured provider
|
||||
"featherless_ai",
|
||||
"flonno",
|
||||
"nscale",
|
||||
"nebius",
|
||||
"dashscope",
|
||||
|
|
|
|||
|
|
@ -265,6 +265,20 @@
|
|||
},
|
||||
"supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"]
|
||||
},
|
||||
"flonno": {
|
||||
"base_url": "https://www.flonno.com/v1",
|
||||
"api_key_env": "FLONNO_API_KEY",
|
||||
"api_base_env": "FLONNO_API_BASE",
|
||||
"provider_info": {
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_reasoning": true
|
||||
},
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/messages"
|
||||
]
|
||||
},
|
||||
"inferenceapis": {
|
||||
"base_url": "https://api.inferenceapis.com/v1",
|
||||
"api_key_env": "INFERENCEAPIS_API_KEY",
|
||||
|
|
|
|||
|
|
@ -2792,6 +2792,10 @@ def _complete_custom_openai(
|
|||
raise litellm.AuthenticationError(
|
||||
message="Set BIGMODEL_API_KEY or pass api_key", llm_provider="bigmodel", model=model
|
||||
)
|
||||
if custom_llm_provider == "flonno" and not api_key:
|
||||
raise litellm.AuthenticationError(
|
||||
message="Set FLONNO_API_KEY or pass api_key", llm_provider="flonno", model=model
|
||||
)
|
||||
organization = (
|
||||
organization
|
||||
or (None if custom_llm_provider == "bigmodel" else litellm.organization or get_secret("OPENAI_ORGANIZATION"))
|
||||
|
|
|
|||
|
|
@ -29129,6 +29129,22 @@
|
|||
"output_cost_per_token": 0.0,
|
||||
"source": "https://fireworks.ai/pricing"
|
||||
},
|
||||
"flonno/qwen3.8-27b": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_token": 3e-08,
|
||||
"litellm_provider": "flonno",
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-07,
|
||||
"source": "https://www.flonno.com/pricing.json",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"friendliai/zai-org/GLM-5.3-Flash": {
|
||||
"litellm_provider": "friendliai",
|
||||
"max_input_tokens": 1048576,
|
||||
|
|
|
|||
|
|
@ -1686,6 +1686,32 @@
|
|||
"generateContent": true
|
||||
}
|
||||
},
|
||||
"flonno": {
|
||||
"display_name": "Flonno (`flonno`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/flonno",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"responses": false,
|
||||
"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
|
||||
}
|
||||
},
|
||||
"firecrawl": {
|
||||
"display_name": "Firecrawl (`firecrawl`)",
|
||||
"url": "https://docs.litellm.ai/docs/search/firecrawl",
|
||||
|
|
|
|||
|
|
@ -1701,6 +1701,34 @@
|
|||
],
|
||||
"default_model_placeholder": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"provider": "Flonno",
|
||||
"provider_display_name": "Flonno",
|
||||
"litellm_provider": "flonno",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://www.flonno.com/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": "flonno/qwen3.8-27b"
|
||||
},
|
||||
{
|
||||
"provider": "FRIENDLIAI",
|
||||
"provider_display_name": "Friendliai",
|
||||
|
|
|
|||
|
|
@ -4692,6 +4692,7 @@ class LlmProviders(str, Enum):
|
|||
FIREWORKS_AI = "fireworks_ai"
|
||||
FRIENDLIAI = "friendliai"
|
||||
FEATHERLESS_AI = "featherless_ai"
|
||||
FLONNO = "flonno"
|
||||
WATSONX = "watsonx"
|
||||
SCALEDOWN = "scaledown"
|
||||
WATSONX_TEXT = "watsonx_text"
|
||||
|
|
|
|||
|
|
@ -29129,6 +29129,22 @@
|
|||
"output_cost_per_token": 0.0,
|
||||
"source": "https://fireworks.ai/pricing"
|
||||
},
|
||||
"flonno/qwen3.8-27b": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_token": 3e-08,
|
||||
"litellm_provider": "flonno",
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-07,
|
||||
"source": "https://www.flonno.com/pricing.json",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"friendliai/zai-org/GLM-5.3-Flash": {
|
||||
"litellm_provider": "friendliai",
|
||||
"max_input_tokens": 1048576,
|
||||
|
|
|
|||
|
|
@ -1686,6 +1686,32 @@
|
|||
"generateContent": true
|
||||
}
|
||||
},
|
||||
"flonno": {
|
||||
"display_name": "Flonno (`flonno`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/flonno",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"responses": false,
|
||||
"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
|
||||
}
|
||||
},
|
||||
"firecrawl": {
|
||||
"display_name": "Firecrawl (`firecrawl`)",
|
||||
"url": "https://docs.litellm.ai/docs/search/firecrawl",
|
||||
|
|
|
|||
238
tests/unit/llms/openai_like/test_flonno_provider.py
Normal file
238
tests/unit/llms/openai_like/test_flonno_provider.py
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import json
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
from openai import OpenAI
|
||||
|
||||
import litellm
|
||||
from litellm.caching.llm_caching_handler import LLMClientCache
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
from litellm.proxy.auth.model_checks import get_provider_models
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
FLONNO_API_BASE: Final = "https://www.flonno.com/v1"
|
||||
FLONNO_MODEL: Final = "qwen3.8-27b"
|
||||
FLONNO_PREFIXED: Final = f"flonno/{FLONNO_MODEL}"
|
||||
|
||||
_REPO_ROOT: Final = Path(__file__).resolve().parents[4]
|
||||
_PRIMARY_PRICES: Final = json.loads((_REPO_ROOT / "model_prices_and_context_window.json").read_text())
|
||||
_BACKUP_PRICES: Final = json.loads((_REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json").read_text())
|
||||
|
||||
|
||||
class _RecordingFlonno:
|
||||
def __init__(self, usage: Mapping[str, object]) -> None:
|
||||
self.usage: Final = usage
|
||||
self.requests: tuple[httpx.Request, ...] = ()
|
||||
|
||||
def respond(self, request: httpx.Request) -> httpx.Response:
|
||||
self.requests = (*self.requests, request)
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"id": "chatcmpl-flonno",
|
||||
"object": "chat.completion",
|
||||
"created": 1234567890,
|
||||
"model": json.loads(request.content)["model"],
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "hi"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": dict(self.usage),
|
||||
},
|
||||
)
|
||||
|
||||
def client(self) -> OpenAI:
|
||||
return OpenAI(
|
||||
api_key="flonno-test-key",
|
||||
base_url=FLONNO_API_BASE,
|
||||
http_client=httpx.Client(transport=httpx.MockTransport(self.respond)),
|
||||
)
|
||||
|
||||
|
||||
def test_flonno_in_provider_list_and_json_registry():
|
||||
assert LlmProviders.FLONNO.value == "flonno"
|
||||
assert "flonno" in litellm.provider_list
|
||||
assert JSONProviderRegistry.exists("flonno")
|
||||
provider = JSONProviderRegistry.get("flonno")
|
||||
assert provider is not None
|
||||
assert provider.base_url == FLONNO_API_BASE
|
||||
assert provider.api_key_env == "FLONNO_API_KEY"
|
||||
assert "/v1/chat/completions" in provider.supported_endpoints
|
||||
assert "/v1/messages" in provider.supported_endpoints
|
||||
assert "/v1/responses" not in provider.supported_endpoints
|
||||
|
||||
|
||||
def test_flonno_in_openai_compatible_providers():
|
||||
from litellm.constants import openai_compatible_providers
|
||||
|
||||
assert "flonno" in openai_compatible_providers
|
||||
|
||||
|
||||
def test_prefixed_model_reaches_flonno_chat_completions_without_the_prefix():
|
||||
flonno: Final = _RecordingFlonno({"prompt_tokens": 3, "completion_tokens": 1, "total_tokens": 4})
|
||||
|
||||
with flonno.client() as client:
|
||||
litellm.completion(
|
||||
model=FLONNO_PREFIXED,
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
api_key="flonno-test-key",
|
||||
client=client,
|
||||
)
|
||||
|
||||
assert len(flonno.requests) == 1
|
||||
assert str(flonno.requests[0].url) == f"{FLONNO_API_BASE}/chat/completions"
|
||||
assert json.loads(flonno.requests[0].content) == {
|
||||
"model": FLONNO_MODEL,
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_messages_request_goes_to_flonno_natively(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
|
||||
monkeypatch.setattr(litellm, "in_memory_llm_clients_cache", LLMClientCache())
|
||||
with respx.mock() as upstream:
|
||||
route: Final = upstream.post(f"{FLONNO_API_BASE}/messages").respond(
|
||||
200,
|
||||
json={
|
||||
"id": "msg_flonno",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": FLONNO_MODEL,
|
||||
"content": [{"type": "text", "text": "Hello from Flonno"}],
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": None,
|
||||
"usage": {"input_tokens": 4, "output_tokens": 3},
|
||||
},
|
||||
)
|
||||
response: Final = await litellm.anthropic.messages.acreate(
|
||||
model=FLONNO_PREFIXED,
|
||||
messages=[{"role": "user", "content": "Say hello"}],
|
||||
max_tokens=32,
|
||||
api_key="sk-test",
|
||||
)
|
||||
|
||||
request: Final = route.calls.last.request
|
||||
body: Final = json.loads(request.content)
|
||||
assert request.headers["authorization"] == "Bearer sk-test"
|
||||
assert body["model"] == FLONNO_MODEL
|
||||
assert body["messages"] == [{"role": "user", "content": "Say hello"}]
|
||||
assert response["content"][0]["text"] == "Hello from Flonno"
|
||||
|
||||
|
||||
def test_flonno_key_and_base_come_from_flonno_env_vars(monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.setenv("FLONNO_API_KEY", "flonno-env-key")
|
||||
monkeypatch.delenv("FLONNO_API_BASE", raising=False)
|
||||
|
||||
assert get_llm_provider(model=FLONNO_PREFIXED) == (
|
||||
FLONNO_MODEL,
|
||||
"flonno",
|
||||
"flonno-env-key",
|
||||
FLONNO_API_BASE,
|
||||
)
|
||||
|
||||
monkeypatch.setenv("FLONNO_API_BASE", "https://flonno.internal.example/v1")
|
||||
|
||||
assert get_llm_provider(model=FLONNO_PREFIXED)[3] == "https://flonno.internal.example/v1"
|
||||
|
||||
|
||||
def test_flonno_wildcard_lists_every_flonno_catalog_model():
|
||||
catalog_models: Final = sorted(
|
||||
key for key, row in litellm.model_cost.items() if row.get("litellm_provider") == "flonno"
|
||||
)
|
||||
|
||||
assert catalog_models == [FLONNO_PREFIXED]
|
||||
assert sorted(get_provider_models("flonno") or []) == catalog_models
|
||||
|
||||
|
||||
def test_catalog_row_matches_its_backup_and_fits_its_context():
|
||||
row: Final = _PRIMARY_PRICES[FLONNO_PREFIXED]
|
||||
|
||||
assert row == _BACKUP_PRICES[FLONNO_PREFIXED]
|
||||
assert row["mode"] == "chat"
|
||||
assert row["max_output_tokens"] <= row["max_input_tokens"]
|
||||
assert row["input_cost_per_token"] == 3e-08
|
||||
assert row["output_cost_per_token"] == 2.5e-07
|
||||
assert litellm.get_model_info(FLONNO_PREFIXED)["litellm_provider"] == "flonno"
|
||||
|
||||
|
||||
def test_flonno_completion_never_borrows_openai_api_key(monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.delenv("FLONNO_API_KEY", raising=False)
|
||||
monkeypatch.setenv("OPENAI_API_KEY", "sk-openai-should-not-leak")
|
||||
monkeypatch.setattr(litellm, "api_key", None)
|
||||
monkeypatch.setattr(litellm, "openai_key", None)
|
||||
|
||||
with respx.mock(assert_all_called=False) as upstream:
|
||||
route: Final = upstream.post(f"{FLONNO_API_BASE}/chat/completions").respond(
|
||||
200,
|
||||
json={
|
||||
"id": "chatcmpl-flonno",
|
||||
"object": "chat.completion",
|
||||
"created": 1,
|
||||
"model": FLONNO_MODEL,
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "hi"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
|
||||
},
|
||||
)
|
||||
with pytest.raises(litellm.AuthenticationError, match="FLONNO_API_KEY"):
|
||||
litellm.completion(
|
||||
model=FLONNO_PREFIXED,
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
)
|
||||
|
||||
assert route.call_count == 0
|
||||
|
||||
|
||||
def test_flonno_completion_uses_flonno_api_key_from_env(monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.setenv("FLONNO_API_KEY", "flonno-env-key")
|
||||
monkeypatch.setenv("OPENAI_API_KEY", "sk-openai-should-not-be-used")
|
||||
monkeypatch.setattr(litellm, "api_key", None)
|
||||
monkeypatch.setattr(litellm, "openai_key", None)
|
||||
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
|
||||
monkeypatch.setattr(litellm, "in_memory_llm_clients_cache", LLMClientCache())
|
||||
|
||||
with respx.mock() as upstream:
|
||||
route: Final = upstream.post(f"{FLONNO_API_BASE}/chat/completions").respond(
|
||||
200,
|
||||
json={
|
||||
"id": "chatcmpl-flonno",
|
||||
"object": "chat.completion",
|
||||
"created": 1,
|
||||
"model": FLONNO_MODEL,
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "hi"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
|
||||
},
|
||||
)
|
||||
response: Final = litellm.completion(
|
||||
model=FLONNO_PREFIXED,
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
)
|
||||
|
||||
request: Final = route.calls.last.request
|
||||
assert route.call_count == 1
|
||||
assert str(request.url) == f"{FLONNO_API_BASE}/chat/completions"
|
||||
assert request.headers["authorization"] == "Bearer flonno-env-key"
|
||||
assert "sk-openai-should-not-be-used" not in request.headers.get("authorization", "")
|
||||
assert response.choices[0].message.content == "hi"
|
||||
|
|
@ -447,6 +447,26 @@ def test_zerogpu_add_model_form_defaults_resolve_to_a_priced_zerogpu_model():
|
|||
assert fields_by_key["api_key"]["field_type"] == "password"
|
||||
|
||||
|
||||
def test_flonno_add_model_form_defaults_resolve_to_a_priced_flonno_model():
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
app_instance: Final = FastAPI()
|
||||
app_instance.include_router(router)
|
||||
providers: Final = TestClient(app_instance).get("/public/providers/fields").json()
|
||||
|
||||
flonno: Final = next(p for p in providers if p["litellm_provider"] == "flonno")
|
||||
fields_by_key: Final = {f["key"]: f for f in flonno["credential_fields"]}
|
||||
placeholder: Final = flonno["default_model_placeholder"]
|
||||
_, provider, _, api_base = get_llm_provider(model=placeholder)
|
||||
|
||||
assert provider == "flonno"
|
||||
assert api_base == fields_by_key["api_base"]["placeholder"]
|
||||
assert litellm.model_cost[placeholder]["litellm_provider"] == "flonno"
|
||||
assert fields_by_key["api_key"]["required"] is True
|
||||
assert fields_by_key["api_key"]["field_type"] == "password"
|
||||
|
||||
|
||||
def test_qwen_mainland_provider_fields_carry_the_qianwen_brand():
|
||||
app_instance = FastAPI()
|
||||
app_instance.include_router(router)
|
||||
|
|
|
|||
BIN
ui/litellm-dashboard/public/assets/logos/flonno.png
Normal file
BIN
ui/litellm-dashboard/public/assets/logos/flonno.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -410,6 +410,10 @@ describe("provider_info_helpers", () => {
|
|||
expect(getPlaceholder("ZeroGPU")).toBe("zerogpu/gpt-oss-120b");
|
||||
});
|
||||
|
||||
it("should return flonno/qwen3.8-27b placeholder for Flonno provider", () => {
|
||||
expect(getPlaceholder("Flonno")).toBe("flonno/qwen3.8-27b");
|
||||
});
|
||||
|
||||
it("should return a chatgpt/ placeholder for the CHATGPT dropdown key", () => {
|
||||
expect(getPlaceholder("CHATGPT")).toBe("chatgpt/gpt-5.4");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import groqLogo from "../../public/assets/logos/groq.svg";
|
|||
import huggingfaceLogo from "../../public/assets/logos/huggingface.svg";
|
||||
import hyperbolicLogo from "../../public/assets/logos/hyperbolic.svg";
|
||||
import hostingerLogo from "../../public/assets/logos/hostinger.png";
|
||||
import flonnoLogo from "../../public/assets/logos/flonno.png";
|
||||
import infinityLogo from "../../public/assets/logos/infinity.png";
|
||||
import jinaLogo from "../../public/assets/logos/jina.png";
|
||||
import lambdaLogo from "../../public/assets/logos/lambda.svg";
|
||||
|
|
@ -123,6 +124,7 @@ export enum Providers {
|
|||
FalAI = "Fal AI",
|
||||
FEATHERLESS_AI = "Featherless Ai",
|
||||
FireworksAI = "Fireworks AI",
|
||||
Flonno = "Flonno",
|
||||
FRIENDLIAI = "Friendliai",
|
||||
GALADRIEL = "Galadriel",
|
||||
GIGACHAT = "GigaChat",
|
||||
|
|
@ -258,6 +260,7 @@ export const provider_map: Record<string, string> = {
|
|||
FalAI: "fal_ai",
|
||||
FEATHERLESS_AI: "featherless_ai",
|
||||
FireworksAI: "fireworks_ai",
|
||||
Flonno: "flonno",
|
||||
FRIENDLIAI: "friendliai",
|
||||
GALADRIEL: "galadriel",
|
||||
GIGACHAT: "gigachat",
|
||||
|
|
@ -389,6 +392,7 @@ export const providerLogoMap: Partial<Record<Providers, string>> = {
|
|||
[Providers.FalAI]: falAiLogo.src,
|
||||
[Providers.FEATHERLESS_AI]: featherlessLogo.src,
|
||||
[Providers.FireworksAI]: fireworksLogo.src,
|
||||
[Providers.Flonno]: flonnoLogo.src,
|
||||
[Providers.FRIENDLIAI]: friendliLogo.src,
|
||||
[Providers.GIGACHAT]: gigachatLogo.src,
|
||||
[Providers.GITHUB_COPILOT]: githubCopilotLogo.src,
|
||||
|
|
@ -530,6 +534,7 @@ const providerPlaceholderMap: Partial<Record<Providers, string>> = {
|
|||
[Providers.ZAI]: "zai/glm-4.5",
|
||||
[Providers.BIGMODEL]: "bigmodel/glm-5.3",
|
||||
[Providers.ZeroGPU]: "zerogpu/gpt-oss-120b",
|
||||
[Providers.Flonno]: "flonno/qwen3.8-27b",
|
||||
};
|
||||
|
||||
export const getPlaceholder = (selectedProvider: string): string => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue