Universal Python SDK for LLM providers. One interface, every model.
Project description
|
The universal Python SDK for every LLM provider.
One interface. 16 providers. Zero vendor lock-in.
Why SwapLM?
Most LLM applications are tightly coupled to a single provider. Switching from Groq to OpenAI to Anthropic means rewriting your API calls, error handling, streaming logic, and auth management. SwapLM eliminates that lock-in.
Write your code once. Swap providers by changing a single string.
from swaplm import chat
# Groq
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hello!"}],
)
# OpenAI — same interface, different provider
response = chat(
provider="openai",
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
# Anthropic — still the same interface
response = chat(
provider="anthropic",
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Hello!"}],
)
Features
- 🔌 16 Production Providers — Groq, Anthropic, Google Gemini, OpenAI, OpenRouter, GitHub Models, NVIDIA NIM, Cerebras, SambaNova, Mistral, xAI, DeepInfra, Fireworks, Cloudflare, Perplexity, Cohere.
- ⚡ Sync & Async —
chat()for synchronous code,achat()forasync/awaitworkflows. - 📡 Streaming — Real-time token-by-token responses with
stream=True. Works sync and async. - 🛠️ Tool / Function Calling — Define tools with JSON schemas. SwapLM handles provider-specific translation automatically.
- 🔄 Retries with Exponential Backoff — Built-in retry on 5xx, 429 rate limits, and timeouts. Configurable per-request or globally.
- 🧩 Middleware Pipeline — Intercept and modify requests/responses with
BaseMiddleware. Supports both sync and async. - 🪝 Lifecycle Hooks — Listen for
before_request,after_request,before_retry,on_errorevents. - 🔌 Custom Transport — Replace the HTTP layer entirely with
BaseTransportfor proxies, caching, mocks, or enterprise security. - 🔍 Provider & Model Discovery — Query available providers and their models programmatically.
- 🧠 Model Capability Awareness — SDK automatically omits unsupported parameters (e.g.,
seed,tool_choice) based on model metadata. - 🔒 Secure Debug Logging —
configure(debug=True)redacts API keys and authorization headers. - 📦 Zero Heavy Dependencies — Only
pydantic>=2.11andhttpx>=0.27.0.
Installation
pip install swaplm
Requires Python 3.10+.
Quick Start
1. Set Your API Key
export GROQ_API_KEY=gsk_... # or any provider's env var
2. Make Your First Call
from swaplm import chat
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the meaning of life?"},
],
temperature=0.7,
max_tokens=256,
)
print(response.content)
# => "The meaning of life is a philosophical question..."
3. Async Version
import asyncio
from swaplm import achat
async def main():
response = await achat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.content)
asyncio.run(main())
API Reference
chat() / achat() Parameters
Both functions accept the same parameters (keyword-only). achat() is the async equivalent.
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
str |
required | Model identifier. When provider is set, this is the raw model ID. Otherwise "provider/model" format (e.g., "groq/llama-3.3-70b-versatile"). |
messages |
list[dict | Message] |
required | Conversation history. Each dict needs "role" and "content". |
provider |
str | None |
None |
Explicit provider slug (e.g., "groq"). When set, bypasses model-string parsing. |
stream |
bool |
False |
If True, returns a StreamResponse that yields StreamChunk objects. |
api_key |
str | None |
None |
Explicit API key. Overrides the provider's environment variable. |
base_url |
str | None |
None |
Override the provider's default API base URL. |
max_tokens |
int | None |
None |
Maximum tokens to generate in the response. |
temperature |
float | None |
None |
Sampling temperature (0.0–2.0). Higher = more random. |
top_p |
float | None |
None |
Nucleus sampling threshold (0.0–1.0). |
response_format |
dict | None |
None |
Structured output format. Use {"type": "json_object"} for JSON mode. |
tools |
list[Tool | dict] | None |
None |
List of tools the model can call. |
tool_choice |
str | dict | None |
None |
Controls tool usage: "auto", "none", "required", or a specific tool dict. |
stop |
str | list[str] | None |
None |
Stop sequence(s) that halt generation. |
seed |
int | None |
None |
Seed for reproducible outputs (provider-dependent). |
timeout |
float | None |
None |
Request timeout in seconds. Falls back to configure() default. |
retries |
int |
0 |
Number of retries on transient failures (5xx, 429, timeouts). |
extra_headers |
dict[str, str] | None |
None |
Additional HTTP headers merged into the request. |
provider_options |
dict[str, Any] | None |
None |
Provider-specific options passed through verbatim (escape hatch). |
Model String Format
Recommended — explicit provider:
chat(provider="groq", model="llama-3.3-70b-versatile")
Still supported — combined model string:
chat(model="groq/llama-3.3-70b-versatile")
| Format | Example | Description |
|---|---|---|
| Explicit provider | provider="groq", model="llama-3.3-70b-versatile" |
Bypasses model-string parsing. Recommended. |
| Combined string | "groq/llama-3.3-70b-versatile" |
Routes directly to the named provider. |
| Nested model ID | provider="nvidia", model="deepseek-ai/deepseek-v4-flash" |
Handles model IDs with slashes cleanly. |
| Alias | "llama-3.3-70b-versatile" |
Searches all providers. Raises AmbiguousModelError if multiple match. |
Return Types
ChatResponse
Returned by chat() / achat() when stream=False.
from swaplm import chat
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hi"}],
)
# Convenience shortcuts
response.content # str | None — text of first choice
response.tool_calls # list[ToolCall] | None — tool calls from first choice
response.finish_reason # str | None — "stop", "length", "tool_calls", etc.
response.reasoning # str | None — reasoning/thinking content (if present)
# Full fields
response.id # str — unique response ID
response.model # str — model that generated the response
response.provider # str — provider slug (e.g., "groq")
response.choices # list[Choice] — all choices
response.usage # Usage | None — token counts
response.usage.prompt_tokens # int
response.usage.completion_tokens # int
response.usage.total_tokens # int
response.created # int | None — Unix timestamp
StreamResponse
Returned by chat() / achat() when stream=True.
from swaplm import chat
stream = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Write a poem."}],
stream=True,
)
# Sync iteration — use convenience properties
for chunk in stream:
print(chunk.content, end="", flush=True)
# Access full text after iteration
print(stream.text) # alias for accumulated_content
print(stream.accumulated_content) # same thing
# Async iteration
async for chunk in stream:
print(chunk.content, end="", flush=True)
StreamChunk
Each yielded chunk during streaming. Provides convenience properties that proxy the first choice:
| Property | Type | Description |
|---|---|---|
content |
str |
Incremental text content (empty string if none) |
reasoning |
str |
Incremental reasoning/thinking content (empty string if none) |
tool_calls |
list[ToolCallDelta] | None |
Incremental tool call data |
finish_reason |
str | None |
"stop", "length", "tool_calls", etc. |
Full access to underlying structure is still available:
| Field | Type | Description |
|---|---|---|
id |
str | None |
Response ID |
choices |
list[ChunkChoice] | None |
Choice deltas |
usage |
Usage | None |
Token usage (if provided by provider) |
model |
str | None |
Model identifier |
provider |
str | None |
Provider slug |
ChunkChoice
| Field | Type | Description |
|---|---|---|
index |
int |
Choice index |
delta |
ChoiceDelta |
Incremental content |
finish_reason |
str | None |
"stop", "length", "tool_calls", etc. |
ChoiceDelta
| Field | Type | Description |
|---|---|---|
role |
str | None |
Message role (usually "assistant") |
content |
str | None |
Incremental text content |
tool_calls |
list[ToolCallDelta] | None |
Incremental tool call data |
Supported Providers
| Provider | Slug | Protocol | Env Variable | Free Tier | Streaming | Tools | Reasoning |
|---|---|---|---|---|---|---|---|
| Groq | groq |
OpenAI | GROQ_API_KEY |
✅ | ✅ | ✅ | ✅ |
| OpenAI | openai |
OpenAI | OPENAI_API_KEY |
❌ | ✅ | ✅ | ✅ |
| Anthropic | anthropic |
Anthropic | ANTHROPIC_API_KEY |
❌ | ✅ | ✅ | ✅ |
| Google Gemini | google |
GEMINI_API_KEY |
✅ | ✅ | ✅ | ✅ | |
| OpenRouter | openrouter |
OpenAI | OPENROUTER_API_KEY |
✅ | ✅ | ✅ | ✅ |
| GitHub Models | github |
OpenAI | GITHUB_TOKEN |
✅ | ✅ | ✅ | ✅ |
| NVIDIA NIM | nvidia |
OpenAI | NVIDIA_API_KEY |
✅ | ✅ | ✅ | ✅ |
| Cerebras | cerebras |
OpenAI | CEREBRAS_API_KEY |
✅ | ✅ | ✅ | ❌ |
| SambaNova | sambanova |
OpenAI | SAMBANOVA_API_KEY |
✅ | ✅ | ✅ | ✅ |
| Mistral AI | mistral |
OpenAI | MISTRAL_API_KEY |
✅ | ✅ | ✅ | ❌ |
| xAI | xai |
OpenAI | XAI_API_KEY |
❌ | ✅ | ✅ | ❌ |
| DeepInfra | deepinfra |
OpenAI | DEEPINFRA_API_KEY |
❌ | ✅ | ✅ | ✅ |
| Fireworks AI | fireworks |
OpenAI | FIREWORKS_API_KEY |
❌ | ✅ | ✅ | ✅ |
| Cloudflare | cloudflare |
OpenAI | CLOUDFLARE_API_KEY |
✅ | ✅ | ✅ | ❌ |
| Perplexity | perplexity |
OpenAI | PERPLEXITY_API_KEY |
❌ | ✅ | ❌ | ✅ |
| Cohere | cohere |
OpenAI | COHERE_API_KEY |
✅ | ✅ | ✅ | ❌ |
OpenAI-compatible providers share the same protocol. Any provider using the
/v1/chat/completionsendpoint works automatically.
Tool / Function Calling
Define tools with JSON Schema parameters. SwapLM translates them to each provider's native format.
from swaplm import Tool, chat
tools = [
Tool(
function={
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
}
)
]
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
tools=tools,
tool_choice="auto",
)
if response.tool_calls:
for tc in response.tool_calls:
print(f"Calling: {tc.function.name}")
print(f"Args: {tc.function.arguments}")
Streaming
Sync Streaming
from swaplm import chat
stream = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Tell me a story"}],
stream=True,
)
for chunk in stream:
print(chunk.content, end="", flush=True)
# Full text available after iteration
print("\n\nFull:", stream.text)
Async Streaming
import asyncio
from swaplm import achat
async def main():
stream = await achat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Tell me a story"}],
stream=True,
)
async for chunk in stream:
print(chunk.content, end="", flush=True)
asyncio.run(main())
Configuration
Set global defaults that apply to all requests:
from swaplm import configure, get_config, reset_config
configure(
timeout=30.0, # Request timeout in seconds
retries=3, # Retry on 5xx/429/timeout
retry_delay=0.5, # Initial retry delay (exponential backoff)
max_tokens=1024, # Default max tokens for all requests
temperature=0.7, # Default temperature
top_p=0.9, # Default top_p
debug=True, # Enable debug logging (redacts secrets)
logging=True, # Enable info-level logging
)
# Read current config
cfg = get_config()
print(cfg.timeout, cfg.retries)
# Reset to defaults
reset_config()
| Config Field | Type | Default | Description |
|---|---|---|---|
timeout |
float | None |
None |
Global request timeout in seconds. |
retries |
int |
0 |
Global retry count for transient failures. |
retry_delay |
float |
0.5 |
Initial delay between retries (doubles each attempt). |
max_tokens |
int | None |
None |
Default max tokens if not specified per-request. |
temperature |
float | None |
None |
Default temperature if not specified per-request. |
top_p |
float | None |
None |
Default top_p if not specified per-request. |
debug |
bool |
False |
Enable debug logging with secret redaction. |
logging |
bool |
False |
Enable info-level request logging. |
transport |
BaseTransport | None |
None |
Custom transport implementation (see below). |
Custom Transport
Replace the HTTP layer for proxies, caching, mocks, or enterprise security:
from collections.abc import AsyncIterator, Iterator
from typing import Any
from swaplm import BaseTransport, chat, configure, reset_config
class MyTransport(BaseTransport):
def send(self, method, url, *, headers=None, json=None, timeout=None, retries=0):
print(f" intercepted: {url}")
return 200, {"choices": [{"message": {"role": "assistant", "content": "Intercepted!"}}]}
async def asend(self, method, url, *, headers=None, json=None, timeout=None, retries=0):
return self.send(method, url, headers=headers, json=json, timeout=timeout, retries=retries)
def send_stream(self, method, url, *, headers=None, json=None, timeout=None):
yield {"choices": [{"delta": {"content": "Stream intercepted"}}]}
async def asend_stream(self, method, url, *, headers=None, json=None, timeout=None):
yield {"choices": [{"delta": {"content": "Async stream intercepted"}}]}
configure(transport=MyTransport())
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.content) # => "Intercepted!"
reset_config()
BaseTransport Interface
class BaseTransport(ABC):
def send(self, method, url, *, headers, json, timeout, retries) -> tuple[int, dict]: ...
async def asend(self, method, url, *, headers, json, timeout, retries) -> tuple[int, dict]: ...
def send_stream(self, method, url, *, headers, json, timeout) -> Iterator[dict]: ...
async def asend_stream(self, method, url, *, headers, json, timeout) -> AsyncIterator[dict]: ...
def close(self) -> None: ...
async def aclose(self) -> None: ...
Middleware
Intercept and transform requests and responses in a pipeline:
from swaplm import (
BaseMiddleware,
ChatRequest,
ChatResponse,
add_middleware,
chat,
reset_middlewares,
)
class LoggingMiddleware(BaseMiddleware):
def process_request(self, request: ChatRequest) -> ChatRequest:
print(f"[REQ] {request.model} | {len(request.messages)} messages")
return request
def process_response(self, response: ChatResponse) -> ChatResponse:
print(f"[RES] {response.provider} | {response.usage.total_tokens} tokens")
return response
add_middleware(LoggingMiddleware())
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hello!"}],
)
# Prints:
# [REQ] groq/llama-3.3-70b-versatile | 1 messages
# [RES] groq | 42 tokens
reset_middlewares()
BaseMiddleware Methods
| Method | Description |
|---|---|
process_request(request) |
Modify request before execution (sync). |
aprocess_request(request) |
Modify request before execution (async). |
process_response(response) |
Modify response after execution (sync). |
aprocess_response(response) |
Modify response after execution (async). |
process_error(error) |
Intercept exceptions (sync). |
aprocess_error(error) |
Intercept exceptions (async). |
Pipeline Management
from swaplm import add_middleware, remove_middleware, get_middlewares, reset_middlewares
add_middleware(my_middleware) # Register
remove_middleware(my_middleware) # Unregister
get_middlewares() # List all registered
reset_middlewares() # Clear all
Lifecycle Hooks
Subscribe to SDK events for logging, metrics, or custom behavior:
from swaplm import ChatRequest, ChatResponse, chat, on, off, reset_hooks
def log_request(request: ChatRequest):
print(f"Sending to {request.model}")
def log_response(response: ChatResponse, request: ChatRequest):
print(f"Got {response.usage.total_tokens} tokens from {response.provider}")
on("before_request", log_request)
on("after_request", log_response)
response = chat(
provider="groq",
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hi"}],
)
off("before_request", log_request) # Unregister
reset_hooks() # Clear all
Available Events
| Event | Callback Signature | When |
|---|---|---|
before_request |
(request: ChatRequest) |
Before the HTTP request is sent. |
after_request |
(response: ChatResponse, request: ChatRequest) |
After a successful response. |
before_retry |
(url, status_or_exc, attempt) |
Before a retry attempt. |
after_retry |
(url, status_or_exc, attempt) |
After a retry attempt. |
before_stream |
(request: ChatRequest) |
Before streaming begins. |
after_stream |
(request: ChatRequest) |
After streaming ends. |
on_error |
(request: ChatRequest, error: SwapLMError) |
When an error occurs. |
Provider & Model Discovery
Query available providers and models programmatically:
import swaplm
# List all providers
for p in swaplm.providers():
print(f"{p.id}: {p.name} ({p.protocol})")
# Get a specific provider
groq = swaplm.provider("groq")
print(groq.base_url) # => "https://api.groq.com/openai/v1"
# List all models across all providers
all_models = swaplm.models()
for provider_info, model_info in all_models:
print(f"{provider_info.id}/{model_info.id}")
# Get a specific model's metadata
provider_info, model_info = swaplm.model("groq/llama-3.3-70b-versatile")
print(model_info.context_window) # => 128000
print(model_info.supports_tool_calling) # => True
Error Handling
SwapLM maps all provider-specific errors into a unified exception hierarchy:
from swaplm import chat
from swaplm.exceptions import (
SwapLMError, # Base — catch everything
ProviderError, # Generic provider error
AuthenticationError, # 401 / invalid API key
RateLimitError, # 429 / rate limited
TimeoutError, # Request timed out
InvalidModelError, # Model not found (404)
AmbiguousModelError, # Alias matches multiple providers
InvalidProviderError, # Unknown provider slug
ConfigurationError, # SDK configuration issue
ModelCapabilityError, # Unsupported operation for model
ToolCallError, # Tool/function calling error
)
try:
response = chat(
provider="groq",
model="nonexistent-model",
messages=[{"role": "user", "content": "Hi"}],
api_key="invalid",
)
except AuthenticationError as e:
print(f"Auth failed: {e}")
print(f"Provider: {e.provider}")
print(f"Status: {e.status_code}")
except InvalidModelError as e:
print(f"Model not found: {e.model}")
except SwapLMError as e:
print(f"SDK error: {e}")
Tool Types Reference
Message
from swaplm import Message
msg = Message(
role="user", # "system" | "user" | "assistant" | "tool"
content="Hello!", # str | list[dict] | None
name="optional_name", # str | None
tool_calls=[...], # list[ToolCall] | None
tool_call_id="call_abc", # str | None (for role="tool")
)
Tool
from swaplm import Tool
tool = Tool(
function={
"name": "my_function",
"description": "Does something useful",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
},
"required": ["query"],
},
}
)
ChatRequest
from swaplm import ChatRequest, Message
request = ChatRequest(
model="llama-3.3-70b-versatile",
messages=[Message(role="user", content="Hi")],
provider="groq", # explicit provider (recommended)
stream=False,
max_tokens=1024,
temperature=0.7,
top_p=0.9,
stop=["\n\n"],
seed=42,
response_format={"type": "json_object"},
tools=[...],
tool_choice="auto",
api_key="optional-explicit-key",
base_url="https://custom-endpoint.com/v1",
timeout=30.0,
retries=3,
extra_headers={"X-Custom": "value"},
provider_options={"thinking": {"type": "enabled"}},
)
# Computed properties
request.provider_id # => "groq"
request.model_id # => "llama-3.3-70b-versatile"
ModelInfo
from swaplm import model
provider_info, model_info = swaplm.model("groq/llama-3.3-70b-versatile")
model_info.id # => "llama-3.3-70b-versatile"
model_info.display_name # => "LLaMA 3.3 70B"
model_info.context_window # => 128000
model_info.max_tokens # => 32768
model_info.supports_streaming # => True
model_info.supports_tool_calling # => True
model_info.supports_json_mode # => True
model_info.supports_thinking # => True
model_info.supports_seed # => False
model_info.supports_vision # => False
model_info.default_temperature # => None
model_info.default_max_tokens # => None
Architecture
chat() / achat()
│
├─ Middleware Pipeline (request)
│
├─ Router ─────────── Resolves "provider/model" string
│ │
│ ├─ ProviderRegistry ── Auto-discovers providers
│ └─ Provider ────────── Metadata + models.json
│
├─ AuthManager ────── Resolves API key (explicit → env var)
│
├─ Protocol ───────── Translates to provider-native format
│ │
│ ├─ OpenAIProtocol ─── OpenAI, Groq, NVIDIA, etc.
│ ├─ AnthropicProtocol Anthropic Claude
│ └─ GoogleProtocol ─── Google Gemini
│
├─ Transport ──────── Executes HTTP (httpx / custom)
│
├─ Protocol ───────── Parses response back to unified format
│
├─ Middleware Pipeline (response)
│
└─ ChatResponse / StreamResponse
Examples
| Example | Description |
|---|---|
01_basic_chat.py |
Synchronous chat with Groq |
02_async_chat.py |
Async chat with achat() |
03_streaming.py |
Real-time token streaming |
04_tool_calling.py |
Function / tool calling |
05_structured_output.py |
JSON structured output |
06_multi_provider.py |
Switching between providers |
07_middleware.py |
Request/response middleware |
08_hooks.py |
Lifecycle event hooks |
09_custom_transport.py |
Custom transport injection |
10_discovery.py |
Provider & model discovery |
Roadmap
| Phase | Milestone | Status |
|---|---|---|
| 1 | Project foundation & repository structure | ✅ |
| 2 | Provider architecture & protocol system | ✅ |
| 3 | First production provider (Groq) & HTTP execution | ✅ |
| 4 | Core SDK infrastructure (routing, discovery, config) | ✅ |
| 5 | Multi-protocol validation (Anthropic & Google Gemini) | ✅ |
| 6 | SDK runtime & developer experience (async, retries, middleware, hooks) | ✅ |
| 7 | OpenAI-compatible provider expansion (16 providers) | ✅ |
| 8 | Enterprise resilience (fallbacks, circuit breakers) | 🔜 |
| 9 | Documentation site & v1.0 release | Planned |
Contributing
git clone https://github.com/krishcodes07/swaplm.git
cd swaplm
pip install -e ".[dev]"
# Lint & format
ruff check .
ruff format .
# Run tests
pytest
License
SwapLM is released under the MIT License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swaplm-0.3.0.tar.gz.
File metadata
- Download URL: swaplm-0.3.0.tar.gz
- Upload date:
- Size: 80.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78596c105e3fd5eba790769d7cf1aca8a3a4e7426710817ac72e414cc2dd399e
|
|
| MD5 |
23ab78e20cf741a2bf36819d2c9458a5
|
|
| BLAKE2b-256 |
705ec582675f4ca6a67d15b09fe5976582e980ff96d5cbff2e3eed468252e7c4
|
File details
Details for the file swaplm-0.3.0-py3-none-any.whl.
File metadata
- Download URL: swaplm-0.3.0-py3-none-any.whl
- Upload date:
- Size: 79.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cb96ac832a1da6b07e83e3bbaa68f498d3f2f6a7fdd6185a16fdc904fd252ee
|
|
| MD5 |
8150965be2a8fa17a4947c90590c4562
|
|
| BLAKE2b-256 |
c18649a1a4b6672b05f909bcdd5048349a82ee221f174e4863aceb1eb6371f4b
|