-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ffbb0b3
commit 8aba1c4
Showing
6 changed files
with
622 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.env | ||
.venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
You are a developer for the Plain web framework. | ||
The name “Plain” reflects our philosophy: straightforward and clear. | ||
When you write code or documentation, you should keep this in mind. | ||
|
||
Key Guidelines: | ||
|
||
1. Simplicity and Clarity: | ||
- Keep explanations concise. Don’t over-explain. | ||
- Be friendly and approachable in your tone. | ||
2. Documentation Approach: | ||
- Use “source docs,” meaning README.md files next to the code they document. | ||
- Cover high-level topics and basic implementations. Mention advanced use cases briefly and direct users to the source code for details. | ||
- Focus on what users need to know to get started and understand the framework’s core concepts. | ||
- Try to verify the correctness of your documentation after writing it. | ||
|
||
Your Task: | ||
|
||
Finding a TODO to Document: | ||
|
||
- Use available tools to navigate the repo and find TODO comments in README.md files. | ||
- Randomly choose one of the TODOs -- don't just choose the first one you find. | ||
- Always refer to the source code to ensure accurate documentation. | ||
- Ensure you have all the information needed to complete the TODO without needing further clarification. | ||
|
||
Making the Improvement: | ||
|
||
- Use a similar voice and style to existing documentation. | ||
- Only work on one TODO, even if multiple TODOs are present in the same file. | ||
- Don't write more than one section of documentation at a time. | ||
- Review your work for clarity and simplicity. Revise if needed. | ||
- Output the changes as a git diff text to the console for review. The PR should be straightforward for reviewers to approve or reject. | ||
|
||
By following these guidelines, you’ll help create clear and accessible documentation for Plain. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
import json | ||
import os | ||
import subprocess | ||
import sys | ||
from pathlib import Path | ||
|
||
from dotenv import load_dotenv | ||
from openai import AssistantEventHandler, OpenAI | ||
from typing_extensions import override | ||
|
||
|
||
def bold(text: str) -> str: | ||
return f"\033[1m{text}\033[0m" | ||
|
||
|
||
class Ottobot: | ||
def __init__(self, live_mode: bool = False): | ||
self.live_mode = live_mode | ||
|
||
def _run(self, cmd: list) -> str: | ||
return subprocess.check_output(cmd).decode("utf-8") | ||
|
||
def ls(self, path: str) -> str: | ||
return self._run(["ls", path]) | ||
|
||
def tree(self, path: str) -> str: | ||
return self._run(["tree", path, "--gitignore", "-i", "-f"]) | ||
|
||
def cat(self, file: str) -> str: | ||
return self._run(["cat", file]) | ||
|
||
def grep(self, pattern: str, path: str, recursive: bool) -> str: | ||
if recursive: | ||
result = subprocess.run( | ||
["git", "grep", "-r", pattern, path], capture_output=True | ||
) | ||
else: | ||
result = subprocess.run(["git", "grep", pattern, path], capture_output=True) | ||
if result.returncode == 0: | ||
return result.stdout.decode("utf-8") | ||
else: | ||
return result.stderr.decode("utf-8") | ||
|
||
|
||
class EventHandler(AssistantEventHandler): | ||
def __init__(self, otto): | ||
self.otto = otto | ||
super().__init__() | ||
|
||
@override | ||
def on_event(self, event): | ||
# Retrieve events that are denoted with 'requires_action' | ||
# since these will have our tool_calls | ||
if event.event == "thread.run.requires_action": | ||
run_id = event.data.id # Retrieve the run ID from the event data | ||
self.handle_requires_action(event.data, run_id) | ||
|
||
def handle_requires_action(self, data, run_id): | ||
tool_outputs = [] | ||
|
||
for tool in data.required_action.submit_tool_outputs.tool_calls: | ||
otto_func = getattr(self.otto, tool.function.name) | ||
kwargs = json.loads(tool.function.arguments) | ||
print(bold(f"Running tool: {tool.function.name} with arguments: {kwargs}")) | ||
tool_outputs.append( | ||
{"tool_call_id": tool.id, "output": otto_func(**kwargs)} | ||
) | ||
|
||
# Submit all tool_outputs at the same time | ||
self.submit_tool_outputs(tool_outputs, run_id) | ||
|
||
def submit_tool_outputs(self, tool_outputs, run_id): | ||
# Use the submit_tool_outputs_stream helper | ||
with client.beta.threads.runs.submit_tool_outputs_stream( | ||
thread_id=self.current_run.thread_id, | ||
run_id=self.current_run.id, | ||
tool_outputs=tool_outputs, | ||
event_handler=EventHandler(self.otto), | ||
) as stream: | ||
for text in stream.text_deltas: | ||
print(text, end="", flush=True) | ||
print() | ||
|
||
|
||
if __name__ == "__main__": | ||
load_dotenv() | ||
otto = Ottobot(live_mode="--live" in sys.argv) | ||
client = OpenAI() | ||
|
||
instructions = Path("instructions.txt").read_text() | ||
|
||
repo_root = ( | ||
subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) | ||
.strip() | ||
.decode("utf-8") | ||
) | ||
print(bold(f"Starting Otto in {repo_root}")) | ||
|
||
os.chdir(repo_root) # Move to the repo root so everything runs from there | ||
|
||
assistant = client.beta.assistants.create( | ||
instructions=instructions, | ||
model="gpt-4o", # try mini? | ||
tools=[ | ||
{ | ||
"type": "function", | ||
"function": { | ||
"name": "ls", | ||
"description": "List files in a directory", | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"path": { | ||
"type": "string", | ||
"description": "The path to the directory to list", | ||
} | ||
}, | ||
"required": ["path"], | ||
}, | ||
}, | ||
}, | ||
{ | ||
"type": "function", | ||
"function": { | ||
"name": "tree", | ||
"description": "List files in a directory in a tree format", | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"path": { | ||
"type": "string", | ||
"description": "The path to the directory to list", | ||
} | ||
}, | ||
"required": ["path"], | ||
}, | ||
}, | ||
}, | ||
{ | ||
"type": "function", | ||
"function": { | ||
"name": "cat", | ||
"description": "Print the contents of a file", | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"file": { | ||
"type": "string", | ||
"description": "The path to the file to print", | ||
} | ||
}, | ||
"required": ["file"], | ||
}, | ||
}, | ||
}, | ||
{ | ||
"type": "function", | ||
"function": { | ||
"name": "grep", | ||
"description": "Search for a pattern in a file", | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"pattern": { | ||
"type": "string", | ||
"description": "The pattern to search for", | ||
}, | ||
"path": { | ||
"type": "string", | ||
"description": "The path to search in", | ||
}, | ||
"recursive": { | ||
"type": "boolean", | ||
"description": "Whether to search recursively", | ||
}, | ||
}, | ||
"required": ["pattern", "path", "recursive"], | ||
}, | ||
}, | ||
}, | ||
# { | ||
# "type": "function", | ||
# "function": { | ||
# "name": "find", | ||
# "description": "Search for a file in a directory", | ||
# "parameters": { | ||
# "type": "object", | ||
# "properties": { | ||
# "name": { | ||
# "type": "string", | ||
# "description": "The name of the file to search for" | ||
# }, | ||
# "path": { | ||
# "type": "string", | ||
# "description": "The path to the directory to search in" | ||
# } | ||
# }, | ||
# "required": ["name", "path"] | ||
# } | ||
# } | ||
# }, | ||
# { | ||
# "type": "function", | ||
# "function": { | ||
# "name": "patch", | ||
# "description": "Generate a git patch for a file", | ||
# "parameters": { | ||
# "type": "object", | ||
# "properties": { | ||
# "file": { | ||
# "type": "string", | ||
# " | ||
], | ||
) | ||
|
||
# Give it the initial information to work from, so it doesn't have to ask for it | ||
files = otto.tree(".") | ||
todos = otto.grep("TODO", ".", recursive=True) | ||
|
||
thread = client.beta.threads.create() | ||
message = client.beta.threads.messages.create( | ||
thread_id=thread.id, | ||
role="user", | ||
content=f"Current files in the repo (from `tree`):\n{files}\n\nCurrent TODOs in the repo (from `grep`):\n{todos}", | ||
) | ||
|
||
with client.beta.threads.runs.stream( | ||
thread_id=thread.id, assistant_id=assistant.id, event_handler=EventHandler(otto) | ||
) as stream: | ||
stream.until_done() |
Oops, something went wrong.