Skip to content
Open
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export VALIDATOR_CADENCE='500'
# Solana wallet for sample queries, should have at least 0.1 SOL
export SOLANA_WALLET='5HHSqMvTCvgtzdqFb5BbtYjB8cEiJjf8UZ6p5rQczagL'

# Gemini API key for question generation
export GEMINI_API_KEY=''


# MINER CONFIGS FOR BITQUANT AGENT
# REPO: https://github.com/OpenGradient/BitQuant-Subnet
Expand Down
9 changes: 8 additions & 1 deletion neurons/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


import time
import sys
import os

# Bittensor
import bittensor as bt
Expand All @@ -31,6 +31,8 @@
# Import the shared Quant agent server module
from neurons import quant_agent_server

from quant.utils.questioner import Questioner

class Validator(BaseValidatorNeuron):
"""
Your validator neuron class. You should use this class to define your validator's behavior. In particular, you should replace the forward function with your own logic.
Expand All @@ -46,6 +48,11 @@ def __init__(self, config=None):
bt.logging.info("load_state()")
self.load_state()

api_key = os.getenv("GEMINI_API_KEY", "")
if not api_key:
bt.logging.warning("No GEMINI_API_KEY found in environment variables!")

self.questioner = Questioner(api_key=api_key)
# TODO(developer): Anything specific to your use case you can do here

async def forward(self):
Expand Down
80 changes: 80 additions & 0 deletions quant/utils/questioner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from datetime import timedelta, datetime
from typing import Optional
import random
import json

import google.genai as genai
from pydantic import BaseModel, Field
import bittensor as bt


GENERATION_PROMPT = """Create a list of {question_count} questions that sound like they're being asked by a real person with varying levels of knowledge. The questions should be about common crypto topics. Some should be professionally formatted and others lazy (e.g., no capitalization, missing punctuation). The questions should cover the following topics, with an example for each:
* Portfolio Analytics: (e.g., "how can i evaluate my portfolio risk")
* Yield & Earning: (e.g., "what's the best way to earn yield on my ETH")
* Trading & Popularity: (e.g., "what are the trading tokens on solana?")
* Investment Advice: (e.g., "should i buy BONK?")
* Token Risk & Security: (e.g., "who are the top holders of pepe?")"""


class Questioner:

def __init__(
self,
api_key: str,
question_lifetime: timedelta = timedelta(days=7),
initial_questions: Optional[list[str]] = None,
question_count: int = 42,
llm_model: str = "gemini-2.5-flash-lite",
temperature: float = 1.0,
):
self.question_lifetime = question_lifetime
self.questions: list[str] = initial_questions or []
self.last_question_update = datetime.now()

self.genai_client = genai.Client(api_key=api_key)
self.llm_model: str = llm_model
self.question_count: int = question_count
self.temperature: float = temperature

def choose_question(self) -> str:
self.maybe_update_questions()
return random.choice(self.questions)

def maybe_update_questions(self):
if not self.questions or self.is_update_time():
self.questions = self.generate_exactly_n_questions(n=self.question_count)
self.last_question_update = datetime.now()

def is_update_time(self) -> bool:
return datetime.now() - self.last_question_update > self.question_lifetime

def generate_exactly_n_questions(self, n: int) -> list[str]:
new_questions = []
while len(new_questions) < n:
qs = self.generate_about_n_questions(n=n - len(new_questions))
print(f"{len(qs) = }")
new_questions.extend(qs)
return new_questions[:n]

def generate_about_n_questions(self, n: int) -> list[str]:
class Questions(BaseModel):
questions: list[str] = Field(..., description="List of crypto questions")

prompt = GENERATION_PROMPT.format(question_count=n)

response = self.genai_client.models.generate_content(
model=self.llm_model,
contents=prompt,
config=genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Questions,
temperature=self.temperature,
),
)

if response_text := response.text:
data = json.loads(response_text)
return data["questions"]
else:
bt.logging.warning("No questions were generated!")
return []
7 changes: 4 additions & 3 deletions quant/validator/forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@

import os
import time
import random
import bittensor as bt

from quant.protocol import QuantQuery, QuantSynapse
from quant.validator.reward import get_rewards
from quant.utils.uids import get_random_uids
from quant.utils.questions import questions


async def forward(self):
Expand All @@ -44,9 +42,12 @@ async def forward(self):
if not wallet_address:
bt.logging.error("SOLANA_WALLET environment variable is not set. Using a default value.")
wallet_address = "5HHSqMvTCvgtzdqFb5BbtYjB8cEiJjf8UZ6p5rQczagL"

question = self.questioner.choose_question()
bt.logging.info(f"Choosen question: '{question}'")

query = QuantQuery(
query=random.choice(questions),
query=question,
userID=wallet_address,
metadata={
"Create_Proof": "True",
Expand Down