bittensor 6.7.2__tar.gz → 6.7.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bittensor-6.7.2/bittensor.egg-info → bittensor-6.7.3}/PKG-INFO +2 -45
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/__init__.py +1 -1
- {bittensor-6.7.2 → bittensor-6.7.3/bittensor.egg-info}/PKG-INFO +2 -45
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor.egg-info/SOURCES.txt +0 -5
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor.egg-info/requires.txt +3 -3
- bittensor-6.7.2/bittensor/subnets/api.py +0 -297
- bittensor-6.7.2/bittensor/subnets/cache.py +0 -230
- bittensor-6.7.2/bittensor/subnets/registry.py +0 -53
- bittensor-6.7.2/bittensor/subnets/request.py +0 -143
- bittensor-6.7.2/bittensor/subnets/utils.py +0 -68
- {bittensor-6.7.2 → bittensor-6.7.3}/LICENSE +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/README.md +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bin/btcli +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/axon.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/btlogging.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/chain_data.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/cli.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/delegates.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/identity.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/inspect.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/list.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/metagraph.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/misc.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/network.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/overview.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/register.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/root.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/senate.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/stake.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/transfer.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/unstake.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/utils.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/commands/wallets.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/config.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/dendrite.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/errors.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/delegation.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/network.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/prometheus.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/registration.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/root.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/senate.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/serving.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/set_weights.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/staking.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/transfer.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/extrinsics/unstaking.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/keyfile.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/metagraph.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/mock/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/mock/keyfile_mock.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/mock/subtensor_mock.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/mock/wallet_mock.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/stream.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/subtensor.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/synapse.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/tensor.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/threadpool.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/types.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/_register_cuda.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/balance.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/formatting.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/networking.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/registration.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/test_utils.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/wallet_utils.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/utils/weight_utils.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor/wallet.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor.egg-info/dependency_links.txt +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/bittensor.egg-info/top_level.txt +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/setup.cfg +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/setup.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/helpers/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/helpers/helpers.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/integration_tests/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/integration_tests/test_cli.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/integration_tests/test_cli_no_network.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/integration_tests/test_metagraph_integration.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/integration_tests/test_subtensor_integration.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_axon.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_chain_data.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_dendrite.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_keyfile.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_metagraph.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_subtensor.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_synapse.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_tensor.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/test_wallet.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/utils/__init__.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/utils/test_balance.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/utils/test_networking.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/utils/test_utils.py +0 -0
- {bittensor-6.7.2 → bittensor-6.7.3}/tests/unit_tests/utils/test_weight_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bittensor
|
|
3
|
-
Version: 6.7.
|
|
3
|
+
Version: 6.7.3
|
|
4
4
|
Summary: bittensor
|
|
5
5
|
Home-page: https://github.com/opentensor/bittensor
|
|
6
6
|
Author: bittensor.com
|
|
@@ -22,51 +22,8 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Python: >=3.8
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: aiohttp==3.9.0b0
|
|
27
|
-
Requires-Dist: ansible==6.7.0
|
|
28
|
-
Requires-Dist: ansible_vault==2.1.0
|
|
29
|
-
Requires-Dist: backoff
|
|
30
|
-
Requires-Dist: black==23.7.0
|
|
31
|
-
Requires-Dist: cryptography==41.0.3
|
|
32
|
-
Requires-Dist: ddt==1.6.0
|
|
33
|
-
Requires-Dist: fuzzywuzzy>=0.18.0
|
|
34
|
-
Requires-Dist: fastapi==0.99.1
|
|
35
|
-
Requires-Dist: loguru==0.7.0
|
|
36
|
-
Requires-Dist: munch==2.5.0
|
|
37
|
-
Requires-Dist: netaddr
|
|
38
|
-
Requires-Dist: numpy
|
|
39
|
-
Requires-Dist: msgpack-numpy-opentensor==0.5.0
|
|
40
|
-
Requires-Dist: nest_asyncio
|
|
41
|
-
Requires-Dist: pycryptodome<4.0.0,>=3.18.0
|
|
42
|
-
Requires-Dist: pyyaml
|
|
43
|
-
Requires-Dist: password_strength
|
|
44
|
-
Requires-Dist: pydantic!=1.8,!=1.8.1,<2.0.0,>=1.7.4
|
|
45
|
-
Requires-Dist: PyNaCl<=1.5.0,>=1.3.0
|
|
46
|
-
Requires-Dist: pytest-asyncio
|
|
47
|
-
Requires-Dist: python-Levenshtein
|
|
48
|
-
Requires-Dist: pytest
|
|
49
|
-
Requires-Dist: retry
|
|
50
|
-
Requires-Dist: requests
|
|
51
|
-
Requires-Dist: rich
|
|
52
|
-
Requires-Dist: scalecodec==1.2.7
|
|
53
|
-
Requires-Dist: shtab==1.6.5
|
|
54
|
-
Requires-Dist: substrate-interface==1.7.5
|
|
55
|
-
Requires-Dist: termcolor
|
|
56
|
-
Requires-Dist: torch>=1.13.1
|
|
57
|
-
Requires-Dist: tqdm
|
|
58
|
-
Requires-Dist: uvicorn==0.22.0
|
|
59
|
-
Requires-Dist: wheel
|
|
60
25
|
Provides-Extra: dev
|
|
61
|
-
|
|
62
|
-
Requires-Dist: pytest-mock==3.12.0; extra == "dev"
|
|
63
|
-
Requires-Dist: pytest-split==0.8.0; extra == "dev"
|
|
64
|
-
Requires-Dist: pytest-xdist==3.0.2; extra == "dev"
|
|
65
|
-
Requires-Dist: pytest-rerunfailures==10.2; extra == "dev"
|
|
66
|
-
Requires-Dist: coveralls==3.3.1; extra == "dev"
|
|
67
|
-
Requires-Dist: pytest-cov==4.0.0; extra == "dev"
|
|
68
|
-
Requires-Dist: ddt==1.6.0; extra == "dev"
|
|
69
|
-
Requires-Dist: hypothesis==6.81.1; extra == "dev"
|
|
26
|
+
License-File: LICENSE
|
|
70
27
|
|
|
71
28
|
<div align="center">
|
|
72
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bittensor
|
|
3
|
-
Version: 6.7.
|
|
3
|
+
Version: 6.7.3
|
|
4
4
|
Summary: bittensor
|
|
5
5
|
Home-page: https://github.com/opentensor/bittensor
|
|
6
6
|
Author: bittensor.com
|
|
@@ -22,51 +22,8 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Python: >=3.8
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: aiohttp==3.9.0b0
|
|
27
|
-
Requires-Dist: ansible==6.7.0
|
|
28
|
-
Requires-Dist: ansible_vault==2.1.0
|
|
29
|
-
Requires-Dist: backoff
|
|
30
|
-
Requires-Dist: black==23.7.0
|
|
31
|
-
Requires-Dist: cryptography==41.0.3
|
|
32
|
-
Requires-Dist: ddt==1.6.0
|
|
33
|
-
Requires-Dist: fuzzywuzzy>=0.18.0
|
|
34
|
-
Requires-Dist: fastapi==0.99.1
|
|
35
|
-
Requires-Dist: loguru==0.7.0
|
|
36
|
-
Requires-Dist: munch==2.5.0
|
|
37
|
-
Requires-Dist: netaddr
|
|
38
|
-
Requires-Dist: numpy
|
|
39
|
-
Requires-Dist: msgpack-numpy-opentensor==0.5.0
|
|
40
|
-
Requires-Dist: nest_asyncio
|
|
41
|
-
Requires-Dist: pycryptodome<4.0.0,>=3.18.0
|
|
42
|
-
Requires-Dist: pyyaml
|
|
43
|
-
Requires-Dist: password_strength
|
|
44
|
-
Requires-Dist: pydantic!=1.8,!=1.8.1,<2.0.0,>=1.7.4
|
|
45
|
-
Requires-Dist: PyNaCl<=1.5.0,>=1.3.0
|
|
46
|
-
Requires-Dist: pytest-asyncio
|
|
47
|
-
Requires-Dist: python-Levenshtein
|
|
48
|
-
Requires-Dist: pytest
|
|
49
|
-
Requires-Dist: retry
|
|
50
|
-
Requires-Dist: requests
|
|
51
|
-
Requires-Dist: rich
|
|
52
|
-
Requires-Dist: scalecodec==1.2.7
|
|
53
|
-
Requires-Dist: shtab==1.6.5
|
|
54
|
-
Requires-Dist: substrate-interface==1.7.5
|
|
55
|
-
Requires-Dist: termcolor
|
|
56
|
-
Requires-Dist: torch>=1.13.1
|
|
57
|
-
Requires-Dist: tqdm
|
|
58
|
-
Requires-Dist: uvicorn==0.22.0
|
|
59
|
-
Requires-Dist: wheel
|
|
60
25
|
Provides-Extra: dev
|
|
61
|
-
|
|
62
|
-
Requires-Dist: pytest-mock==3.12.0; extra == "dev"
|
|
63
|
-
Requires-Dist: pytest-split==0.8.0; extra == "dev"
|
|
64
|
-
Requires-Dist: pytest-xdist==3.0.2; extra == "dev"
|
|
65
|
-
Requires-Dist: pytest-rerunfailures==10.2; extra == "dev"
|
|
66
|
-
Requires-Dist: coveralls==3.3.1; extra == "dev"
|
|
67
|
-
Requires-Dist: pytest-cov==4.0.0; extra == "dev"
|
|
68
|
-
Requires-Dist: ddt==1.6.0; extra == "dev"
|
|
69
|
-
Requires-Dist: hypothesis==6.81.1; extra == "dev"
|
|
26
|
+
License-File: LICENSE
|
|
70
27
|
|
|
71
28
|
<div align="center">
|
|
72
29
|
|
|
@@ -57,11 +57,6 @@ bittensor/mock/__init__.py
|
|
|
57
57
|
bittensor/mock/keyfile_mock.py
|
|
58
58
|
bittensor/mock/subtensor_mock.py
|
|
59
59
|
bittensor/mock/wallet_mock.py
|
|
60
|
-
bittensor/subnets/api.py
|
|
61
|
-
bittensor/subnets/cache.py
|
|
62
|
-
bittensor/subnets/registry.py
|
|
63
|
-
bittensor/subnets/request.py
|
|
64
|
-
bittensor/subnets/utils.py
|
|
65
60
|
bittensor/utils/__init__.py
|
|
66
61
|
bittensor/utils/_register_cuda.py
|
|
67
62
|
bittensor/utils/balance.py
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
# The MIT License (MIT)
|
|
2
|
-
# Copyright © 2021 Yuma Rao
|
|
3
|
-
# Copyright © 2023 Opentensor Foundation
|
|
4
|
-
# Copyright © 2023 Opentensor Technologies Inc
|
|
5
|
-
|
|
6
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
7
|
-
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
|
|
8
|
-
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
9
|
-
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
-
|
|
11
|
-
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
|
12
|
-
# the Software.
|
|
13
|
-
|
|
14
|
-
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
15
|
-
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
16
|
-
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
17
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
18
|
-
# DEALINGS IN THE SOFTWARE.
|
|
19
|
-
|
|
20
|
-
import time
|
|
21
|
-
import redis
|
|
22
|
-
import random
|
|
23
|
-
import bittensor as bt
|
|
24
|
-
|
|
25
|
-
from abc import ABC, abstractmethod
|
|
26
|
-
from typing import Any, List, Union, Optional, Tuple, Dict
|
|
27
|
-
from .utils import serialize_metagraph, deserialize_metagraph
|
|
28
|
-
from .registry import APIRegistry, register_handler
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class SubnetsAPI(ABC):
|
|
32
|
-
def __init__(
|
|
33
|
-
self, wallet: "bt.wallet", network: str, netuids: List[int] = None, interval=60
|
|
34
|
-
):
|
|
35
|
-
self.wallet = wallet
|
|
36
|
-
self.dendrite = bittensor.dendrite(wallet=wallet)
|
|
37
|
-
self.subtensor = bittensor.subtensor(network)
|
|
38
|
-
|
|
39
|
-
host = getenv("REDIS_HOST") or "localhost"
|
|
40
|
-
port = getenv("REDIS_PORT") or 6379
|
|
41
|
-
db = getenv("REDIS_DB") or 10
|
|
42
|
-
self.database = redis.StrictRedis(host=host, port=port, db=db)
|
|
43
|
-
|
|
44
|
-
stored_netuids = self.get_available_redis_metagraph_netuids()
|
|
45
|
-
if netuids is not None:
|
|
46
|
-
if not all([netuid in stored_netuids for netuid in netuids]):
|
|
47
|
-
bt.logging.debug(
|
|
48
|
-
f"Not all passed netuids in stored metagraphs. Fetching: {netuids}"
|
|
49
|
-
)
|
|
50
|
-
self.init_redis_global_metagraph(netuids)
|
|
51
|
-
else:
|
|
52
|
-
bt.logging.debug(
|
|
53
|
-
f"All passed netuids in stored metagraphs... skipping fetch"
|
|
54
|
-
)
|
|
55
|
-
else:
|
|
56
|
-
netuids = self.subtensor.get_all_subnet_netuids()
|
|
57
|
-
bt.logging.info(f"Initializing global metagraph for all netuids...")
|
|
58
|
-
self.init_global_redis_metagraph(netuids)
|
|
59
|
-
|
|
60
|
-
self.sync_thread = None
|
|
61
|
-
self.sync_running = False
|
|
62
|
-
self.update_interval = interval
|
|
63
|
-
self.run_sync_in_background_thread()
|
|
64
|
-
|
|
65
|
-
@abstractmethod
|
|
66
|
-
def prepare_synapse(self, *args, **kwargs) -> Any:
|
|
67
|
-
"""
|
|
68
|
-
Prepare the synapse-specific payload.
|
|
69
|
-
"""
|
|
70
|
-
...
|
|
71
|
-
|
|
72
|
-
@abstractmethod
|
|
73
|
-
def process_responses(self, responses: List[Union["bt.Synapse", Any]]) -> Any:
|
|
74
|
-
"""
|
|
75
|
-
Process the responses from the network.
|
|
76
|
-
"""
|
|
77
|
-
...
|
|
78
|
-
|
|
79
|
-
def __call__(self, *args, **kwargs):
|
|
80
|
-
return self.query_api(*args, **kwargs)
|
|
81
|
-
|
|
82
|
-
def fetch_metagraphs(self, netuids=None):
|
|
83
|
-
for netuid in netuids:
|
|
84
|
-
bt.logging.debug(f"Fetching metagraph for netuid: {netuid}")
|
|
85
|
-
yield netuid, self.subtensor.metagraph(netuid)
|
|
86
|
-
|
|
87
|
-
def store_metagraph_in_redis(self, metagraph: "bt.metagraph"):
|
|
88
|
-
serialized_data = serialize_metagraph(metagraph)
|
|
89
|
-
|
|
90
|
-
for attr, value in serialized_data.items():
|
|
91
|
-
self.database.hset(f"metagraph:{metagraph.netuid}", attr, json.dumps(value))
|
|
92
|
-
|
|
93
|
-
self.database.set(f"metagraph:{metagraph.netuid}:updated", time.time())
|
|
94
|
-
|
|
95
|
-
def retrieve_metagraph_from_redis(self, netuid: int):
|
|
96
|
-
redis_key = f"metagraph:{netuid}"
|
|
97
|
-
data = {
|
|
98
|
-
attr.decode(): json.loads(self.database.hget(redis_key, attr))
|
|
99
|
-
for attr in self.database.hkeys(redis_key)
|
|
100
|
-
}
|
|
101
|
-
return deserialize_metagraph(data)
|
|
102
|
-
|
|
103
|
-
def get_available_redis_metagraph_netuids(self):
|
|
104
|
-
keys = self.database.keys("metagraph:*")
|
|
105
|
-
return [int(key.decode().split(":")[1]) for key in keys]
|
|
106
|
-
|
|
107
|
-
def retrieve_all_metagraphs_from_redis(self, netuids: Optional[List[int]] = None):
|
|
108
|
-
if netuids is None:
|
|
109
|
-
netuids = self.get_available_redis_metagraph_netuids()
|
|
110
|
-
return {
|
|
111
|
-
netuid: self.retrieve_metagraph_from_redis(netuid) for netuid in netuids
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
def init_redis_global_metagraph(self, netuids=None):
|
|
115
|
-
metagraphs = self.fetch_metagraphs(netuids)
|
|
116
|
-
for _, metagraph in metagraphs:
|
|
117
|
-
self.store_metagraph_in_redis(metagraph)
|
|
118
|
-
|
|
119
|
-
def update_metagraphs(self):
|
|
120
|
-
metagraphs = self.retrieve_all_metagraphs_from_redis()
|
|
121
|
-
for netuid, metagraph in metagraphs.items():
|
|
122
|
-
bt.logging.debug(
|
|
123
|
-
f"syncing {metagraph.netuid} | {self.subtensor.network}..."
|
|
124
|
-
)
|
|
125
|
-
if self.database.get(f"metagraph:{metagraph.netuid}:updated") is not None:
|
|
126
|
-
last_update = float(
|
|
127
|
-
self.database.get(f"metagraph:{metagraph.netuid}:updated")
|
|
128
|
-
)
|
|
129
|
-
if last_update + self.update_interval > time.time():
|
|
130
|
-
bt.logging.debug(f"Skipping sync for {metagraph.netuid}...")
|
|
131
|
-
continue
|
|
132
|
-
if self.subtensor.network == metagraph.network:
|
|
133
|
-
metagraph.sync(subtensor=self.subtensor, lite=True)
|
|
134
|
-
else:
|
|
135
|
-
metagraph = self.subtensor.metagraph(netuid)
|
|
136
|
-
bt.logging.debug(f"storing {metagraph.netuid} in redis...")
|
|
137
|
-
self.store_metagraph_in_redis(metagraph)
|
|
138
|
-
|
|
139
|
-
def periodic_sync_metagraphs(self):
|
|
140
|
-
while self.sync_thread is not None and self.sync_running:
|
|
141
|
-
bt.logging.trace(f"Sleeping sync for {interval} seconds...")
|
|
142
|
-
time.sleep(self.update_interval)
|
|
143
|
-
self.update_metagraphs()
|
|
144
|
-
|
|
145
|
-
def run_sync_in_background_thread(self):
|
|
146
|
-
"""
|
|
147
|
-
Starts the API's operations in a separate background thread.
|
|
148
|
-
This is useful for non-blocking operations.
|
|
149
|
-
"""
|
|
150
|
-
bt.logging.debug("Starting API background service in separate thread.")
|
|
151
|
-
self.sync_thread = threading.Thread(
|
|
152
|
-
target=periodic_sync_metagraphs, daemon=True
|
|
153
|
-
)
|
|
154
|
-
self.sync_thread.start()
|
|
155
|
-
bt.logging.debug("Started")
|
|
156
|
-
|
|
157
|
-
def stop_sync_thread(self):
|
|
158
|
-
"""
|
|
159
|
-
Stops the API's operations that are running in the background thread.
|
|
160
|
-
"""
|
|
161
|
-
bt.logging.debug("Stopping API in background thread.")
|
|
162
|
-
self.sync_thread.join(5)
|
|
163
|
-
self.sync_running = False
|
|
164
|
-
self.sync_thread = None
|
|
165
|
-
bt.logging.debug("Stopped")
|
|
166
|
-
|
|
167
|
-
async def ping_uids(self, uids, netuid, timeout=3):
|
|
168
|
-
"""
|
|
169
|
-
Pings a list of UIDs to check their availability on the Bittensor network.
|
|
170
|
-
|
|
171
|
-
Args:
|
|
172
|
-
uids (list): A list of UIDs (unique identifiers) to ping.
|
|
173
|
-
netuid (int): The unique identifier of the subnet to ping.
|
|
174
|
-
timeout (int, optional): The timeout in seconds for each ping. Defaults to 3.
|
|
175
|
-
|
|
176
|
-
Returns:
|
|
177
|
-
tuple: A tuple containing two lists:
|
|
178
|
-
- The first list contains UIDs that were successfully pinged.
|
|
179
|
-
- The second list contains UIDs that failed to respond.
|
|
180
|
-
"""
|
|
181
|
-
# Fetch from redis
|
|
182
|
-
metagraph = retrieve_metagraph_from_redis(netuid)
|
|
183
|
-
|
|
184
|
-
axons = [metagraph.axons[uid] for uid in uids]
|
|
185
|
-
try:
|
|
186
|
-
responses = await self.dendrite(
|
|
187
|
-
axons,
|
|
188
|
-
bt.Synapse(),
|
|
189
|
-
deserialize=False,
|
|
190
|
-
timeout=timeout,
|
|
191
|
-
)
|
|
192
|
-
successful_uids = [
|
|
193
|
-
uid
|
|
194
|
-
for uid, response in zip(uids, responses)
|
|
195
|
-
if response.dendrite.status_code == 200
|
|
196
|
-
]
|
|
197
|
-
failed_uids = [
|
|
198
|
-
uid
|
|
199
|
-
for uid, response in zip(uids, responses)
|
|
200
|
-
if response.dendrite.status_code != 200
|
|
201
|
-
]
|
|
202
|
-
except Exception as e:
|
|
203
|
-
bt.logging.error(f"Dendrite ping failed: {e}")
|
|
204
|
-
successful_uids = []
|
|
205
|
-
failed_uids = uids
|
|
206
|
-
bt.logging.debug("ping() successful uids:", successful_uids)
|
|
207
|
-
bt.logging.debug("ping() failed uids :", failed_uids)
|
|
208
|
-
return successful_uids, failed_uids
|
|
209
|
-
|
|
210
|
-
async def get_query_api_nodes(self, netuid, n=0.1, timeout=3):
|
|
211
|
-
"""
|
|
212
|
-
Fetches the available API nodes to query for the particular subnet.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
dendrite (bittensor.dendrite): The dendrite instance to use for querying.
|
|
216
|
-
metagraph (bittensor.metagraph): The metagraph instance containing network information.
|
|
217
|
-
n (float, optional): The fraction of top nodes to consider based on stake. Defaults to 0.1.
|
|
218
|
-
timeout (int, optional): The timeout in seconds for pinging nodes. Defaults to 3.
|
|
219
|
-
|
|
220
|
-
Returns:
|
|
221
|
-
list: A list of UIDs representing the available API nodes.
|
|
222
|
-
"""
|
|
223
|
-
metagraph = retrieve_metagraph_from_redis(netuid)
|
|
224
|
-
|
|
225
|
-
bt.logging.debug(f"Fetching available API nodes for subnet {metagraph.netuid}")
|
|
226
|
-
vtrust_uids = [
|
|
227
|
-
uid.item() for uid in metagraph.uids if metagraph.validator_trust[uid] > 0
|
|
228
|
-
]
|
|
229
|
-
top_uids = torch.where(metagraph.S > torch.quantile(metagraph.S, 1 - n))
|
|
230
|
-
top_uids = top_uids[0].tolist()
|
|
231
|
-
init_query_uids = set(top_uids).intersection(set(vtrust_uids))
|
|
232
|
-
query_uids, _ = await self.ping_uids(init_query_uids, netuid, timeout=timeout)
|
|
233
|
-
bt.logging.debug(
|
|
234
|
-
f"Available API node UIDs for subnet {metagraph.netuid}: {query_uids}"
|
|
235
|
-
)
|
|
236
|
-
if len(query_uids) > 3:
|
|
237
|
-
query_uids = random.sample(query_uids, 3)
|
|
238
|
-
return query_uids
|
|
239
|
-
|
|
240
|
-
async def get_query_api_axons(self, netuid, n=0.1, timeout=3, uid=None):
|
|
241
|
-
"""
|
|
242
|
-
Retrieves the axons of query API nodes based on their availability and stake.
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
netuid (int): The unique identifier of the subnet to query.
|
|
246
|
-
n (float, optional): The fraction of top nodes to consider based on stake. Defaults to 0.1.
|
|
247
|
-
timeout (int, optional): The timeout in seconds for pinging nodes. Defaults to 3.
|
|
248
|
-
uid (int, optional): The specific UID of the API node to query. Defaults to None.
|
|
249
|
-
|
|
250
|
-
Returns:
|
|
251
|
-
list: A list of axon objects for the available API nodes.
|
|
252
|
-
"""
|
|
253
|
-
metagraph = retrieve_metagraph_from_redis(netuid)
|
|
254
|
-
|
|
255
|
-
if uid is not None:
|
|
256
|
-
query_uids = [uid]
|
|
257
|
-
else:
|
|
258
|
-
query_uids = await self.get_query_api_nodes(netuid, n=n, timeout=timeout)
|
|
259
|
-
return [metagraph.axons[uid] for uid in query_uids]
|
|
260
|
-
|
|
261
|
-
async def query_api(
|
|
262
|
-
self,
|
|
263
|
-
netuid: int,
|
|
264
|
-
deserialize: bool = False,
|
|
265
|
-
timeout: int = 12,
|
|
266
|
-
n: float = 0.1,
|
|
267
|
-
uid: int = None,
|
|
268
|
-
**kwargs: Any,
|
|
269
|
-
) -> Any:
|
|
270
|
-
"""
|
|
271
|
-
Queries the API nodes of a subnet using the given synapse and bespoke query function.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
dendrite (bittensor.dendrite): The dendrite instance to use for querying.
|
|
275
|
-
deserialize (bool, optional): Whether to deserialize the responses. Defaults to False.
|
|
276
|
-
timeout (int, optional): The timeout in seconds for the query. Defaults to 12.
|
|
277
|
-
n (float, optional): The fraction of top nodes to consider based on stake. Defaults to 0.1.
|
|
278
|
-
uid (int, optional): The specific UID of the API node to query. Defaults to None.
|
|
279
|
-
**kwargs: Keyword arguments for the prepare_synapse_fn.
|
|
280
|
-
|
|
281
|
-
Returns:
|
|
282
|
-
Any: The result of the process_responses_fn.
|
|
283
|
-
"""
|
|
284
|
-
synapse = self.prepare_synapse(**kwargs)
|
|
285
|
-
axons = await self.get_query_api_axons(
|
|
286
|
-
netuid=netuid, n=n, timeout=timeout, uid=uid
|
|
287
|
-
)
|
|
288
|
-
bt.logging.debug(
|
|
289
|
-
f"Quering valdidator axons with synapse {synapse.name} for subnet {netuid}..."
|
|
290
|
-
)
|
|
291
|
-
responses = await self.dendrite(
|
|
292
|
-
axons=axons,
|
|
293
|
-
synapse=synapse,
|
|
294
|
-
deserialize=deserialize,
|
|
295
|
-
timeout=timeout,
|
|
296
|
-
)
|
|
297
|
-
return self.process_responses(responses)
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import torch
|
|
3
|
-
import time
|
|
4
|
-
import functools
|
|
5
|
-
import threading
|
|
6
|
-
from os import getenv
|
|
7
|
-
from redis import StrictRedis
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
|
|
10
|
-
import bittensor as bt
|
|
11
|
-
from bittensor import __ss58_format__ as ss58_format, __type_registry__ as type_registry
|
|
12
|
-
from bittensor import (
|
|
13
|
-
__finney_entrypoint__,
|
|
14
|
-
__local_entrypoint__,
|
|
15
|
-
__finney_test_entrypoint__,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
from typing import List, Dict, Any, Optional, Union
|
|
19
|
-
|
|
20
|
-
bt.trace()
|
|
21
|
-
|
|
22
|
-
redis_db = None
|
|
23
|
-
subtensor = None
|
|
24
|
-
sync_thread = None
|
|
25
|
-
|
|
26
|
-
METAGRAPH_ATTRIBUTES = [
|
|
27
|
-
"n",
|
|
28
|
-
"block",
|
|
29
|
-
"uids",
|
|
30
|
-
"stake",
|
|
31
|
-
"total_stake",
|
|
32
|
-
"ranks",
|
|
33
|
-
"trust",
|
|
34
|
-
"consensus",
|
|
35
|
-
"validator_trust",
|
|
36
|
-
"incentive",
|
|
37
|
-
"emission",
|
|
38
|
-
"dividends",
|
|
39
|
-
"active",
|
|
40
|
-
"last_update",
|
|
41
|
-
"validator_permit",
|
|
42
|
-
"weights",
|
|
43
|
-
"bonds",
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_subtensor(network: str = "local"):
|
|
48
|
-
return bt.subtensor(network)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def get_database() -> StrictRedis:
|
|
52
|
-
host = getenv("REDIS_HOST") or "localhost"
|
|
53
|
-
port = getenv("REDIS_PORT") or 6379
|
|
54
|
-
db = getenv("REDIS_DB") or 10
|
|
55
|
-
return StrictRedis(host=host, port=port, db=db) if redis_db == None else redis_db
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def startup(network: str = "local"):
|
|
59
|
-
global redis_db
|
|
60
|
-
global subtensor
|
|
61
|
-
|
|
62
|
-
if subtensor is not None:
|
|
63
|
-
subtensor.close()
|
|
64
|
-
|
|
65
|
-
redis_db = get_database()
|
|
66
|
-
subtensor = get_subtensor(network)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def store_metagraph_in_redis(metagraph):
|
|
70
|
-
db = get_database()
|
|
71
|
-
serialized_data = serialize_metagraph(metagraph)
|
|
72
|
-
|
|
73
|
-
for attr, value in serialized_data.items():
|
|
74
|
-
db.hset(f"metagraph:{metagraph.netuid}", attr, json.dumps(value))
|
|
75
|
-
|
|
76
|
-
db.set(f"metagraph:{metagraph.netuid}:updated", time.time())
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def retrieve_metagraph_from_redis(netuid: int):
|
|
80
|
-
db = get_database()
|
|
81
|
-
redis_key = f"metagraph:{netuid}"
|
|
82
|
-
data = {
|
|
83
|
-
attr.decode(): json.loads(db.hget(redis_key, attr))
|
|
84
|
-
for attr in db.hkeys(redis_key)
|
|
85
|
-
}
|
|
86
|
-
return deserialize_metagraph(data)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def get_metagraph(netuid: int) -> "bt.metagraph":
|
|
90
|
-
key = f"metagraph:{netuid}"
|
|
91
|
-
if redis_db.exists(key):
|
|
92
|
-
return retrieve_metagraph_from_redis(netuid)
|
|
93
|
-
return subtensor.metagraph(netuid)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_available_redis_metagraphs():
|
|
97
|
-
keys = redis_db.keys("metagraph:*")
|
|
98
|
-
return [int(key.decode().split(":")[1]) for key in keys]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def retrieve_all_metagraphs_from_redis(netuids=None):
|
|
102
|
-
if netuids is None:
|
|
103
|
-
netuids = get_available_redis_metagraphs()
|
|
104
|
-
return {netuid: retrieve_metagraph_from_redis(netuid) for netuid in netuids}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def serialize_metagraph(metagraph_obj, dump=False):
|
|
108
|
-
serialized_data = {}
|
|
109
|
-
for attr in METAGRAPH_ATTRIBUTES:
|
|
110
|
-
tensor = getattr(metagraph_obj, attr, None)
|
|
111
|
-
if tensor is not None:
|
|
112
|
-
serialized_data[attr] = tensor.cpu().numpy().tolist()
|
|
113
|
-
|
|
114
|
-
serialized_data["netuid"] = metagraph_obj.netuid
|
|
115
|
-
serialized_data["network"] = metagraph_obj.network
|
|
116
|
-
serialized_data["version"] = metagraph_obj.version.item()
|
|
117
|
-
serialized_data["axons"] = [axon.to_string() for axon in metagraph_obj.axons]
|
|
118
|
-
serialized_data["netuid"] = metagraph_obj.netuid
|
|
119
|
-
|
|
120
|
-
return json.dumps(serialized_data) if dump else serialized_data
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def deserialize_metagraph(serialized_str):
|
|
124
|
-
if isinstance(serialized_str, str):
|
|
125
|
-
data = json.loads(serialized_str)
|
|
126
|
-
else:
|
|
127
|
-
data = serialized_str
|
|
128
|
-
metagraph_obj = bt.metagraph(
|
|
129
|
-
netuid=data["netuid"], network=data["network"], lite=False, sync=False
|
|
130
|
-
)
|
|
131
|
-
metagraph_obj.version = torch.nn.Parameter(
|
|
132
|
-
torch.tensor([data["version"]], dtype=torch.int64), requires_grad=False
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
for attr in METAGRAPH_ATTRIBUTES:
|
|
136
|
-
if attr in data:
|
|
137
|
-
setattr(
|
|
138
|
-
metagraph_obj,
|
|
139
|
-
attr,
|
|
140
|
-
torch.nn.Parameter(torch.tensor(data[attr]), requires_grad=False),
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
metagraph_obj.axons = [
|
|
144
|
-
bt.chain_data.AxonInfo.from_string(axon_data) for axon_data in data["axons"]
|
|
145
|
-
]
|
|
146
|
-
|
|
147
|
-
return metagraph_obj
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def fetch_metagraphs(netuids=None):
|
|
151
|
-
for netuid in netuids:
|
|
152
|
-
bt.logging.debug(f"Fetching metagraph for netuid: {netuid}")
|
|
153
|
-
yield netuid, subtensor.metagraph(netuid)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def init_redis_global_metagraph(netuids=None):
|
|
157
|
-
metagraphs = fetch_metagraphs(netuids)
|
|
158
|
-
for netuid, metagraph in metagraphs:
|
|
159
|
-
store_metagraph_in_redis(metagraph)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def update_metagraphs():
|
|
163
|
-
metagraphs = retrieve_all_metagraphs_from_redis()
|
|
164
|
-
for netuid, metagraph in metagraphs.items():
|
|
165
|
-
bt.logging.debug(f"syncing {metagraph.netuid} | {subtensor.network}...")
|
|
166
|
-
if redis_db.get(f"metagraph:{metagraph.netuid}:updated") is not None:
|
|
167
|
-
last_update = float(redis_db.get(f"metagraph:{metagraph.netuid}:updated"))
|
|
168
|
-
if last_update + 60 * 60 * 24 > time.time():
|
|
169
|
-
bt.logging.debug(f"Skipping sync for {metagraph.netuid}...")
|
|
170
|
-
continue
|
|
171
|
-
if subtensor.network == metagraph.network:
|
|
172
|
-
metagraph.sync(subtensor=subtensor, lite=True)
|
|
173
|
-
else:
|
|
174
|
-
metagraph = subtensor.metagraph(netuid)
|
|
175
|
-
bt.logging.debug(f"storing {metagraph.netuid} in redis...")
|
|
176
|
-
store_metagraph_in_redis(metagraph)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def periodic_sync_metagraphs(interval: int = 30):
|
|
180
|
-
global sync_thread
|
|
181
|
-
while sync_thread is not None:
|
|
182
|
-
bt.logging.trace(f"Sleeping sync for {interval} seconds...")
|
|
183
|
-
time.sleep(interval)
|
|
184
|
-
update_metagraphs()
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def run_sync_in_background_thread(interval: int = 60 * 60 * 2):
|
|
188
|
-
"""
|
|
189
|
-
Starts the API's operations in a separate background thread.
|
|
190
|
-
This is useful for non-blocking operations.
|
|
191
|
-
"""
|
|
192
|
-
global sync_thread
|
|
193
|
-
bt.logging.debug("Starting API background service in separate thread.")
|
|
194
|
-
wrapped_sync = functools.partial(periodic_sync_metagraphs, interval=interval)
|
|
195
|
-
sync_thread = threading.Thread(target=wrapped_sync, daemon=True)
|
|
196
|
-
sync_thread.start()
|
|
197
|
-
bt.logging.debug("Started")
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def stop_sync_thread():
|
|
201
|
-
"""
|
|
202
|
-
Stops the API's operations that are running in the background thread.
|
|
203
|
-
"""
|
|
204
|
-
global sync_thread
|
|
205
|
-
bt.logging.debug("Stopping API in background thread.")
|
|
206
|
-
sync_thread.join(5)
|
|
207
|
-
sync_thread = None
|
|
208
|
-
bt.logging.debug("Stopped")
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def start(network: str = "local", netuids: List[int] = None, interval: int = 180):
|
|
212
|
-
startup(network)
|
|
213
|
-
|
|
214
|
-
stored_netuids = get_available_redis_metagraphs()
|
|
215
|
-
if netuids is not None:
|
|
216
|
-
if not all([netuid in stored_netuids for netuid in netuids]):
|
|
217
|
-
bt.logging.debug(
|
|
218
|
-
f"Not all passed netuids in stored metagraphs. Fetching: {netuids}"
|
|
219
|
-
)
|
|
220
|
-
init_redis_global_metagraph(netuids)
|
|
221
|
-
else:
|
|
222
|
-
bt.logging.debug(
|
|
223
|
-
f"All passed netuids in stored metagraphs... skipping fetch"
|
|
224
|
-
)
|
|
225
|
-
else:
|
|
226
|
-
netuids = subtensor.get_all_subnet_netuids()
|
|
227
|
-
bt.logging.info(f"Initializing global metagraph for all netuids...")
|
|
228
|
-
init_global_redis_metagraph(netuids)
|
|
229
|
-
|
|
230
|
-
run_sync_in_background_thread(interval)
|