Skip to content
This repository was archived by the owner on Sep 18, 2024. It is now read-only.

feat: implement miner certificate #112

Merged
merged 1 commit into from
May 11, 2024
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
Empty file.
33 changes: 33 additions & 0 deletions subnet/miner-cloudflare/api/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import asyncio
from fastapi import FastAPI
from pydantic import BaseModel
from miner.miner import miner
from certification.certification_manager import run_certification_manager


class ChatRequest(BaseModel):
messages: list
model: str

app = FastAPI()

@app.get("/")
def index():
return "ok"


@app.post("/chat")
async def chat(request: ChatRequest):
print(request)
messages = request.messages
model = request.model

response = await miner.prompt(messages=messages, model=model)
messages.append({"role": "system", "content": response})

return messages


@app.on_event("startup")
async def startup_event():
asyncio.create_task(run_certification_manager())
Empty file.
65 changes: 65 additions & 0 deletions subnet/miner-cloudflare/certification/certification_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import aiohttp
import os
import asyncio
from .hash import hash_multiple_files
from dotenv import load_dotenv
import urllib.parse


class CertificationManager:
current_hash: str
image_signature: str
service_mesh_url: str
_certificate: str = None

def __init__(self):
""" Initialize the CertificationManager class. """
load_dotenv()
self.current_hash = hash_multiple_files(['main.py', 'protocol.py', './api/api.py'])
self.image_signature = os.getenv("DOCKER_IMAGE_SIGNATURE", '')
self.service_mesh_url = os.getenv('SERVICE_MESH_URL')

async def run(self):
""" Run the CertificationManager and start the certification process """
await self._get_certificate()

async def _get_certificate(self):
""" Get the renewed certificate """
try:
async with aiohttp.ClientSession() as session:

# we must get the certificate for the current docker image and proove the right code is present.
params = {
"hash": self.current_hash,
"imageSignature": self.image_signature
}
# encode parameters
search = urllib.parse.urlencode(params)

async with session.get(f"{self.service_mesh_url}/api/certification?{search}", ) as response:
if response.status == 200:
self._certificate = await response.text()
else:
print(f"Error getting certificate")
except aiohttp.ClientError as e:
# Handle any errors that occur during the request
print(f"Error discovering miners: {e}")
except Exception as e:
# Handle any other unexpected errors
print(f"Unexpected error: {e}")

@property
def certificate(self):
return self._certificate



certification_manager = CertificationManager()


async def run_certification_manager():
while True:
await certification_manager.run()
# get recertified often to avoid getting the wrong rotation of certificate
await asyncio.sleep(120)

14 changes: 14 additions & 0 deletions subnet/miner-cloudflare/certification/hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import hashlib

def hash_multiple_files(file_paths):
"""Generate MD5 hash for the concatenated content of multiple files."""
md5 = hashlib.md5()
# Process each file in the list
for file_path in file_paths:
# Open each file in binary read mode
with open(file_path, "rb") as file:
# Read and update hash string value in blocks of 4K
for chunk in iter(lambda: file.read(4096), b""):
md5.update(chunk)
# Return the hexadecimal MD5 hash of the concatenated content
return md5.hexdigest()
7 changes: 0 additions & 7 deletions subnet/miner-cloudflare/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,6 @@ def get_config() -> "bt.Config":
default=False,
)

parser.add_argument(
"--api_only",
action="store_true",
help="Bypass connection to metagraph and subtensor and only starts the akeru API layer",
default=True,
)

# Adds subtensor specific arguments i.e. --subtensor.chain_endpoint ... --subtensor.network ...
bt.subtensor.add_args(parser)

Expand Down
6 changes: 2 additions & 4 deletions subnet/miner-cloudflare/dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ FROM python:3.10-slim
WORKDIR /code

COPY . /code
# COPY ./requirements.txt /code/requirements.txt
# COPY ./setup.py /code/setup.py

RUN python -m pip install -e .


CMD ["python", "main.py"]
ENTRYPOINT ["python"]
CMD ["main.py"]
58 changes: 2 additions & 56 deletions subnet/miner-cloudflare/main.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,11 @@
import argparse
import os
import aiohttp
import bittensor as bt
from dotenv import load_dotenv
from protocol import StreamPrompting
from fastapi import FastAPI
from pydantic import BaseModel
from api.api import app

from stream_miner import StreamMiner

load_dotenv()


class Miner(StreamMiner):
def config(self) -> "bt.Config":
parser = argparse.ArgumentParser(description="Streaming Miner Configs")
self.add_args(parser)
return bt.config(parser)

def add_args(cls, parser: argparse.ArgumentParser):
pass

async def prompt(self, messages, model) -> StreamPrompting:
async with aiohttp.ClientSession() as session:
response = await session.post(
f"https://api.cloudflare.com/client/v4/accounts/{self.CLOUDFLARE_ACCOUNT_ID}/ai/run/@cf/meta/{model}",
headers={"Authorization": f"Bearer {self.CLOUDFLARE_AUTH_TOKEN}"},
json={
"messages": messages
}
)
json_resp = await response.json()

return json_resp['result']['response']


app = FastAPI()
miner = Miner()


class ChatRequest(BaseModel):
messages: list
model: str


@app.get("/")
def index():
return "ok"


@app.post("/chat")
async def chat(request: ChatRequest):
messages = request.messages
model = request.model

response = await miner.prompt(messages=messages, model=model)
messages.append({"role": "system", "content": response})

return messages


if __name__ == "__main__":
load_dotenv()
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=os.getenv('PORT', 8080))
Empty file.
33 changes: 33 additions & 0 deletions subnet/miner-cloudflare/miner/miner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import argparse
import aiohttp
import bittensor as bt
from dotenv import load_dotenv
from protocol import StreamPrompting

from miner.stream_miner import StreamMiner

load_dotenv()

class Miner(StreamMiner):
def config(self) -> "bt.Config":
parser = argparse.ArgumentParser(description="Streaming Miner Configs")
self.add_args(parser)
return bt.config(parser)

def add_args(cls, parser: argparse.ArgumentParser):
pass

async def prompt(self, messages, model) -> StreamPrompting:
async with aiohttp.ClientSession() as session:
response = await session.post(
f"https://api.cloudflare.com/client/v4/accounts/{self.CLOUDFLARE_ACCOUNT_ID}/ai/run/@cf/meta/{model}",
headers={"Authorization": f"Bearer {self.CLOUDFLARE_AUTH_TOKEN}"},
json={
"messages": messages
}
)
json_resp = await response.json()

return json_resp['result']['response']

miner = Miner()
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from protocol import StreamPrompting

from config import check_config, get_config, get_ip_address
from config import check_config, get_config
from dotenv import load_dotenv
from requests import post

Expand Down Expand Up @@ -56,6 +56,7 @@ def __init__(self, config=None, axon=None, wallet=None, subtensor=None):

# Wallet holds cryptographic information, ensuring secure transactions and communication.
self.wallet = wallet or bt.wallet(config=self.config)
print(self.wallet)
bt.logging.info(f"Wallet {self.wallet}")

# subtensor manages the blockchain connection, facilitating interaction with the Bittensor blockchain.
Expand All @@ -67,6 +68,7 @@ def __init__(self, config=None, axon=None, wallet=None, subtensor=None):

# metagraph provides the network's current state, holding state about other participants in a subnet.
self.metagraph = self.subtensor.metagraph(self.config.netuid)
print(self.metagraph)
bt.logging.info(f"Metagraph: {self.metagraph}")

if self.wallet.hotkey.ss58_address not in self.metagraph.hotkeys:
Expand Down Expand Up @@ -96,6 +98,8 @@ def __init__(self, config=None, axon=None, wallet=None, subtensor=None):
**self.miner_services
}

json.dumps(service_map_dict)

# send to the service map
post(f'{url}/api/miner',
data=json.dumps(service_map_dict), headers=headers)
Expand Down
1 change: 0 additions & 1 deletion subnet/miner-cloudflare/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import json

from typing import List
from starlette.responses import StreamingResponse


class StreamPrompting(bt.StreamingSynapse):
Expand Down
12 changes: 12 additions & 0 deletions subnet/validator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.10-slim

WORKDIR /code

COPY . /code
# COPY ./requirements.txt /code/requirements.txt
# COPY ./setup.py /code/setup.py

RUN python -m pip install -e .


CMD ["python", "main.py"]
Loading