Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
d041b91
Fix API spin up with large amount of workers
dbobrenko Jan 31, 2025
5d4e316
Remove redundant api_keys save
dbobrenko Jan 31, 2025
787239f
Revert test_api changes
dbobrenko Jan 31, 2025
0013df9
Run pre-commit
dbobrenko Feb 1, 2025
44e7de5
Improve docs
dbobrenko Feb 1, 2025
118cac4
Clean llm and sn19 wrappers
dbobrenko Feb 5, 2025
a4e9de8
Merge branch 'staging' into feature/SN1-394-scoring-queue
dbobrenko Feb 5, 2025
97c33be
Add scoring queue
dbobrenko Feb 5, 2025
7446b5c
Revert non-related files
dbobrenko Feb 5, 2025
1199ae1
Remove unused code
dbobrenko Feb 5, 2025
a249bf4
Move Api Docs
richwardle Feb 5, 2025
f5a7d56
Merge pull request #566 from macrocosm-os/SN1-396-move-api-docs-to-th…
bkb2135 Feb 5, 2025
1c2a505
Fix scoring queue
dbobrenko Feb 6, 2025
6f37d23
Fix pre-commit
dbobrenko Feb 6, 2025
11159ce
Improve logging
dbobrenko Feb 6, 2025
345cf21
Minor fixes
dbobrenko Feb 6, 2025
dc8c1e5
Run pre-commit
dbobrenko Feb 6, 2025
0a3119f
Remove tasks deps from API
dbobrenko Feb 6, 2025
420ecbf
Remove unused imports
dbobrenko Feb 6, 2025
bb3a03b
Fix uid key error
dbobrenko Feb 6, 2025
5635855
Fix test api script
dbobrenko Feb 6, 2025
f474a61
Clean up the code
dbobrenko Feb 6, 2025
25b8168
AttributeError: 'SharedSettings' object has no attribute 'INFERENCE_T…
0xxfu Feb 6, 2025
c119e41
Fix extract content with cache (#568)
0xxfu Feb 6, 2025
2ed42b3
Hot-fix task argument in inference task
dbobrenko Feb 6, 2025
dce7df5
Move website cache to DDG
dbobrenko Feb 6, 2025
40cc656
Clean up the code
dbobrenko Feb 6, 2025
624611d
Run pre-commit
dbobrenko Feb 6, 2025
eea5e8d
Restore workers
dbobrenko Feb 6, 2025
7f67427
Fix api
dbobrenko Feb 6, 2025
a9a0d84
Fix background tasks
dbobrenko Feb 6, 2025
63b9afc
Remove non-related files
dbobrenko Feb 6, 2025
06526cb
Move background tasks to lifespan
dbobrenko Feb 6, 2025
f07d1e3
Run pre-commit
dbobrenko Feb 6, 2025
d57b51d
Merge pull request #569 from macrocosm-os/hotfix/SN1-398-inference-web
bkb2135 Feb 7, 2025
e1b13ff
Merge pull request #570 from macrocosm-os/fix/SN1-399-restore-api-wor…
bkb2135 Feb 7, 2025
3c88090
Merge branch 'staging' into feature/SN1-394-scoring-queue
bkb2135 Feb 7, 2025
70980e3
Move Docs to docs folder
bkb2135 Feb 7, 2025
9703236
Merge pull request #565 from macrocosm-os/feature/SN1-394-scoring-queue
dbobrenko Feb 7, 2025
003d40d
Prevent relevant content not on site
bkb2135 Feb 7, 2025
006abf5
Merge pull request #573 from macrocosm-os/features/move-all-docs-to-o…
bkb2135 Feb 7, 2025
c7b52a0
Linting
richwardle Feb 7, 2025
207fc0a
Improving mixture of miners such that system prompt stays consistent
richwardle Feb 7, 2025
639c6f2
Merge pull request #558 from macrocosm-os/SN1-390-network-api-bandwidth
dbobrenko Feb 7, 2025
b64cf1b
Merge pull request #574 from macrocosm-os/hotfix/check-if-relevant-in…
bkb2135 Feb 7, 2025
dc02c71
Bump v2.17.1; reduce log verbosity; apply task fixes (#577)
dbobrenko Feb 9, 2025
e889245
Randomizing seed for mixture of miners (#571)
Hollyqui Feb 10, 2025
533be36
Hotfix/ensuring multiple messages passed (#575)
Hollyqui Feb 10, 2025
28a5889
Merge branch 'staging' of github.com:macrocosm-os/prompting into staging
richwardle Feb 10, 2025
876ffab
Fix everything
dbobrenko Feb 10, 2025
5f967c2
Clean up code
dbobrenko Feb 10, 2025
317e542
Run pre-commit
dbobrenko Feb 10, 2025
3a3d8d3
Remove commented code
dbobrenko Feb 10, 2025
27ddc36
Move settings mode before wandb init
dbobrenko Feb 10, 2025
ff85ece
Remove wrong settings reference
dbobrenko Feb 10, 2025
a0ace82
Add mock to wandb tags
dbobrenko Feb 10, 2025
82b37d4
Fix wrong settings references
dbobrenko Feb 10, 2025
82463f4
Remove unecessary logging
richwardle Feb 10, 2025
4d41eb2
Remove unused imports
richwardle Feb 10, 2025
629a5ae
Use app state to manage task scorer
richwardle Feb 10, 2025
45500a3
Precommit Fixes
richwardle Feb 11, 2025
8eb5b7b
Merge pull request #578 from macrocosm-os/fix/SN1-401-fix-wandb
bkb2135 Feb 11, 2025
9c9c87b
hotfix/fix-exploit-in-web-retrieval (#580)
Hollyqui Feb 11, 2025
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Validators and miners are based on large language models (LLM). The validation p

<div align="center">

**[For Validators](./assets/validator.md)** · **[For Miners](./assets/miner.md)** · **[API Documentation](./validator_api/API_docs.md)**
**[For Validators](./docs/validator.md)** · **[For Miners](./docs/epistula_miner.md)** · **[API Documentation]((./docs/API_docs.md))**


</div>
Expand Down Expand Up @@ -66,7 +66,7 @@ The miner is given a complex problem that requires multiple steps to solve. Each

# API Documentation

For detailed information on the available API endpoints, request/response formats, and usage examples, please refer to the [API Documentation](./validator_api/API_docs.md).
For detailed information on the available API endpoints, request/response formats, and usage examples, please refer to the [API Documentation](./docs/API_docs.md).

# Contribute
<div align="center">
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions neurons/miners/epistula_miner/web_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from openai import OpenAI

from prompting.base.duckduckgo_patch import PatchedDDGS
from shared.settings import shared_settings
from shared import settings

# Import the patched DDGS and use that

Expand Down Expand Up @@ -56,7 +56,7 @@ async def get_websites_with_similarity(
List of dictionaries containing website URLs and their best matching chunks
"""
logger.debug("Getting results")
ddgs = PatchedDDGS(proxy=shared_settings.PROXY_URL, verify=False)
ddgs = PatchedDDGS(proxy=settings.shared_settings.PROXY_URL, verify=False)
results = list(ddgs.text(query))
logger.debug(f"Got {len(results)} results")
urls = [r["href"] for r in results][:n_results]
Expand All @@ -66,7 +66,7 @@ async def get_websites_with_similarity(
extracted = await asyncio.gather(*[extract_content(c) for c in content])

# Create embeddings
client = OpenAI(api_key=shared_settings.OPENAI_API_KEY)
client = OpenAI(api_key=settings.shared_settings.OPENAI_API_KEY)
query_embedding = client.embeddings.create(model="text-embedding-ada-002", input=query).data[0].embedding
# Process each website
results_with_similarity = []
Expand Down
45 changes: 14 additions & 31 deletions neurons/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

# ruff: noqa: E402
from shared import settings
from shared.logging import init_wandb

shared_settings = settings.shared_settings
settings.shared_settings = settings.SharedSettings.load(mode="validator")


Expand All @@ -26,13 +26,12 @@


def create_loop_process(task_queue, scoring_queue, reward_events):
settings.shared_settings = settings.SharedSettings.load(mode="validator")
if settings.shared_settings.WANDB_ON:
init_wandb(neuron="validator")

async def spawn_loops(task_queue, scoring_queue, reward_events):
# ruff: noqa: E402
wandb.setup()
from shared import settings

settings.shared_settings = settings.SharedSettings.load(mode="validator")

from prompting.llms.model_manager import model_scheduler
from prompting.miner_availability.miner_availability import availability_checking_loop
from prompting.rewards.scoring import task_scorer
Expand Down Expand Up @@ -88,10 +87,11 @@ async def start():
# TODO: We should not use 2 availability loops for each process, in reality
# we should only be sharing the miner availability data between processes.
from prompting.miner_availability.miner_availability import availability_checking_loop
from prompting.rewards.scoring import task_scorer

asyncio.create_task(availability_checking_loop.start())

await start_scoring_api(scoring_queue, reward_events)
await start_scoring_api(task_scorer, scoring_queue, reward_events)

while True:
await asyncio.sleep(10)
Expand All @@ -100,23 +100,6 @@ async def start():
asyncio.run(start())


# def create_task_loop(task_queue, scoring_queue):
# async def start(task_queue, scoring_queue):
# logger.info("Starting AvailabilityCheckingLoop...")
# asyncio.create_task(availability_checking_loop.start())

# logger.info("Starting TaskSender...")
# asyncio.create_task(task_sender.start(task_queue, scoring_queue))

# logger.info("Starting TaskLoop...")
# asyncio.create_task(task_loop.start(task_queue, scoring_queue))
# while True:
# await asyncio.sleep(10)
# logger.debug("Running task loop...")

# asyncio.run(start(task_queue, scoring_queue))


async def main():
# will start checking the availability of miners at regular intervals, needed for API and Validator
with torch.multiprocessing.Manager() as manager:
Expand All @@ -130,7 +113,7 @@ async def main():
try:
# # Start checking the availability of miners at regular intervals

if shared_settings.DEPLOY_SCORING_API:
if settings.shared_settings.DEPLOY_SCORING_API:
# Use multiprocessing to bypass API blocking issue
api_process = mp.Process(target=start_api, args=(scoring_queue, reward_events), name="API_Process")
api_process.start()
Expand All @@ -152,17 +135,17 @@ async def main():
while True:
await asyncio.sleep(30)
if (
shared_settings.SUBTENSOR.get_current_block()
- shared_settings.METAGRAPH.last_update[shared_settings.UID]
settings.shared_settings.SUBTENSOR.get_current_block()
- settings.shared_settings.METAGRAPH.last_update[settings.shared_settings.UID]
> 500
and step > 120
):
current_block = settings.shared_settings.SUBTENSOR.get_current_block()
last_update_block = settings.shared_settings.METAGRAPH.last_update[settings.shared_settings.UID]
logger.warning(
f"UPDATES HAVE STALED FOR {shared_settings.SUBTENSOR.get_current_block() - shared_settings.METAGRAPH.last_update[shared_settings.UID]} BLOCKS AND {step} STEPS"
)
logger.warning(
f"STALED: {shared_settings.SUBTENSOR.get_current_block()}, {shared_settings.METAGRAPH.block}"
f"UPDATES HAVE STALED FOR {current_block - last_update_block} BLOCKS AND {step} STEPS"
)
logger.warning(f"STALED: {current_block}, {settings.shared_settings.METAGRAPH.block}")
sys.exit(1)
step += 1

Expand Down
20 changes: 13 additions & 7 deletions prompting/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

from prompting.api.miner_availabilities.api import router as miner_availabilities_router
from prompting.api.scoring.api import router as scoring_router
from prompting.rewards.scoring import task_scorer
from shared.settings import shared_settings
from shared import settings

app = FastAPI()
app.include_router(miner_availabilities_router, prefix="/miner_availabilities", tags=["miner_availabilities"])
Expand All @@ -18,10 +17,17 @@ def health():
return {"status": "healthy"}


async def start_scoring_api(scoring_queue, reward_events):
task_scorer.scoring_queue = scoring_queue
task_scorer.reward_events = reward_events
logger.info(f"Starting Scoring API on https://0.0.0.0:{shared_settings.SCORING_API_PORT}")
async def start_scoring_api(task_scorer, scoring_queue, reward_events):
# We pass an object of task scorer then override it's attributes to ensure that they are managed by mp
app.state.task_scorer = task_scorer
app.state.task_scorer.scoring_queue = scoring_queue
app.state.task_scorer.reward_events = reward_events

logger.info(f"Starting Scoring API on https://0.0.0.0:{settings.shared_settings.SCORING_API_PORT}")
uvicorn.run(
"prompting.api.api:app", host="0.0.0.0", port=shared_settings.SCORING_API_PORT, loop="asyncio", reload=False
"prompting.api.api:app",
host="0.0.0.0",
port=settings.shared_settings.SCORING_API_PORT,
loop="asyncio",
reload=False,
)
23 changes: 14 additions & 9 deletions prompting/api/scoring/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,33 @@

from prompting.datasets.random_website import DDGDatasetEntry
from prompting.llms.model_zoo import ModelZoo
from prompting.rewards.scoring import task_scorer
from prompting.tasks.inference import InferenceTask
from prompting.tasks.web_retrieval import WebRetrievalTask
from shared import settings
from shared.base import DatasetEntry
from shared.dendrite import DendriteResponseEvent
from shared.epistula import SynapseStreamResult
from shared.settings import shared_settings

router = APIRouter()


def validate_scoring_key(api_key: str = Header(...)):
if api_key != shared_settings.SCORING_KEY:
if api_key != settings.shared_settings.SCORING_KEY:
raise HTTPException(status_code=403, detail="Invalid API key")


def get_task_scorer(request: Request):
return request.app.state.task_scorer


@router.post("/scoring")
async def score_response(request: Request, api_key_data: dict = Depends(validate_scoring_key)):
async def score_response(
request: Request, api_key_data: dict = Depends(validate_scoring_key), task_scorer=Depends(get_task_scorer)
):
model = None
payload: dict[str, Any] = await request.json()
body = payload.get("body")
timeout = payload.get("timeout", shared_settings.NEURON_TIMEOUT)
timeout = payload.get("timeout", settings.shared_settings.NEURON_TIMEOUT)
uids = payload.get("uid", [])
chunks = payload.get("chunks", {})
if not uids or not chunks:
Expand All @@ -54,7 +59,7 @@ async def score_response(request: Request, api_key_data: dict = Depends(validate
llm_model=llm_model,
llm_model_id=body.get("model"),
seed=int(body.get("seed", 0)),
sampling_params=body.get("sampling_parameters", shared_settings.SAMPLING_PARAMS),
sampling_params=body.get("sampling_parameters", settings.shared_settings.SAMPLING_PARAMS),
query=body.get("messages"),
)
logger.info(f"Task created: {organic_task}")
Expand All @@ -66,7 +71,7 @@ async def score_response(request: Request, api_key_data: dict = Depends(validate
timeout=timeout,
),
dataset_entry=DatasetEntry(),
block=shared_settings.METAGRAPH.block,
block=settings.shared_settings.METAGRAPH.block,
step=-1,
task_id=str(uuid.uuid4()),
)
Expand All @@ -90,10 +95,10 @@ async def score_response(request: Request, api_key_data: dict = Depends(validate
stream_results=[
SynapseStreamResult(accumulated_chunks=[chunk for chunk in chunks if chunk is not None])
],
timeout=body.get("timeout", shared_settings.NEURON_TIMEOUT),
timeout=body.get("timeout", settings.shared_settings.NEURON_TIMEOUT),
),
dataset_entry=DDGDatasetEntry(search_term=search_term),
block=shared_settings.METAGRAPH.block,
block=settings.shared_settings.METAGRAPH.block,
step=-1,
task_id=str(uuid.uuid4()),
)
Expand Down
7 changes: 4 additions & 3 deletions prompting/datasets/random_website.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import random
from functools import lru_cache
from typing import Optional

import trafilatura
from loguru import logger

# from duckduckgo_search import DDGS
from prompting.base.duckduckgo_patch import PatchedDDGS
from prompting.datasets.utils import ENGLISH_WORDS
from shared import settings
from shared.base import BaseDataset, Context, DatasetEntry
from shared.settings import shared_settings

MAX_CHARS = 5000

Expand All @@ -25,7 +25,7 @@ class DDGDataset(BaseDataset):
english_words: list[str] = None

def search_random_term(self, retries: int = 3) -> tuple[Optional[str], Optional[list[dict[str, str]]]]:
ddg = PatchedDDGS(proxy=shared_settings.PROXY_URL, verify=False)
ddg = PatchedDDGS(proxy=settings.shared_settings.PROXY_URL, verify=False)
for _ in range(retries):
random_words = " ".join(random.sample(ENGLISH_WORDS, 3))
try:
Expand All @@ -38,6 +38,7 @@ def search_random_term(self, retries: int = 3) -> tuple[Optional[str], Optional[
return None, None

@staticmethod
@lru_cache(maxsize=1000)
def extract_website_content(url: str) -> Optional[str]:
try:
website = trafilatura.fetch_url(url)
Expand Down
4 changes: 3 additions & 1 deletion prompting/llms/apis/gpt_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from pydantic import BaseModel

from prompting.llms.apis.llm_messages import LLMMessage, LLMMessages
from shared.settings import shared_settings
from shared import settings

shared_settings = settings.shared_settings


class GPT(BaseModel):
Expand Down
41 changes: 20 additions & 21 deletions prompting/llms/apis/llm_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
from prompting.llms.apis.gpt_wrapper import openai_client
from prompting.llms.apis.llm_messages import LLMMessages
from prompting.llms.apis.sn19_wrapper import chat_complete
from shared.settings import shared_settings
from shared import settings

shared_settings = settings.shared_settings


class LLMWrapper:
@staticmethod
def chat_complete(
messages: LLMMessages,
model="chat-llama-3-1-70b",
Expand All @@ -29,27 +32,23 @@ def chat_complete(
logprobs=logprobs,
)

except Exception as ex:
logger.exception(ex)
logger.warning("Failed to use SN19 API, falling back to GPT-3.5")
else:
if response is not None:
logger.debug(f"Generated {len(response)} characters using {model}")
return response
logger.warning(
"Failed to use SN19 API (check the SN19_API_KEY and/or SN19_API_URL), " "falling back to GPT-3.5"
except Exception:
logger.error(
"Failed to use SN19 API, falling back to GPT-3.5. "
"Make sure to specify 'SN19_API_KEY' and 'SN19_API_URL' in .env.validator"
)

model = "gpt-3.5-turbo"
response, _ = openai_client.chat_complete(
messages=messages,
model=model,
temperature=temperature,
max_tokens=max_tokens,
top_p=top_p,
stream=stream,
logprobs=logprobs,
)
response = response.choices[0].message.content
if response is None:
model = "gpt-3.5-turbo"
response, _ = openai_client.chat_complete(
messages=messages,
model=model,
temperature=temperature,
max_tokens=max_tokens,
top_p=top_p,
stream=stream,
logprobs=logprobs,
)
response = response.choices[0].message.content
logger.debug(f"Generated {len(response)} characters using {model}")
return response
16 changes: 7 additions & 9 deletions prompting/llms/apis/sn19_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import json

import requests
from loguru import logger
from tenacity import retry, stop_after_attempt, wait_exponential

from prompting.llms.apis.llm_messages import LLMMessages
from shared.settings import shared_settings
from shared import settings

shared_settings = settings.shared_settings


# TODO: key error in response.json() when response is 500
Expand Down Expand Up @@ -37,11 +38,8 @@ def chat_complete(
"logprobs": logprobs,
}
response = requests.post(url, headers=headers, data=json.dumps(data), timeout=30)
response_json = response.json()
try:
response_json = response.json()
try:
return response_json["choices"][0]["message"].get("content")
except KeyError:
return response_json["choices"][0]["delta"].get("content")
except Exception as e:
logger.exception(f"Error in chat_complete: {e}")
return response_json["choices"][0]["message"].get("content")
except KeyError:
return response_json["choices"][0]["delta"].get("content")
Loading