Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 0 additions & 36 deletions __init__.py

This file was deleted.

8 changes: 8 additions & 0 deletions apex/common/epistula.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import json
from typing import Any

import bittensor as bt


def generate_header(hotkey: bt.wallet.hotkey, body: dict[Any, Any] | list[Any]) -> dict[str, Any]:
return {"Body-Signature": "0x" + hotkey.sign(json.dumps(body)).hex()}
10 changes: 9 additions & 1 deletion apex/validator/miner_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from apex.common.async_chain import AsyncChain
from apex.common.constants import VALIDATOR_REFERENCE_LABEL
from apex.common.epistula import generate_header
from apex.common.models import MinerDiscriminatorResults, MinerGeneratorResults
from apex.common.utils import async_cache
from apex.validator.logger_db import LoggerDB
Expand Down Expand Up @@ -127,9 +128,16 @@ async def _sample_miners(self) -> list[MinerInfo]:

async def query_miners(self, body: dict[str, Any], endpoint: str) -> str:
"""Query the miners for the query."""
body["signer"] = await self._chain.wallet.hotkey.ss58_address
body["signed_for"] = endpoint
body["nonce"] = str(int(time.time()))
try:
async with aiohttp.ClientSession() as session:
async with session.post(endpoint + "/v1/chat/completions", json=body) as resp:
async with session.post(
endpoint + "/v1/chat/completions",
headers=generate_header(self._chain.wallet.hotkey, body),
json=body,
) as resp:
result = await resp.text()
except BaseException:
# Error during miner query, return empty string.
Expand Down
1 change: 0 additions & 1 deletion config/mainnet.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,3 @@ deep_research:
research_model: "Qwen/Qwen3-235B-A22B-Instruct-2507"
compression_model: "deepseek-ai/DeepSeek-V3-0324"
final_model: "deepseek-ai/DeepSeek-V3-0324"

14 changes: 7 additions & 7 deletions docs/incentive-mechanism.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The ground truth system works as follows:
When the validator generates references, it uses a sophisticated deep research process:

```
System Prompt: "You are Deep Researcher, a meticulous assistant. For each claim you make,
System Prompt: "You are Deep Researcher, a meticulous assistant. For each claim you make,
provide step-by-step reasoning and cite exact source numbers from the provided context."

Process: Research Question → Web Search → LLM Analysis → Cited Response
Expand Down Expand Up @@ -98,14 +98,14 @@ The system maintains a 22-hour rolling window for score aggregation:

**Objective**: Produce responses that are indistinguishable from high-quality validator references

**Strategy**:
**Strategy**:
- Generate human-like, well-reasoned responses
- Match the style and quality of validator deep research outputs
- Avoid patterns that discriminators can easily identify as miner-generated

**Reward**: Higher scores when discriminators fail to correctly classify their output as miner-generated

### For Discriminators
### For Discriminators

**Objective**: Accurately distinguish between miner-generated and validator-generated content

Expand Down Expand Up @@ -149,7 +149,7 @@ Miners receive requests through a standardized API:
**Discriminator Request**:
```json
{
"step": "discriminator",
"step": "discriminator",
"query": "Input query",
"generation": "Response to classify"
}
Expand All @@ -164,7 +164,7 @@ Miners receive requests through a standardized API:

The validator uses a `MinerSampler` that:
- Samples miners from the network metagraph
- Supports multiple sampling modes (random, sequential)
- Supports multiple sampling modes (random, sequential)
- Queries multiple miners simultaneously for each task
- Tracks miner information and performance metrics

Expand Down Expand Up @@ -203,12 +203,12 @@ The current codebase includes a dummy miner implementation that:
For effective participation, miners must implement:

1. **Advanced Generation**: High-quality language models or API integrations
2. **Sophisticated Discrimination**: ML models or heuristics for content classification
2. **Sophisticated Discrimination**: ML models or heuristics for content classification
3. **Robust Error Handling**: Network failures, malformed requests, etc.
4. **Performance Optimization**: Fast response times to maximize participation

## Conclusion

The Apex incentive mechanism creates a sophisticated competitive environment that drives continuous improvement in both content generation and quality assessment. By requiring miners to excel in both generator and discriminator roles, the system ensures that network participants develop comprehensive AI capabilities while maintaining high standards for content quality.

The zero-sum competition between generators and discriminators, combined with the high-quality validator references, creates powerful incentives for miners to approach or exceed validator-level performance, ultimately benefiting the entire network with increasingly sophisticated AI capabilities.
The zero-sum competition between generators and discriminators, combined with the high-quality validator references, creates powerful incentives for miners to approach or exceed validator-level performance, ultimately benefiting the entire network with increasingly sophisticated AI capabilities.
2 changes: 1 addition & 1 deletion docs/miner.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,4 @@ The `MinerScorer` evaluates miner performance:
1. **Port Already in Use**: Change the `--port` parameter
2. **Wallet Not Found**: Verify coldkey and hotkey names
3. **Network Connection**: Check internet connectivity and firewall settings
4. **Subnet Registration**: Ensure sufficient balance for registration fees
4. **Subnet Registration**: Ensure sufficient balance for registration fees
1 change: 0 additions & 1 deletion py.typed
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.lint.isort]
# Sort by module, then by case-sensitive name.
# See https://docs.astral.sh/ruff/settings/#lint-isort-order-by-type
order-by-type = true


[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
Expand All @@ -183,7 +189,7 @@ apex-deep-research = { git = "https://github.com/macrocosm-os/apex_deep_research
override-dependencies = [
# Override artificial pins from bittensor ecosystem for production
"starlette>=0.37.2",
"aiohttp>=3.8.3",
"aiohttp>=3.8.3",
"protobuf>=3.20.2",
"cachetools>=2.0.0",
]
Expand Down
19 changes: 15 additions & 4 deletions tests/validator/test_miner_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,14 @@ async def test_sample_miners_sequential(monkeypatch: MagicMock, miner_sampler: M
@pytest.mark.asyncio
async def test_query_miners() -> None:
"""Tests that a query to a miner is successful."""
sampler = MinerSampler(chain=MagicMock()) # type: ignore
mock_chain = MagicMock()
mock_chain.wallet.hotkey.ss58_address = "test_address"
sampler = MinerSampler(chain=mock_chain) # type: ignore
endpoint = "http://test.com"
body = {"test": "data"}

mock_resp = AsyncMock()
mock_resp.text.return_value = '{"response": "ok"}'
mock_resp.text = AsyncMock(return_value='{"response": "ok"}')

mock_session_post = MagicMock()
mock_session_post.__aenter__.return_value = mock_resp
Expand All @@ -200,11 +202,20 @@ async def test_query_miners() -> None:
mock_session.__aenter__.return_value = mock_session
mock_session.__aexit__.return_value = None

with patch("aiohttp.ClientSession", return_value=mock_session) as mock_client_session:
with (
patch("aiohttp.ClientSession", return_value=mock_session) as mock_client_session,
patch("apex.validator.miner_sampler.generate_header") as mock_generate_header,
patch("time.time", return_value=12345),
):
mock_generate_header.return_value = {"some": "header"}
result = await sampler.query_miners(body, endpoint)

mock_client_session.assert_called_once()
mock_session.post.assert_called_with(endpoint + "/v1/chat/completions", json=body)
expected_body = {"test": "data", "signer": "test_address", "signed_for": "http://test.com", "nonce": "12345"}
mock_session.post.assert_called_with(
endpoint + "/v1/chat/completions", headers={"some": "header"}, json=expected_body
)
mock_generate_header.assert_called_with(mock_chain.wallet.hotkey, expected_body)
assert result == '{"response": "ok"}'


Expand Down
5 changes: 2 additions & 3 deletions validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ async def read_args() -> argparse.Namespace:
parser.add_argument(
"-c",
"--config",
# required=True,
# default="config/mainnet.yaml",
default="config/testnet.yaml",
# default="config/testnet.yaml",
default="config/mainnet.yaml",
help="Config file path (e.g. config/mainnet.yaml).",
type=Path,
)
Expand Down
Loading