Skip to main content

Python bindings for Enki's Rust agent runtime.

Project description

enki-py

Python bindings for Enki's Rust agent runtime.

enki-py exposes two layers:

  • A high-level Python API built around Agent and MultiAgentRuntime
  • The lower-level native bindings generated from the Rust runtime, including workflow APIs

The package is designed for Python-first usage without giving up the native runtime features that already exist in the Rust core.

Requirements

  • Python >=3.8
  • A supported model string such as openai::gpt-4o, anthropic::claude-sonnet-4-6, or ollama::qwen3.5:latest

Install

Published package:

pip install enki-py

With the built-in LiteLLM adapter:

pip install "enki-py[litellm]"

Using uv:

uv add enki-py
uv add "enki-py[litellm]"

What It Exports

High-level Python API

The main Python-facing exports are:

  • Agent
  • AgentRunResult
  • ExecutionStep
  • Tool
  • RunContext
  • MemoryBackend
  • MemoryModule
  • MemoryEntry
  • MemoryKind
  • MultiAgentRuntime
  • MultiAgentMember
  • AgentCard
  • LiteLlmProvider
  • LlmProviderBackend

Low-level native bindings

The generated module is also re-exported, so lower-level runtime types remain available when you want to work closer to the Rust layer. Commonly used native types include:

  • EnkiAgent
  • EnkiWorkflowRuntime
  • EnkiTool and EnkiToolSpec
  • EnkiMemoryEntry
  • EnkiMemoryModule

Agent API

Agent is the main Python entrypoint. Its constructor supports:

  • model: required provider and model string
  • deps_type: optional dependency type used with RunContext
  • instructions: system guidance / prompt preamble
  • agentic_loop: optional custom loop instructions
  • name: display name for the agent
  • max_iterations: iteration cap for the runtime loop
  • workspace_home: optional workspace root for persisted runtime state
  • tools: optional list of pre-registered Tool objects
  • memories: optional list of MemoryModule objects
  • llm: optional Python-side LLM backend or callback

Main methods:

  • run(...): async single-agent execution
  • run_sync(...): sync wrapper around run(...)
  • tool_plain(...): decorator for plain Python tools
  • tool(...): decorator for tools that receive RunContext
  • register_tool(...)
  • register_memory(...)
  • as_workflow_agent(...): converts the wrapper into a workflow-configured low-level agent

The result of run(...) or run_sync(...) is AgentRunResult, which contains:

  • output: final model response text
  • steps: ExecutionStep entries describing runtime progress

Both run(...) and run_sync(...) accept:

  • session_id: optional explicit session id
  • deps: optional dependency object passed into RunContext
  • on_step: optional callback for streaming step visibility

Basic Agent

Synchronous example:

import uuid

from enki_py import Agent


agent = Agent(
    "anthropic::claude-sonnet-4-6",
    name="Simple Agent",
    instructions="Answer clearly and keep responses short.",
)

result = agent.run_sync(
    "Explain what this Enki Python example demonstrates.",
    session_id=f"simple-agent-{uuid.uuid4()}",
)

print(result.output)
for step in result.steps:
    print(f"{step.index}. [{step.phase}] {step.kind}: {step.detail}")

Async version:

import asyncio

from enki_py import Agent


async def main() -> None:
    agent = Agent(
        "ollama::qwen3.5:latest",
        name="Async Agent",
        instructions="Answer clearly and keep responses short.",
    )

    result = await agent.run("What does enki-py provide?")
    print(result.output)


asyncio.run(main())

Tools

The high-level wrapper lets you register Python functions directly as tools.

@agent.tool_plain

Use this when the tool only needs its declared arguments:

from enki_py import Agent


agent = Agent(
    "ollama::qwen3.5:latest",
    name="Researcher",
    instructions="Use tools when they help answer factual questions.",
)


@agent.tool_plain
def lookup_example_topics(topic: str) -> str:
    """Return a canned fact for an example topic."""
    facts = {
        "memory": "Memory lets the agent persist and recall useful session context.",
        "tools": "Tools let the agent call Python functions to fetch or compute structured results.",
        "multi-agent": "Multi-agent runtimes let a coordinator route work to specialized agents.",
    }
    return facts.get(topic.lower(), f"No prepared fact exists for '{topic}'.")

@agent.tool

Use this when the tool should receive a RunContext with typed dependencies:

from dataclasses import dataclass

from enki_py import Agent, RunContext


@dataclass
class AppDeps:
    project_name: str


agent = Agent(
    "ollama::qwen3.5:latest",
    deps_type=AppDeps,
    name="Context Agent",
    instructions="Use the provided context when it helps answer.",
)


@agent.tool
def project_name(ctx: RunContext[AppDeps]) -> str:
    """Return the active project name from runtime dependencies."""
    return ctx.deps.project_name

Tool schemas are inferred from Python signatures and type annotations. Required parameters come from arguments without defaults, and optional parameters come from arguments with defaults.

If you need lower-level control, you can also build Tool(...) values directly and register them with register_tool(...).

Memory

enki-py supports Python-defined memory modules through MemoryBackend and MemoryModule.

Implement MemoryBackend when you want custom record and recall behavior:

from dataclasses import dataclass, field
from time import time_ns

from enki_py import MemoryBackend, MemoryEntry, MemoryKind


@dataclass
class SessionMemory(MemoryBackend):
    name: str = "session_memory"
    _entries: dict[str, list[MemoryEntry]] = field(default_factory=dict)

    def record(self, session_id: str, user_msg: str, assistant_msg: str) -> None:
        entries = self._entries.setdefault(session_id, [])
        entries.append(
            MemoryEntry(
                key=f"{session_id}-{len(entries)}",
                content=f"User: {user_msg}\nAssistant: {assistant_msg}",
                kind=MemoryKind.RECENT_MESSAGE,
                relevance=1.0,
                timestamp_ns=time_ns(),
            )
        )

    def recall(self, session_id: str, query: str, max_entries: int) -> list[MemoryEntry]:
        entries = self._entries.get(session_id, [])
        return entries[-max_entries:]

    def flush(self, session_id: str) -> None:
        return None

Register it like this:

memory = SessionMemory()
agent = Agent(
    "ollama::qwen3.5:latest",
    memories=[memory.as_memory_module()],
)

Supported memory kinds:

  • MemoryKind.RECENT_MESSAGE
  • MemoryKind.SUMMARY
  • MemoryKind.ENTITY
  • MemoryKind.PREFERENCE

Memory callbacks may be synchronous or async def coroutines.

Multi-Agent Runtime

MultiAgentRuntime wires multiple Agent instances together and installs the coordination tools used for agent discovery and delegation.

Available methods:

  • registry()
  • discover(...)
  • process(...)
  • process_sync(...)

The runtime automatically installs two reserved tools on member agents:

  • discover_agents
  • delegate_task

Minimal example:

from enki_py import Agent, MultiAgentMember, MultiAgentRuntime


coordinator = Agent(
    "ollama::qwen3.5:latest",
    name="Coordinator",
    instructions=(
        "Use discover_agents first. "
        "Delegate research work to the researcher with delegate_task."
    ),
)

researcher = Agent(
    "ollama::qwen3.5:latest",
    name="Researcher",
    instructions="Answer delegated questions clearly and briefly.",
)

runtime = MultiAgentRuntime(
    [
        MultiAgentMember(
            agent_id="coordinator",
            agent=coordinator,
            capabilities=["planning", "orchestration"],
        ),
        MultiAgentMember(
            agent_id="researcher",
            agent=researcher,
            capabilities=["research"],
        ),
    ]
)

result = runtime.process_sync(
    "coordinator",
    "Use discover_agents first, then delegate to the researcher.",
    session_id="simple-multi-agent-example",
)

print(result.output)

Repository examples:

Workflow Runtime

For workflow execution, the lower-level EnkiWorkflowRuntime is the main entrypoint. A common pattern is:

  1. Create Python Agent wrappers
  2. Convert them with as_workflow_agent(agent_id=..., capabilities=[...])
  3. Provide JSON task definitions and JSON workflow definitions
  4. Start and inspect runs through the workflow runtime

Methods used in the checked-in Python examples include:

  • list_workflows_json()
  • list_runs_json()
  • inspect_json(run_id)
  • start_json(payload_json)
  • resume_json(run_id)
  • submit_intervention_json(run_id, intervention_id, response)

Workflow example:

import asyncio
import json
from pathlib import Path

import enki_py


WORKSPACE_HOME = Path("./example/enki-py/.enki-workflow")
MODEL = "ollama::qwen3.5:latest"


async def main() -> None:
    researcher = enki_py.Agent(
        MODEL,
        name="Researcher",
        instructions="Return short factual notes.",
        workspace_home=str(WORKSPACE_HOME),
    )
    writer = enki_py.Agent(
        MODEL,
        name="Writer",
        instructions="Turn notes into a concise summary.",
        workspace_home=str(WORKSPACE_HOME),
    )

    runtime = enki_py.EnkiWorkflowRuntime(
        agents=[
            researcher.as_workflow_agent(agent_id="researcher", capabilities=["research"]),
            writer.as_workflow_agent(agent_id="writer", capabilities=["writing"]),
        ],
        tasks_json=[],
        workflows_json=[],
        workspace_home=str(WORKSPACE_HOME),
    )

    print(json.loads(await runtime.list_workflows_json()))


asyncio.run(main())

For a complete runnable version with task and edge definitions, see example/enki-py/agent_workflow.py.

Human Intervention

Workflow runs can pause for human input and then resume from persisted state.

Two built-in patterns are demonstrated in the repository examples:

  • human_gate workflow nodes that pause and wait for a human response
  • task nodes with failure_policy: "pause_for_intervention" that convert a failure into a human resolution step

The runtime interaction loop is:

  1. start_json(...) starts the workflow and may return a paused response
  2. inspect_json(run_id) exposes pending_interventions
  3. submit_intervention_json(run_id, intervention_id, response) resolves the intervention
  4. resume_json(run_id) continues the persisted run

See example/enki-py/human_intervention_workflow.py.

Custom LLM Providers

You can keep model execution on the Python side by passing either:

  • an instance of LlmProviderBackend
  • a compatible Python callable matching the LLM completion shape

The built-in LiteLlmProvider is the default adapter for LiteLLM-backed usage. It is useful when you want:

  • provider-specific configuration in Python
  • LiteLLM-managed credentials or routing
  • a Python extension point without changing Rust code

Notes from the current implementation:

  • LiteLlmProvider raises a clear error if litellm is not installed
  • for Ollama, the default base URL comes from OLLAMA_URL or falls back to http://127.0.0.1:11434
  • ENKI_LITELLM_TIMEOUT controls the request timeout used by the adapter
  • Ollama tool calling is only forwarded when ENKI_OLLAMA_TOOLS is truthy

The repository workflow example also includes a custom OllamaProvider implementation in example/enki-py/agent_workflow.py.

Custom Agentic Loop

You can override the default loop instructions with agentic_loop=:

from enki_py import Agent


agent = Agent(
    "anthropic::claude-sonnet-4-6",
    name="Custom Agentic Loop",
    instructions="Answer clearly and keep responses short.",
    agentic_loop=(
        "1. Understand the user request.\n"
        "2. Decide whether a tool is necessary before acting.\n"
        "3. If you use a tool, summarize what you learned.\n"
        "4. Verify that the answer is complete.\n"
        "5. Return the final response."
    ),
)

See example/enki-py/custom_agentic_loop.py.

Running The Examples

After building the local package with maturin develop, you can run the checked-in examples from the repository root:

python example\enki-py\simple_agent.py
python example\enki-py\simple_agent_ollama.py
python example\enki-py\simple_multi_agent.py
python example\enki-py\multi_agent_with_memory_and_tools.py
python example\enki-py\agent_workflow.py
python example\enki-py\human_intervention_workflow.py

Model notes:

  • simple_agent.py uses anthropic::claude-sonnet-4-6
  • simple_agent_ollama.py uses ollama::qwen3.5
  • several other checked-in examples use ollama::qwen3.5:latest
  • agent_workflow.py reads ENKI_MODEL and falls back to ollama::qwen3.5:latest

Make sure the selected provider and model are available in your local environment before running the examples.

Development

From crates/bindings/enki-py:

pip install maturin
maturin develop
pytest python/tests

Useful commands:

  • maturin develop: build the Rust extension and install the package in editable form
  • pytest python/tests: run the Python-side tests and examples under test coverage

Project Layout

crates/bindings/enki-py/
|-- Cargo.toml
|-- pyproject.toml
|-- python/enki_py/
|-- python/tests/
`-- src/
  • src/: Rust UniFFI binding implementation
  • python/enki_py/: Python wrapper and generated native module
  • python/tests/: Python-side tests and usage coverage

Related Docs

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

enki_py-0.5.40.tar.gz (421.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

enki_py-0.5.40-py3-none-win_arm64.whl (1.5 MB view details)

Uploaded Python 3Windows ARM64

enki_py-0.5.40-py3-none-win_amd64.whl (1.5 MB view details)

Uploaded Python 3Windows x86-64

enki_py-0.5.40-py3-none-win32.whl (1.4 MB view details)

Uploaded Python 3Windows x86

enki_py-0.5.40-py3-none-musllinux_1_2_x86_64.whl (1.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

enki_py-0.5.40-py3-none-musllinux_1_2_i686.whl (1.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ i686

enki_py-0.5.40-py3-none-musllinux_1_2_armv7l.whl (1.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

enki_py-0.5.40-py3-none-musllinux_1_2_aarch64.whl (1.6 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

enki_py-0.5.40-py3-none-manylinux_2_28_x86_64.whl (1.5 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

enki_py-0.5.40-py3-none-manylinux_2_28_s390x.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ s390x

enki_py-0.5.40-py3-none-manylinux_2_28_ppc64le.whl (1.5 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ppc64le

enki_py-0.5.40-py3-none-manylinux_2_28_i686.whl (1.5 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ i686

enki_py-0.5.40-py3-none-manylinux_2_28_armv7l.whl (1.4 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARMv7l

enki_py-0.5.40-py3-none-manylinux_2_28_aarch64.whl (1.4 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

enki_py-0.5.40-py3-none-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

enki_py-0.5.40-py3-none-macosx_10_12_x86_64.whl (1.4 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file enki_py-0.5.40.tar.gz.

File metadata

  • Download URL: enki_py-0.5.40.tar.gz
  • Upload date:
  • Size: 421.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for enki_py-0.5.40.tar.gz
Algorithm Hash digest
SHA256 722e03cc3e7eba2a490acb5b3b83bbcf6f668f491e70d3b9936bba582bf1fda7
MD5 d6c84a9041f23d58a2d1329889173386
BLAKE2b-256 980900fa3caeec61fb27d3247562e23358e0562ff8a09c7d89ef75c09c5e6d2b

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-win_arm64.whl.

File metadata

  • Download URL: enki_py-0.5.40-py3-none-win_arm64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for enki_py-0.5.40-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 fc5267cafd74e5db3bbbd9f98532ad1f26f29a58aab355e6fc87702178ee97aa
MD5 504c423752e1ce21dddefe0fc6735c09
BLAKE2b-256 4f7e91333c1a15ce08840229078f796a476a86535b1147ca406c6b268ac0c024

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-win_amd64.whl.

File metadata

  • Download URL: enki_py-0.5.40-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for enki_py-0.5.40-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 e5cd13176409eb1bd65b7988441981ed014dc5508472cdc5761f93d2ccf4cdeb
MD5 0f8270da4fe977f30e6bd5b144628a4f
BLAKE2b-256 c1a4e61d806846457a9e7db86cb360baaf2dde12e9e8208b7bdb27e5f38292bd

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-win32.whl.

File metadata

  • Download URL: enki_py-0.5.40-py3-none-win32.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for enki_py-0.5.40-py3-none-win32.whl
Algorithm Hash digest
SHA256 ae00e4abe1db0939cb201f5249cd16552a550cfef205ff750d00f4956e32f45a
MD5 d63d044e71db94c7432e6a6e7a174d47
BLAKE2b-256 5782d10ee122be25fd0c9057c5d6185004a1c9052460acf16ccdb2194aa78764

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 647a3b6df678c1259516ba03cbcd35c10300ca04e9c797a2e55a07524cc19e1c
MD5 a917a639798f61d35cbd7b66572c6325
BLAKE2b-256 87600d7e8e549e2a31715a10f16e12363e2690ed9b0afaa8df8c671e91d2c02f

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 5de0d7b6ed3fb466275f57716062b529d930cd132b256eefc9b29f7c02b67cb3
MD5 bc56ec5130c61950277440572169669e
BLAKE2b-256 042680c28fa05dff43b9e6094c50f526094a94e266da87c5a2609284187291c5

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 2740cdebc3741e15af9ef8e1986746f9d2b135ee03d2b88f2c323e1370df6bce
MD5 9edb24bc790650b02a6f2164df4cbf60
BLAKE2b-256 27c6e7da2ea276270d95139b21398107fe72757182900361dcbe4152cf587144

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 f0e441eea945b07a7200b687e38838338d492f5ff6ae7918e3343afcc95e94f6
MD5 002011310b8cd330603e7920a69f406c
BLAKE2b-256 f457c09736fc340b9fe16e893936e9178330d823fa8a6d972023b6361185add4

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 aef1e30cb703c720e3be4dd86e29640fe44db62b80c00feeef276e1a19596337
MD5 6fc27ded09a0434e035085bc56da6ca6
BLAKE2b-256 96f43dac9c22e694825f262f058cbcdc0ae1267109fd4d55bca274983cd1dd04

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 323f738db505c2a33153fdb6fcb65d230d58b38cd7728e34af8bb7185fc3d21c
MD5 a43a8ce625871909ed3a5f0af87bd2b1
BLAKE2b-256 c6aebb1488fad9b459cd1357af4acc7d72f8dad7f31149b0070a6eab8baa3330

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 a5a2412abc4f4145e0676b0aefe3ec4ac757a092e8590fb132f8ceb0308c10eb
MD5 4ae642e1ad3a7712f794a3452248ab09
BLAKE2b-256 591cfdbbcf313a245d7244bec01e142a118a106d0dbb988ee1f8f0b5cf621052

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_i686.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_i686.whl
Algorithm Hash digest
SHA256 371bd4e9b6c424b310e47b2a97cdda9f313dd523fc5eade642eaf6e1f6fc211c
MD5 f62d09a41cb23d691410aebfc9cfe2a1
BLAKE2b-256 3fe8164e2a1f5b516f21b84069535bb101293e9f62a5a477ea4bdc403f376284

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_armv7l.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_armv7l.whl
Algorithm Hash digest
SHA256 313ed8ed1f59a39305a3a309c0b3e39b0974c1488a2c6610bde4249f6ce91c44
MD5 3f659a2ae4b3e61b04d7ed4bfd9689f6
BLAKE2b-256 c730c06be33f2d79a7cec0fe3ff69669606896e393ea15359e1c6f42314ab7f4

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 cb3b1eecf17938d99576448e8f075116528fe858ccb5367f8dc049677d8896eb
MD5 668a992627d1825024298e810ec1b513
BLAKE2b-256 9173fdc2bcc5f5eb727ed44ae6a7a3b7b8d54ed93385a23903b7eb39c7390db0

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e28fe9012bfbc211d3594673d3e17fff679e61df3dac421d2f53da8050f3fe2f
MD5 4065833c3bb70e3d98501eb27cf81696
BLAKE2b-256 9553647256dfc2953343c4c90e94b84894f8d7511b021238440cb772c85db6d0

See more details on using hashes here.

File details

Details for the file enki_py-0.5.40-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for enki_py-0.5.40-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2c4df7928f1269e69bf67d07d17720d72fa3072e7bb4752c5be0abe1121aa94b
MD5 9344abfce32910e606d74d46de543c3b
BLAKE2b-256 0aa7fb732cd2c0ca9ea1e513b5313b65b0a568bd4ded88bebfa70af67687c130

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page