Created
July 14, 2024 17:17
-
-
Save whacked/81b04159378c125998911ee1b6f65227 to your computer and use it in GitHub Desktop.
temp prompt2code
This file contains hidden or 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
# core | |
> Fill in a module description here | |
#| default_exp core | |
#| hide | |
from nbdev.showdoc import * | |
import diskcache as dc | |
import getpass | |
import litellm | |
import json | |
import os | |
import hashlib | |
import os.path as _p | |
import datetime | |
from litellm import completion | |
from dataclasses import dataclass | |
from typing import List, Dict, Optional, Tuple, Union, Any | |
import yaml | |
from schematized_config.core import ConfigValidator | |
from fs.osfs import OSFS | |
litellm.set_verbose=False | |
WORKING_DIRECTORY = _p.join(os.environ['CLOUDSYNC'], 'main/devsync', 'prompt2code') | |
MODULE_GEN_DIRECTORY = "gen_modules" | |
@dataclass | |
class TrackedPrompt: | |
text: str | |
sha256: str | |
def get_sha256(s: str) -> str: | |
return str(hashlib.sha256(s.encode('utf-8')).hexdigest()) | |
@dataclass | |
class TrackedCodegen: | |
time_added: Optional[datetime] | |
tracked_prompt: TrackedPrompt | |
llm_model: str | |
code: str | |
if 0: | |
response2 = completion( | |
model="claude-3-opus-20240229", messages=[{ "content": "Hello, how are you?","role": "user"}], | |
) | |
schema = json.load(open("../schemas/dotenv.schema.json")) | |
dotenv = ConfigValidator.load_dotenv( | |
json_schema=schema, dotenv_path=".env", | |
storage_driver=OSFS(WORKING_DIRECTORY), | |
override=True) | |
cache = dc.Cache(WORKING_DIRECTORY, size_limit=2**30) | |
PREAMBLE = '''\ | |
Respond with raw python code without any markdown code fence. | |
Any example code usage should go into the docstring of functions. | |
Do not add any example code usage to the bottom of the response. | |
''' | |
messages = [ | |
{"role": "user", | |
"content": PREAMBLE + """\ | |
write a function that takes in the vertices of a polygon, | |
and returns this datastructure: | |
@dataclass | |
class EdgeShiftedPolygon: | |
vertices: List[Tuple[float, float]] | |
edge_indices: Tuple[int, int] # indices of 2 neighboring vertices that form an edge; can be [-1, 0] | |
shift_magnitude: float # >0 means expand, <0 means shrink, in the direction of the normal, where >0 faces outwards of the polygon's enclosed region | |
the input polygon is defined by its vertices <List[Tuple[float, float]]>, | |
where the edges are either horizontal or vertical, and all angles are 90 degrees. | |
the function header is | |
def transform_polygon( | |
vertices: List[Tuple[float, float]], | |
edge_indices: Optional[Tuple[int, int]]=None, | |
shift_magnitude: Optional[float]=None) -> EdgeShiftedPolygon: | |
# when edge_indices is None, pick a random edge | |
# when shift_magnitude is None, pick a random size | |
""", | |
}, | |
] | |
response = completion( | |
model="gpt-4o", | |
messages=messages, | |
) | |
class Tracker: | |
PROMPT_OPEN = "# <PROMPT TEXT>" | |
PROMPT_CLOSE = "# </PROMPT TEXT>" | |
history: List[TrackedCodegen] = [] | |
@classmethod | |
def m2tp(cls, messsages: List[Dict[str, str]]) -> TrackedPrompt: | |
serialized_message = json.dumps(messages) | |
tp = TrackedPrompt(text=serialized_message, sha256=get_sha256(serialized_message)) | |
return tp | |
@classmethod | |
def add(cls, messages: List[Dict[str, str]], llm_model: str="gpt-4o"): | |
tp = cls.m2tp(messages) | |
response = completion( | |
model=llm_model, | |
messages=messages, | |
) | |
code = response.choices[0].message.content | |
cg = TrackedCodegen( | |
time_added=datetime.datetime.now(datetime.UTC), | |
llm_model=llm_model, | |
tracked_prompt=tp, | |
code=code, | |
) | |
cls.history.append(cg) | |
codegens = cache.get(tp.sha256, []) | |
codegens.append(cg) | |
cache.set(tp.sha256, codegens) | |
@classmethod | |
def load(cls, modules_directory: str): | |
# too much metadata. | |
# probably need to yamlize | |
return | |
for f in os.listdir(modules_directory): | |
if not f.endswith(".py"): | |
continue | |
with open(_p.join(MODULE_GEN_DIRECTORY, f)) as ifile: | |
prompt_lines = [] | |
code_lines = [] | |
in_prompt = False | |
for line in ifile.readlines(): | |
if line.startswith(cls.PROMPT_OPEN): | |
in_prompt = True | |
continue | |
elif line.startswith(cls.PROMPT_CLOSE): | |
in_prompt = False | |
continue | |
if in_prompt: | |
prompt_lines.append(line) | |
elif prompt_lines: | |
code_lines.append(line) | |
prompt = ''.join(prompt_lines) | |
code = ''.join(code_lines) | |
Tracker.history.append( | |
TrackedCodegen( | |
llm_model='gpt-4o', | |
tracked_prompt=Tracker.m2tp(messages), | |
code=response.choices[0].message.content, | |
), | |
) | |
Tracker.add([ | |
{"role": "user", | |
"content": PREAMBLE + """\ | |
write a function that takes a string and saves it to a python module. | |
the function header is | |
def save_module( | |
python_code: str, | |
module_name: str, | |
module_directory: str) -> module: | |
# save python_code to <module_directory>/<module_name>.py | |
# dynamically import and reload the saved file's module and return it | |
# so it is assignable as a run time variable | |
""", | |
}, | |
]) | |
print(Tracker.history[-1].code) | |
import os | |
import importlib.util | |
import sys | |
def save_module( | |
prompt_text: str, | |
python_code: str, | |
module_name: str, | |
module_directory: str): | |
""" | |
Saves the given python code to a file in the specified directory with the given module name. | |
Dynamically imports and returns the module. | |
Args: | |
python_code (str): The Python code to save. | |
module_name (str): The name of the module to create. | |
module_directory (str): The directory to save the module in. | |
Returns: | |
module: The dynamically loaded mbodule. | |
""" | |
# Ensure the directory exists | |
os.makedirs(module_directory, exist_ok=True) | |
# Path to the new module | |
module_path = os.path.join(module_directory, f"{module_name}.py") | |
# Write the Python code to the file | |
with open(module_path, 'w') as file: | |
file.write('\n'.join([ | |
f"""\ | |
{Tracker.PROMPT_OPEN} | |
__doc__ = '''\ | |
{prompt_text} | |
''' | |
{Tracker.PROMPT_CLOSE} | |
""", | |
python_code, | |
])) | |
# Load the module dynamically | |
spec = importlib.util.spec_from_file_location(module_name, module_path) | |
module = importlib.util.module_from_spec(spec) | |
sys.modules[module_name] = module | |
spec.loader.exec_module(module) | |
return module | |
module_saver = save_module( | |
Tracker.history[-1].code, | |
"module_saver", | |
MODULE_GEN_DIRECTORY, | |
) | |
edge_transform = module_saver.save_module( | |
Tracker.history[0].code, | |
"edge_transform", | |
MODULE_GEN_DIRECTORY, | |
) | |
edge_transform.transform_polygon | |
Tracker.add([ | |
{"role": "user", | |
"content": PREAMBLE + """\ | |
write a function that generates a random polygon. | |
the polygon vertices are not just randomly placed. | |
its vertices can only form horizontal and vertical edges, | |
so you must iteratively create vertices; the next vertex | |
in the iteration must only be horizontally or vertically | |
separated from the previous vertex. | |
the final point implicitly joins back ot the first point. | |
you must also make sure the polygon does not cross itself; | |
it must be a convex polygon. | |
the function header is | |
def generate_polygon(...) -> List[Tuple[float, float]]: | |
# ... should be arguments that allow the caller to specify | |
# parameters controlling the complexity of the polygon. | |
# parameters should be typed, and have default values | |
# such that the function can be called with no arguments. | |
then, write a function that draws the polygon. it should use the shapely | |
library and output a viz to be shown in an ipython notebook | |
def show_polygon(polygon: List[Tuple[float, float]]) | |
""", | |
}, | |
]) | |
generate_polygon = module_saver.save_module( | |
Tracker.history[-1].code, | |
"generate_polygon", | |
MODULE_GEN_DIRECTORY, | |
) | |
print(Tracker.history[-1].code) | |
import random | |
from typing import List, Tuple | |
def cut_rectangle(vertices: List[Tuple[float, float]]) -> List[Tuple[float, float]]: | |
# Pick a random edge to cut | |
edge_index = random.randint(0, len(vertices) - 2) | |
(x1, y1) = vertices[edge_index] | |
(x2, y2) = vertices[edge_index + 1] | |
if x1 == x2: # Vertical edge | |
cut_y = random.uniform(min(y1, y2), max(y1, y2)) | |
if y1 < y2: | |
new_vertex1 = (x1, cut_y) | |
new_vertex2 = (x2, cut_y) | |
else: | |
new_vertex1 = (x1, cut_y) | |
new_vertex2 = (x2, cut_y) | |
else: # Horizontal edge | |
cut_x = random.uniform(min(x1, x2), max(x1, x2)) | |
if x1 < x2: | |
new_vertex1 = (cut_x, y1) | |
new_vertex2 = (cut_x, y2) | |
else: | |
new_vertex1 = (cut_x, y1) | |
new_vertex2 = (cut_x, y2) | |
# Insert the new vertices into the list of vertices | |
new_vertices = vertices[:edge_index + 1] + [new_vertex1, new_vertex2] + vertices[edge_index + 1:] | |
# Ensure the new vertices do not cause intersections by adjusting the vertices list | |
if new_vertex1 in new_vertices[new_vertices.index(new_vertex2)+1:]: | |
new_vertices.remove(new_vertex1) | |
if new_vertex2 in new_vertices[new_vertices.index(new_vertex1)+1:]: | |
new_vertices.remove(new_vertex2) | |
return new_vertices | |
def generate_polygon(num_vertices: int = 10, max_x: float = 10.0, max_y: float = 10.0) -> List[Tuple[float, float]]: | |
""" | |
Generates a random polygon with specified number of vertices. | |
The polygon vertices can only form horizontal and vertical edges, | |
and it ensures the polygon does not cross itself by iteratively cutting rectangles. | |
:param num_vertices: Number of vertices of the polygon | |
:param max_x: Maximum x-coordinate value for vertices | |
:param max_y: Maximum y-coordinate value for vertices | |
:return: List of vertices forming the polygon | |
Example: | |
vertices = generate_polygon(num_vertices=10, max_x=20, max_y=20) | |
""" | |
assert num_vertices >= 4, "A polygon must have at least 4 vertices to ensure horizontal and vertical edges" | |
assert num_vertices % 2 == 0, "Number of vertices must be even to form horizontal and vertical edges" | |
# Start with a large rectangle | |
vertices = [(0, 0), (0, max_y), (max_x, max_y), (max_x, 0), (0, 0)] | |
# Perform cuts | |
while len(vertices) - 1 < num_vertices: | |
vertices = cut_rectangle(vertices) | |
return vertices[:-1] # Remove the duplicate closing vertex for simplicity | |
# Example usage | |
vertices = generate_polygon(num_vertices=10, max_x=20, max_y=20) | |
print(vertices) | |
show_polygon(generate_polygon()) | |
#| export | |
#| hide | |
import nbdev; nbdev.nbdev_export() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment