AutoGPT
/
cli.py
933 строки · 32.3 Кб
1"""
2This is a minimal file intended to be run by users to help them manage the autogpt projects.
3
4If you want to contribute, please use only libraries that come as part of Python.
5To ensure efficiency, add the imports to the functions so only what is needed is imported.
6"""
7try:
8import click
9import github
10except ImportError:
11import os
12
13os.system("pip3 install click")
14os.system("pip3 install PyGithub")
15import click
16
17
18@click.group()
19def cli():
20pass
21
22
23@cli.command()
24def setup():
25"""Installs dependencies needed for your system. Works with Linux, MacOS and Windows WSL."""
26import os
27import subprocess
28
29click.echo(
30click.style(
31"""
32d8888 888 .d8888b. 8888888b. 88888888888
33d88888 888 d88P Y88b 888 Y88b 888
34d88P888 888 888 888 888 888 888
35d88P 888 888 888 888888 .d88b. 888 888 d88P 888
36d88P 888 888 888 888 d88""88b 888 88888 8888888P" 888
37d88P 888 888 888 888 888 888 888 888 888 888
38d8888888888 Y88b 888 Y88b. Y88..88P Y88b d88P 888 888
39d88P 888 "Y88888 "Y888 "Y88P" "Y8888P88 888 888
40
41""",
42fg="green",
43)
44)
45
46script_dir = os.path.dirname(os.path.realpath(__file__))
47setup_script = os.path.join(script_dir, "setup.sh")
48install_error = False
49if os.path.exists(setup_script):
50click.echo(click.style("🚀 Setup initiated...\n", fg="green"))
51try:
52subprocess.check_call([setup_script], cwd=script_dir)
53except subprocess.CalledProcessError:
54click.echo(
55click.style("❌ There was an issue with the installation.", fg="red")
56)
57install_error = True
58else:
59click.echo(
60click.style(
61"❌ Error: setup.sh does not exist in the current directory.", fg="red"
62)
63)
64install_error = True
65
66try:
67# Check if git user is configured
68user_name = (
69subprocess.check_output(["git", "config", "user.name"])
70.decode("utf-8")
71.strip()
72)
73user_email = (
74subprocess.check_output(["git", "config", "user.email"])
75.decode("utf-8")
76.strip()
77)
78
79if user_name and user_email:
80click.echo(
81click.style(
82f"✅ Git is configured with name '{user_name}' and email '{user_email}'",
83fg="green",
84)
85)
86else:
87raise subprocess.CalledProcessError(
88returncode=1, cmd="git config user.name or user.email"
89)
90
91except subprocess.CalledProcessError:
92# If the GitHub account is not configured, print instructions on how to set it up
93click.echo(click.style("⚠️ Git user is not configured.", fg="red"))
94click.echo(
95click.style(
96"To configure Git with your user info, use the following commands:",
97fg="red",
98)
99)
100click.echo(
101click.style(' git config --global user.name "Your (user)name"', fg="red")
102)
103click.echo(
104click.style(' git config --global user.email "Your email"', fg="red")
105)
106install_error = True
107
108print_access_token_instructions = False
109
110# Check for the existence of the .github_access_token file
111if os.path.exists(".github_access_token"):
112with open(".github_access_token", "r") as file:
113github_access_token = file.read().strip()
114if github_access_token:
115click.echo(
116click.style(
117"✅ GitHub access token loaded successfully.", fg="green"
118)
119)
120# Check if the token has the required permissions
121import requests
122
123headers = {"Authorization": f"token {github_access_token}"}
124response = requests.get("https://api.github.com/user", headers=headers)
125if response.status_code == 200:
126scopes = response.headers.get("X-OAuth-Scopes")
127if "public_repo" in scopes or "repo" in scopes:
128click.echo(
129click.style(
130"✅ GitHub access token has the required permissions.",
131fg="green",
132)
133)
134else:
135install_error = True
136click.echo(
137click.style(
138"❌ GitHub access token does not have the required permissions. Please ensure it has 'public_repo' or 'repo' scope.",
139fg="red",
140)
141)
142else:
143install_error = True
144click.echo(
145click.style(
146"❌ Failed to validate GitHub access token. Please ensure it is correct.",
147fg="red",
148)
149)
150else:
151install_error = True
152click.echo(
153click.style(
154"❌ GitHub access token file is empty. Please follow the instructions below to set up your GitHub access token.",
155fg="red",
156)
157)
158print_access_token_instructions = True
159else:
160# Create the .github_access_token file if it doesn't exist
161with open(".github_access_token", "w") as file:
162file.write("")
163install_error = True
164print_access_token_instructions = True
165
166if print_access_token_instructions:
167# Instructions to set up GitHub access token
168click.echo(
169click.style(
170"💡 To configure your GitHub access token, follow these steps:", fg="red"
171)
172)
173click.echo(
174click.style("\t1. Ensure you are logged into your GitHub account", fg="red")
175)
176click.echo(
177click.style("\t2. Navigate to https://github.com/settings/tokens", fg="red")
178)
179click.echo(click.style("\t3. Click on 'Generate new token'.", fg="red"))
180click.echo(
181click.style("\t4. Click on 'Generate new token (classic)'.", fg="red")
182)
183click.echo(
184click.style(
185"\t5. Fill out the form to generate a new token. Ensure you select the 'repo' scope.",
186fg="red",
187)
188)
189click.echo(
190click.style(
191"\t6. Open the '.github_access_token' file in the same directory as this script and paste the token into this file.",
192fg="red",
193)
194)
195click.echo(
196click.style("\t7. Save the file and run the setup command again.", fg="red")
197)
198
199if install_error:
200click.echo(
201click.style(
202"\n\n🔴 If you need help, please raise a ticket on GitHub at https://github.com/Significant-Gravitas/AutoGPT/issues\n\n",
203fg="magenta",
204bold=True,
205)
206)
207
208
209@cli.group()
210def agent():
211"""Commands to create, start and stop agents"""
212pass
213
214
215@agent.command()
216@click.argument("agent_name")
217def create(agent_name):
218"""Create's a new agent with the agent name provided"""
219import os
220import re
221import shutil
222
223if not re.match(r"\w*$", agent_name):
224click.echo(
225click.style(
226f"😞 Agent name '{agent_name}' is not valid. It should not contain spaces or special characters other than -_",
227fg="red",
228)
229)
230return
231try:
232new_agent_dir = f"./autogpts/{agent_name}"
233new_agent_name = f"{agent_name.lower()}.json"
234
235existing_arena_files = [name.lower() for name in os.listdir("./arena/")]
236
237if (
238not os.path.exists(new_agent_dir)
239and not new_agent_name in existing_arena_files
240):
241shutil.copytree("./autogpts/forge", new_agent_dir)
242click.echo(
243click.style(
244f"🎉 New agent '{agent_name}' created. The code for your new agent is in: autogpts/{agent_name}",
245fg="green",
246)
247)
248else:
249click.echo(
250click.style(
251f"😞 Agent '{agent_name}' already exists. Enter a different name for your agent, the name needs to be unique regardless of case",
252fg="red",
253)
254)
255except Exception as e:
256click.echo(click.style(f"😢 An error occurred: {e}", fg="red"))
257
258
259@agent.command()
260@click.argument("agent_name")
261@click.option(
262"--no-setup",
263is_flag=True,
264help="Disables running the setup script before starting the agent",
265)
266def start(agent_name, no_setup):
267"""Start agent command"""
268import os
269import subprocess
270
271script_dir = os.path.dirname(os.path.realpath(__file__))
272agent_dir = os.path.join(script_dir, f"autogpts/{agent_name}")
273run_command = os.path.join(agent_dir, "run")
274run_bench_command = os.path.join(agent_dir, "run_benchmark")
275if (
276os.path.exists(agent_dir)
277and os.path.isfile(run_command)
278and os.path.isfile(run_bench_command)
279):
280os.chdir(agent_dir)
281if not no_setup:
282click.echo(f"⌛ Running setup for agent '{agent_name}'...")
283setup_process = subprocess.Popen(["./setup"], cwd=agent_dir)
284setup_process.wait()
285click.echo()
286
287subprocess.Popen(["./run_benchmark", "serve"], cwd=agent_dir)
288click.echo("⌛ (Re)starting benchmark server...")
289wait_until_conn_ready(8080)
290click.echo()
291
292subprocess.Popen(["./run"], cwd=agent_dir)
293click.echo(f"⌛ (Re)starting agent '{agent_name}'...")
294wait_until_conn_ready(8000)
295click.echo("✅ Agent application started and available on port 8000")
296elif not os.path.exists(agent_dir):
297click.echo(
298click.style(
299f"😞 Agent '{agent_name}' does not exist. Please create the agent first.",
300fg="red",
301)
302)
303else:
304click.echo(
305click.style(
306f"😞 Run command does not exist in the agent '{agent_name}' directory.",
307fg="red",
308)
309)
310
311
312@agent.command()
313def stop():
314"""Stop agent command"""
315import os
316import signal
317import subprocess
318
319try:
320pids = subprocess.check_output(["lsof", "-t", "-i", ":8000"]).split()
321if isinstance(pids, int):
322os.kill(int(pids), signal.SIGTERM)
323else:
324for pid in pids:
325os.kill(int(pid), signal.SIGTERM)
326except subprocess.CalledProcessError:
327click.echo("No process is running on port 8000")
328
329try:
330pids = int(subprocess.check_output(["lsof", "-t", "-i", ":8080"]))
331if isinstance(pids, int):
332os.kill(int(pids), signal.SIGTERM)
333else:
334for pid in pids:
335os.kill(int(pid), signal.SIGTERM)
336except subprocess.CalledProcessError:
337click.echo("No process is running on port 8080")
338
339
340@agent.command()
341def list():
342"""List agents command"""
343import os
344
345try:
346agents_dir = "./autogpts"
347agents_list = [
348d
349for d in os.listdir(agents_dir)
350if os.path.isdir(os.path.join(agents_dir, d))
351]
352if agents_list:
353click.echo(click.style("Available agents: 🤖", fg="green"))
354for agent in agents_list:
355click.echo(click.style(f"\t🐙 {agent}", fg="blue"))
356else:
357click.echo(click.style("No agents found 😞", fg="red"))
358except FileNotFoundError:
359click.echo(click.style("The autogpts directory does not exist 😢", fg="red"))
360except Exception as e:
361click.echo(click.style(f"An error occurred: {e} 😢", fg="red"))
362
363
364@cli.group()
365def benchmark():
366"""Commands to start the benchmark and list tests and categories"""
367pass
368
369
370@benchmark.command(
371context_settings=dict(
372ignore_unknown_options=True,
373)
374)
375@click.argument("agent_name")
376@click.argument("subprocess_args", nargs=-1, type=click.UNPROCESSED)
377def start(agent_name, subprocess_args):
378"""Starts the benchmark command"""
379import os
380import subprocess
381
382script_dir = os.path.dirname(os.path.realpath(__file__))
383agent_dir = os.path.join(script_dir, f"autogpts/{agent_name}")
384benchmark_script = os.path.join(agent_dir, "run_benchmark")
385if os.path.exists(agent_dir) and os.path.isfile(benchmark_script):
386os.chdir(agent_dir)
387subprocess.Popen([benchmark_script, *subprocess_args], cwd=agent_dir)
388click.echo(
389click.style(
390f"🚀 Running benchmark for '{agent_name}' with subprocess arguments: {' '.join(subprocess_args)}",
391fg="green",
392)
393)
394else:
395click.echo(
396click.style(
397f"😞 Agent '{agent_name}' does not exist. Please create the agent first.",
398fg="red",
399)
400)
401
402
403@benchmark.group(name="categories")
404def benchmark_categories():
405"""Benchmark categories group command"""
406pass
407
408
409@benchmark_categories.command(name="list")
410def benchmark_categories_list():
411"""List benchmark categories command"""
412import glob
413import json
414import os
415
416categories = set()
417
418# Get the directory of this file
419this_dir = os.path.dirname(os.path.abspath(__file__))
420
421glob_path = os.path.join(
422this_dir, "./benchmark/agbenchmark/challenges/**/[!deprecated]*/data.json"
423)
424# Use it as the base for the glob pattern, excluding 'deprecated' directory
425for data_file in glob.glob(glob_path, recursive=True):
426if "deprecated" not in data_file:
427with open(data_file, "r") as f:
428try:
429data = json.load(f)
430categories.update(data.get("category", []))
431except json.JSONDecodeError:
432print(f"Error: {data_file} is not a valid JSON file.")
433continue
434except IOError:
435print(f"IOError: file could not be read: {data_file}")
436continue
437
438if categories:
439click.echo(click.style("Available categories: 📚", fg="green"))
440for category in categories:
441click.echo(click.style(f"\t📖 {category}", fg="blue"))
442else:
443click.echo(click.style("No categories found 😞", fg="red"))
444
445
446@benchmark.group(name="tests")
447def benchmark_tests():
448"""Benchmark tests group command"""
449pass
450
451
452@benchmark_tests.command(name="list")
453def benchmark_tests_list():
454"""List benchmark tests command"""
455import glob
456import json
457import os
458import re
459
460tests = {}
461
462# Get the directory of this file
463this_dir = os.path.dirname(os.path.abspath(__file__))
464
465glob_path = os.path.join(
466this_dir, "./benchmark/agbenchmark/challenges/**/[!deprecated]*/data.json"
467)
468# Use it as the base for the glob pattern, excluding 'deprecated' directory
469for data_file in glob.glob(glob_path, recursive=True):
470if "deprecated" not in data_file:
471with open(data_file, "r") as f:
472try:
473data = json.load(f)
474category = data.get("category", [])
475test_name = data.get("name", "")
476if category and test_name:
477if category[0] not in tests:
478tests[category[0]] = []
479tests[category[0]].append(test_name)
480except json.JSONDecodeError:
481print(f"Error: {data_file} is not a valid JSON file.")
482continue
483except IOError:
484print(f"IOError: file could not be read: {data_file}")
485continue
486
487if tests:
488click.echo(click.style("Available tests: 📚", fg="green"))
489for category, test_list in tests.items():
490click.echo(click.style(f"\t📖 {category}", fg="blue"))
491for test in sorted(test_list):
492test_name = (
493" ".join(word for word in re.split("([A-Z][a-z]*)", test) if word)
494.replace("_", "")
495.replace("C L I", "CLI")
496.replace(" ", " ")
497)
498test_name_padded = f"{test_name:<40}"
499click.echo(click.style(f"\t\t🔬 {test_name_padded} - {test}", fg="cyan"))
500else:
501click.echo(click.style("No tests found 😞", fg="red"))
502
503
504@benchmark_tests.command(name="details")
505@click.argument("test_name")
506def benchmark_tests_details(test_name):
507"""Benchmark test details command"""
508import glob
509import json
510import os
511
512# Get the directory of this file
513this_dir = os.path.dirname(os.path.abspath(__file__))
514
515glob_path = os.path.join(
516this_dir, "./benchmark/agbenchmark/challenges/**/[!deprecated]*/data.json"
517)
518# Use it as the base for the glob pattern, excluding 'deprecated' directory
519for data_file in glob.glob(glob_path, recursive=True):
520with open(data_file, "r") as f:
521try:
522data = json.load(f)
523if data.get("name") == test_name:
524click.echo(
525click.style(
526f"\n{data.get('name')}\n{'-'*len(data.get('name'))}\n",
527fg="blue",
528)
529)
530click.echo(
531click.style(
532f"\tCategory: {', '.join(data.get('category'))}",
533fg="green",
534)
535)
536click.echo(click.style(f"\tTask: {data.get('task')}", fg="green"))
537click.echo(
538click.style(
539f"\tDependencies: {', '.join(data.get('dependencies')) if data.get('dependencies') else 'None'}",
540fg="green",
541)
542)
543click.echo(
544click.style(f"\tCutoff: {data.get('cutoff')}\n", fg="green")
545)
546click.echo(
547click.style("\tTest Conditions\n\t-------", fg="magenta")
548)
549click.echo(
550click.style(
551f"\t\tAnswer: {data.get('ground').get('answer')}",
552fg="magenta",
553)
554)
555click.echo(
556click.style(
557f"\t\tShould Contain: {', '.join(data.get('ground').get('should_contain'))}",
558fg="magenta",
559)
560)
561click.echo(
562click.style(
563f"\t\tShould Not Contain: {', '.join(data.get('ground').get('should_not_contain'))}",
564fg="magenta",
565)
566)
567click.echo(
568click.style(
569f"\t\tFiles: {', '.join(data.get('ground').get('files'))}",
570fg="magenta",
571)
572)
573click.echo(
574click.style(
575f"\t\tEval: {data.get('ground').get('eval').get('type')}\n",
576fg="magenta",
577)
578)
579click.echo(click.style("\tInfo\n\t-------", fg="yellow"))
580click.echo(
581click.style(
582f"\t\tDifficulty: {data.get('info').get('difficulty')}",
583fg="yellow",
584)
585)
586click.echo(
587click.style(
588f"\t\tDescription: {data.get('info').get('description')}",
589fg="yellow",
590)
591)
592click.echo(
593click.style(
594f"\t\tSide Effects: {', '.join(data.get('info').get('side_effects'))}",
595fg="yellow",
596)
597)
598break
599
600except json.JSONDecodeError:
601print(f"Error: {data_file} is not a valid JSON file.")
602continue
603except IOError:
604print(f"IOError: file could not be read: {data_file}")
605continue
606
607
608@cli.group()
609def arena():
610"""Commands to enter the arena"""
611pass
612
613
614@arena.command()
615@click.argument("agent_name")
616@click.option("--branch", default="master", help="Branch to use instead of master")
617def enter(agent_name, branch):
618import json
619import os
620import subprocess
621from datetime import datetime
622
623from github import Github
624
625# Check if the agent_name directory exists in the autogpts directory
626agent_dir = f"./autogpts/{agent_name}"
627if not os.path.exists(agent_dir):
628click.echo(
629click.style(
630f"❌ The directory for agent '{agent_name}' does not exist in the autogpts directory.",
631fg="red",
632)
633)
634click.echo(
635click.style(
636f"🚀 Run './run agent create {agent_name}' to create the agent.",
637fg="yellow",
638)
639)
640
641return
642else:
643# Check if the agent has already entered the arena
644try:
645subprocess.check_output(
646[
647"git",
648"rev-parse",
649"--verify",
650"--quiet",
651f"arena_submission_{agent_name}",
652]
653)
654except subprocess.CalledProcessError:
655pass
656else:
657click.echo(
658click.style(
659f"⚠️ The agent '{agent_name}' has already entered the arena. To update your submission, follow these steps:",
660fg="yellow",
661)
662)
663click.echo(
664click.style(
665f"1. Get the git hash of your submission by running 'git rev-parse HEAD' on the branch you want to submit to the arena.",
666fg="yellow",
667)
668)
669click.echo(
670click.style(
671f"2. Change the branch to 'arena_submission_{agent_name}' by running 'git checkout arena_submission_{agent_name}'.",
672fg="yellow",
673)
674)
675click.echo(
676click.style(
677f"3. Modify the 'arena/{agent_name}.json' to include the new commit hash of your submission (the hash you got from step 1) and an up-to-date timestamp by running './run arena update {agent_name} hash --branch x'.",
678fg="yellow",
679)
680)
681click.echo(
682click.style(
683f"Note: The '--branch' option is only needed if you want to change the branch that will be used.",
684fg="yellow",
685)
686)
687return
688
689# Check if there are staged changes
690staged_changes = [
691line
692for line in subprocess.check_output(["git", "status", "--porcelain"])
693.decode("utf-8")
694.split("\n")
695if line and line[0] in ("A", "M", "D", "R", "C")
696]
697if staged_changes:
698click.echo(
699click.style(
700f"❌ There are staged changes. Please commit or stash them and run the command again.",
701fg="red",
702)
703)
704return
705
706try:
707# Load GitHub access token from file
708with open(".github_access_token", "r") as file:
709github_access_token = file.read().strip()
710
711# Get GitHub repository URL
712github_repo_url = (
713subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
714.decode("utf-8")
715.strip()
716)
717
718if github_repo_url.startswith("git@"):
719github_repo_url = (
720github_repo_url.replace(":", "/")
721.replace("git@", "https://")
722.replace(".git", "")
723)
724
725# If --branch is passed, use it instead of master
726if branch:
727branch_to_use = branch
728else:
729branch_to_use = "master"
730
731# Get the commit hash of HEAD of the branch_to_use
732commit_hash_to_benchmark = (
733subprocess.check_output(["git", "rev-parse", branch_to_use])
734.decode("utf-8")
735.strip()
736)
737
738arena_submission_branch = f"arena_submission_{agent_name}"
739# Create a new branch called arena_submission_{agent_name}
740subprocess.check_call(["git", "checkout", "-b", arena_submission_branch])
741# Create a dictionary with the necessary fields
742data = {
743"github_repo_url": github_repo_url,
744"timestamp": datetime.utcnow().isoformat(),
745"commit_hash_to_benchmark": commit_hash_to_benchmark,
746}
747
748# If --branch was passed, add branch_to_benchmark to the JSON file
749if branch:
750data["branch_to_benchmark"] = branch
751
752# Create agent directory if it does not exist
753subprocess.check_call(["mkdir", "-p", "arena"])
754
755# Create a JSON file with the data
756with open(f"arena/{agent_name}.json", "w") as json_file:
757json.dump(data, json_file, indent=4)
758
759# Create a commit with the specified message
760subprocess.check_call(["git", "add", f"arena/{agent_name}.json"])
761subprocess.check_call(
762["git", "commit", "-m", f"{agent_name} entering the arena"]
763)
764
765# Push the commit
766subprocess.check_call(["git", "push", "origin", arena_submission_branch])
767
768# Create a PR into the parent repository
769g = Github(github_access_token)
770repo_name = github_repo_url.replace("https://github.com/", "")
771repo = g.get_repo(repo_name)
772parent_repo = repo.parent
773if parent_repo:
774pr_message = f"""
775### 🌟 Welcome to the AutoGPT Arena Hacks Hackathon! 🌟
776
777Hey there amazing builders! We're thrilled to have you join this exciting journey. Before you dive deep into building, we'd love to know more about you and the awesome project you are envisioning. Fill out the template below to kickstart your hackathon journey. May the best agent win! 🏆
778
779#### 🤖 Team Introduction
780
781- **Agent Name:** {agent_name}
782- **Team Members:** (Who are the amazing minds behind this team? Do list everyone along with their roles!)
783- **Repository Link:** [{github_repo_url.replace('https://github.com/', '')}]({github_repo_url})
784
785#### 🌟 Project Vision
786
787- **Starting Point:** (Are you building from scratch or starting with an existing agent? Do tell!)
788- **Preliminary Ideas:** (Share your initial ideas and what kind of project you are aiming to build. We are all ears!)
789
790#### 🏆 Prize Category
791
792- **Target Prize:** (Which prize caught your eye? Which one are you aiming for?)
793- **Why this Prize:** (We'd love to know why this prize feels like the right fit for your team!)
794
795#### 🎬 Introduction Video
796
797- **Video Link:** (If you'd like, share a short video where you introduce your team and talk about your project. We'd love to see your enthusiastic faces!)
798
799#### 📝 Notes and Compliance
800
801- **Additional Notes:** (Any other things you want to share? We're here to listen!)
802- **Compliance with Hackathon Rules:** (Just a gentle reminder to stick to the rules outlined for the hackathon)
803
804#### ✅ Checklist
805
806- [ ] We have read and are aligned with the [Hackathon Rules](https://lablab.ai/event/autogpt-arena-hacks).
807- [ ] We confirm that our project will be open-source and adhere to the MIT License.
808- [ ] Our lablab.ai registration email matches our OpenAI account to claim the bonus credits (if applicable).
809"""
810head = f"{repo.owner.login}:{arena_submission_branch}"
811pr = parent_repo.create_pull(
812title=f"{agent_name} entering the arena",
813body=pr_message,
814head=head,
815base=branch_to_use,
816)
817click.echo(
818click.style(
819f"🚀 {agent_name} has entered the arena! Please edit your PR description at the following URL: {pr.html_url}",
820fg="green",
821)
822)
823else:
824click.echo(
825click.style(
826"❌ This repository does not have a parent repository to sync with.",
827fg="red",
828)
829)
830return
831
832# Switch back to the master branch
833subprocess.check_call(["git", "checkout", branch_to_use])
834
835except Exception as e:
836click.echo(click.style(f"❌ An error occurred: {e}", fg="red"))
837# Switch back to the master branch
838subprocess.check_call(["git", "checkout", branch_to_use])
839
840
841@arena.command()
842@click.argument("agent_name")
843@click.argument("hash")
844@click.option("--branch", default=None, help="Branch to use instead of current branch")
845def update(agent_name, hash, branch):
846import json
847import os
848import subprocess
849from datetime import datetime
850
851# Check if the agent_name.json file exists in the arena directory
852agent_json_file = f"./arena/{agent_name}.json"
853# Check if they are on the correct branch
854current_branch = (
855subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
856.decode("utf-8")
857.strip()
858)
859correct_branch = f"arena_submission_{agent_name}"
860if current_branch != correct_branch:
861click.echo(
862click.style(
863f"❌ You are not on the correct branch. Please switch to the '{correct_branch}' branch.",
864fg="red",
865)
866)
867return
868
869if not os.path.exists(agent_json_file):
870click.echo(
871click.style(
872f"❌ The file for agent '{agent_name}' does not exist in the arena directory.",
873fg="red",
874)
875)
876click.echo(
877click.style(
878f"⚠️ You need to enter the arena first. Run './run arena enter {agent_name}'",
879fg="yellow",
880)
881)
882return
883else:
884# Load the existing data
885with open(agent_json_file, "r") as json_file:
886data = json.load(json_file)
887
888# Update the commit hash and timestamp
889data["commit_hash_to_benchmark"] = hash
890data["timestamp"] = datetime.utcnow().isoformat()
891
892# If --branch was passed, update the branch_to_benchmark in the JSON file
893if branch:
894data["branch_to_benchmark"] = branch
895
896# Write the updated data back to the JSON file
897with open(agent_json_file, "w") as json_file:
898json.dump(data, json_file, indent=4)
899
900click.echo(
901click.style(
902f"🚀 The file for agent '{agent_name}' has been updated in the arena directory.",
903fg="green",
904)
905)
906
907
908def wait_until_conn_ready(port: int = 8000, timeout: int = 30):
909"""
910Polls localhost:{port} until it is available for connections
911
912Params:
913port: The port for which to wait until it opens
914timeout: Timeout in seconds; maximum amount of time to wait
915
916Raises:
917TimeoutError: If the timeout (seconds) expires before the port opens
918"""
919import socket
920import time
921
922start = time.time()
923while True:
924time.sleep(0.5)
925with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
926if s.connect_ex(("localhost", port)) == 0:
927break
928if time.time() > start + timeout:
929raise TimeoutError(f"Port {port} did not open within {timeout} seconds")
930
931
932if __name__ == "__main__":
933cli()
934