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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@



[Dashboard](https://app.hex.tech/1644b22a-abe5-4113-9d5f-3ad05e4a8de7/app/Numinous-031erYRYSssIrH3W3KcyHg/latest) • [Website](https://numinouslabs.io/) • [Twitter](https://x.com/numinous_ai) •
[Discord](https://discord.gg/qKPeYPc3) • [Dashboard](https://app.hex.tech/1644b22a-abe5-4113-9d5f-3ad05e4a8de7/app/Numinous-031erYRYSssIrH3W3KcyHg/latest) • [Website](https://numinouslabs.io/) • [Twitter](https://x.com/numinous_ai) •
[Network](https://taostats.io/subnets/6/chart)
---

Expand Down
2 changes: 1 addition & 1 deletion docs/miner-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,4 +465,4 @@ numi fetch-logs # Fetch validator execution logs
**Next Steps:**
1. Read [subnet-rules.md](./subnet-rules.md) for competition rules and constraints
2. Review [architecture.md](./architecture.md) for system details
3. Check the example [agents](https://github.com/numinouslabs/numinous/blob/main/neurons/miner/agents/example.py)
3. Check example agents in `neurons/miner/agents/`
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Add agent_runs table

Revision ID: 40606aaa49f9
Revises: d679a148c4f2
Create Date: 2025-11-26 16:53:09.000000

"""

from typing import Sequence, Union

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "40606aaa49f9"
down_revision: Union[str, None] = "d679a148c4f2"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.execute(
"""
CREATE TABLE IF NOT EXISTS agent_runs (
run_id TEXT PRIMARY KEY,
unique_event_id TEXT NOT NULL,
agent_version_id TEXT NOT NULL,
miner_uid INTEGER NOT NULL,
miner_hotkey TEXT NOT NULL,
status TEXT NOT NULL,
exported INTEGER DEFAULT 0,
is_final INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (unique_event_id) REFERENCES events(unique_event_id),
FOREIGN KEY (agent_version_id) REFERENCES miner_agents(version_id)
)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_event ON agent_runs(unique_event_id)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_agent ON agent_runs(agent_version_id)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_miner ON agent_runs(miner_uid, miner_hotkey)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_status ON agent_runs(status)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_exported ON agent_runs(exported) WHERE exported = 0
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_is_final ON agent_runs(is_final)
"""
)

op.execute(
"""
CREATE INDEX idx_agent_runs_created_at ON agent_runs(created_at)
"""
)


def downgrade() -> None:
pass
5 changes: 2 additions & 3 deletions neurons/validator/db/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ async def get_miners_count(self) -> int:

return row[0]

async def get_predictions_to_export(self, current_interval_minutes: int, batch_size: int):
async def get_predictions_to_export(self, batch_size: int):
return await self.__db_client.many(
"""
SELECT
Expand All @@ -383,13 +383,12 @@ async def get_predictions_to_export(self, current_interval_minutes: int, batch_s
events e ON e.unique_event_id = p.unique_event_id
WHERE
p.exported = ?
AND p.interval_start_minutes < ?
ORDER BY
p.ROWID ASC
LIMIT
?
""",
[PredictionExportedStatus.NOT_EXPORTED, current_interval_minutes, batch_size],
[PredictionExportedStatus.NOT_EXPORTED, batch_size],
)

async def mark_predictions_as_exported(self, ids: list[str]):
Expand Down
13 changes: 4 additions & 9 deletions neurons/validator/db/tests/test_db_operations_part_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -1082,20 +1082,15 @@ async def test_get_predictions_to_export(
[PredictionExportedStatus.EXPORTED, "unique_event_id_2"],
)

current_interval_minutes = 11

result = await db_operations.get_predictions_to_export(
current_interval_minutes=current_interval_minutes, batch_size=1
)
result = await db_operations.get_predictions_to_export(batch_size=1)

assert len(result) == 1
assert result[0][1] == "unique_event_id_1"

result = await db_operations.get_predictions_to_export(
current_interval_minutes=current_interval_minutes, batch_size=20
)
result = await db_operations.get_predictions_to_export(batch_size=20)

assert len(result) == 2
# (unique_event_id_2 is already exported)
assert len(result) == 3

async def test_mark_predictions_as_exported(
self, db_client: DatabaseClient, db_operations: DatabaseOperations
Expand Down
57 changes: 57 additions & 0 deletions neurons/validator/models/agent_runs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from datetime import datetime
from enum import Enum, IntEnum
from typing import Any, Optional

from pydantic import BaseModel, field_validator


class AgentRunStatus(str, Enum):
SUCCESS = "success"
INTERNAL_AGENT_ERROR = "internal_agent_error"
INVALID_SANDBOX_OUTPUT = "invalid_sandbox_output"
SANDBOX_TIMEOUT = "sandbox_timeout"


class AgentRunExportedStatus(IntEnum):
NOT_EXPORTED = 0
EXPORTED = 1


class IsFinalStatus(IntEnum):
NOT_FINAL = 0
IS_FINAL = 1


class AgentRunsModel(BaseModel):
run_id: str
unique_event_id: str
agent_version_id: str
miner_uid: int
miner_hotkey: str
status: AgentRunStatus
exported: Optional[bool] = False
is_final: Optional[bool] = True
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = {"arbitrary_types_allowed": True}

@property
def primary_key(self):
return ["run_id"]

@field_validator("exported", mode="before")
def parse_exported_as_bool(cls, v: Any) -> bool:
# If the DB returns an integer, convert it to boolean
if isinstance(v, int):
return bool(v)
return v

@field_validator("is_final", mode="before")
def parse_is_final_as_bool(cls, v: Any) -> bool:
# If the DB returns an integer, convert it to boolean
if isinstance(v, int):
return bool(v)
return v


RUN_FIELDS = AgentRunsModel.model_fields.keys()
4 changes: 2 additions & 2 deletions neurons/validator/models/desearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ class DesearchEndpoint(StrEnum):
# Cost per 100 searches
DESEARCH_PRICING: typing.Dict[DesearchEndpoint, typing.Any] = {
DesearchEndpoint.AI_SEARCH: {
ModelEnum.NOVA: 0.6,
ModelEnum.NOVA: 0.4,
ModelEnum.ORBIT: 2.2,
ModelEnum.HORIZON: 2.6,
},
DesearchEndpoint.AI_WEB_SEARCH: {
ModelEnum.NOVA: 0.6,
ModelEnum.NOVA: 0.4,
ModelEnum.ORBIT: 1.7,
ModelEnum.HORIZON: 2.1,
},
Expand Down
Loading
Loading