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

Commit

Permalink
feat: implement miner certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
GuiBibeau committed May 9, 2024
1 parent 9d9c4f2 commit 8cc4076
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 69 deletions.
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"]

0 comments on commit 8cc4076

Please sign in to comment.