1 line
14 KiB
JSON
1 line
14 KiB
JSON
{"repo": "bitrecs/bitrecs-v2", "files": {"bitrecs_cli.py": {"28ade1474596e4f83c1bc43f259784816b406ddc": {"t": 1785674364.3875408, "n": 13196, "h": "8f9991082643a32e", "c": "\"\"\"\nBitrecs CLI - Upload miner artifacts to the Bitrecs platform.\n\n\"\"\"\nimport os\nimport secrets\nimport time\nimport httpx\nimport click\nimport asyncio\nimport functools\nimport utils.logger as logger\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom bittensor_wallet.wallet import Wallet\nfrom typing import Optional, Tuple\nfrom models.agent import Agent\nfrom models.miner_submission import MinerSubmission\nfrom rules.agent_validator import validate_artifact_template\nfrom rules.gist_validator import validate_artifact_gist\nfrom utils.gist import get_gist, get_gist_created_at\nfrom rich.console import Console\nfrom rich.panel import Panel\nfrom rich.progress import Progress, SpinnerColumn, TextColumn\nfrom rich.prompt import Prompt\nfrom version import __version__ as this_version\nfrom utils.commitment import commit_to_chain_with_reveal\nfrom utils.subtensor import close_subtensor\nfrom utils.verify import create_transport_signature\nfrom utils.commands import run_cmd\nfrom utils.orkp import DEFAULT_KEY_CREDIT_LIMIT_USD, DEFAULT_KEY_EXPIRY_HOURS, create_temporary_openrouter_key\nfrom llm.llm_provider import LLM\n\nconsole = Console()\nDEFAULT_API_BASE_URL = \"https://v2.api.bitrecs.ai\"\n#DEFAULT_API_BASE_URL = \"https://v2.testnet.api.bitrecs.ai\"\n#DEFAULT_API_BASE_URL = \"http://localhost:8000\"\n\nconsole.print(f\"Bitrecs CLI - Version {this_version} using Endpoint {DEFAULT_API_BASE_URL}\", style=\"bold cyan\")\n\ndef get_or_prompt(key: str, prompt: str, default: Optional[str] = None) -> str:\n \"\"\"Get value from environment or prompt user.\"\"\"\n value = os.getenv(key)\n if not value:\n value = Prompt.ask(f\"{prompt}\", default=default) if default else Prompt.ask(f\"{prompt}\")\n return value\n\nclass BitrecsCLI:\n def __init__(self, api_url: Optional[str] = None):\n self.api_url = api_url or DEFAULT_API_BASE_URL\n \n\n@click.group()\n@click.version_option(version=this_version, prog_name=\"Bitrecs CLI\")\n@click.option(\"--url\", help=f\"Custom API URL (default: {DEFAULT_API_BASE_URL})\")\n@click.pass_context\ndef cli(ctx, url):\n \"\"\"Bitrecs CLI - Manage your Bitrecs miners and validators\"\"\"\n ctx.ensure_object(dict)\n ctx.obj['url'] = url\n\ndef async_run(f):\n \"\"\"Decorator to run async functions in Click.\"\"\"\n @functools.wraps(f)\n def wrapper(*args, **kwargs):\n return asyncio.run(f(*args, **kwargs))\n return wrapper\n\n@cli.command()\n@click.option(\"--github-account\", help=\"GitHub account name\")\n@click.option(\"--gist-id\", help=\"Gist ID containing your miner_artifact.yaml\")\n@click.option(\"--coldkey-name\", help=\"Coldkey name\")\n@click.option(\"--hotkey-name\", help=\"Hotkey name\")\n@click.option(\"--netuid\", type=int, help=\"Netuid for the subnet\")\n@click.pass_context\n@async_run\nasync def upload(ctx, github_account: Optional[str], gist_id: Optional[str], coldkey_name: Optional[str], hotkey_name: Optional[str], netuid: Optional[int]):\n \"\"\"Upload a miner artifact to the Bitrecs API\"\"\"\n \n start_time = time.perf_counter()\n bitrecs = BitrecsCLI(ctx.obj.get('url'))\n netuid = netuid or int(get_or_prompt(\"NETUID\", \"Enter the Netuid\", \"122\"))\n if not any([github_account, gist_id]):\n console.print(\"Please provide either --github-account and --gist-id, or ensure the corresponding environment variables are set.\", style=\"bold red\")\n return\n if not any([coldkey_name, hotkey_name]):\n console.print(\"Please provide either --coldkey-name and --hotkey-name, or ensure the corresponding environment variables are set.\", style=\"bold red\")\n return\n \n coldkey = coldkey_name or get_or_prompt(\"COLDKEY_NAME\", \"Enter your coldkey name\", \"default\")\n hotkey = hotkey_name or get_or_prompt(\"HOTKEY_NAME\", \"Enter your hotkey name\", \"default\")\n wallet = Wallet(name=coldkey, hotkey=hotkey)\n \n validated, reason = validate_artifact_gist(gist_id)\n if not validated:\n console.print(f\"Artifact Gist validation failed: {reason}\", style=\"bold red\")\n return\n\n gist_raw_data = get_gist(github_account, gist_id)\n artifact = Agent.from_yaml(gist_raw_data)\n validated, reason = validate_artifact_template(artifact, gist_raw_data)\n if not validated:\n console.print(f\"Artifact validation failed: {reason}\", style=\"bold red\")\n return\n\n gist_created_at = get_gist_created_at(gist_id)\n preamble = f\"{gist_created_at.isoformat()}:{github_account}:{gist_id}:{wallet.hotkey.ss58_address}\"\n signature = wallet.hotkey.sign(preamble).hex()\n submission = MinerSubmission(\n created_at=gist_created_at.isoformat(),\n github_account=github_account,\n gist_id=gist_id,\n hotkey=wallet.hotkey.ss58_address,\n signature=signature)\n \n console.print(Panel(f\"[bold cyan]Preparing to Upload Artifact with Commitment[/bold cyan]\\n[yellow]GitHub Account:[/yellow] {github_account}\\n[yellow]Gist ID:[/yellow] {gist_id}\\n[yellow]Hotkey:[/yellow] {wallet.hotkey.ss58_address}\\n[yellow]Netuid:[/yellow] {netuid}\", title=\"Upload\", border_style=\"cyan\"))\n try:\n logger.info(f\"Starting upload with Commitment for Gist {gist_id} using wallet {wallet.hotkey.ss58_address} on Netuid {netuid}\") \n with httpx.Client() as client:\n console.print(\"Checking agent eligibility with the Bitrecs API...\", style=\"dim\") \n check_response = client.post(f\"{bitrecs.api_url}/check\", json=submission.to_dict(), timeout=120)\n if check_response.status_code != 200:\n console.print(f\"Error checking agent: {check_response.text}\", style=\"bold red\")\n return \n\n provider = LLM.try_parse(artifact.provider)\n if provider == LLM.OPEN_ROUTER:\n console.print(\"This artifact uses OpenRouter as its provider, which requires a temporary API key to be generated and attached to your submission.\\nThis key will be created with a limited lifespan and credit to ensure security. Do you want to proceed?\", style=\"yellow\")\n console.print(\"Note: you must have a valid OPENROUTER_MGMT_KEY environment variable set to use this feature.\", style=\"yellow\")\n proceed = Prompt.ask(\"Proceed with creating temporary OpenRouter key?\", choices=[\"y\", \"n\"], default=\"y\")\n if not proceed or proceed.lower() != \"y\":\n console.print(\"Aborting upload as per user request.\", style=\"bold red\")\n return\n open_router_mgmt_key = os.getenv(\"OPENROUTER_MGMT_KEY\")\n if not open_router_mgmt_key:\n console.print(\"OPENROUTER_MGMT_KEY environment variable is not set. Please set it and try again.\", style=\"bold red\")\n return\n temp_key = await create_temporary_openrouter_key(\n mgmt_key=open_router_mgmt_key,\n name=f\"miner-{wallet.hotkey.ss58_address[:8]}-{gist_id}\"\n )\n if temp_key is None or not temp_key.startswith(\"sk-or-v1-\"):\n console.print(\"Failed to create temporary OpenRouter key. Please check your OPENROUTER_MGMT_KEY and try again.\", style=\"bold red\")\n console.print(\"For information on management keys see https://openrouter.ai/docs/guides/overview/auth/management-api-keys\", style=\"bold red\")\n return\n else:\n console.print(\"Temporary OpenRouter key created successfully and will be used for this submission.\", style=\"bold green\")\n console.print(f\"This key has a limit of {DEFAULT_KEY_CREDIT_LIMIT_USD} USD and expires in {DEFAULT_KEY_EXPIRY_HOURS} hours.\", style=\"bold green\")\n\n \n async def commit_to_chain_task() -> Tuple[bool, int]:\n commited, current_block = await commit_to_chain_with_reveal(submission.github_account, submission.gist_id, wallet)\n return commited, current_block\n \n with Progress(SpinnerColumn(), TextColumn(\"[progress.description]{task.description}\"), console=console, transient=True) as progress: \n commit_task_id = progress.add_task(\"Committing to chain...this can take up to 1 minute\", total=None)\n commit_task = asyncio.create_task(commit_to_chain_task())\n await asyncio.gather(commit_task)\n progress.update(commit_task_id, completed=True)\n \n commited, current_block = commit_task.result()\n if not commited:\n console.print(\"Commitment to chain failed. Please check to ensure you are connected to the correct network and registered and try again.\", style=\"bold red\") \n return\n \n console.print(f\"\\n[bold green]Commitment to chain successful on block {current_block}![/bold green]\") \n with Progress(SpinnerColumn(), TextColumn(\"[progress.description]{task.description}\"), console=console, transient=True) as progress:\n # Wait for propogation\n prop_id = progress.add_task(f\"Confirming blocks ... please stand by\", total=None)\n await asyncio.sleep(24)\n progress.update(prop_id, completed=True)\n \n progress.add_task(\"Submitting artifact...\", total=None)\n nonce = secrets.token_hex(16)\n ts = int(time.time())\n t_sig, t_nonce = create_transport_signature(wallet, submission, nonce, ts)\n headers = { \n 'Accept': 'application/json',\n 'Referer': f'{bitrecs.api_url}/',\n 'X-Signature': t_sig,\n 'X-Timestamp': str(ts),\n 'X-Nonce': nonce,\n 'X-T-Nonce': t_nonce\n }\n if provider == LLM.OPEN_ROUTER and temp_key:\n headers[\"X-ORTK\"] = temp_key\n \n submit_url =f\"{bitrecs.api_url}/submit\"\n response = client.post(submit_url, json=submission.to_dict(), timeout=120, headers=headers)\n \n end_time = time.perf_counter()\n elapsed = end_time - start_time\n console.print(f\"Upload process took {elapsed:.2f} seconds.\", style=\"dim\")\n\n if response.status_code == 201:\n response_data = response.json()\n console.print(Panel(f\"[bold green]Upload Complete[/bold green]\\n[cyan]Artifact uploaded successfully![/cyan]\", title=\"Success\", border_style=\"green\"))\n console.print(f\"The {submission.github_account}/{submission.gist_id} artifact has been uploaded and is queued for evaluation.\")\n artifact_id = response_data.get('artifact_id')\n console.print(f\"Your artifact ID is: [yellow]{artifact_id}[/yellow]\")\n artifact_url = f\"https://dashboard.bitrecs.ai/agent/{artifact_id}\"\n console.print(f\"View your artifact here: [yellow]{artifact_url}[/yellow]\") \n \n request_id = response_data.get('request_id')\n message = response_data.get('message')\n similarity_check = response_data.get('similarity_check')\n similar_results = response_data.get('similar_results', [])\n \n console.print(f\"\\n[bold cyan]Response Details:[/bold cyan]\")\n console.print(f\"Request ID: [yellow]{request_id}[/yellow]\")\n console.print(f\"Message: [yellow]{message}[/yellow]\")\n console.print(f\"Similarity Check: [yellow]{similarity_check}[/yellow]\")\n if similar_results:\n console.print(f\"Similar Agents:\")\n for result in similar_results:\n agent_id = result.get('agent_id')\n distance = result.get('distance')\n console.print(f\" - Agent ID: [yellow]{agent_id}[/yellow], Distance: [yellow]{distance}[/yellow]\")\n else:\n console.print(\"No similar agents found.\")\n console.print(f\"Thank you for contributing to the Bitrecs ecosystem!\", style=\"bold cyan\")\n else:\n try:\n error_data = response.json()\n error_msg = error_data.get('error') or error_data.get('detail') or 'Unknown error'\n console.print(f\"Upload failed (status {response.status_code}): {error_msg}\", style=\"bold red\")\n except ValueError:\n console.print(f\"Upload failed (status {response.status_code})\", style=\"bold red\")\n console.print(\"Your artifact was not uploaded - Please check the error message and try again.\", style=\"bold red\")\n \n except Exception as e:\n end_time = time.perf_counter()\n elapsed = end_time - start_time\n logger.info(f\"Upload failed after {elapsed:.2f} seconds with error: {str(e)}\") \n console.print(f\"Error after {elapsed:.2f} seconds: {e}\", style=\"bold red\")\n raise e\n finally:\n await close_subtensor()\n\n\n\nif __name__ == \"__main__\": \n run_cmd(\". .venv/bin/activate\")\n cli()"}}}} |