Skip to content

Instantly share code, notes, and snippets.

@whacked
Created July 14, 2024 17:17
Show Gist options
  • Save whacked/81b04159378c125998911ee1b6f65227 to your computer and use it in GitHub Desktop.
Save whacked/81b04159378c125998911ee1b6f65227 to your computer and use it in GitHub Desktop.
temp prompt2code
# 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