Add Skypool OpenAI-compatible provider

This commit is contained in:
Azard 2026-05-24 10:30:02 +08:00
parent 4148667671
commit 8d4c7dda52
8 changed files with 130 additions and 0 deletions

View file

@ -615,6 +615,7 @@ LITELLM_CHAT_PROVIDERS = [
"dashscope",
"moonshot",
"publicai",
"skypool",
"v0",
"heroku",
"oci",
@ -775,6 +776,7 @@ openai_compatible_endpoints: List = [
"https://nano-gpt.com/api/v1",
"https://api.poe.com/v1",
"https://llm.chutes.ai/v1/",
"https://a.skypool.xyz/v1",
"https://api.v0.dev/v1",
"https://api.morphllm.com/v1",
"https://api.lambda.ai/v1",
@ -824,6 +826,7 @@ openai_compatible_providers: List = [
"nano-gpt", # Nano-GPT - JSON-configured provider
"poe", # Poe - JSON-configured provider
"chutes", # Chutes - JSON-configured provider
"skypool", # Skypool - JSON-configured provider
"featherless_ai",
"nscale",
"nebius",

View file

@ -114,5 +114,13 @@
"param_mappings": {
"max_completion_tokens": "max_tokens"
}
},
"skypool": {
"base_url": "https://a.skypool.xyz/v1",
"api_key_env": "SKYPOOL_API_KEY",
"api_base_env": "SKYPOOL_API_BASE",
"param_mappings": {
"max_completion_tokens": "max_tokens"
}
}
}

View file

@ -3354,6 +3354,7 @@ class LlmProviders(str, Enum):
NANOGPT = "nano-gpt"
POE = "poe"
CHUTES = "chutes"
SKYPOOL = "skypool"
XIAOMI_MIMO = "xiaomi_mimo"
LITELLM_AGENT = "litellm_agent"
CURSOR = "cursor"

View file

@ -2063,6 +2063,22 @@
"interactions": true
}
},
"skypool": {
"display_name": "Skypool (`skypool`)",
"endpoints": {
"chat_completions": true,
"messages": false,
"responses": false,
"embeddings": false,
"image_generations": false,
"audio_transcriptions": false,
"audio_speech": false,
"moderations": false,
"batches": false,
"rerank": false,
"a2a": false
}
},
"synthetic": {
"display_name": "Synthetic (`synthetic`)",
"endpoints": {

View file

@ -0,0 +1,87 @@
"""
Unit tests for the Skypool OpenAI-compatible provider.
"""
import os
import sys
sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
)
from litellm.llms.openai_like.dynamic_config import create_config_class
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
SKYPOOL_BASE_URL = "https://a.skypool.xyz/v1"
def _get_config():
provider = JSONProviderRegistry.get("skypool")
assert provider is not None
config_class = create_config_class(provider)
return config_class()
def test_skypool_provider_registered():
provider = JSONProviderRegistry.get("skypool")
assert provider is not None
assert provider.base_url == SKYPOOL_BASE_URL
assert provider.api_key_env == "SKYPOOL_API_KEY"
assert provider.api_base_env == "SKYPOOL_API_BASE"
assert provider.param_mappings["max_completion_tokens"] == "max_tokens"
def test_skypool_resolves_env_api_key(monkeypatch):
config = _get_config()
monkeypatch.setenv("SKYPOOL_API_KEY", "test-key")
api_base, api_key = config._get_openai_compatible_provider_info(None, None)
assert api_base == SKYPOOL_BASE_URL
assert api_key == "test-key"
def test_skypool_resolves_env_api_base(monkeypatch):
config = _get_config()
monkeypatch.setenv("SKYPOOL_API_BASE", "https://example.com/v1")
api_base, api_key = config._get_openai_compatible_provider_info(None, "test-key")
assert api_base == "https://example.com/v1"
assert api_key == "test-key"
def test_skypool_complete_url_appends_endpoint():
config = _get_config()
url = config.get_complete_url(
api_base=SKYPOOL_BASE_URL,
api_key="test-key",
model="skypool/gemma4:26b",
optional_params={},
litellm_params={},
stream=False,
)
assert url == f"{SKYPOOL_BASE_URL}/chat/completions"
def test_skypool_provider_resolution():
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
model, provider, api_key, api_base = get_llm_provider(
model="skypool/gemma4:26b",
custom_llm_provider=None,
api_base=None,
api_key=None,
)
assert model == "gemma4:26b"
assert provider == "skypool"
assert api_base == SKYPOOL_BASE_URL
def test_skypool_provider_config_manager():
from litellm import LlmProviders
from litellm.utils import ProviderConfigManager
config = ProviderConfigManager.get_provider_chat_config(
model="gemma4:26b", provider=LlmProviders.SKYPOOL
)
assert config is not None
assert config.custom_llm_provider == "skypool"

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -62,6 +62,12 @@ describe("provider_info_helpers", () => {
expect(result.logo).toBe(providerLogoMap[Providers.Groq]);
});
it("should map skypool provider value to Skypool display name and logo", () => {
const result = getProviderLogoAndName("skypool");
expect(result.displayName).toBe(Providers.Skypool);
expect(result.logo).toBe(providerLogoMap[Providers.Skypool]);
});
it("should handle provider values case-insensitively", () => {
const result = getProviderLogoAndName("OPENAI");
expect(result.displayName).toBe(Providers.OpenAI);
@ -138,6 +144,10 @@ describe("provider_info_helpers", () => {
expect(getPlaceholder(Providers.Snowflake)).toBe("snowflake/mistral-7b");
});
it("should return skypool placeholder for Skypool provider", () => {
expect(getPlaceholder(Providers.Skypool)).toBe("skypool/gemma4:26b");
});
it("should return voyage placeholder for Voyage provider", () => {
expect(getPlaceholder(Providers.Voyage)).toBe("voyage/");
});

View file

@ -87,6 +87,7 @@ export enum Providers {
Sambanova = "Sambanova",
SAP = "SAP Generative AI Hub",
Snowflake = "Snowflake",
Skypool = "Skypool",
TEXT_COMPLETION_CODESTRAL = "Text-Completion-Codestral",
TogetherAI = "TogetherAI",
TOPAZ = "Topaz",
@ -195,6 +196,7 @@ export const provider_map: Record<string, string> = {
Sambanova: "sambanova",
SAP: "sap",
Snowflake: "snowflake",
Skypool: "skypool",
TEXT_COMPLETION_CODESTRAL: "text-completion-codestral",
TogetherAI: "together_ai",
TOPAZ: "topaz",
@ -286,6 +288,7 @@ export const providerLogoMap: Record<string, string> = {
[Providers.Sambanova]: `${asset_logos_folder}sambanova.svg`,
[Providers.SAP]: `${asset_logos_folder}sap.png`,
[Providers.Snowflake]: `${asset_logos_folder}snowflake.svg`,
[Providers.Skypool]: `${asset_logos_folder}skypool.png`,
[Providers.TEXT_COMPLETION_CODESTRAL]: `${asset_logos_folder}mistral.svg`,
[Providers.TogetherAI]: `${asset_logos_folder}togetherai.svg`,
[Providers.TOPAZ]: `${asset_logos_folder}topaz.svg`,
@ -352,6 +355,8 @@ export const getPlaceholder = (selectedProvider: string): string => {
return "oci/xai.grok-4";
} else if (selectedProvider == Providers.Snowflake) {
return "snowflake/mistral-7b";
} else if (selectedProvider == Providers.Skypool) {
return "skypool/gemma4:26b";
} else if (selectedProvider == Providers.Voyage) {
return "voyage/";
} else if (selectedProvider == Providers.JinaAI) {